diff --git a/.gitignore b/.gitignore
index 82df0fd9047bc71d1ff6237296ed61ccd45c6c7f..a96e31806f554d02ddad06f05c5be49f722d5804 100644
--- a/.gitignore
+++ b/.gitignore
@@ -25,6 +25,7 @@ GTAGS
 *.log
 *.trs
 *.test
+test/pytest/*.py
 #=================================
 .rake.json
 #==== Autogen files ==============
@@ -126,3 +127,5 @@ doc/html/
 #Vim/clangd/treesitter files
 compile_commands.json
 .cache
+#cmake 
+*.cmake
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 9745dbb865c40ab2fb7f532f6e3aba19e0d3aa04..07fdad595f56b008f69211875ad3ea1ea7cd2da0 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -23,7 +23,7 @@ build-gcc112:
     when: always
     expire_in: 3 days
   tags:
-    - xen, levante
+    - levante-fake, hpc, dkrz
   only:
     - develop
     - merge_requests
@@ -44,7 +44,7 @@ check-gcc112:
     when: always
     expire_in: 3 days
   tags:
-    - xen, levante
+    - levante-fake, hpc, dkrz
   only:
     - develop
     - merge_requests
@@ -57,7 +57,7 @@ build-intel22:
     - autoreconf -vfi --no-recursive
   script:
     - module load gcc/11.2.0-gcc-11.2.0 intel-oneapi-compilers/2022.0.1-gcc-11.2.0
-    - mkdir intel22 && cd intel22 && ../configure --prefix=/home/k/k202125/local --with-eccodes=/sw/spack-levante/eccodes-2.21.0-4ywkk4 --with-netcdf=/sw/spack-levante/netcdf-c-4.8.1-qk24yp --with-hdf5=/sw/spack-levante/hdf5-1.12.1-akf2kp --with-udunits2=/sw/spack-levante/udunits-2.2.28-da6pla --with-fftw3 --with-szlib=/sw/spack-levante/libaec-1.0.5-r5sdw5 --with-curl --with-ossp-uuid --with-libxml2 --with-proj=/sw/spack-levante/proj-8.1.0-i6a6ah --with-magics=/sw/spack-levante/magics-4.9.3-z64bdu LIBS='-lm -lstdc++' LDFLAGS='-L/sw/spack-levante/eccodes-2.21.0-4ywkk4/lib64 -Wl,-rpath,/sw/spack-levante/eccodes-2.21.0-4ywkk4/lib64 -L/sw/spack-levante/magics-4.9.3-z64bdu/lib64 -Wl,-rpath,/sw/spack-levante/magics-4.9.3-z64bdu/lib64 -L/sw/spack-levante/libaec-1.0.5-r5sdw5/lib64 -Wl,-rpath,/sw/spack-levante/libaec-1.0.5-r5sdw5/lib64 -Wl,-rpath,/sw/spack-levante/netcdf-c-4.8.1-qk24yp/lib -Wl,-rpath,/sw/spack-levante/hdf5-1.12.1-akf2kp/lib -Wl,-rpath,/sw/spack-levante/udunits-2.2.28-da6pla/lib -Wl,-rpath,/sw/spack-levante/proj-8.1.0-i6a6ah/lib -L/sw/spack-levante/fftw-3.3.10-fnfhvr/lib -Wl,-rpath,/sw/spack-levante/fftw-3.3.10-fnfhvr/lib -L/sw/spack-levante/gcc-11.2.0-bcn7mb/lib64 -Wl,-rpath,/sw/spack-levante/gcc-11.2.0-bcn7mb/lib64 -Wl,-rpath,/sw/spack-levante/intel-oneapi-compilers-2022.0.1-an2cbq/compiler/2022.0.1/linux/compiler/lib/intel64' F77=ifx FFLAGS="-g -O2" CXX=icpx CXXFLAGS="-g -Wall -O2 -I/sw/spack-levante/fftw-3.3.10-fnfhvr/include" CC=icx   CFLAGS="-g -Wall -O2"
+    - mkdir intel22 && cd intel22 && ../configure --prefix=/home/k/k202125/local --with-eccodes=/sw/spack-levante/eccodes-2.21.0-4ywkk4 --with-netcdf=/sw/spack-levante/netcdf-c-4.8.1-qk24yp --with-hdf5=/sw/spack-levante/hdf5-1.12.1-akf2kp --with-udunits2=/sw/spack-levante/udunits-2.2.28-da6pla --with-fftw3 --with-szlib=/sw/spack-levante/libaec-1.0.5-r5sdw5 --with-curl --with-ossp-uuid --with-libxml2 --with-proj=/sw/spack-levante/proj-8.1.0-i6a6ah --with-magics=/sw/spack-levante/magics-4.9.3-z64bdu LIBS='-lm -lstdc++' LDFLAGS='-L/sw/spack-levante/eccodes-2.21.0-4ywkk4/lib64 -Wl,-rpath,/sw/spack-levante/eccodes-2.21.0-4ywkk4/lib64 -L/sw/spack-levante/magics-4.9.3-z64bdu/lib64 -Wl,-rpath,/sw/spack-levante/magics-4.9.3-z64bdu/lib64 -L/sw/spack-levante/libaec-1.0.5-r5sdw5/lib64 -Wl,-rpath,/sw/spack-levante/libaec-1.0.5-r5sdw5/lib64 -Wl,-rpath,/sw/spack-levante/netcdf-c-4.8.1-qk24yp/lib -Wl,-rpath,/sw/spack-levante/hdf5-1.12.1-akf2kp/lib -Wl,-rpath,/sw/spack-levante/udunits-2.2.28-da6pla/lib -Wl,-rpath,/sw/spack-levante/proj-8.1.0-i6a6ah/lib -L/sw/spack-levante/fftw-3.3.10-fnfhvr/lib -Wl,-rpath,/sw/spack-levante/fftw-3.3.10-fnfhvr/lib -L/sw/spack-levante/gcc-11.2.0-bcn7mb/lib64 -Wl,-rpath,/sw/spack-levante/gcc-11.2.0-bcn7mb/lib64 -Wl,-rpath,/sw/spack-levante/intel-oneapi-compilers-2022.0.1-an2cbq/compiler/2022.0.1/linux/compiler/lib/intel64' F77=ifx FFLAGS="-g -O2" CXX=icpx CXXFLAGS="-g -Wall -O2 -fhonor-nans -I/sw/spack-levante/fftw-3.3.10-fnfhvr/include" CC=icx   CFLAGS="-g -Wall -O2 -fhonor-nans"
     - make -j 12
   artifacts:
     paths:
@@ -67,7 +67,7 @@ build-intel22:
     when: always
     expire_in: 3 days
   tags:
-    - xen, levante
+    - levante-fake, hpc, dkrz
   only:
     - develop
     - merge_requests
@@ -88,7 +88,7 @@ check-intel22:
     when: always
     expire_in: 3 days
   tags:
-    - xen, levante
+    - levante-fake, hpc, dkrz
   only:
     - develop
     - merge_requests
diff --git a/CMakeLists.txt b/CMakeLists.txt
index b5710d59f73624034568008ef561b3dae62b90c5..52d251a9ecfbb436f372fea12210271a0c9c3dbd 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -2,9 +2,15 @@
 #
 #       -DCMAKE_INSTALL_PREFIX=/path/to/install
 
-cmake_minimum_required( VERSION 3.12 FATAL_ERROR )
+cmake_minimum_required( VERSION 3.24 FATAL_ERROR )
+
+set(CMAKE_POSITION_INDEPENDENT_CODE ON)
 
 project( cdo VERSION 2.4.0 LANGUAGES C CXX )
+
+include(CTest)
+include(CheckIncludeFileCXX)
+
 set(CMAKE_C_STANDARD 11)
 set(CMAKE_CXX_STANDARD 20)
 
@@ -12,22 +18,55 @@ configure_file (
   "${PROJECT_SOURCE_DIR}/cmake/cdo_config.h.in"
   "${PROJECT_BINARY_DIR}/config.h"
   )
+# Include the CheckIncludeFiles module
+include(CheckIncludeFiles)
 
-include_directories("${PROJECT_BINARY_DIR}")
 
+include_directories("${PROJECT_BINARY_DIR}")
 list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
 
-find_package(NetCDF)
+set(pthread_flag HAVE_LIBPTHREAD)
+check_include_files("pthread.h" ${pthread_flag})
+IF(${pthread_flag})
+  add_compile_definitions(${pthread_flag}=1)
+ELSE()
+  message( FATAL_ERROR "pthread.h is not found" )
+ENDIF()
+
+set(config_flag HAVE_CONFIG_H)
+set(${config_flag} 1)
+check_include_files("config.h" ${config_flag})
+IF(${config_flag})
+  add_compile_definitions(${config_flag}=1)
+ELSE()
+  message( FATAL_ERROR "config.h is not found" )
+ENDIF()
 
-set(HAVE_NETCDF ${netCDF_FOUND} )
+set(wordexp_flag HAVE_WORDEXP_H)
+check_include_files("wordexp.h" ${wordexp_flag})
+IF(${wordexp_flag})
+add_compile_definitions(${wordexp_flag}=1)
+ELSE()
+  message( STATUS "wordexp.h is not found" )
+ENDIF()
 
-if (netCDF_FOUND)
-  set(HAVE_NETCDF 1)
+
+find_package(netCDF REQUIRED)
+set(netcdf_flag HAVE_LIBNETCDF)
+if (${netCDF_FOUND})
+  add_compile_definitions(${netcdf_flag}=${netCDF_FOUND})
+else()
+  message(WARNING "netcdf not found, compiling without netcdf")
 endif ()
+message(STATUS ${pthread_flag} ": "  ${${pthread_flag}})
+message(STATUS ${wordexp_flag} ": "  ${${wordexp_flag}})
+message(STATUS ${config_flag} ": "  ${${config_flag}})
+message(STATUS ${netcdf_flag} ": "  ${netCDF_FOUND})
 
 add_subdirectory( libcdi/src )
 add_subdirectory( src/lib/yac )
 add_subdirectory( src/lib/gradsdes )
 add_subdirectory( src/lib/healpix )
+add_subdirectory( test )
 add_subdirectory( src )
-#target_link_libraries(cdolib PRIVATE NetCDF)
+
diff --git a/ChangeLog b/ChangeLog
index 246830ee911d12a1e1d0cd4a65a2b17e7f452b19..32dc98671fe02f4c07d3147c93f08f757f7b2695 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,64 @@
+2024-05-21  Uwe Schulzweida
+
+	* Using CDI library version 2.4.1
+	* Version 2.4.1 release
+
+2024-04-24  Uwe Schulzweida
+
+	* minc/maxc: fixed wrong handling of missing values
+	* eca_csu/eca_cfd: fixed stack memory error which occurs with clang option -Os [Bug #11790]
+
+2024-04-18  Uwe Schulzweida
+
+	* Info: added asyncron processing task
+
+2024-04-17  Uwe Schulzweida
+
+	* Diff: added asyncron processing task
+
+2024-04-15  Uwe Schulzweida
+
+	* inttime: Add memory support for 32-bit float data
+
+2024-04-14  Uwe Schulzweida
+
+        * New module: dminute<stat>  - Multi-day by the minute statistics
+
+2024-04-04  Uwe Schulzweida
+
+	* Detrend: refactor; improve performance
+
+2024-04-03  Uwe Schulzweida
+
+	* Timstat: Add async branch
+	* Trend: Add async branch
+	* Trend: Add memory support for 32-bit float data
+
+2024-03-18  Uwe Schulzweida
+
+	* showatttribute: change output format
+	* Removed operator showatts and showattsglob; use showattribute
+
+2024-03-07  Uwe Schulzweida
+
+	* New operator: setprojparam - Set proj_param attribute
+
+2024-03-01  Uwe Schulzweida
+
+	* Yearstat; add complete_only parameter
+
+2024-02-29  Uwe Schulzweida
+
+	* mergetime: add names  parameter (union|intersect)
+
+2024-02-28  Uwe Schulzweida
+
+	* mergetime: add skip_same_time  parameter
+
+2024-02-27  Uwe Schulzweida
+
+	* Yseasstat: failed with seasonal data  since release 2.2.0 (bug fix)
+
 2024-02-22  Uwe Schulzweida
 
 	* Using CDI library version 2.4.0
@@ -59,7 +120,7 @@
 
 2023-10-28  Uwe Schulzweida
 
-	* Remapweights: Use environment variable REMAP_MAP3D=1 to generate all mapfiles of the first 3D field with variing masks
+	* Remapweights: Use environment variable REMAP_MAP3D=1 to generate all mapfiles of the first 3D field with varying masks
 
 2023-10-27  Uwe Schulzweida
 
@@ -1393,9 +1454,9 @@
 
 2019-04-18  Uwe Schulzweida
 
-	* New operator timminidx: Time minimum indices
-	* New operator timmaxidx: Time maximum indices
-	* New operator seltimeidx: Select indices of time
+	* New operator timminidx: Index of time minimum
+	* New operator timmaxidx: Index of time maximum
+	* New operator seltimeidx: Select timestep by index
 
 2019-04-11  Uwe Schulzweida
 
diff --git a/NEWS b/NEWS
index d57123914b77e3f770a3b261abd77b3cf7f0482b..8d3227c32fd60aed8b51663b1920c7a03a291770 100644
--- a/NEWS
+++ b/NEWS
@@ -3,19 +3,37 @@ CDO NEWS
 
 Improvement
 
+Version 2.4.1 (21 May 2024):
+
+   New features:
+     * mergetime: added skip_same_time parameter
+     * mergetime: added names parameter (union|intersect)
+     * Yearstat; added complete_only parameter
+     * showatttribute: changed output format
+   New operators:
+     * timmaxidx: Index of time maximum
+     * timminidx: Index of time minimum
+     * seltimeidx: Select timestep by index
+     * setprojparam: Set proj_param attribute
+     * dminute<stat>: Multi-day by the minute statistics
+   Fixed bugs:
+     * Yseasstat: failed with seasonal data since release 2.2.0
+     * eca_csu/eca_cfd: fixed stack memory error which occurs with clang option -Os [Bug #11790]
+     * minc/maxc: fixed wrong handling of missing values
+
 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
+     * Remapweights: Use environment variable REMAP_MAP3D=1 to generate all mapfiles of the first 3D field with varying 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
+     * pressure - pressure on full-levels
+     * pressure_half - pressure on half-levels
+     * delta_pressure - pressure difference of half-levels
+     * 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
diff --git a/OPERATORS b/OPERATORS
index 129dee6f7ea4e41ada682e99fef6082039a9d662..09419c625fe0fb38bde07a9922b829771d497db6 100644
--- a/OPERATORS
+++ b/OPERATORS
@@ -113,6 +113,7 @@ Operator catalog:
    Selgridcell   delgridcell     Delete grid cells
    Samplegrid    samplegrid      Resample grid
    Selyearidx    selyearidx      Select year by index
+   Seltimeidx    seltimeidx      Select timestep by index
    Selsurface    bottomvalue     Extract bottom level
    Selsurface    topvalue        Extract top level
    Selsurface    isosurface      Extract isosurface
@@ -182,6 +183,7 @@ Operator catalog:
    Setgrid       setgridtype     Set grid type
    Setgrid       setgridarea     Set grid cell area
    Setgrid       setgridmask     Set grid mask
+   Setgrid       setprojparams   Set proj params
    Setzaxis      setzaxis        Set z-axis
    Setzaxis      genlevelbounds  Generate level bounds
    Invert        invertlat       Invert latitudes
@@ -414,6 +416,8 @@ Operator catalog:
    Runpctl       runpctl         Running percentiles
    Timstat       timmin          Time minimum
    Timstat       timmax          Time maximum
+   Timstat       timminidx       Index of time minimum
+   Timstat       timmaxidx       Index of time maximum
    Timstat       timrange        Time range
    Timstat       timsum          Time sum
    Timstat       timmean         Time mean
@@ -459,8 +463,8 @@ Operator catalog:
    Yearmonstat   yearmonmean     Yearly mean from monthly data
    Yearstat      yearmin         Yearly minimum
    Yearstat      yearmax         Yearly maximum
-   Yearstat      yearminidx      Yearly minimum indices
-   Yearstat      yearmaxidx      Yearly maximum indices
+   Yearstat      yearminidx      Index of yearly minimum
+   Yearstat      yearmaxidx      Index of yearly maximum
    Yearstat      yearrange       Yearly range
    Yearstat      yearsum         Yearly sum
    Yearstat      yearmean        Yearly mean
@@ -501,6 +505,16 @@ Operator catalog:
    Dhourstat     dhourstd1       Multi-day hourly standard deviation (n-1)
    Dhourstat     dhourvar        Multi-day hourly variance
    Dhourstat     dhourvar1       Multi-day hourly variance (n-1)
+   Dminutestat   dminutemin      Multi-day by the minute minimum
+   Dminutestat   dminutemax      Multi-day by the minute maximum
+   Dminutestat   dminuterange    Multi-day by the minute range
+   Dminutestat   dminutesum      Multi-day by the minute sum
+   Dminutestat   dminutemean     Multi-day by the minute mean
+   Dminutestat   dminuteavg      Multi-day by the minute average
+   Dminutestat   dminutestd      Multi-day by the minute standard deviation
+   Dminutestat   dminutestd1     Multi-day by the minute standard deviation (n-1)
+   Dminutestat   dminutevar      Multi-day by the minute variance
+   Dminutestat   dminutevar1     Multi-day by the minute variance (n-1)
    Ydaystat      ydaymin         Multi-year daily minimum
    Ydaystat      ydaymax         Multi-year daily maximum
    Ydaystat      ydayrange       Multi-year daily range
diff --git a/config/default b/config/default
index ed4724e917f1ca461ed85760af1ed0172ba174d2..85e757a05b1c1a8c79eecfcd2fdc7b03176a6094 100755
--- a/config/default
+++ b/config/default
@@ -132,7 +132,7 @@ case "${HOSTNAME}" in
                     LDFLAGS="$LDFLAGS" \
 	            CXX=clang++ CXXFLAGS="-g -Wall -Wextra -O3 -fsanitize=undefined -fno-omit-frame-pointer" \
                     CC=clang    CFLAGS="-g -Wall -Wextra -O3 -fsanitize=undefined -fno-omit-frame-pointer" --disable-openmp
-#                    --with-libxml2=/usr 
+#                    --with-libxml2=/usr
 #                    --with-magics=/Users/m214003/local/magics-2.14.9
         elif  test "$COMP" = gnu ; then
 	  ${CONFPATH}configure --prefix=$HOME/local \
@@ -358,6 +358,8 @@ case "${HOSTNAME}" in
 # levante
 #        NETCDFPATH=/sw/spack-levante/netcdf-c-main-bdxvs5
 #        HDF5PATH=/sw/spack-levante/hdf5-1.14.2-gxhi2f
+#        NETCDFPATH=/sw/spack-levante/netcdf-c-4.8.1-qk24yp
+#        HDF5PATH=/sw/spack-levante/hdf5-1.12.1-akf2kp
     levante*)
         NETCDFPATH=/sw/spack-levante/netcdf-c-4.8.1-qk24yp
         HDF5PATH=/sw/spack-levante/hdf5-1.12.1-akf2kp
@@ -382,6 +384,7 @@ case "${HOSTNAME}" in
         LDFLAGS="$LDFLAGS -L$ECCODESPATH/lib64 -Wl,-rpath,$ECCODESPATH/lib64"
         LDFLAGS="$LDFLAGS -L$SZPATH/lib64 -Wl,-rpath,$SZPATH/lib64"
         LDFLAGS="$LDFLAGS -Wl,-rpath,$NETCDFPATH/lib"
+        # LDFLAGS="$LDFLAGS -Wl,-rpath,/sw/spack-levante/hdf-4.2.16-2-h6elwi/lib"
         LDFLAGS="$LDFLAGS -Wl,-rpath,$HDF5PATH/lib"
         LDFLAGS="$LDFLAGS -Wl,-rpath,$UDUNITS2PATH/lib"
         # LDFLAGS="$LDFLAGS -Wl,-rpath,$PROJPATH/lib"
@@ -395,8 +398,8 @@ case "${HOSTNAME}" in
                     $CDOLIBS \
                     LDFLAGS="$LDFLAGS" \
                     F77=ifx FFLAGS="-g -O2" \
-	            CXX=icpx CXXFLAGS="-g -Wall -O2 -I${FFTW3PATH}/include" \
-	            CC=icx   CFLAGS="-g -Wall -O2"
+	            CXX=icpx CXXFLAGS="-g -Wall -O2 -fhonor-nans -I${FFTW3PATH}/include" \
+	            CC=icx   CFLAGS="-g -Wall -O2 -fhonor-nans"
         elif  test "$COMP" = pgi ; then
           ${CONFPATH}configure --disable-fortran --disable-openmp \
                     $CDOLIBS \
diff --git a/configure.ac b/configure.ac
index 10758c3928977333fe402cb0f5d42216399474fc..4f681a66c621c77872bf7dff71aab8c901e929ce 100644
--- a/configure.ac
+++ b/configure.ac
@@ -5,7 +5,7 @@
 #  libtool  2.4.2
 
 AC_PREREQ([2.69])
-AC_INIT([cdo],[2.4.0],[https://mpimet.mpg.de/cdo])
+AC_INIT([cdo],[2.4.1],[https://mpimet.mpg.de/cdo])
 
 AC_DEFINE_UNQUOTED(CDO, ["$PACKAGE_VERSION"], [CDO version])
 
diff --git a/doc/tex/Modules b/doc/tex/Modules
index 8cb6e0746403b927ee095e3035f2d44e797b4107..dffc4bc75da229c50ffb07e757f1c846d11c7452 100644
--- a/doc/tex/Modules
+++ b/doc/tex/Modules
@@ -31,6 +31,7 @@ Selregion     Selection
 Selgridcell   Selection
 Samplegrid    Selection
 Selyearidx    Selection
+Seltimeidx    Selection
 Selsurface    Selection
 Cond          Conditional selection
 Cond2         Conditional selection
@@ -101,6 +102,7 @@ Seasstat      Statistical values
 Seaspctl      Statistical values
 Yhourstat     Statistical values
 Dhourstat     Statistical values
+Dminutestat   Statistical values
 Ydaystat      Statistical values
 Ydaypctl      Statistical values
 Ymonstat      Statistical values
diff --git a/doc/tex/cdo.tex b/doc/tex/cdo.tex
index 763cdedbe86ecf72a1b00f3ac83ccb7574ae299a..665bee1438b89227d37a0b8a5cba6eb2a41181f5 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.4.0  \\ February 2024}
+\large \textbf{Climate Data Operator \\ Version 2.4.1  \\ May 2024}
 \end{flushright}
 
 \vfill
diff --git a/doc/tex/cdoprog.tex b/doc/tex/cdoprog.tex
index 23b7bf54dbc7e777ba51ce9c3af9be4d9202bcfa..4671580b93eeefb70deac60a00347e69a829408d 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.4.0 \\ February 2024}}
+{\small{Climate Data Operator \\ Version 2.4.1 \\ May 2024}}
 \end{flushright}
 
 \vspace*{0mm}
diff --git a/doc/tex/mod/Daystat b/doc/tex/mod/Daystat
index e6c2498abcb0d37a70b9c48c61e228ae13102b5f..e44b48e4727681deec5e1688412aa7bb116c2b48 100644
--- a/doc/tex/mod/Daystat
+++ b/doc/tex/mod/Daystat
@@ -1,7 +1,7 @@
 @BeginModule
 @NewPage
 @Name      = Daystat
-@Title     = Daily statistical values
+@Title     = Daily statistics
 @Section   = Statistical values
 @Class     = Statistic
 @Arguments = infile outfile
@@ -19,6 +19,7 @@ This can be change with the CDO option --timestat_date <first|middle|last>.
 
 @BeginOperator_daymin
 @Title     = Daily minimum
+@Parameter = [parameter]
 
 @BeginDescription
 @IfMan
@@ -38,6 +39,7 @@ o(t,x) = \mbox{\textbf{min}}\{i(t',x), t_1 < t' \leq t_n\}
 
 @BeginOperator_daymax
 @Title     = Daily maximum
+@Parameter = [parameter]
 
 @BeginDescription
 @IfMan
@@ -57,6 +59,7 @@ o(t,x) = \mbox{\textbf{max}}\{i(t',x), t_1 < t' \leq t_n\}
 
 @BeginOperator_dayrange
 @Title     = Daily range
+@Parameter = [parameter]
 
 @BeginDescription
 @IfMan
@@ -76,6 +79,7 @@ o(t,x) = \mbox{\textbf{range}}\{i(t',x), t_1 < t' \leq t_n\}
 
 @BeginOperator_daysum
 @Title     = Daily sum
+@Parameter = [parameter]
 
 @BeginDescription
 @IfMan
@@ -95,6 +99,7 @@ o(t,x) = \mbox{\textbf{sum}}\{i(t',x), t_1 < t' \leq t_n\}
 
 @BeginOperator_daymean
 @Title     = Daily mean
+@Parameter = [parameter]
 
 @BeginDescription
 @IfMan
@@ -114,6 +119,7 @@ o(t,x) = \mbox{\textbf{mean}}\{i(t',x), t_1 < t' \leq t_n\}
 
 @BeginOperator_dayavg
 @Title     = Daily average
+@Parameter = [parameter]
 
 @BeginDescription
 @IfMan
@@ -133,6 +139,7 @@ o(t,x) = \mbox{\textbf{avg}}\{i(t',x), t_1 < t' \leq t_n\}
 
 @BeginOperator_dayvar
 @Title     = Daily variance
+@Parameter = [parameter]
 
 @BeginDescription
 @IfMan
@@ -152,6 +159,7 @@ o(t,x) = \mbox{\textbf{var}}\{i(t',x), t_1 < t' \leq t_n\}
 
 @BeginOperator_dayvar1
 @Title     = Daily variance (n-1)
+@Parameter = [parameter]
 
 @BeginDescription
 @IfMan
@@ -171,6 +179,7 @@ o(t,x) = \mbox{\textbf{var1}}\{i(t',x), t_1 < t' \leq t_n\}
 
 @BeginOperator_daystd
 @Title     = Daily standard deviation
+@Parameter = [parameter]
 
 @BeginDescription
 @IfMan
@@ -190,6 +199,7 @@ o(t,x) = \mbox{\textbf{std}}\{i(t',x), t_1 < t' \leq t_n\}
 
 @BeginOperator_daystd1
 @Title     = Daily standard deviation (n-1)
+@Parameter = [parameter]
 
 @BeginDescription
 @IfMan
@@ -207,6 +217,12 @@ o(t,x) = \mbox{\textbf{std1}}\{i(t',x), t_1 < t' \leq t_n\}
 @EndOperator
 
 
+@BeginParameter
+@Item = complete_only
+BOOL Process only complete years
+@EndParameter
+
+
 @BeginExample
 To compute the daily mean of a time series use:
 @BeginVerbatim
diff --git a/doc/tex/mod/Dhourstat b/doc/tex/mod/Dhourstat
index f2196a1ddab94e3b063054158a50b376725d0eab..89d54ae8d677c2f46bc7f352a5b23b176d162284 100644
--- a/doc/tex/mod/Dhourstat
+++ b/doc/tex/mod/Dhourstat
@@ -1,7 +1,7 @@
 @BeginModule
 @NewPage
 @Name      = Dhourstat
-@Title     = Multi-day hourly statistical values
+@Title     = Multi-day hourly statistics
 @Section   = Statistical values
 @Class     = Statistic
 @Arguments = infile outfile
@@ -151,7 +151,7 @@ o(\mbox{24},x) = \mbox{\textbf{avg}}\{i(t,x), \mbox{day}(i(t)) = \mbox{24}\} \\
 @Title     = Multi-day hourly variance
 
 @BeginDescription
-Normalize by n. 
+Normalize by n.
 
 @IfMan
 o(01,x) = var{i(t,x), day(i(t)) = 01}
@@ -175,7 +175,7 @@ o(\mbox{24},x) = \mbox{\textbf{var}}\{i(t,x), \mbox{day}(i(t)) = \mbox{24}\} \\
 @Title     = Multi-day hourly variance (n-1)
 
 @BeginDescription
-Normalize by (n-1). 
+Normalize by (n-1).
 
 @IfMan
 o(01,x) = var1{i(t,x), day(i(t)) = 01}
@@ -199,7 +199,7 @@ o(\mbox{24},x) = \mbox{\textbf{var1}}\{i(t,x), \mbox{day}(i(t)) = \mbox{24}\} \\
 @Title     = Multi-day hourly standard deviation
 
 @BeginDescription
-Normalize by n. 
+Normalize by n.
 
 @IfMan
 o(01,x) = std{i(t,x), day(i(t)) = 01}
@@ -223,7 +223,7 @@ o(\mbox{24},x) = \mbox{\textbf{std}}\{i(t,x), \mbox{day}(i(t)) = \mbox{24}\} \\
 @Title     = Multi-day hourly standard deviation (n-1)
 
 @BeginDescription
-Normalize by (n-1). 
+Normalize by (n-1).
 
 @IfMan
 o(01,x) = std1{i(t,x), day(i(t)) = 01}
diff --git a/doc/tex/mod/Diff b/doc/tex/mod/Diff
index 5cbec50245d895f5eecb851903cbc5d9dfc52ed7..f8577203478b4a88abd4badbd996eb725b5e2192 100644
--- a/doc/tex/mod/Diff
+++ b/doc/tex/mod/Diff
@@ -18,7 +18,7 @@ Exit status is 0 if inputs are the same and 1 if they differ.
 
 @BeginOperator_diff
 @Title     = Compare two datasets listed by parameter id
-@Parameter = [options]
+@Parameter = [parameter]
 
 @BeginDescription
 Provides statistics on differences between two datasets.
@@ -47,7 +47,7 @@ For each pair of fields the operator prints one line with the following informat
 
 @BeginOperator_diffn
 @Title     = Compare two datasets listed by parameter name
-@Parameter = [options]
+@Parameter = [parameter]
 
 @BeginDescription
 The same as operator @oper{diff}. Using the name instead of the
@@ -65,7 +65,6 @@ FLOAT   Limit of the maximum absolute difference (default: 0)
 FLOAT   Limit of the maximum relative difference (default: 1)
 @Item = names
 STRING  Consideration of the variable names of only one input file (left/right) or the intersection of both (intersect).
-
 @EndParameter
 
 
diff --git a/doc/tex/mod/Dminutestat b/doc/tex/mod/Dminutestat
new file mode 100644
index 0000000000000000000000000000000000000000..8f2543e06485fb9f34422f6fbd4fb7ae2d1d8b73
--- /dev/null
+++ b/doc/tex/mod/Dminutestat
@@ -0,0 +1,243 @@
+@BeginModule
+@NewPage
+@Name      = Dminutestat
+@Title     = Multi-day by the minute statistics
+@Section   = Statistical values
+@Class     = Statistic
+@Arguments = infile outfile
+@Operators = dminutemin dminutemax dminuterange dminutesum dminutemean dminuteavg dminutestd dminutestd1 dminutevar dminutevar1
+
+@BeginDescription
+This module computes statistical values of each minute of day.
+Depending on the chosen operator the minimum, maximum, range, sum, average, variance
+or standard deviation of each minute of day in @file{infile} is written to @file{outfile}.
+The date information in an output field is the date of the last contributing input field.
+@EndModule
+
+
+@BeginOperator_dminutemin
+@Title     = Multi-day by the minute minimum
+
+@BeginDescription
+@IfMan
+o(01,x) = min{i(t,x), day(i(t)) = 01}
+                 ...
+o(1440,x) = min{i(t,x), day(i(t)) = 1440}
+@EndifMan
+@IfDoc
+@BeginMath
+\begin{array}{c}
+o(\mbox{01},x) = \mbox{\textbf{min}}\{i(t,x), \mbox{day}(i(t)) = \mbox{01}\} \\
+\vdots \\
+o(\mbox{1440},x) = \mbox{\textbf{min}}\{i(t,x), \mbox{day}(i(t)) = \mbox{1440}\} \\
+\end{array}
+@EndMath
+@EndifDoc
+@EndDescription
+@EndOperator
+
+
+@BeginOperator_dminutemax
+@Title     = Multi-day by the minute maximum
+
+@BeginDescription
+@IfMan
+o(01,x) = max{i(t,x), day(i(t)) = 01}
+                 ...
+o(1440,x) = max{i(t,x), day(i(t)) = 1440}
+@EndifMan
+@IfDoc
+@BeginMath
+\begin{array}{c}
+o(\mbox{01},x) = \mbox{\textbf{max}}\{i(t,x), \mbox{day}(i(t)) = \mbox{01}\} \\
+\vdots \\
+o(\mbox{1440},x) = \mbox{\textbf{max}}\{i(t,x), \mbox{day}(i(t)) = \mbox{1440}\} \\
+\end{array}
+@EndMath
+@EndifDoc
+@EndDescription
+@EndOperator
+
+
+@BeginOperator_dminuterange
+@Title     = Multi-day by the minute range
+
+@BeginDescription
+@IfMan
+o(01,x) = range{i(t,x), day(i(t)) = 01}
+                 ...
+o(1440,x) = range{i(t,x), day(i(t)) = 1440}
+@EndifMan
+@IfDoc
+@BeginMath
+\begin{array}{c}
+o(\mbox{01},x) = \mbox{\textbf{range}}\{i(t,x), \mbox{day}(i(t)) = \mbox{01}\} \\
+\vdots \\
+o(\mbox{1440},x) = \mbox{\textbf{range}}\{i(t,x), \mbox{day}(i(t)) = \mbox{1440}\} \\
+\end{array}
+@EndMath
+@EndifDoc
+@EndDescription
+@EndOperator
+
+
+@BeginOperator_dminutesum
+@Title     = Multi-day by the minute sum
+
+@BeginDescription
+@IfMan
+o(01,x) = sum{i(t,x), day(i(t)) = 01}
+                 ...
+o(1440,x) = sum{i(t,x), day(i(t)) = 1440}
+@EndifMan
+@IfDoc
+@BeginMath
+\begin{array}{c}
+o(\mbox{01},x) = \mbox{\textbf{sum}}\{i(t,x), \mbox{day}(i(t)) = \mbox{01}\} \\
+\vdots \\
+o(\mbox{1440},x) = \mbox{\textbf{sum}}\{i(t,x), \mbox{day}(i(t)) = \mbox{1440}\} \\
+\end{array}
+@EndMath
+@EndifDoc
+@EndDescription
+@EndOperator
+
+
+@BeginOperator_dminutemean
+@Title     = Multi-day by the minute mean
+
+@BeginDescription
+@IfMan
+o(01,x) = mean{i(t,x), day(i(t)) = 01}
+                 ...
+o(1440,x) = mean{i(t,x), day(i(t)) = 1440}
+@EndifMan
+@IfDoc
+@BeginMath
+\begin{array}{c}
+o(\mbox{01},x) = \mbox{\textbf{mean}}\{i(t,x), \mbox{day}(i(t)) = \mbox{01}\} \\
+\vdots \\
+o(\mbox{1440},x) = \mbox{\textbf{mean}}\{i(t,x), \mbox{day}(i(t)) = \mbox{1440}\} \\
+\end{array}
+@EndMath
+@EndifDoc
+@EndDescription
+@EndOperator
+
+
+@BeginOperator_dminuteavg
+@Title     = Multi-day by the minute average
+
+@BeginDescription
+@IfMan
+o(01,x) = avg{i(t,x), day(i(t)) = 01}
+                 ...
+o(1440,x) = avg{i(t,x), day(i(t)) = 1440}
+@EndifMan
+@IfDoc
+@BeginMath
+\begin{array}{c}
+o(\mbox{01},x) = \mbox{\textbf{avg}}\{i(t,x), \mbox{day}(i(t)) = \mbox{01}\} \\
+\vdots \\
+o(\mbox{1440},x) = \mbox{\textbf{avg}}\{i(t,x), \mbox{day}(i(t)) = \mbox{1440}\} \\
+\end{array}
+@EndMath
+@EndifDoc
+@EndDescription
+@EndOperator
+
+
+@BeginOperator_dminutevar
+@Title     = Multi-day by the minute variance
+
+@BeginDescription
+Normalize by n.
+
+@IfMan
+o(01,x) = var{i(t,x), day(i(t)) = 01}
+                 ...
+o(1440,x) = var{i(t,x), day(i(t)) = 1440}
+@EndifMan
+@IfDoc
+@BeginMath
+\begin{array}{c}
+o(\mbox{01},x) = \mbox{\textbf{var}}\{i(t,x), \mbox{day}(i(t)) = \mbox{01}\} \\
+\vdots \\
+o(\mbox{1440},x) = \mbox{\textbf{var}}\{i(t,x), \mbox{day}(i(t)) = \mbox{1440}\} \\
+\end{array}
+@EndMath
+@EndifDoc
+@EndDescription
+@EndOperator
+
+
+@BeginOperator_dminutevar1
+@Title     = Multi-day by the minute variance (n-1)
+
+@BeginDescription
+Normalize by (n-1).
+
+@IfMan
+o(01,x) = var1{i(t,x), day(i(t)) = 01}
+                 ...
+o(1440,x) = var1{i(t,x), day(i(t)) = 1440}
+@EndifMan
+@IfDoc
+@BeginMath
+\begin{array}{c}
+o(\mbox{01},x) = \mbox{\textbf{var1}}\{i(t,x), \mbox{day}(i(t)) = \mbox{01}\} \\
+\vdots \\
+o(\mbox{1440},x) = \mbox{\textbf{var1}}\{i(t,x), \mbox{day}(i(t)) = \mbox{1440}\} \\
+\end{array}
+@EndMath
+@EndifDoc
+@EndDescription
+@EndOperator
+
+
+@BeginOperator_dminutestd
+@Title     = Multi-day by the minute standard deviation
+
+@BeginDescription
+Normalize by n.
+
+@IfMan
+o(01,x) = std{i(t,x), day(i(t)) = 01}
+                 ...
+o(1440,x) = std{i(t,x), day(i(t)) = 1440}
+@EndifMan
+@IfDoc
+@BeginMath
+\begin{array}{c}
+o(\mbox{01},x) = \mbox{\textbf{std}}\{i(t,x), \mbox{day}(i(t)) = \mbox{01}\} \\
+\vdots \\
+o(\mbox{1440},x) = \mbox{\textbf{std}}\{i(t,x), \mbox{day}(i(t)) = \mbox{1440}\} \\
+\end{array}
+@EndMath
+@EndifDoc
+@EndDescription
+@EndOperator
+
+
+@BeginOperator_dminutestd1
+@Title     = Multi-day by the minute standard deviation (n-1)
+
+@BeginDescription
+Normalize by (n-1).
+
+@IfMan
+o(01,x) = std1{i(t,x), day(i(t)) = 01}
+                 ...
+o(1440,x) = std1{i(t,x), day(i(t)) = 1440}
+@EndifMan
+@IfDoc
+@BeginMath
+\begin{array}{c}
+o(\mbox{01},x) = \mbox{\textbf{std1}}\{i(t,x), \mbox{day}(i(t)) = \mbox{01}\} \\
+\vdots \\
+o(\mbox{1440},x) = \mbox{\textbf{std1}}\{i(t,x), \mbox{day}(i(t)) = \mbox{1440}\} \\
+\end{array}
+@EndMath
+@EndifDoc
+@EndDescription
+@EndOperator
diff --git a/doc/tex/mod/Exprf b/doc/tex/mod/Exprf
index 5dffe41d6c2afdb97dbcfc8f4fd143cef13f1541..2b93115c14dd82b74c4908a370feebe72b8c00ec 100644
--- a/doc/tex/mod/Exprf
+++ b/doc/tex/mod/Exprf
@@ -26,24 +26,24 @@ when the variable is declared.
 The following operators are supported:
 
 @BeginTable4
- @bold{Operator}   & @bold{Meaning}             & @bold{Example}   & @bold{Result} 
+ @bold{Operator}   & @bold{Meaning}             & @bold{Example}   & @bold{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 
+     -      & subtraction         & x - y     & Difference of x and y
+     *      & multiplication      & x * y     & Product of x and y
      /      & division            & x / y     & Quotient of x and y
-     \exp       & exponentiation      & x \exp y      & Exponentiates x with y 
+     \exp       & exponentiation      & x \exp 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 
+     <=>    & less equal greater  & x <=> y   & -1, if x less y; 1, if x greater y; else 0
      \and     & logical AND         & x \and y    &  1, if x and y not equal 0; else 0
      \or     & logical OR          & x \or 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 
+     ?:     & ternary conditional & x ? y : z & y, if x not equal 0, else z
 @EndTable
 
 The following functions are supported:
@@ -102,7 +102,7 @@ Convert x from degrees to radians
 @Item = deg(x)
 Convert x from radians to degrees
 @Item = rand(x)
-Replace x by pseudo-random numbers in the range of 0 to 1 
+Replace x by pseudo-random numbers in the range of 0 to 1
 @Item = isMissval(x)
 Returns 1 where x is missing
 @EndList
@@ -119,7 +119,7 @@ Power function
 @Item = hypot(x,y)
 Euclidean distance function, sqrt(x*x + y*y)
 @Item = atan2(x,y)
-Arc tangent function of y/x, using signs to determine quadrants 
+Arc tangent function of y/x, using signs to determine quadrants
 @EndList
 
 Coordinates:
@@ -174,17 +174,17 @@ Total number of elements (ngp(x)*nlev(x))
 Returns the missing value of variable x
 @EndList
 
-Statistical values over a field:
+Statistics over a field:
 
 @mod{fldmin}(x), @mod{fldmax}(x), @mod{fldrange}(x), @mod{fldsum}(x), @mod{fldmean}(x), @mod{fldavg}(x), @mod{fldstd}(x), @mod{fldstd1}(x),
 @mod{fldvar}(x), @mod{fldvar1}(x), @mod{fldskew}(x), @mod{fldkurt}(x), @mod{fldmedian}(x)
 
-Zonal statistical values for regular 2D grids:
+Zonal statistics for regular 2D grids:
 
 @mod{zonmin}(x), @mod{zonmax}(x), @mod{zonrange}(x), @mod{zonsum}(x), @mod{zonmean}(x), @mod{zonavg}(x), @mod{zonstd}(x), @mod{zonstd1}(x),
 @mod{zonvar}(x), @mod{zonvar1}(x), @mod{zonskew}(x), @mod{zonkurt}(x), @mod{zonmedian}(x)
 
-Vertical statistical values:
+Vertical statistics:
 
 @mod{vertmin}(x), @mod{vertmax}(x), @mod{vertrange}(x), @mod{vertsum}(x), @mod{vertmean}(x), @mod{vertavg}(x), @mod{vertstd}(x), @mod{vertstd1}(x),
 @mod{vertvar}(x), @mod{vertvar1}(x)
diff --git a/doc/tex/mod/Hourstat b/doc/tex/mod/Hourstat
index 7e31d8fd5b9c6fc5407ed1970a1c70a6abd4458a..f169f64e52a5c9c36611e9d1030db189c00ff5cb 100644
--- a/doc/tex/mod/Hourstat
+++ b/doc/tex/mod/Hourstat
@@ -1,7 +1,7 @@
 @BeginModule
 @NewPage
 @Name      = Hourstat
-@Title     = Hourly statistical values
+@Title     = Hourly statistics
 @Section   = Statistical values
 @Class     = Statistic
 @Arguments = infile outfile
diff --git a/doc/tex/mod/Intlevel b/doc/tex/mod/Intlevel
index 643a75c856fcf033ede96d83ba72a1ec65f0d890..1d549d7f93c779f888966cab76acb706cf249f24 100644
--- a/doc/tex/mod/Intlevel
+++ b/doc/tex/mod/Intlevel
@@ -23,7 +23,7 @@ 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 = zaxisdescription
+@Item = zdescription
 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
diff --git a/doc/tex/mod/Merge b/doc/tex/mod/Merge
index ad72c1ca45e0f35ff00dc9516eb02e112dc30e22..fbae996279fa5c78e0d486a08389a4ba6f5741b9 100644
--- a/doc/tex/mod/Merge
+++ b/doc/tex/mod/Merge
@@ -30,6 +30,7 @@ different levels in different input files is not allowed.
 
 @BeginOperator_mergetime
 @Title     = Merge datasets sorted by date and time
+@Parameter = [options]
 
 @BeginDescription
 Merges all timesteps of all input files sorted by date and time.
@@ -40,10 +41,12 @@ and all timesteps are sorted by date and time.
 @EndOperator
 
 
-@BeginEnvironment
-@Item = SKIP_SAME_TIME
-If set to 1, skips all consecutive timesteps with a double entry of the same timestamp.
-@EndEnvironment
+@BeginParameter
+@Item = skip_same_time
+BOOL    Skips all consecutive timesteps with a double entry of the same timestamp.
+@Item = names
+STRING  Fill missing variable names with missing values (union) or use the intersection (intersect).
+@EndParameter
 
 
 @BeginNote
diff --git a/doc/tex/mod/Merstat b/doc/tex/mod/Merstat
index 9e905fce874cab9aafa7ec4da1a1b226ad72f48a..1f297f66de5016f2a0dcb2e88e40377f9a648ac9 100644
--- a/doc/tex/mod/Merstat
+++ b/doc/tex/mod/Merstat
@@ -1,7 +1,7 @@
 @BeginModule
 @NewPage
 @Name      = Merstat
-@Title     = Meridional statistical values
+@Title     = Meridional statistics
 @Section   = Statistical values
 @Class     = Statistic
 @Arguments = infile outfile
diff --git a/doc/tex/mod/Monstat b/doc/tex/mod/Monstat
index 1a820b9896bcd0228997e0f108652bfcc5e409ef..ad5556bcf54c490368f5cd0e2e295e56a2b8e666 100644
--- a/doc/tex/mod/Monstat
+++ b/doc/tex/mod/Monstat
@@ -1,7 +1,7 @@
 @BeginModule
 @NewPage
 @Name      = Monstat
-@Title     = Monthly statistical values
+@Title     = Monthly statistics
 @Section   = Statistical values
 @Class     = Statistic
 @Arguments = infile outfile
@@ -19,6 +19,7 @@ This can be change with the CDO option --timestat_date <first|middle|last>.
 
 @BeginOperator_monmin
 @Title     = Monthly minimum
+@Parameter = [parameter]
 
 @BeginDescription
 @IfMan
@@ -38,6 +39,7 @@ o(t,x) = \mbox{\textbf{min}}\{i(t',x), t_1 < t' \leq t_n\}
 
 @BeginOperator_monmax
 @Title     = Monthly maximum
+@Parameter = [parameter]
 
 @BeginDescription
 @IfMan
@@ -57,6 +59,7 @@ o(t,x) = \mbox{\textbf{max}}\{i(t',x), t_1 < t' \leq t_n\}
 
 @BeginOperator_monrange
 @Title     = Monthly range
+@Parameter = [parameter]
 
 @BeginDescription
 @IfMan
@@ -76,6 +79,7 @@ o(t,x) = \mbox{\textbf{range}}\{i(t',x), t_1 < t' \leq t_n\}
 
 @BeginOperator_monsum
 @Title     = Monthly sum
+@Parameter = [parameter]
 
 @BeginDescription
 @IfMan
@@ -95,6 +99,7 @@ o(t,x) = \mbox{\textbf{sum}}\{i(t',x), t_1 < t' \leq t_n\}
 
 @BeginOperator_monmean
 @Title     = Monthly mean
+@Parameter = [parameter]
 
 @BeginDescription
 @IfMan
@@ -114,6 +119,7 @@ o(t,x) = \mbox{\textbf{mean}}\{i(t',x), t_1 < t' \leq t_n\}
 
 @BeginOperator_monavg
 @Title     = Monthly average
+@Parameter = [parameter]
 
 @BeginDescription
 @IfMan
@@ -133,6 +139,7 @@ o(t,x) = \mbox{\textbf{avg}}\{i(t',x), t_1 < t' \leq t_n\}
 
 @BeginOperator_monvar
 @Title     = Monthly variance
+@Parameter = [parameter]
 
 @BeginDescription
 @IfMan
@@ -152,6 +159,7 @@ o(t,x) = \mbox{\textbf{var}}\{i(t',x), t_1 < t' \leq t_n\}
 
 @BeginOperator_monvar1
 @Title     = Monthly variance (n-1)
+@Parameter = [parameter]
 
 @BeginDescription
 @IfMan
@@ -171,6 +179,7 @@ o(t,x) = \mbox{\textbf{var1}}\{i(t',x), t_1 < t' \leq t_n\}
 
 @BeginOperator_monstd
 @Title     = Monthly standard deviation
+@Parameter = [parameter]
 
 @BeginDescription
 @IfMan
@@ -190,6 +199,7 @@ o(t,x) = \mbox{\textbf{std}}\{i(t',x), t_1 < t' \leq t_n\}
 
 @BeginOperator_monstd1
 @Title     = Monthly standard deviation (n-1)
+@Parameter = [parameter]
 
 @BeginDescription
 @IfMan
@@ -207,6 +217,12 @@ o(t,x) = \mbox{\textbf{std1}}\{i(t',x), t_1 < t' \leq t_n\}
 @EndOperator
 
 
+@BeginParameter
+@Item = complete_only
+BOOL Process only complete years
+@EndParameter
+
+
 @BeginExample
 To compute the monthly mean of a time series use:
 @BeginVerbatim
diff --git a/doc/tex/mod/Remapbic b/doc/tex/mod/Remapbic
index d1e58a75834d4bc2303b32df1de3cd01cb3df27b..6b68c3b867f25d0e9414161176fc37f6822ebb48 100644
--- a/doc/tex/mod/Remapbic
+++ b/doc/tex/mod/Remapbic
@@ -49,7 +49,7 @@ Performs a bicubic interpolation on all input fields.
 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 @mod{remap} to apply this remapping weights to a data file with the same source grid.
-Set the parameter @env{map3d=true} to generate all mapfiles of the first 3D field with variing masks.
+Set the parameter @env{map3d=true} to generate all mapfiles of the first 3D field with varying masks.
 In this case the mapfiles will be named <outfile><xxx>.nc. xxx will have five digits with the number of the mapfile.
 @EndDescription
 @EndOperator
diff --git a/doc/tex/mod/Remapbil b/doc/tex/mod/Remapbil
index 839c888cf90e47540071b8a181478eab918008d7..24a139b8665cf787510fc92a7bcd5c4c6843a363 100644
--- a/doc/tex/mod/Remapbil
+++ b/doc/tex/mod/Remapbil
@@ -49,7 +49,7 @@ Performs a bilinear interpolation on all input fields.
 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 @mod{remap} to apply this remapping weights to a data file with the same source grid.
-Set the parameter @env{map3d=true} to generate all mapfiles of the first 3D field with variing masks.
+Set the parameter @env{map3d=true} to generate all mapfiles of the first 3D field with varying masks.
 In this case the mapfiles will be named <outfile><xxx>.nc. xxx will have five digits with the number of the mapfile.
 @EndDescription
 @EndOperator
diff --git a/doc/tex/mod/Remapcon b/doc/tex/mod/Remapcon
index 1a90844d47332bf5fac4a2b7e34b7047730ba408..06420dd1cc8c96c7973a9f9fd27eb7e94969f71e 100644
--- a/doc/tex/mod/Remapcon
+++ b/doc/tex/mod/Remapcon
@@ -50,7 +50,7 @@ Performs a first order conservative remapping on all input fields.
 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 @mod{remap} to apply this remapping weights to a data file with the same source grid.
-Set the parameter @env{map3d=true} to generate all mapfiles of the first 3D field with variing masks.
+Set the parameter @env{map3d=true} to generate all mapfiles of the first 3D field with varying masks.
 In this case the mapfiles will be named <outfile><xxx>.nc. xxx will have five digits with the number of the mapfile.
 @EndDescription
 @EndOperator
diff --git a/doc/tex/mod/Remapdis b/doc/tex/mod/Remapdis
index 43d2a0dc63ac2b44b3f62cdc511a2d39cab7e167..555217989f07ac2ead79663ca6f677d45085a168 100644
--- a/doc/tex/mod/Remapdis
+++ b/doc/tex/mod/Remapdis
@@ -48,7 +48,7 @@ Performs an inverse distance weighted averaged remapping of the nearest neighbor
 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 @mod{remap} to apply this remapping weights to a data file with the same source grid.
-Set the parameter @env{map3d=true} to generate all mapfiles of the first 3D field with variing masks.
+Set the parameter @env{map3d=true} to generate all mapfiles of the first 3D field with varying masks.
 In this case the mapfiles will be named <outfile><xxx>.nc. xxx will have five digits with the number of the mapfile.
 @EndDescription
 @EndOperator
diff --git a/doc/tex/mod/Remapnn b/doc/tex/mod/Remapnn
index 84b041eb07bc7b857b0831cbadfecfe6a4f6ec52..089adc59e5dacbe5e6b00fd2c2e2ffa89ef7740c 100644
--- a/doc/tex/mod/Remapnn
+++ b/doc/tex/mod/Remapnn
@@ -47,7 +47,7 @@ Performs a nearest neighbor remapping on all input fields.
 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 @mod{remap} to apply this remapping weights to a data file with the same source grid.
-Set the parameter @env{map3d=true} to generate all mapfiles of the first 3D field with variing masks.
+Set the parameter @env{map3d=true} to generate all mapfiles of the first 3D field with varying masks.
 In this case the mapfiles will be named <outfile><xxx>.nc. xxx will have five digits with the number of the mapfile.
 @EndDescription
 @EndOperator
diff --git a/doc/tex/mod/Runstat b/doc/tex/mod/Runstat
index bb5bc532d2e268115051bb250d06364b54fe7384..0207ffb2424a49dcd676af6377c4451efc71e543 100644
--- a/doc/tex/mod/Runstat
+++ b/doc/tex/mod/Runstat
@@ -1,16 +1,16 @@
 @BeginModule
 @NewPage
 @Name      = Runstat
-@Title     = Running statistical values
+@Title     = Running statistics
 @Section   = Statistical values
 @Class     = Statistic
 @Arguments = infile outfile
 @Operators = runmin runmax runrange runsum runmean runavg runstd runstd1 runvar runvar1
 
 @BeginDescription
-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 @file{infile} is written to @file{outfile}. 
+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 @file{infile} is written to @file{outfile}.
 The time of @file{outfile} is determined by the time in the middle of all contributing timesteps of @file{infile}.
 This can be change with the CDO option --timestat_date <first|middle|last>.
 @EndDescription
@@ -124,7 +124,7 @@ o(t+(nts-1)/2,x) = \mbox{\textbf{avg}}\{i(t,x), i(t+1,x), ..., i(t+nts-1,x)\}
 @Parameter = nts
 
 @BeginDescription
-Normalize by n. 
+Normalize by n.
 
 @IfMan
 o(t+(nts-1)/2,x) = var{i(t,x), i(t+1,x), ..., i(t+nts-1,x)}
@@ -143,7 +143,7 @@ o(t+(nts-1)/2,x) = \mbox{\textbf{var}}\{i(t,x), i(t+1,x), ..., i(t+nts-1,x)\}
 @Parameter = nts
 
 @BeginDescription
-Normalize by (n-1). 
+Normalize by (n-1).
 
 @IfMan
 o(t+(nts-1)/2,x) = var1{i(t,x), i(t+1,x), ..., i(t+nts-1,x)}
@@ -162,7 +162,7 @@ o(t+(nts-1)/2,x) = \mbox{\textbf{var1}}\{i(t,x), i(t+1,x), ..., i(t+nts-1,x)\}
 @Parameter = nts
 
 @BeginDescription
-Normalize by n. 
+Normalize by n.
 
 @IfMan
 o(t+(nts-1)/2,x) = std{i(t,x), i(t+1,x), ..., i(t+nts-1,x)}
@@ -181,7 +181,7 @@ o(t+(nts-1)/2,x) = \mbox{\textbf{std}}\{i(t,x), i(t+1,x), ..., i(t+nts-1,x)\}
 @Parameter = nts
 
 @BeginDescription
-Normalize by (n-1). 
+Normalize by (n-1).
 
 @IfMan
 o(t+(nts-1)/2,x) = std1{i(t,x), i(t+1,x), ..., i(t+nts-1,x)}
diff --git a/doc/tex/mod/Seasstat b/doc/tex/mod/Seasstat
index ec80d82a72b5c1bab864ad59da86e2e3d65cfc53..bd09f06596b45906798c29768d5f9c7418f50f5a 100644
--- a/doc/tex/mod/Seasstat
+++ b/doc/tex/mod/Seasstat
@@ -1,7 +1,7 @@
 @BeginModule
 @NewPage
 @Name      = Seasstat
-@Title     = Seasonal statistical values
+@Title     = Seasonal statistics
 @Section   = Statistical values
 @Class     = Statistic
 @Arguments = infile outfile
@@ -13,7 +13,7 @@ Depending on the chosen operator the minimum, maximum, range, sum, average, vari
 or standard deviation of timesteps of the same season is written to @file{outfile}.
 The time of @file{outfile} is determined by the time in the middle of all contributing timesteps of @file{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 
+Be careful about the first and the last output timestep, they may be incorrect values
 if the seasons have incomplete timesteps.
 @EndDescription
 @EndModule
diff --git a/doc/tex/mod/Seltimeidx b/doc/tex/mod/Seltimeidx
new file mode 100644
index 0000000000000000000000000000000000000000..61ffd0dd1389daebe4f08a46ea7710ec754867b6
--- /dev/null
+++ b/doc/tex/mod/Seltimeidx
@@ -0,0 +1,21 @@
+@BeginModule
+@Name      = Seltimeidx
+@Title     = Select timestep by index
+@Section   = Selection
+@Class     = Selection
+@Arguments = infile1 infile2 outfile
+@Operators = seltimeidx
+
+@BeginDescription
+Selects field elements from @file{infile2} according to a timestep index from @file{infile1}.
+The index of the timestep in @file{infile1} should be the result of corresponding @mod{timminidx} or @mod{timmaxidx} operations, respectively.
+@EndDescription
+@EndModule
+
+
+@BeginOperator_seltimeidx
+@Title     = Select timestep by index
+
+@BeginDescription
+@EndDescription
+@EndOperator
diff --git a/doc/tex/mod/Selyearidx b/doc/tex/mod/Selyearidx
index 082ab523e994bf14cc1828d6df35827722c52eee..9b6bce8489441448e96099e979658388fb4636f6 100644
--- a/doc/tex/mod/Selyearidx
+++ b/doc/tex/mod/Selyearidx
@@ -7,8 +7,8 @@
 @Operators = selyearidx
 
 @BeginDescription
-Selects field elements from @file{infile2} by a yearly time index from @file{infile1}.
-The yearly indices in @file{infile1} should be the result of corresponding @mod{yearminidx} and @mod{yearmaxidx} operations, respectively.
+Selects field elements from @file{infile2} according to a year index from @file{infile1}.
+The index of the year in @file{infile1} should be the result of corresponding @mod{yearminidx} or @mod{yearmaxidx} operations, respectively.
 @EndDescription
 @EndModule
 
diff --git a/doc/tex/mod/Setgrid b/doc/tex/mod/Setgrid
index a41a3741dda3f76e26497ff530a105e2ad1c3bfa..7ae7c906d376a7c033f752fead4e9e1f6bd60f07 100644
--- a/doc/tex/mod/Setgrid
+++ b/doc/tex/mod/Setgrid
@@ -5,7 +5,7 @@
 @Section   = Modification
 @Class     = Modification
 @Arguments = infile outfile
-@Operators = setgrid setgridtype setgridarea setgridmask
+@Operators = setgrid setgridtype setgridarea setgridmask setprojparams
 
 @BeginDescription
 This module modifies the metadata of the horizontal grid. Depending on the chosen operator
@@ -75,6 +75,16 @@ remapping, e.g. for @oper{remapbil}.
 @EndOperator
 
 
+@BeginOperator_setprojparams
+@Title     = Set proj params
+@Parameter = projparams
+
+@BeginDescription
+Sets the @env{proj_params} attribute of a projection. This attribute is used to compute
+geographic coordinates of a projecton with the proj library.
+@EndOperator
+
+
 @BeginParameter
 @Item = grid
 STRING  Grid description file or name
@@ -84,6 +94,8 @@ STRING  Grid type (curvilinear, unstructured, regular, lonlat, projection or der
 STRING  Data file, the first field is used as grid cell area
 @Item = gridmask
 STRING  Data file, the first field is used as grid mask
+@Item = projparams
+STRING  Proj library parameter (e.g.:+init=EPSG:3413)
 @EndParameter
 
 
diff --git a/doc/tex/mod/Timselstat b/doc/tex/mod/Timselstat
index a1d0d3b953116f21547517a6608beba0f5d58e85..691e12de3f694f1dc218cb76013652bed0eba576 100644
--- a/doc/tex/mod/Timselstat
+++ b/doc/tex/mod/Timselstat
@@ -1,15 +1,15 @@
 @BeginModule
 @NewPage
 @Name      = Timselstat
-@Title     = Time range statistical values
+@Title     = Time range statistics
 @Section   = Statistical values
 @Class     = Statistic
 @Arguments = infile outfile
 @Operators = timselmin timselmax timselrange timselsum timselmean timselavg timselstd timselstd1 timselvar timselvar1
 
 @BeginDescription
-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 
+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 @file{outfile}.
 The time of @file{outfile} is determined by the time in the middle of all contributing timesteps of @file{infile}.
 This can be change with the CDO option --timestat_date <first|middle|last>.
@@ -219,7 +219,7 @@ o(t,x) = \mbox{\textbf{std1}}\{i(t',x), t_1 < t' \le t_n\}
 
 @BeginParameter noffset
 @Item = nsets
-INTEGER  Number of input timesteps for each output timestep 
+INTEGER  Number of input timesteps for each output timestep
 @Item = noffset
 INTEGER  Number of input timesteps skipped before the first timestep range (optional)
 @Item = nskip
diff --git a/doc/tex/mod/Timstat b/doc/tex/mod/Timstat
index ca2529f9a4e23ec332515773cd41b70835dc93f2..179374c499056178902b99446976c0aa567d8f9f 100644
--- a/doc/tex/mod/Timstat
+++ b/doc/tex/mod/Timstat
@@ -5,7 +5,7 @@
 @Section   = Statistical values
 @Class     = Statistic
 @Arguments = infile outfile
-@Operators = timmin timmax timrange timsum timmean timavg timstd timstd1 timvar timvar1
+@Operators = timmin timmax timminidx timmaxidx timrange timsum timmean timavg timstd timstd1 timvar timvar1
 
 @BeginDescription
 This module computes statistical values over all timesteps in @file{infile}. Depending on 
@@ -49,6 +49,38 @@ o(1,x) = \mbox{\textbf{max}}\{i(t',x), t_1 < t' \leq t_n\}
 @EndOperator
 
 
+@BeginOperator_timminidx
+@Title     = Index of time minimum
+
+@BeginDescription
+@IfMan
+o(1,x) = minidx{i(t',x), t_1<t'<=t_n}
+@EndifMan
+@IfDoc
+@BeginMath
+o(1,x) = \mbox{\textbf{minidx}}\{i(t',x), t_1 < t' \leq t_n\}
+@EndMath
+@EndifDoc
+@EndDescription
+@EndOperator
+
+
+@BeginOperator_timmaxidx
+@Title     = Index of time maximum
+
+@BeginDescription
+@IfMan
+o(1,x) = maxidx{i(t',x), t_1<t'<=t_n}
+@EndifMan
+@IfDoc
+@BeginMath
+o(1,x) = \mbox{\textbf{maxidx}}\{i(t',x), t_1 < t' \leq t_n\}
+@EndMath
+@EndifDoc
+@EndDescription
+@EndOperator
+
+
 @BeginOperator_timrange
 @Title     = Time range
 
diff --git a/doc/tex/mod/Vargen b/doc/tex/mod/Vargen
index 78076ccce40034133f064c114ab771d713de0a46..1888e520f1773f32526b12553ace173a076bc540 100644
--- a/doc/tex/mod/Vargen
+++ b/doc/tex/mod/Vargen
@@ -59,7 +59,7 @@ which is increased from one time step to the next.
 
 @BeginDescription
 Creates pressure and temperature values for the given list of vertical levels.
-The formulars are:
+The formulas are:
 
 @IfMan
 P(z) = P_0 * exp(-1 * g/R * H/T_0 * log( (exp(z/H)*T_0 + T_Delta)/(T_0 + T_Delta))
diff --git a/doc/tex/mod/Vertstat b/doc/tex/mod/Vertstat
index af38e0c935858c51119d856c749bf6617a45af17..8a2f5ff125d3ce078a1a90c1032d192cecc5bef9 100644
--- a/doc/tex/mod/Vertstat
+++ b/doc/tex/mod/Vertstat
@@ -1,7 +1,7 @@
 @BeginModule
 @NewPage
 @Name      = Vertstat
-@Title     = Vertical statistical values
+@Title     = Vertical statistics
 @Section   = Statistical values
 @Class     = Statistic
 @Arguments = infile outfile
diff --git a/doc/tex/mod/Ydaystat b/doc/tex/mod/Ydaystat
index aab474a6e31bbf6b8a933f956e8f99b77a6deb84..272f8951ea0c1204f748e65fa16563c27d786f61 100644
--- a/doc/tex/mod/Ydaystat
+++ b/doc/tex/mod/Ydaystat
@@ -1,7 +1,7 @@
 @BeginModule
 @NewPage
 @Name      = Ydaystat
-@Title     = Multi-year daily statistical values
+@Title     = Multi-year daily statistics
 @Section   = Statistical values
 @Class     = Statistic
 @Arguments = infile outfile
@@ -151,7 +151,7 @@ o(\mbox{366},x) = \mbox{\textbf{avg}}\{i(t,x), \mbox{day}(i(t)) = \mbox{366}\} \
 @Title     = Multi-year daily variance
 
 @BeginDescription
-Normalize by n. 
+Normalize by n.
 
 @IfMan
 o(001,x) = var{i(t,x), day(i(t)) = 001}
@@ -175,7 +175,7 @@ o(\mbox{366},x) = \mbox{\textbf{var}}\{i(t,x), \mbox{day}(i(t)) = \mbox{366}\} \
 @Title     = Multi-year daily variance (n-1)
 
 @BeginDescription
-Normalize by (n-1). 
+Normalize by (n-1).
 
 @IfMan
 o(001,x) = var1{i(t,x), day(i(t)) = 001}
@@ -199,7 +199,7 @@ o(\mbox{366},x) = \mbox{\textbf{var1}}\{i(t,x), \mbox{day}(i(t)) = \mbox{366}\}
 @Title     = Multi-year daily standard deviation
 
 @BeginDescription
-Normalize by n. 
+Normalize by n.
 
 @IfMan
 o(001,x) = std{i(t,x), day(i(t)) = 001}
@@ -223,7 +223,7 @@ o(\mbox{366},x) = \mbox{\textbf{std}}\{i(t,x), \mbox{day}(i(t)) = \mbox{366}\} \
 @Title     = Multi-year daily standard deviation (n-1)
 
 @BeginDescription
-Normalize by (n-1). 
+Normalize by (n-1).
 
 @IfMan
 o(001,x) = std1{i(t,x), day(i(t)) = 001}
diff --git a/doc/tex/mod/Ydrunstat b/doc/tex/mod/Ydrunstat
index ecedec1e6d127d2da83a1088657b243c7bff5535..26ff47455215d129a25984b56d713cdbf1392598 100644
--- a/doc/tex/mod/Ydrunstat
+++ b/doc/tex/mod/Ydrunstat
@@ -1,7 +1,7 @@
 @BeginModule
 @NewPage
 @Name      = Ydrunstat
-@Title     = Multi-year daily running statistical values
+@Title     = Multi-year daily running statistics
 @Section   = Statistical values
 @Class     = Statistic
 @Arguments = infile outfile
@@ -9,15 +9,15 @@
 
 @BeginDescription
 This module writes running statistical values for each day of year in @file{infile} to @file{outfile}.
-Depending on the chosen operator, the minimum, maximum, sum, average, variance or standard deviation 
+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 
+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 
+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 
+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.
 @EndModule
 
@@ -142,7 +142,7 @@ o(\mbox{366},x) = \mbox{\textbf{avg}}\{i(t,x), i(t+1,x), ..., i(t+nts-1,x); \mbo
 @Parameter = nts
 
 @BeginDescription
-Normalize by n. 
+Normalize by n.
 
 @IfMan
 o(001,x) = var{i(t,x), i(t+1,x), ..., i(t+nts-1,x); day[(i(t+(nts-1)/2)] = 001}
@@ -167,7 +167,7 @@ o(\mbox{366},x) = \mbox{\textbf{var}}\{i(t,x), i(t+1,x), ..., i(t+nts-1,x); \mbo
 @Parameter = nts
 
 @BeginDescription
-Normalize by (n-1). 
+Normalize by (n-1).
 
 @IfMan
 o(001,x) = var1{i(t,x), i(t+1,x), ..., i(t+nts-1,x); day[(i(t+(nts-1)/2)] = 001}
@@ -192,7 +192,7 @@ o(\mbox{366},x) = \mbox{\textbf{var1}}\{i(t,x), i(t+1,x), ..., i(t+nts-1,x); \mb
 @Parameter = nts
 
 @BeginDescription
-Normalize by n. 
+Normalize by n.
 
 @IfMan
 o(001,x) = std{i(t,x), i(t+1,x), ..., i(t+nts-1,x); day[i(t+(nts-1)/2)] = 001}
@@ -217,7 +217,7 @@ o(\mbox{366},x) = \mbox{\textbf{std}}\{i(t,x), i(t+1,x), ..., i(t+nts-1,x); \mbo
 @Parameter = nts
 
 @BeginDescription
-Normalize by (n-1). 
+Normalize by (n-1).
 
 @IfMan
 o(001,x) = std1{i(t,x), i(t+1,x), ..., i(t+nts-1,x); day[i(t+(nts-1)/2)] = 001}
diff --git a/doc/tex/mod/Yearpctl b/doc/tex/mod/Yearpctl
index a9afb1c9794369c89539dcec5f74a875b0385385..bedbfa44958927187d3199d7edd49eab1471fa3d 100644
--- a/doc/tex/mod/Yearpctl
+++ b/doc/tex/mod/Yearpctl
@@ -1,4 +1,5 @@
 @BeginModule
+@NewPage
 @Name      = Yearpctl
 @Title     = Yearly percentile values
 @Section   = Statistical values
diff --git a/doc/tex/mod/Yearstat b/doc/tex/mod/Yearstat
index 67ea28535c4493b21bbfeae00152a6d26c73976b..a9ad7deec19ea23695bcca88da645fe3a23bd85e 100644
--- a/doc/tex/mod/Yearstat
+++ b/doc/tex/mod/Yearstat
@@ -1,7 +1,7 @@
 @BeginModule
 @NewPage
 @Name      = Yearstat
-@Title     = Yearly statistical values
+@Title     = Yearly statistics
 @Section   = Statistical values
 @Class     = Statistic
 @Arguments = infile outfile
@@ -19,6 +19,7 @@ This can be change with the CDO option --timestat_date <first|middle|last>.
 
 @BeginOperator_yearmin
 @Title     = Yearly minimum
+@Parameter = [parameter]
 
 @BeginDescription
 @IfMan
@@ -38,6 +39,7 @@ o(t,x) = \mbox{\textbf{min}}\{i(t',x), t_1 < t' \leq t_n\}
 
 @BeginOperator_yearmax
 @Title     = Yearly maximum
+@Parameter = [parameter]
 
 @BeginDescription
 @IfMan
@@ -56,7 +58,8 @@ o(t,x) = \mbox{\textbf{max}}\{i(t',x), t_1 < t' \leq t_n\}
 
 
 @BeginOperator_yearminidx
-@Title     = Yearly minimum indices
+@Title     = Index of yearly minimum
+@Parameter = [parameter]
 
 @BeginDescription
 @IfMan
@@ -75,7 +78,8 @@ o(t,x) = \mbox{\textbf{minidx}}\{i(t',x), t_1 < t' \leq t_n\}
 
 
 @BeginOperator_yearmaxidx
-@Title     = Yearly maximum indices
+@Title     = Index of yearly maximum
+@Parameter = [parameter]
 
 @BeginDescription
 @IfMan
@@ -95,6 +99,7 @@ o(t,x) = \mbox{\textbf{maxidx}}\{i(t',x), t_1 < t' \leq t_n\}
 
 @BeginOperator_yearrange
 @Title     = Yearly range
+@Parameter = [parameter]
 
 @BeginDescription
 @IfMan
@@ -114,6 +119,7 @@ o(t,x) = \mbox{\textbf{range}}\{i(t',x), t_1 < t' \leq t_n\}
 
 @BeginOperator_yearsum
 @Title     = Yearly sum
+@Parameter = [parameter]
 
 @BeginDescription
 @IfMan
@@ -133,6 +139,7 @@ o(t,x) = \mbox{\textbf{sum}}\{i(t',x), t_1 < t' \leq t_n\}
 
 @BeginOperator_yearmean
 @Title     = Yearly mean
+@Parameter = [parameter]
 
 @BeginDescription
 @IfMan
@@ -152,6 +159,7 @@ o(t,x) = \mbox{\textbf{mean}}\{i(t',x), t_1 < t' \leq t_n\}
 
 @BeginOperator_yearavg
 @Title     = Yearly average
+@Parameter = [parameter]
 
 @BeginDescription
 @IfMan
@@ -171,6 +179,7 @@ o(t,x) = \mbox{\textbf{avg}}\{i(t',x), t_1 < t' \leq t_n\}
 
 @BeginOperator_yearvar
 @Title     = Yearly variance
+@Parameter = [parameter]
 
 @BeginDescription
 @IfMan
@@ -190,6 +199,7 @@ o(t,x) = \mbox{\textbf{var}}\{i(t',x), t_1 < t' \leq t_n\}
 
 @BeginOperator_yearvar1
 @Title     = Yearly variance (n-1)
+@Parameter = [parameter]
 
 @BeginDescription
 @IfMan
@@ -209,6 +219,7 @@ o(t,x) = \mbox{\textbf{var1}}\{i(t',x), t_1 < t' \leq t_n\}
 
 @BeginOperator_yearstd
 @Title     = Yearly standard deviation
+@Parameter = [parameter]
 
 @BeginDescription
 @IfMan
@@ -228,6 +239,7 @@ o(t,x) = \mbox{\textbf{std}}\{i(t',x), t_1 < t' \leq t_n\}
 
 @BeginOperator_yearstd1
 @Title     = Yearly standard deviation (n-1)
+@Parameter = [parameter]
 
 @BeginDescription
 @IfMan
@@ -245,6 +257,12 @@ o(t,x) = \mbox{\textbf{std1}}\{i(t',x), t_1 < t' \leq t_n\}
 @EndOperator
 
 
+@BeginParameter
+@Item = complete_only
+BOOL Process only complete years
+@EndParameter
+
+
 @BeginNote
 The operators yearmean and yearavg compute only arithmetical means!
 @EndNote
diff --git a/doc/tex/mod/Yhourstat b/doc/tex/mod/Yhourstat
index 690478c1d6db3d4130940667e3b5ec15f0022564..820c337c586a67ba3728f7e42cc523152baa3bd5 100644
--- a/doc/tex/mod/Yhourstat
+++ b/doc/tex/mod/Yhourstat
@@ -1,7 +1,7 @@
 @BeginModule
 @NewPage
 @Name      = Yhourstat
-@Title     = Multi-year hourly statistical values
+@Title     = Multi-year hourly statistics
 @Section   = Statistical values
 @Class     = Statistic
 @Arguments = infile outfile
@@ -151,7 +151,7 @@ o(\mbox{8784},x) = \mbox{\textbf{avg}}\{i(t,x), \mbox{day}(i(t)) = \mbox{8784}\}
 @Title     = Multi-year hourly variance
 
 @BeginDescription
-Normalize by n. 
+Normalize by n.
 
 @IfMan
 o(0001,x) = var{i(t,x), day(i(t)) = 0001}
@@ -175,7 +175,7 @@ o(\mbox{8784},x) = \mbox{\textbf{var}}\{i(t,x), \mbox{day}(i(t)) = \mbox{8784}\}
 @Title     = Multi-year hourly variance (n-1)
 
 @BeginDescription
-Normalize by (n-1). 
+Normalize by (n-1).
 
 @IfMan
 o(0001,x) = var1{i(t,x), day(i(t)) = 0001}
@@ -199,7 +199,7 @@ o(\mbox{8784},x) = \mbox{\textbf{var1}}\{i(t,x), \mbox{day}(i(t)) = \mbox{8784}\
 @Title     = Multi-year hourly standard deviation
 
 @BeginDescription
-Normalize by n. 
+Normalize by n.
 
 @IfMan
 o(0001,x) = std{i(t,x), day(i(t)) = 0001}
@@ -223,7 +223,7 @@ o(\mbox{8784},x) = \mbox{\textbf{std}}\{i(t,x), \mbox{day}(i(t)) = \mbox{8784}\}
 @Title     = Multi-year hourly standard deviation (n-1)
 
 @BeginDescription
-Normalize by (n-1). 
+Normalize by (n-1).
 
 @IfMan
 o(0001,x) = std1{i(t,x), day(i(t)) = 0001}
diff --git a/doc/tex/mod/Ymonstat b/doc/tex/mod/Ymonstat
index a894efe86eb6dd6ae0d8a93c5ccebf6d366d62fc..58d6bd649b3cc4f6b35cc6facd54de4ad7cd445a 100644
--- a/doc/tex/mod/Ymonstat
+++ b/doc/tex/mod/Ymonstat
@@ -1,7 +1,7 @@
 @BeginModule
 @NewPage
 @Name      = Ymonstat
-@Title     = Multi-year monthly statistical values
+@Title     = Multi-year monthly statistics
 @Section   = Statistical values
 @Class     = Statistic
 @Arguments = infile outfile
@@ -153,7 +153,7 @@ o(\mbox{12},x) = \mbox{\textbf{avg}}\{i(t,x), \mbox{month}(i(t)) = \mbox{12}\} \
 @Title     = Multi-year monthly variance
 
 @BeginDescription
-Normalize by n. 
+Normalize by n.
 
 @IfMan
 o(01,x) = var{i(t,x), month(i(t)) = 01}
@@ -177,7 +177,7 @@ o(\mbox{12},x) = \mbox{\textbf{var}}\{i(t,x), \mbox{month}(i(t)) = \mbox{12}\} \
 @Title     = Multi-year monthly variance (n-1)
 
 @BeginDescription
-Normalize by (n-1). 
+Normalize by (n-1).
 
 @IfMan
 o(01,x) = var1{i(t,x), month(i(t)) = 01}
@@ -201,7 +201,7 @@ o(\mbox{12},x) = \mbox{\textbf{var1}}\{i(t,x), \mbox{month}(i(t)) = \mbox{12}\}
 @Title     = Multi-year monthly standard deviation
 
 @BeginDescription
-Normalize by n. 
+Normalize by n.
 
 @IfMan
 o(01,x) = std{i(t,x), month(i(t)) = 01}
@@ -225,7 +225,7 @@ o(\mbox{12},x) = \mbox{\textbf{std}}\{i(t,x), \mbox{month}(i(t)) = \mbox{12}\} \
 @Title     = Multi-year monthly standard deviation (n-1)
 
 @BeginDescription
-Normalize by (n-1). 
+Normalize by (n-1).
 
 @IfMan
 o(01,x) = std1{i(t,x), month(i(t)) = 01}
diff --git a/doc/tex/mod/Yseasstat b/doc/tex/mod/Yseasstat
index 5cf6bd661bdf8c5c09dad76c6a9d0498fe22a50d..64bdcecbe4f2a94ee3c1ba53e49482119ad2e5d1 100644
--- a/doc/tex/mod/Yseasstat
+++ b/doc/tex/mod/Yseasstat
@@ -1,7 +1,7 @@
 @BeginModule
 @NewPage
 @Name      = Yseasstat
-@Title     = Multi-year seasonal statistical values
+@Title     = Multi-year seasonal statistics
 @Section   = Statistical values
 @Class     = Statistic
 @Arguments = infile outfile
diff --git a/doc/tex/mod/Zonstat b/doc/tex/mod/Zonstat
index a868b587e8288fb00b846089b7146005bb73b485..b9cbfec88ed6130510c4113af65d92f2597ff2a2 100644
--- a/doc/tex/mod/Zonstat
+++ b/doc/tex/mod/Zonstat
@@ -1,7 +1,7 @@
 @BeginModule
 @NewPage
 @Name      = Zonstat
-@Title     = Zonal statistical values
+@Title     = Zonal statistics
 @Section   = Statistical values
 @Class     = Statistic
 @Arguments = infile outfile
@@ -105,7 +105,7 @@ For every latitude the standard deviation over all longitudes is computed. Norma
 @Title     = Zonal standard deviation (n-1)
 
 @BeginDescription
-For every latitude the standard deviation over all longitudes is computed. Normalize by (n-1). 
+For every latitude the standard deviation over all longitudes is computed. Normalize by (n-1).
 @EndDescription
 @EndOperator
 
diff --git a/include/cdo.h b/include/cdo.h
new file mode 100644
index 0000000000000000000000000000000000000000..5c95229450b2168a6b461f7875c041e439e0d70b
--- /dev/null
+++ b/include/cdo.h
@@ -0,0 +1,13 @@
+#ifndef CDO_H
+#define CDO_H
+
+#include "../src/processManager.h"
+#include "../src/node.h"
+#include "../src/parser.h"
+#include "../src/factory.h"
+#include "../src/cdo_node_attach_exception.h"
+#include "../src/cdo_exception.h"
+#include "../src/cdo_module.h"
+#include "../src/cdo_syntax_error.h"
+
+#endif
diff --git a/libcdi b/libcdi
index d64b85168fad6fd7a8bebe3a12e386b0f8c6a41a..e2ab8d4028d9b9d32c7f7050d67a94bae69bdcf6 160000
--- a/libcdi
+++ b/libcdi
@@ -1 +1 @@
-Subproject commit d64b85168fad6fd7a8bebe3a12e386b0f8c6a41a
+Subproject commit e2ab8d4028d9b9d32c7f7050d67a94bae69bdcf6
diff --git a/m4/acx_options.m4 b/m4/acx_options.m4
index da193fa0f879a3c3987cf1b277e0ccc82cfc4d65..857711247fada6fd81dc50e4668710de7ef016ef 100644
--- a/m4/acx_options.m4
+++ b/m4/acx_options.m4
@@ -276,7 +276,8 @@ ECCODES_INCLUDE=''
 ECCODES_LIBS=''
 ENABLE_GRIBAPI=no
 AC_ARG_WITH([eccodes],
-            [AS_HELP_STRING([--with-eccodes=<yes|no|directory> (default=no)],[location of ECCODES library (lib and include subdirs)])],
+            [AS_HELP_STRING([--with-eccodes=<yes|no|directory> (default=no)],
+                            [location of ECCODES library for grib2 encoding/decoding (lib and include subdirs)])],
             [AS_CASE(["$with_eccodes"],
                      [no],[AC_MSG_CHECKING([for ECCODES library])
                            AC_MSG_RESULT([suppressed])],
@@ -303,40 +304,6 @@ AC_ARG_WITH([eccodes],
              AC_MSG_RESULT([suppressed])])
 AC_SUBST([ECCODES_INCLUDE])
 AC_SUBST([ECCODES_LIBS])
-#AC_SUBST([ENABLE_GRIBAPI])
-#  ----------------------------------------------------------------------
-#  Compile application with GRIB_API library (for GRIB2 support)
-GRIB_API_INCLUDE=''
-GRIB_API_LIBS=''
-#ENABLE_GRIBAPI=no
-AC_ARG_WITH([grib_api],
-            [AS_HELP_STRING([--with-grib_api=<yes|no|directory> (default=no)],[location of GRIB_API library (lib and include subdirs)])],
-            [AS_CASE(["$with_grib_api"],
-                     [no],[AC_MSG_CHECKING([for GRIB_API library])
-                           AC_MSG_RESULT([suppressed])],
-                     [yes],[AC_CHECK_HEADERS([grib_api.h])
-                            AC_SEARCH_LIBS([grib_get_message],
-                                           [grib_api],
-                                           [AC_DEFINE([HAVE_LIBGRIB_API],[1],[GRIB_API library is present if defined to 1])
-                                            ENABLE_GRIBAPI=yes],
-                                           [AC_MSG_ERROR([Could not link to grib_api library])])],
-                     [*],[GRIB_API_ROOT=$with_grib_api
-                          AS_IF([test -d "$GRIB_API_ROOT"],
-                                [LDFLAGS="-L$GRIB_API_ROOT/lib $LDFLAGS"
-                                 CPPFLAGS="-I$GRIB_API_ROOT/include $CPPFLAGS"
-                                 AC_CHECK_HEADERS([grib_api.h])
-                                 AC_SEARCH_LIBS([grib_get_message],
-                                                [grib_api],
-                                                [AC_DEFINE([HAVE_LIBGRIB_API],[1],[GRIB_API library is present if defined to 1])
-                                                 ENABLE_GRIBAPI=yes],
-                                                [AC_MSG_ERROR([Could not link to grib_api library])])
-                                 GRIB_API_LIBS=" -L$GRIB_API_ROOT/lib -lgrib_api"
-                                 GRIB_API_INCLUDE=" -I$GRIB_API_ROOT/include"],
-                                [AC_MSG_ERROR([$GRIB_API_ROOT is not a directory! GRIB_API suppressed])])])],
-            [AC_MSG_CHECKING([for the GRIB_API library])
-             AC_MSG_RESULT([suppressed])])
-AC_SUBST([GRIB_API_INCLUDE])
-AC_SUBST([GRIB_API_LIBS])
 AC_SUBST([ENABLE_GRIBAPI])
 #  ----------------------------------------------------------------------
 #  Enable GRIB support
diff --git a/scripts/cppcheck.sh b/scripts/cppcheck.sh
index 6d3b1eef3632240e3023fca27c56a8014daa025e..9e39ce7d9d6bfb3c7fec030d59ce454173b097b7 100755
--- a/scripts/cppcheck.sh
+++ b/scripts/cppcheck.sh
@@ -22,9 +22,9 @@ echo $TOPDIR
 # set -x
 echo "" > ${LOG_FILE}
 dirname=$TOPDIR/src
-cppcheck --std=c++20  --force --enable=all --inline-suppr --template='{file}:{line},{severity},{id},{message}' \
+cppcheck --checkers-report=critical_errors --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  \
+         -DHAVE_CONFIG_H  -DCDI_SIZE_TYPE=size_t \
          "$dirname" >>${LOG_FILE} 2>&1
 #
 
diff --git a/src/Adisit.cc b/src/Adisit.cc
index a772ac61f8cd4796a29ce2a9e6d4e5b7dd4c5cd3..4a7303727928aa56c4b93ad5b4d7e93f3e2ad7f3 100644
--- a/src/Adisit.cc
+++ b/src/Adisit.cc
@@ -20,7 +20,6 @@
 #include "cdo_vlist.h"
 #include "param_conversion.h"
 #include "util_string.h"
-#include "field_functions.h"
 
 /*
   Transformation from potential to in situ temperature according to Bryden, 1973,
@@ -155,23 +154,23 @@ struct IOSettings
 };
 
 IOSettings
-configureOutput(const std::function<void(const int, const int)> &outputSettingFunc, int vlistID1, int thoID, int saoID,
+configureOutput(const std::function<void(int, int)> &outputSettingFunc, int vlistID, int thoID, int saoID,
                 FieldVector &tho, FieldVector &sao, FieldVector &tis, Varray<double> &pressure)
 {
   double pin = (cdo_operator_argc() == 1) ? parameter_to_double(cdo_operator_argv(0)) : -1.0;
 
-  VarList varList1;
-  varList_init(varList1, vlistID1);
+  VarList varList(vlistID);
+  const auto &vars = varList.vars;
 
-  auto units = varList1[thoID].units;
+  auto units = vars[thoID].units;
   if (units.empty()) units = "Celcius";
 
-  auto gridID = vlistGrid(vlistID1, 0);
-  auto gridsize = vlist_check_gridsize(vlistID1);
+  auto gridID = vlistGrid(vlistID, 0);
+  auto gridsize = vlist_check_gridsize(vlistID);
 
-  auto nlevels1 = varList1[saoID].nlevels;
-  auto nlevels2 = varList1[thoID].nlevels;
-  auto zaxisID = varList1[thoID].zaxisID;
+  auto nlevels1 = vars[saoID].nlevels;
+  auto nlevels2 = vars[thoID].nlevels;
+  auto zaxisID = vars[thoID].zaxisID;
 
   if (nlevels1 != nlevels2) cdo_abort("temperature and salinity have different number of levels!");
   auto nlevels = nlevels1;
@@ -198,17 +197,16 @@ configureOutput(const std::function<void(const int, const int)> &outputSettingFu
       tho[levelID].resize(gridsize);
       sao[levelID].resize(gridsize);
       tis[levelID].resize(gridsize);
-      tho[levelID].missval = varList1[thoID].missval;
-      sao[levelID].missval = varList1[saoID].missval;
+      tho[levelID].missval = vars[thoID].missval;
+      sao[levelID].missval = vars[saoID].missval;
       tis[levelID].missval = tho[levelID].missval;
     }
 
   int datatype = CDI_DATATYPE_FLT32;
-  if (varList1[thoID].datatype == CDI_DATATYPE_FLT64 && varList1[saoID].datatype == CDI_DATATYPE_FLT64)
-    datatype = CDI_DATATYPE_FLT64;
+  if (vars[thoID].dataType == CDI_DATATYPE_FLT64 && vars[saoID].dataType == CDI_DATATYPE_FLT64) datatype = CDI_DATATYPE_FLT64;
 
   auto vlistID2 = vlistCreate();
-  vlistDefNtsteps(vlistID2, vlistNtsteps(vlistID1));
+  vlistDefNtsteps(vlistID2, vlistNtsteps(vlistID));
 
   auto tisID2 = vlistDefVar(vlistID2, gridID, zaxisID, TIME_VARYING);
 
@@ -226,7 +224,7 @@ configureOutput(const std::function<void(const int, const int)> &outputSettingFu
   vlistDefVarMissval(vlistID2, saoID2, sao[0].missval);
   vlistDefVarDatatype(vlistID2, saoID2, datatype);
 
-  auto taxisID1 = vlistInqTaxis(vlistID1);
+  auto taxisID1 = vlistInqTaxis(vlistID);
   auto taxisID2 = taxisDuplicate(taxisID1);
   vlistDefTaxis(vlistID2, taxisID2);
 
@@ -249,6 +247,7 @@ public:
     .constraints = { 1, 1, NoRestriction },
   };
   inline static RegisterEntry<Adisit> registration = RegisterEntry<Adisit>(module);
+
   int ADISIT, ADIPOT;
   int thoID = -1, saoID = -1;
 
@@ -272,9 +271,8 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
-
     ADISIT = module.get_id("adisit");
     ADIPOT = module.get_id("adipot");
 
@@ -328,8 +326,9 @@ public:
     tisID2 = configResults.tisID2;
     saoID2 = configResults.saoID2;
   }
+
   void
-  run()
+  run() override
   {
     int tsID = 0;
     while (true)
@@ -378,8 +377,9 @@ public:
         tsID++;
       }
   }
+
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
diff --git a/src/Afterburner.cc b/src/Afterburner.cc
index 29c38dfd5f5ebc58c3132dec590d2ace0a1acc02..37dc2a92f0b7d2f2ac0967ede8751966d73beb0f 100644
--- a/src/Afterburner.cc
+++ b/src/Afterburner.cc
@@ -16,10 +16,6 @@
 /*                                                               */
 /* ============================================================= */
 
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
 #ifdef _OPENMP
 #include <omp.h>
 #endif
@@ -33,7 +29,6 @@
 #define streamDefTimestep cdo_def_timestep
 
 #include "afterburner.h"
-#include "constants.h"
 #include "compare.h"
 #include "cdo_options.h"
 #include "cdi_lockedIO.h"
@@ -54,8 +49,6 @@ struct RARG
   AfterControl *globs;
 };
 
-cdo::Task *afterReadTask = nullptr;
-
 static bool lstdout = true;
 static int Source = 0;
 static int ofiletype = -1;
@@ -76,7 +69,6 @@ static int oVertID_half = -1;
 static bool Lhybrid2pressure = false;
 
 static int TsID;
-bool afterReadAsync = true;
 
 #define TIMESTEP_INTERVAL -1
 #define MONTHLY_INTERVAL 0
@@ -763,10 +755,7 @@ after_setLevel(AfterControl &globs)
         }
       else if (globs.Verbose)
         {
-          if (Lhybrid2pressure)
-            {
-              fprintf(stdout, " Selected %s level:\n", (globs.unitsel == 0) ? "pressure" : "height");
-            }
+          if (Lhybrid2pressure) { fprintf(stdout, " Selected %s level:\n", (globs.unitsel == 0) ? "pressure" : "height"); }
           else
             {
               if (iLevelType == ZAXIS_HYBRID)
@@ -935,7 +924,8 @@ after_defineLevel(const AfterControl &globs, struct Variable *vars)
                   {
                     int zaxisType = zaxisInqType(zaxisID);
                     int numLevels = zaxisInqSize(zaxisID);
-                    if (zaxisType == zaxisInqType(iVertID) && (numLevels == globs.NumLevel || numLevels == globs.NumLevel + 1) && numLevels > 1)
+                    if (zaxisType == zaxisInqType(iVertID) && (numLevels == globs.NumLevel || numLevels == globs.NumLevel + 1)
+                        && numLevels > 1)
                       vars[code].ozaxisID = oVertID;
                   }
               }
@@ -1278,8 +1268,8 @@ after_precntl(AfterControl &globs, struct Variable *vars)
   int gridSizeMax = 0;
 
   auto nvars = vlistNvars(globs.ivlistID);
-  auto ngrids = vlistNgrids(globs.ivlistID);
-  auto nverts = vlistNzaxis(globs.ivlistID);
+  auto ngrids = vlistNumGrids(globs.ivlistID);
+  auto nverts = vlistNumZaxis(globs.ivlistID);
   auto ntsteps = vlistNtsteps(globs.ivlistID);
 
   if (globs.Debug)
@@ -1382,7 +1372,7 @@ after_precntl(AfterControl &globs, struct Variable *vars)
                         {
                           globs.nvct = zaxisInqVctSize(zaxisID);
                           globs.numHalfLevels = globs.nvct / 2;
-                          if ((int)globs.vct.size() != globs.nvct)
+                          if ((int) globs.vct.size() != globs.nvct)
                             {
                               globs.vct.resize(globs.nvct);
                               zaxisInqVct(zaxisID, globs.vct.data());
@@ -1391,10 +1381,7 @@ after_precntl(AfterControl &globs, struct Variable *vars)
                       else { afterAbort("VCT not defined in inputfile!"); }
                     }
 
-                  if (numlevel == globs.numHalfLevels)
-                    {
-                      globs.NumLevelFound--;
-                    }
+                  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);
@@ -1811,7 +1798,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
     lstdout = !Options::silentMode;
 
@@ -1844,13 +1831,13 @@ public:
   }
 
   void
-  run()
+  run() override
   {
     after_processing(globs, vars);
   }
 
   void
-  close()
+  close() override
   {
     FreeMean(vars);
   }
diff --git a/src/Arith.cc b/src/Arith.cc
index 68b8f6bffe48b4b3a8632a571090446af6653ab8..87d1ee69fe21da46f7cd4ed0f776c6d2d91f9bf2 100644
--- a/src/Arith.cc
+++ b/src/Arith.cc
@@ -88,11 +88,12 @@ public:
   Field *fieldx1;
   Field *fieldx2;
 
-  VarList varList1, varList2;
+  VarList varList1;
+  VarList varList2;
 
 public:
   void
-  init()
+  init() override
   {
     auto operatorID = cdo_operator_id();
     operfunc = cdo_operator_f1(operatorID);
@@ -129,8 +130,8 @@ public:
 
     if (nvars1 == 1 && nvars2 == 1)
       {
-        fillStream2 = (vlistNrecs(vlistID1) != 1 && vlistNrecs(vlistID2) == 1);
-        fillStream1 = (vlistNrecs(vlistID1) == 1 && vlistNrecs(vlistID2) != 1);
+        fillStream2 = (vlistNumRecords(vlistID1) != 1 && vlistNumRecords(vlistID2) == 1);
+        fillStream1 = (vlistNumRecords(vlistID1) == 1 && vlistNumRecords(vlistID2) != 1);
         if (fillStream1 && ntsteps1 != 1 && ntsteps2 == 1)
           {
             fillStream1 = false;
@@ -146,14 +147,14 @@ public:
 
     if (fillStream1x)
       {
-        nlevels2 = vlist_compare_x(vlistID2, vlistID1, CmpVlist::Dim);
+        nlevels2 = vlist_compare_x(vlistID2, vlistID1, CmpVarList::Dim);
 
         fillType = FillType::NONE;
         cdo_print("Filling up stream1 >%s< by copying the first variable of each timestep.", cdo_get_stream_name(0));
       }
     else if (fillStream2)
       {
-        nlevels2 = vlist_compare_x(vlistID1, vlistID2, CmpVlist::Dim);
+        nlevels2 = vlist_compare_x(vlistID1, vlistID2, CmpVarList::Dim);
 
         if (ntsteps1 != 1 && ntsteps2 == 1)
           {
@@ -168,7 +169,7 @@ public:
       }
     else if (fillStream1)
       {
-        nlevels2 = vlist_compare_x(vlistID2, vlistID1, CmpVlist::Dim);
+        nlevels2 = vlist_compare_x(vlistID2, vlistID1, CmpVarList::Dim);
 
         if (ntsteps1 == 1 && ntsteps2 != 1)
           {
@@ -189,7 +190,7 @@ public:
         fieldx2 = &field1;
       }
 
-    if (fillStream1x == false && fillType == FillType::NONE) vlist_compare(vlistID1, vlistID2, CmpVlist::All);
+    if (fillStream1x == false && fillType == FillType::NONE) vlist_compare(vlistID1, vlistID2, CmpVarList::All);
 
     nwpv = (vlistNumber(vlistID1x) == CDI_COMP && vlistNumber(vlistID2x) == CDI_COMP) ? 2 : 1;
     if (nwpv == 2 && !opercplx) cdo_abort("Fields with complex numbers are not supported by this operator!");
@@ -252,12 +253,12 @@ public:
     streamID3 = cdo_open_write(2);
     cdo_def_vlist(streamID3, vlistID3);
 
-    varList_init(varList1, vlistID1x);
-    varList_init(varList2, vlistID2x);
+    varList1 = VarList(vlistID1x);
+    varList2 = VarList(vlistID2x);
   }
 
   void
-  run()
+  run() override
   {
     int nrecs, nrecs2;
     int tsID = 0;
@@ -318,7 +319,7 @@ public:
 
                 if (fillStream1x)
                   {
-                    auto gridsize = nwpv * varList1[varID].gridsize;
+                    auto gridsize = nwpv * varList1.vars[varID].gridsize;
                     array_copy(gridsize, fieldx1->vec_d.data(), &vardata2[0]);
                     varnumMissVals2[0] = fieldx1->numMissVals;
                   }
@@ -342,14 +343,14 @@ public:
 
                 if (fillType == FillType::TS)
                   {
-                    auto gridsize = nwpv * varList2[varID].gridsize;
+                    auto gridsize = nwpv * varList2.vars[varID].gridsize;
                     auto offset = gridsize * levelID;
                     array_copy(gridsize, fieldx2->vec_d.data(), &vardata[varID][offset]);
                     varnumMissVals[varID][levelID] = fieldx2->numMissVals;
                   }
                 else if (lstatus && (fillType == FillType::VAR || fillType == FillType::VARTS))
                   {
-                    auto gridsize = nwpv * varList2[0].gridsize;
+                    auto gridsize = nwpv * varList2.vars[0].gridsize;
                     auto offset = gridsize * levelID2;
                     array_copy(gridsize, fieldx2->vec_d.data(), &vardata2[offset]);
                     varnumMissVals2[levelID2] = fieldx2->numMissVals;
@@ -357,7 +358,7 @@ public:
               }
             else if (fillType == FillType::TS)
               {
-                auto gridsize = nwpv * varList2[varID2].gridsize;
+                auto gridsize = nwpv * varList2.vars[varID2].gridsize;
                 auto offset = gridsize * levelID;
                 array_copy(gridsize, &vardata[varID][offset], fieldx2->vec_d.data());
                 fieldx2->numMissVals = varnumMissVals[varID][levelID];
@@ -365,31 +366,31 @@ public:
 
             if (fillStream1x)
               {
-                auto gridsize = nwpv * varList1[0].gridsize;
+                auto gridsize = nwpv * varList1.vars[0].gridsize;
                 array_copy(gridsize, &vardata2[0], fieldx1->vec_d.data());
                 fieldx1->numMissVals = varnumMissVals2[0];
               }
 
-            fieldx1->grid = varList1[varID].gridID;
-            fieldx1->missval = varList1[varID].missval;
-            fieldx1->nwpv = varList1[varID].nwpv;
+            fieldx1->grid = varList1.vars[varID].gridID;
+            fieldx1->missval = varList1.vars[varID].missval;
+            fieldx1->nwpv = varList1.vars[varID].nwpv;
 
             if (fillType == FillType::VAR || fillType == FillType::VARTS)
               {
                 levelID2 = (nlevels2 > 1) ? levelID : 0;
-                auto gridsize = nwpv * varList2[0].gridsize;
+                auto gridsize = nwpv * varList2.vars[0].gridsize;
                 auto offset = gridsize * levelID2;
                 array_copy(gridsize, &vardata2[offset], fieldx2->vec_d.data());
                 fieldx2->numMissVals = varnumMissVals2[levelID2];
-                fieldx2->grid = varList2[0].gridID;
-                fieldx2->missval = varList2[0].missval;
-                fieldx2->nwpv = varList2[0].nwpv;
+                fieldx2->grid = varList2.vars[0].gridID;
+                fieldx2->missval = varList2.vars[0].missval;
+                fieldx2->nwpv = varList2.vars[0].nwpv;
               }
             else
               {
-                fieldx2->grid = varList2[varID2].gridID;
-                fieldx2->missval = varList2[varID2].missval;
-                fieldx2->nwpv = varList2[varID2].nwpv;
+                fieldx2->grid = varList2.vars[varID2].gridID;
+                fieldx2->missval = varList2.vars[varID2].missval;
+                fieldx2->nwpv = varList2.vars[varID2].nwpv;
               }
 
             if (nwpv == 2)
@@ -410,7 +411,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID3);
     cdo_stream_close(streamID2);
diff --git a/src/Arithc.cc b/src/Arithc.cc
index cb68f769e885a906505e11667406a0a469834b47..7b520feae8894e01e9368ebacc24fbb8c036b9c2 100644
--- a/src/Arithc.cc
+++ b/src/Arithc.cc
@@ -12,6 +12,8 @@
       Arithc     subc            Subtract by constant
       Arithc     mulc            Multiply by constant
       Arithc     divc            Divide by constant
+      Arithc     minc            Minimum of a field and a constant
+      Arithc     maxc            Maximum of a field and a constant
       Arithc     mod             Modulo operator
 */
 
@@ -26,16 +28,16 @@
 static void
 fill_vars(const VarList &varList, std::vector<bool> &vars)
 {
-  auto nvars = vars.size();
+  auto numVars = varList.numVars();
 
   if (Options::cdo_num_varnames() > 0)
     {
       auto found = false;
-      for (size_t varID = 0; varID < nvars; ++varID)
+      for (int varID = 0; varID < numVars; ++varID)
         {
           vars[varID] = false;
           for (size_t i = 0; i < Options::cdo_num_varnames(); ++i)
-            if (varList[varID].name == Options::cdoVarnames[i])
+            if (varList.vars[varID].name == Options::cdoVarnames[i])
               {
                 vars[varID] = true;
                 found = true;
@@ -47,7 +49,7 @@ fill_vars(const VarList &varList, std::vector<bool> &vars)
     }
   else
     {
-      for (size_t varID = 0; varID < nvars; ++varID) vars[varID] = true;
+      for (int varID = 0; varID < numVars; ++varID) vars[varID] = true;
     }
 }
 
@@ -86,7 +88,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
     auto operatorID = cdo_operator_id();
     operfunc = cdo_operator_f1(operatorID);
@@ -109,10 +111,10 @@ public:
 
     vlist_unpack(vlistID2);
 
-    varList_init(varList1, vlistID1);
+    varList1 = VarList(vlistID1);
 
     auto nvars = vlistNvars(vlistID1);
-    // for (int varID = 0; varID < nvars; ++varID) varList1[varID].memType = MemType::Double;
+    // for (int varID = 0; varID < nvars; ++varID) varList1.vars[varID].memType = MemType::Double;
 
     vars = std::vector<bool>(nvars);
     fill_vars(varList1, vars);
@@ -131,7 +133,7 @@ public:
   }
 
   void
-  run()
+  run() override
   {
     Field field;
     int tsID = 0;
@@ -147,7 +149,7 @@ public:
           {
             int varID, levelID;
             cdo_inq_record(streamID1, &varID, &levelID);
-            field.init(varList1[varID]);
+            field.init(varList1.vars[varID]);
             cdo_read_record(streamID1, field);
 
             if (vars[varID])
@@ -170,7 +172,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
diff --git a/src/Arithdays.cc b/src/Arithdays.cc
index be68aa8a89172879b4689609a791eb9e3a1ed82b..7890cb7972e6fcbf4515af7f94b9ecf51aa514ff 100644
--- a/src/Arithdays.cc
+++ b/src/Arithdays.cc
@@ -89,16 +89,12 @@ public:
   int calendar;
 
   VarList varList1;
-  Field field;
 
 public:
   void
-  init()
+  init() override
   {
-
-    // clang-format off
-MULDOY = module.get_id("muldoy");
-    // clang-format on
+    MULDOY = module.get_id("muldoy");
 
     operatorID = cdo_operator_id();
     operfunc = cdo_operator_f1(operatorID);
@@ -108,8 +104,8 @@ MULDOY = module.get_id("muldoy");
 
     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);
@@ -120,15 +116,18 @@ MULDOY = module.get_id("muldoy");
     streamID2 = cdo_open_write(1);
     cdo_def_vlist(streamID2, vlistID2);
 
-    varList_init(varList1, vlistID1);
+    varList1 = VarList(vlistID1);
   }
+
   void
-  run()
+  run() override
   {
+    Field field;
+
     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;
 
         const auto vDateTime = taxisInqVdatetime(taxisID1);
@@ -151,7 +150,7 @@ MULDOY = module.get_id("muldoy");
           {
             int varID, levelID;
             cdo_inq_record(streamID1, &varID, &levelID);
-            field.init(varList1[varID]);
+            field.init(varList1.vars[varID]);
             cdo_read_record(streamID1, field);
 
             fieldc_function(field, rconst, operfunc);
@@ -163,8 +162,9 @@ MULDOY = module.get_id("muldoy");
         tsID++;
       }
   }
+
   void
-  close()
+  close() override
   {
 
     cdo_stream_close(streamID2);
diff --git a/src/Arithlat.cc b/src/Arithlat.cc
index c4500d937aad4fd2cddcd0090b5cee77a57b3732..698b4b0aa5dd5ac311dda2ed8ca93f55a4eb0419 100644
--- a/src/Arithlat.cc
+++ b/src/Arithlat.cc
@@ -50,7 +50,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
     auto operatorID = cdo_operator_id();
     operfunc = cdo_operator_f1(operatorID);
@@ -69,7 +69,7 @@ public:
     streamID2 = cdo_open_write(1);
     cdo_def_vlist(streamID2, vlistID2);
 
-    varList_init(varList1, vlistID1);
+    varList1 = VarList(vlistID1);
 
     auto gridsizemax = vlistGridsizeMax(vlistID1);
 
@@ -77,7 +77,7 @@ public:
   }
 
   void
-  run()
+  run() override
   {
     int gridID0 = -1;
     int tsID = 0;
@@ -96,9 +96,10 @@ public:
             size_t numMissVals;
             cdo_read_record(streamID1, array.data(), &numMissVals);
 
-            auto gridID = varList1[varID].gridID;
-            auto gridsize = varList1[varID].gridsize;
-            auto missval = varList1[varID].missval;
+            const auto &var1 = varList1.vars[varID];
+            auto gridID = var1.gridID;
+            auto gridsize = var1.gridsize;
+            auto missval = var1.missval;
 
             if (gridID != gridID0)
               {
@@ -124,7 +125,7 @@ public:
             if (numMissVals)
               {
                 for (size_t i = 0; i < gridsize; ++i)
-                  if (!DBL_IS_EQUAL(array[i], missval)) array[i] *= scale[i];
+                  if (dbl_is_not_equal(array[i], missval)) array[i] *= scale[i];
               }
             else
               {
@@ -140,7 +141,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
diff --git a/src/Bitrounding.cc b/src/Bitrounding.cc
index cb0b2ae0ed2ab10dfadbec44f883a034cf93ad78..f9273592a4ed1d89d372088169a016e942a3af16 100644
--- a/src/Bitrounding.cc
+++ b/src/Bitrounding.cc
@@ -8,7 +8,6 @@
 #include <cdi.h>
 
 #include "process_int.h"
-#include "cdo_vlist.h"
 #include "cdo_options.h"
 #include "pmlist.h"
 #include "param_conversion.h"
@@ -247,7 +246,7 @@ set_global_attributes(int vlistID, const BitroundParams &params, int numVarsHave
 static std::vector<int>
 get_vars_numbits(const VarList &varList, const std::string &filename)
 {
-  auto numVars = varList.size();
+  auto numVars = varList.numVars();
   std::vector<int> varsNumbits(numVars, -1);
 
   if (filename.size())
@@ -266,14 +265,14 @@ get_vars_numbits(const VarList &varList, const std::string &filename)
           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);
 
-          for (size_t varID = 0; varID < numVars; ++varID)
+          for (const auto &var : varList.vars)
             {
-              if (key == varList[varID].name)
+              if (key == var.name)
                 {
                   const auto &value = kv.values[0];
                   auto numBits = parameter_to_int(value);
                   check_range(numBits, 1, 23, key);
-                  varsNumbits[varID] = numBits;
+                  varsNumbits[var.ID] = numBits;
                 }
             }
         }
@@ -314,11 +313,8 @@ public:
   int vlistID2;
 
   BitroundParams params;
-  int ntsteps;
-  int nvars;
 
   VarList varList1;
-  Field field;
 
   std::vector<VarStat> varsStatGlob;
   std::vector<bool> varsCheckMiss;
@@ -328,7 +324,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
     params = get_parameter();
     if (Options::cdoVerbose) print_parameter(params);
@@ -337,7 +333,7 @@ public:
     auto vlistID1 = cdo_stream_inq_vlist(streamID1);
     taxisID1 = vlistInqTaxis(vlistID1);
 
-    varList_init(varList1, vlistID1);
+    varList1 = VarList(vlistID1);
 
     varsNumbits = get_vars_numbits(varList1, params.filename);
     auto numVarsHaveNumbits = num_vars_have_numbits(varsNumbits);
@@ -349,34 +345,35 @@ public:
     check_attributes(vlistID1);
     set_global_attributes(vlistID2, params, numVarsHaveNumbits);
 
-    nvars = vlistNvars(vlistID1);
-    for (int varID = 0; varID < nvars; ++varID)
+    auto numVars = varList1.numVars();
+    for (const auto &var : varList1.vars)
       {
-        if (varList1[varID].memType == MemType::Float)
+        if (var.memType == MemType::Float)
           {
-            int nsb = (varsNumbits[varID] != -1) ? varsNumbits[varID] : params.numBits;
-            if (nsb >= 1 && nsb <= 23) set_local_attributes(vlistID2, varID, nsb);
+            int nsb = (varsNumbits[var.ID] != -1) ? varsNumbits[var.ID] : params.numBits;
+            if (nsb >= 1 && nsb <= 23) set_local_attributes(vlistID2, var.ID, nsb);
           }
       }
 
     streamID2 = cdo_open_write(1);
     cdo_def_vlist(streamID2, vlistID2);
 
-    varsStatGlob = std::vector<VarStat>(nvars);
-    varsCheckMiss = std::vector<bool>(nvars, true);
-    varsCheckFloat = std::vector<bool>(nvars, true);
+    varsStatGlob = std::vector<VarStat>(numVars);
+    varsCheckMiss = std::vector<bool>(numVars, true);
+    varsCheckFloat = std::vector<bool>(numVars, true);
 
-    nsbVarLevels = std::vector<std::vector<int>>(nvars);
-    for (int varID = 0; varID < nvars; ++varID) nsbVarLevels[varID].resize(varList1[varID].nlevels, 0);
-
-    ntsteps = vlistNtsteps(vlistID1);
-
-    progress::init();
+    nsbVarLevels = std::vector<std::vector<int>>(numVars);
+    for (int varID = 0; varID < numVars; ++varID) nsbVarLevels[varID].resize(varList1.vars[varID].nlevels, 0);
   }
 
   void
-  run()
+  run() override
   {
+    Field field;
+    auto numVars = varList1.numVars();
+    auto numSteps = varList1.numSteps();
+    cdo::Progress progress;
+
     int tsID = 0;
     while (true)
       {
@@ -386,18 +383,17 @@ public:
         cdo_taxis_copy_timestep(taxisID2, taxisID1);
         cdo_def_timestep(streamID2, tsID);
 
-        std::vector<VarStat> varsStat(nvars);
+        std::vector<VarStat> varsStat(numVars);
 
         for (int recID = 0; recID < nrecs; ++recID)
           {
-            auto fstatus = (ntsteps >= 0) ? (tsID + (recID + 1.0) / nrecs) / ntsteps : 1.0;
-            if (!Options::cdoVerbose) progress::update(0, 1, fstatus);
+            auto fstatus = (tsID + (recID + 1.0) / nrecs) / numSteps;
+            if (numSteps > 0) progress.update(fstatus);
 
-            int varID, levelID;
-            cdo_inq_record(streamID1, &varID, &levelID);
+            auto [varID, levelID] = cdo_inq_record(streamID1);
             cdo_def_record(streamID2, varID, levelID);
 
-            const auto &var = varList1[varID];
+            const auto &var = varList1.vars[varID];
             field.init(var);
             cdo_read_record(streamID1, field);
 
@@ -427,8 +423,9 @@ public:
                     else if (params.numSteps == 1) { nsb = nsbVarLevels[varID][levelID]; }
                   }
 
-                varsStat[varID].nsbMin = std::min(varsStat[varID].nsbMin, nsb);
-                varsStat[varID].nsbMax = std::max(varsStat[varID].nsbMax, nsb);
+                auto &varStat = varsStat[varID];
+                varStat.nsbMin = std::min(varStat.nsbMin, nsb);
+                varStat.nsbMax = std::max(varStat.nsbMax, nsb);
 
                 if (nsb >= 1 && nsb <= 23) bitround(nsb, field.size, field.vec_f, var.missval);
 
@@ -445,19 +442,23 @@ public:
         if (Options::cdoVerbose && params.numBits == -1)
           {
             fprintf(stderr, "NSB: step=%d:", tsID + 1);
-            for (int varID = 0; varID < nvars; ++varID)
-              if (varsStat[varID].nsbMin >= 1 && varsStat[varID].nsbMin <= 23)
-                {
-                  fprintf(stderr, " %s=%d", varList1[varID].name.c_str(), varsStat[varID].nsbMin);
-                  if (varList1[varID].nlevels > 1) fprintf(stderr, "-%d ", varsStat[varID].nsbMax);
-                }
+            for (const auto &var1 : varList1.vars)
+              {
+                const auto &varStat = varsStat[var1.ID];
+                if (varStat.nsbMin >= 1 && varStat.nsbMin <= 23)
+                  {
+                    fprintf(stderr, " %s=%d", var1.name.c_str(), varStat.nsbMin);
+                    if (var1.nlevels > 1) fprintf(stderr, "-%d ", varStat.nsbMax);
+                  }
+              }
             fprintf(stderr, "\n");
           }
 
-        for (int varID = 0; varID < nvars; ++varID)
+        for (int varID = 0; varID < numVars; ++varID)
           {
-            varsStatGlob[varID].nsbMin = std::min(varsStatGlob[varID].nsbMin, varsStat[varID].nsbMin);
-            varsStatGlob[varID].nsbMax = std::max(varsStatGlob[varID].nsbMax, varsStat[varID].nsbMax);
+            auto &varStatGlob = varsStatGlob[varID];
+            varStatGlob.nsbMin = std::min(varStatGlob.nsbMin, varsStat[varID].nsbMin);
+            varStatGlob.nsbMax = std::max(varStatGlob.nsbMax, varsStat[varID].nsbMax);
           }
 
         if (params.printBits) break;
@@ -467,25 +468,31 @@ public:
 
     if (params.printBits)
       {
-        for (int varID = 0; varID < nvars; ++varID)
-          if (varsStatGlob[varID].nsbMin >= 1 && varsStatGlob[varID].nsbMin <= 23)
-            fprintf(stdout, "%s=%d\n", varList1[varID].name.c_str(), varsStatGlob[varID].nsbMax);
+        for (const auto &var1 : varList1.vars)
+          {
+            const auto &varStatGlob = varsStatGlob[var1.ID];
+            if (varStatGlob.nsbMin >= 1 && varStatGlob.nsbMin <= 23)
+              fprintf(stdout, "%s=%d\n", var1.name.c_str(), varStatGlob.nsbMax);
+          }
       }
     else if (Options::cdoVerbose && params.numBits == -1)
       {
         fprintf(stderr, "NSB: step=all:");
-        for (int varID = 0; varID < nvars; ++varID)
-          if (varsStatGlob[varID].nsbMin >= 1 && varsStatGlob[varID].nsbMin <= 23)
-            {
-              fprintf(stderr, " %s=%d", varList1[varID].name.c_str(), varsStatGlob[varID].nsbMin);
-              if (varsStatGlob[varID].nsbMin != varsStatGlob[varID].nsbMax) fprintf(stderr, "-%d", varsStatGlob[varID].nsbMax);
-            }
-        fprintf(stderr, "\n");
+        for (const auto &var1 : varList1.vars)
+          {
+            const auto &varStatGlob = varsStatGlob[var1.ID];
+            if (varStatGlob.nsbMin >= 1 && varStatGlob.nsbMin <= 23)
+              {
+                fprintf(stderr, " %s=%d", var1.name.c_str(), varStatGlob.nsbMin);
+                if (varStatGlob.nsbMin != varStatGlob.nsbMax) fprintf(stderr, "-%d", varStatGlob.nsbMax);
+              }
+            fprintf(stderr, "\n");
+          }
       }
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID1);
     cdo_stream_close(streamID2);
diff --git a/src/CDIread.cc b/src/CDIread.cc
index 1e211bdc4b53c05e5d28d116d089645760400571..e5b7718c75c7d095dc0cecd331d36256fabdb33b 100644
--- a/src/CDIread.cc
+++ b/src/CDIread.cc
@@ -9,7 +9,6 @@
 
 #include "cdo_options.h"
 #include "cdo_timer.h"
-#include "cdo_vlist.h"
 #include "param_conversion.h"
 #include "process_int.h"
 #include "util_files.h"
@@ -34,7 +33,7 @@ public:
   using Process::Process;
   inline static CdoModule module = {
     .name = "CDIread",
-    .operators = { { "cdiread"} },
+    .operators = { { "cdiread" } },
     .aliases = {},
     .mode = EXPOSED,     // Module mode: 0:intern 1:extern
     .number = CDI_REAL,  // Allowed number type
@@ -52,9 +51,8 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
-
     sinfo[0] = 0;
 
     if (Options::cdoVerbose) cdo_print("parameter: <nruns>");
@@ -69,8 +67,9 @@ public:
 
     // vlistDefNtsteps(vlistID, 1);
   }
+
   void
-  run()
+  run() override
   {
     for (int irun = 0; irun < nruns; ++irun)
       {
@@ -81,8 +80,7 @@ public:
         auto streamID = cdo_open_read(0);
         auto vlistID = cdo_stream_inq_vlist(streamID);
 
-        VarList varList;
-        varList_init(varList, vlistID);
+        VarList varList(vlistID);
 
         filetype = cdo_inq_filetype(streamID);
         datatype = vlistInqVarDatatype(vlistID, 0);
@@ -107,7 +105,7 @@ public:
                 int varID, levelID;
                 cdo_inq_record(streamID, &varID, &levelID);
 
-                auto gridsize = varList[varID].gridsize;
+                auto gridsize = varList.vars[varID].gridsize;
                 nvalues += gridsize;
 
                 size_t numMissVals;
@@ -142,8 +140,9 @@ public:
 
     if (nruns > 1) print_stat("(mean)", memtype, datatype, filetype, nvalues, dataSize, fileSize, runTimeSum / nruns);
   }
+
   void
-  close()
+  close() override
   {
   }
 };
diff --git a/src/CDItest.cc b/src/CDItest.cc
index c734c2c092ffebcea3c8e22b2337ac9d7e216707..f5e8b31bd5dc7831e5a1e9d23df9900e70db9845 100644
--- a/src/CDItest.cc
+++ b/src/CDItest.cc
@@ -21,22 +21,22 @@ public:
   using Process::Process;
   inline static CdoModule module = {
     .name = "CDItest",
-    .operators = { { "ncopy"} },
+    .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()
+  init() override
   {
-
     dataIsUnchanged = false;
     // auto dataIsUnchanged = data_is_unchanged();
 
@@ -49,8 +49,9 @@ public:
     //  operator_input_arg("Number of copies");
     max_copy = (cdo_operator_argc() == 1) ? parameter_to_int(cdo_operator_argv(0)) : 3;
   }
+
   void
-  run()
+  run() override
   {
     int n = 0;
     while (true)
@@ -71,8 +72,7 @@ public:
 
         Field field;
 
-        VarList varList1;
-        varList_init(varList1, vlistID1);
+        VarList varList1(vlistID1);
 
         int tsID = 0;
         while (true)
@@ -93,7 +93,7 @@ public:
                 if (dataIsUnchanged) { cdo_copy_record(streamID2, streamID1); }
                 else
                   {
-                    field.init(varList1[varID]);
+                    field.init(varList1.vars[varID]);
                     cdo_read_record(streamID1, field);
                     cdo_write_record(streamID2, field);
                   }
@@ -117,7 +117,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
   }
 };
diff --git a/src/CDIwrite.cc b/src/CDIwrite.cc
index 15dac3d8549d4a70c4e1a10a3a2bfa09829b528d..092e633af63bb4fe64890668ec33d4d281690ec4 100644
--- a/src/CDIwrite.cc
+++ b/src/CDIwrite.cc
@@ -7,7 +7,6 @@
 
 #include <cdi.h>
 #include <algorithm>
-#include "julian_date.h"
 
 #include "cdo_options.h"
 #include "cdo_timer.h"
@@ -133,7 +132,7 @@ public:
   using Process::Process;
   inline static CdoModule module = {
     .name = "CDIwrite",
-    .operators = { { "cdiwrite"} },
+    .operators = { { "cdiwrite" } },
     .aliases = {},
     .mode = EXPOSED,     // Module mode: 0:intern 1:extern
     .number = CDI_REAL,  // Allowed number type
@@ -160,7 +159,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
     if (Options::cdoVerbose) cdo_print("parameter: nruns/nvars/nlevs/nsteps/grid/varysteps");
 
@@ -225,7 +224,7 @@ public:
   }
 
   void
-  run()
+  run() override
   {
     for (int irun = 0; irun < params.nruns; ++irun)
       {
@@ -309,7 +308,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     vlistDestroy(vlistID);
   }
diff --git a/src/CMOR.cc b/src/CMOR.cc
index 2ca53f3c8466d0373efdbb02628afee88398544b..0c1bb5930586dd30eaa7bea4eeeed9772f79d17c 100644
--- a/src/CMOR.cc
+++ b/src/CMOR.cc
@@ -6373,7 +6373,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
 #ifndef HAVE_LIBCMOR
     cdo_abort("CMOR support not compiled in!");
@@ -6426,7 +6426,7 @@ public:
   }
 
   void
-  run()
+  run() override
   {
 
 #ifdef HAVE_LIBCMOR
@@ -6462,7 +6462,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
 #ifdef HAVE_LIBCMOR
     destruct_var_mapping(vars);
diff --git a/src/CMOR_lite.cc b/src/CMOR_lite.cc
index e2e2e5bea71ce3dd6316f59697212707613a4e56..73dbaaa1e87cbf8e95284c01a48bfa3d91ee7d7f 100644
--- a/src/CMOR_lite.cc
+++ b/src/CMOR_lite.cc
@@ -118,7 +118,7 @@ cmor_check_prep(CmorVar &var, long gridsize, double missval, const double *const
       for (long i = 0; i < gridsize; ++i)
         {
           auto aval = array[i];
-          if (!dbl_is_equal(aval, missval))
+          if (dbl_is_not_equal(aval, missval))
             {
               amean += std::fabs(aval);
               nvals++;
@@ -133,7 +133,7 @@ cmor_check_prep(CmorVar &var, long gridsize, double missval, const double *const
       for (long i = 0; i < gridsize; ++i)
         {
           auto aval = array[i];
-          if (!dbl_is_equal(aval, missval))
+          if (dbl_is_not_equal(aval, missval))
             {
               if (aval < var.valid_min) n_lower_min++;
               if (aval > var.valid_max) n_greater_max++;
@@ -185,11 +185,11 @@ apply_cmor_list(PMList &pmlist, int nvars, int vlistID2, std::vector<CmorVar> &v
 
       if (hasMissvals)
         {
-          auto missval_old = vlistInqVarMissval(vlistID2, varID);
-          if (!dbl_is_equal(missval, missval_old))
+          auto missvalOld = vlistInqVarMissval(vlistID2, varID);
+          if (dbl_is_not_equal(missval, missvalOld))
             {
               var.changemissval = true;
-              var.missval_old = missval_old;
+              var.missvalOld = missvalOld;
               vlistDefVarMissval(vlistID2, varID, missval);
             }
         }
@@ -238,12 +238,12 @@ apply_cmor_list(PMList &pmlist, int nvars, int vlistID2, std::vector<CmorVar> &v
               else if (key == "missval" || key == "missing_value")
                 {
                   missval = parameter_to_double(value);
-                  auto missval_old = vlistInqVarMissval(vlistID2, varID);
-                  if (!dbl_is_equal(missval, missval_old))
+                  auto missvalOld = vlistInqVarMissval(vlistID2, varID);
+                  if (dbl_is_not_equal(missval, missvalOld))
                     {
-                      if (Options::cdoVerbose) cdo_print("%s - change missval from %g to %g", var.name, missval_old, missval);
+                      if (Options::cdoVerbose) cdo_print("%s - change missval from %g to %g", var.name, missvalOld, missval);
                       var.changemissval = true;
-                      var.missval_old = missval_old;
+                      var.missvalOld = missvalOld;
                       vlistDefVarMissval(vlistID2, varID, missval);
                     }
                 }
@@ -316,9 +316,8 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
-
     Options::CMOR_Mode = 1;
     if (Options::CMOR_Mode) cdiDefGlobal("CMOR_MODE", Options::CMOR_Mode);
 
@@ -360,7 +359,7 @@ public:
 
     apply_cmor_list(pmlist, nvars, vlistID2, vars);
 
-    varList_init(varList2, vlistID2);
+    varList2 = VarList(vlistID2);
 
     for (int varID = 0; varID < nvars; ++varID)
       if (vars[varID].remove)
@@ -376,7 +375,7 @@ public:
 
         for (int varID = 0; varID < nvars; ++varID)
           {
-            for (int levID = 0; levID < varList2[varID].nlevels; levID++)
+            for (int levID = 0; levID < varList2.vars[varID].nlevels; levID++)
               {
                 vlistDefFlag(vlistID1, varID, levID, true);
                 vlistDefFlag(vlistID2, varID, levID, true);
@@ -419,7 +418,7 @@ public:
   }
 
   void
-  run()
+  run() override
   {
     int tsID = 0;
     while (true)
@@ -458,15 +457,16 @@ public:
             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;
+            const auto &var2 = varList2.vars[varID2];
+            auto missval = var2.missval;
+            auto gridsize = var2.gridsize;
+            if (var2.nwpv != CDI_REAL) gridsize *= 2;
 
             if (numMissVals && var.changemissval)
               {
                 for (size_t i = 0; i < gridsize; ++i)
                   {
-                    if (dbl_is_equal(array[i], var.missval_old)) array[i] = missval;
+                    if (dbl_is_equal(array[i], var.missvalOld)) array[i] = missval;
                   }
               }
 
@@ -474,7 +474,7 @@ public:
               {
                 for (size_t i = 0; i < gridsize; ++i)
                   {
-                    if (!dbl_is_equal(array[i], missval)) array[i] *= var.factor;
+                    if (dbl_is_not_equal(array[i], missval)) array[i] *= var.factor;
                   }
               }
 
@@ -484,7 +484,7 @@ public:
                 int nerr = 0;
                 for (size_t i = 0; i < gridsize; ++i)
                   {
-                    if (!dbl_is_equal(array[i], missval))
+                    if (dbl_is_not_equal(array[i], missval))
                       {
                         array[i] = cv_convert_double((const cv_converter *) var.ut_converter, array[i]);
                         if (ut_get_status() != UT_SUCCESS) nerr++;
@@ -511,7 +511,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
diff --git a/src/CMOR_table.cc b/src/CMOR_table.cc
index 30f4d9f9dbe4da3e573824c7e24376e549ef5997..6a7460c147b55de062cb8ad5d4259e719de89cc9 100644
--- a/src/CMOR_table.cc
+++ b/src/CMOR_table.cc
@@ -126,7 +126,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
 
     DUMP_CMOR_TABLE = module.get_id("dump_cmor_table");
@@ -143,7 +143,7 @@ public:
     if (fp == nullptr) cdo_abort("Open failed on: %s\n", filename);
   }
   void
-  run()
+  run() override
   {
     PMList pmlist;
     pmlist.read_cmor_table(fp, filename);
@@ -155,7 +155,7 @@ public:
       conv_cmor_table(pmlist);
   }
   void
-  close()
+  close() override
   {
   }
 };
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 0385ad05cab6b6913587002f305c0e57ad493dae..079f4b82fc6805d5de438889dce204004e4d46fe 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,288 +1,294 @@
 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        
+	         	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  
+                cdi_lockedIO.h
+                cdi_uuid.h
+                cdoStream.cc
+                cdoStream.h
+                cdo_cdi_wrapper.cc
+                cdo_cdi_wrapper.h
+                cdo_cmor.h
+                cdo_data.cc
+                cdo_data.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_exception.h
+                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_node_attach_exception.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_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
+                cpp_lib.h
+                cthread_debug.cc
+                cthread_debug.h
+                custom_modules.cc
+                custom_modules.h
+                datarangelist.h
+                datetime.cc
+                datetime.h
+                dcw_reader.cc
+                dcw_reader.h
+                ecacore.cc
+                ecacore.h
+                ecautil.cc
+                ecautil.h
+                eigen_solution.cc
+                eigen_solution.h
+                eof_mode.cc
+                eof_mode.h
+                expr.cc
+                expr.h
+                expr_fun.cc
+                expr_fun.h
+                expr_lex.cc
+                expr_yacc.cc
+                expr_yacc.hh
+                factory.cc
+                factory.h
+                field.cc
+                field.h
+                field2.cc
+                field2_complex.cc
+                field_functions.h
+                field_memory.cc
+                field_meridional.cc
+                field_vinterp.cc
+                field_vinterp.h
+                field_zonal.cc
+                field_trend.cc
+                field_trend.h
+                fieldc.cc
+                fieldc_complex.cc
+                fileStream.cc
+                fileStream.h
+                fill_1d.cc
+                fill_1d.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_healpix.cc
+                mpim_grid/grid_healpix.h
+                mpim_grid/grid_proj.cc
+                mpim_grid/grid_proj.h
+                mpim_grid/grid_rot.cc
+                mpim_grid/grid_rot.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	          
+				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
+				oper_args.cc
+				oper_args.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.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/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
 )
 
@@ -294,244 +300,261 @@ add_library(cdolib
   ${cdolib_src_files}
 )
 
+target_include_directories(cdolib
+    PRIVATE
+        # where the library itself will look for its internal headers
+        ${CMAKE_CURRENT_SOURCE_DIR}/src
+    PUBLIC
+        # where top-level project will look for the library's public headers
+        $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
+        # where external projects will look for the library's public headers
+        $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
+)
+
+
+
 include_directories("${PROJECT_SOURCE_DIR}/src/mpim_grid"
                                   "${PROJECT_SOURCE_DIR}/src"
                                   "${PROJECT_SOURCE_DIR}/libcdi/src"
 )
 
+target_link_libraries(cdolib netCDF::netcdf)
+
 target_compile_definitions(cdolib PUBLIC HAVE_CONFIG_H restrict= CDI_SIZE_TYPE=size_t YAC_FOR_CDO)
 
 
 list( APPEND cdo_src_files
-				cdo.cc               
+				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         
+list( APPEND cdo_operators_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.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
+				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_library(operators ${cdo_operators_src_files})
+target_link_libraries(operators PUBLIC  cdolib cdilib yac gradsdes healpix netCDF::netcdf )
+
 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 )
+target_link_libraries(cdo PUBLIC "$<LINK_LIBRARY:WHOLE_ARCHIVE,operators>")
+target_link_libraries(cdo PUBLIC cdolib cdilib yac gradsdes healpix netCDF::netcdf )
diff --git a/src/Cat.cc b/src/Cat.cc
index aff6e36a3831951e4b697b006c4834f8eb522fa6..9f7d2ee9963a374f956d2447bcea0d09646f2371 100644
--- a/src/Cat.cc
+++ b/src/Cat.cc
@@ -15,8 +15,6 @@
 
 #include "process_int.h"
 #include "cdo_timer.h"
-#include "cdo_vlist.h"
-#include "timer.h"
 #include "util_files.h"
 #include "progress.h"
 #include "cdo_options.h"
@@ -48,27 +46,27 @@ public:
   CdoStreamID streamID2;
   int vlistID2 = CDI_UNDEFID;
   int taxisID2 = CDI_UNDEFID;
-  int nfiles;
+  int numFiles;
   Field field;
 
 public:
   void
-  init()
+  init() override
   {
     operator_check_argc(0);
 
     dataIsUnchanged = data_is_unchanged();
 
     auto streamCnt = cdo_stream_cnt();
-    nfiles = streamCnt - 1;
-
-    progress::init();
+    numFiles = streamCnt - 1;
   }
 
   void
-  run()
+  run() override
   {
-    for (int indf = 0; indf < nfiles; ++indf)
+    cdo::Progress progress;
+
+    for (int indf = 0; indf < numFiles; ++indf)
       {
         cdo::timer timer;
 
@@ -76,80 +74,80 @@ public:
         auto vlistID1 = cdo_stream_inq_vlist(streamID1);
         auto taxisID1 = vlistInqTaxis(vlistID1);
 
-        VarList varList1;
-        varList_init(varList1, vlistID1);
+        VarList varList1(vlistID1);
 
         if (indf == 0)
           {
-            auto nvars = vlistNvars(vlistID1);
-
-            auto ntsteps = vlistNtsteps(vlistID1);
-            if (ntsteps == 1 && varList_numVaryingVars(varList1) == 0) ntsteps = 0;
+            auto numVars = varList1.numVars();
+            auto numSteps = varList1.numSteps();
+            if (numSteps == 1 && varList1.numVaryingVars() == 0) numSteps = 0;
 
-            std::string ofilename = cdo_get_stream_name(nfiles);
+            std::string ofilename = cdo_get_stream_name(numFiles);
             if (!Options::cdoOverwriteMode && FileUtils::file_exists(ofilename))
               {
-                streamID2 = cdo_open_append(nfiles);
-
+                streamID2 = cdo_open_append(numFiles);
                 vlistID2 = cdo_stream_inq_vlist(streamID2);
                 taxisID2 = vlistInqTaxis(vlistID2);
 
-                vlist_compare(vlistID1, vlistID2, CmpVlist::All);
+                VarList varList2(vlistID2);
+                varList_compare(varList1, varList2);
 
-                tsID2 = vlistNtsteps(vlistID2);
+                tsID2 = varList2.numSteps();
                 if (tsID2 == 0) tsID2 = 1;  // bug fix for time constant data only
 
-                if (ntsteps == 0) hasConstVars = false;
+                if (numSteps == 0) hasConstVars = false;
               }
             else
               {
                 if (Options::cdoVerbose) cdo_print("Output file doesn't exist, creating: %s", ofilename);
 
                 streamMode = StreamMode::CREATE;
-                streamID2 = cdo_open_write(nfiles);
-
+                streamID2 = cdo_open_write(numFiles);
                 vlistID2 = vlistDuplicate(vlistID1);
                 taxisID2 = taxisDuplicate(taxisID1);
                 vlistDefTaxis(vlistID2, taxisID2);
 
-                if (ntsteps == 0 && nfiles > 1)
+                if (numSteps == 0 && numFiles > 1)
                   {
                     hasConstVars = false;
-                    for (int varID = 0; varID < nvars; ++varID) vlistDefVarTimetype(vlistID2, varID, TIME_VARYING);
+                    for (int varID = 0; varID < numVars; ++varID) vlistDefVarTimetype(vlistID2, varID, TIME_VARYING);
                   }
 
                 cdo_def_vlist(streamID2, vlistID2);
               }
           }
-        else { vlist_compare(vlistID1, vlistID2, CmpVlist::All); }
+        else
+          {
+            VarList varList2(vlistID2);
+            varList_compare(varList1, varList2);
+          }
 
-        auto ntsteps = vlistNtsteps(vlistID1);
+        auto numSteps = varList1.numSteps();
         int tsID1 = 0;
         while (true)
           {
-            auto nrecs = cdo_stream_inq_timestep(streamID1, tsID1);
-            if (nrecs == 0) break;
+            auto numRecords = cdo_stream_inq_timestep(streamID1, tsID1);
+            if (numRecords == 0) break;
 
-            const double fstatus = (ntsteps > 1) ? indf + (tsID1 + 1.0) / ntsteps : indf + 1.0;
-            if (!Options::cdoVerbose) progress::update(0, 1, fstatus / nfiles);
+            auto fstatus = (numSteps > 1) ? indf + (tsID1 + 1.0) / numSteps : indf + 1.0;
+            progress.update(fstatus / numFiles);
 
             cdo_taxis_copy_timestep(taxisID2, taxisID1);
             cdo_def_timestep(streamID2, tsID2);
 
-            for (int recID = 0; recID < nrecs; ++recID)
+            for (int recID = 0; recID < numRecords; ++recID)
               {
-                int varID, levelID;
-                cdo_inq_record(streamID1, &varID, &levelID);
+                auto [varID, levelID] = cdo_inq_record(streamID1);
 
-                if (hasConstVars && tsID2 > 0 && tsID1 == 0)
-                  if (varList1[varID].isConstant) continue;
+                const auto &var1 = varList1.vars[varID];
+                if (hasConstVars && tsID2 > 0 && tsID1 == 0 && var1.isConstant) continue;
 
                 cdo_def_record(streamID2, varID, levelID);
 
                 if (dataIsUnchanged) { cdo_copy_record(streamID2, streamID1); }
                 else
                   {
-                    field.init(varList1[varID]);
+                    field.init(var1);
                     cdo_read_record(streamID1, field);
                     cdo_write_record(streamID2, field);
                   }
@@ -166,14 +164,10 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
 
-    if (streamMode == StreamMode::CREATE)
-      {
-        vlistDestroy(vlistID2);
-        taxisDestroy(taxisID2);
-      }
+    if (streamMode == StreamMode::CREATE) { vlistDestroy(vlistID2); }
   }
 };
diff --git a/src/Change.cc b/src/Change.cc
index c505b6f7cafeaf82e3d7d4aa1d1df61ddfe03ea9..5d24ebf6a23610d041340b71079d7397bdc00619 100644
--- a/src/Change.cc
+++ b/src/Change.cc
@@ -25,68 +25,62 @@
 #include "param_conversion.h"
 
 static void
-changeCode(const VarList &varList1, int vlistID2, int nch, const std::vector<int> &chints)
+change_code(const VarList &varList1, int vlistID2, int nch, const std::vector<int> &chints)
 {
-  auto nvars = vlistNvars(vlistID2);
-  for (int varID = 0; varID < nvars; ++varID)
+  for (const auto &var1 : varList1.vars)
     {
-      auto code = varList1[varID].code;
       for (int i = 0; i < nch; i += 2)
-        if (code == chints[i]) vlistDefVarCode(vlistID2, varID, chints[i + 1]);
+        if (var1.code == chints[i]) vlistDefVarCode(vlistID2, var1.ID, chints[i + 1]);
     }
 }
 
 static void
-changeTabnum(int vlistID2, int nch, const std::vector<int> &chints)
+change_tabnum(const VarList &varList1, int vlistID2, int nch, const std::vector<int> &chints)
 {
-  auto nvars = vlistNvars(vlistID2);
-  for (int varID = 0; varID < nvars; ++varID)
+  for (const auto &var1 : varList1.vars)
     {
-      auto tabnum = tableInqNum(vlistInqVarTable(vlistID2, varID));
+      auto tabnum = tableInqNum(vlistInqVarTable(vlistID2, var1.ID));
       for (int i = 0; i < nch; i += 2)
         if (tabnum == chints[i])
           {
             auto tableID = tableDef(-1, chints[i + 1], nullptr);
-            vlistDefVarTable(vlistID2, varID, tableID);
+            vlistDefVarTable(vlistID2, var1.ID, tableID);
           }
     }
 }
 
 static void
-changeParam(const VarList &varList1, int vlistID2, int nch, const std::vector<const char *> &chnames)
+change_param(const VarList &varList1, int vlistID2, int nch, const std::vector<const char *> &chnames)
 {
-  auto nvars = vlistNvars(vlistID2);
-  for (int varID = 0; varID < nvars; ++varID)
+  for (const auto &var1 : varList1.vars)
     {
-      auto param = varList1[varID].param;
       if (Options::cdoVerbose)
         {
           int pnum, pcat, pdis;
-          cdiDecodeParam(param, &pnum, &pcat, &pdis);
+          cdiDecodeParam(var1.param, &pnum, &pcat, &pdis);
           cdo_print("pnum, pcat, pdis: %d.%d.%d", pnum, pcat, pdis);
         }
       for (int i = 0; i < nch; i += 2)
-        if (param == string_to_param(chnames[i])) vlistDefVarParam(vlistID2, varID, string_to_param(chnames[i + 1]));
+        if (var1.param == string_to_param(chnames[i])) vlistDefVarParam(vlistID2, var1.ID, string_to_param(chnames[i + 1]));
     }
 }
 
 static void
-changeName(const VarList &varList1, int vlistID2, int nch, const std::vector<const char *> &chnames)
+change_name(const VarList &varList1, int vlistID2, int nch, const std::vector<const char *> &chnames)
 {
   auto npairs = nch / 2;
   std::vector<std::pair<const char *, const char *>> vpairs(npairs);
   for (int i = 0; i < npairs; ++i) vpairs[i].first = chnames[i * 2];
   for (int i = 0; i < npairs; ++i) vpairs[i].second = chnames[i * 2 + 1];
 
-  auto nvars = vlistNvars(vlistID2);
   std::vector<bool> namefound(npairs, false);
-  for (int varID = 0; varID < nvars; ++varID)
+  for (const auto &var1 : varList1.vars)
     {
       for (int i = 0; i < npairs; ++i)
-        if (varList1[varID].name == vpairs[i].first)
+        if (var1.name == vpairs[i].first)
           {
             namefound[i] = true;
-            cdiDefKeyString(vlistID2, varID, CDI_KEY_NAME, vpairs[i].second);
+            cdiDefKeyString(vlistID2, var1.ID, CDI_KEY_NAME, vpairs[i].second);
             break;
           }
     }
@@ -101,7 +95,7 @@ changeName(const VarList &varList1, int vlistID2, int nch, const std::vector<con
 
   if (searchForGridName)
     {
-      auto ngrids = vlistNgrids(vlistID2);
+      auto ngrids = vlistNumGrids(vlistID2);
       for (int index = 0; index < ngrids; ++index)
         {
           int gridID2 = -1;
@@ -149,7 +143,7 @@ changeName(const VarList &varList1, int vlistID2, int nch, const std::vector<con
 
   if (searchForZaxisName)
     {
-      auto nzaxis = vlistNzaxis(vlistID2);
+      auto nzaxis = vlistNumZaxis(vlistID2);
       for (int index = 0; index < nzaxis; ++index)
         {
           auto zaxisID1 = vlistZaxis(vlistID2, index);
@@ -176,20 +170,19 @@ changeName(const VarList &varList1, int vlistID2, int nch, const std::vector<con
 }
 
 static void
-changeUnit(const VarList &varList1, int vlistID2, int nch, const std::vector<const char *> &chnames)
+change_unit(const VarList &varList1, int vlistID2, int nch, const std::vector<const char *> &chnames)
 {
-  auto nvars = vlistNvars(vlistID2);
-  for (int varID = 0; varID < nvars; ++varID)
+  for (const auto &var1 : varList1.vars)
     {
       for (int i = 0; i < nch; i += 2)
-        if (varList1[varID].units == chnames[i]) cdiDefKeyString(vlistID2, varID, CDI_KEY_UNITS, chnames[i + 1]);
+        if (var1.units == chnames[i]) cdiDefKeyString(vlistID2, var1.ID, CDI_KEY_UNITS, chnames[i + 1]);
     }
 }
 
 static void
-changeLevel(int vlistID2, int nch, const std::vector<double> &chlevels)
+change_level(int vlistID2, int nch, const std::vector<double> &chlevels)
 {
-  auto nzaxis = vlistNzaxis(vlistID2);
+  auto nzaxis = vlistNumZaxis(vlistID2);
   for (int index = 0; index < nzaxis; ++index)
     {
       auto zaxisID1 = vlistZaxis(vlistID2, index);
@@ -220,7 +213,7 @@ changeLevel(int vlistID2, int nch, const std::vector<double> &chlevels)
 }
 
 static void
-changeVarLevel(int varID, int vlistID2, const std::vector<double> &chlevels)
+change_varLevel(int varID, int vlistID2, const std::vector<double> &chlevels)
 {
   auto zaxisID1 = vlistInqVarZaxis(vlistID2, varID);
   if (zaxisInqLevels(zaxisID1, nullptr))
@@ -248,38 +241,39 @@ changeVarLevel(int varID, int vlistID2, const std::vector<double> &chlevels)
 }
 
 static void
-changeLevelByCode(int chcode, int vlistID2, const std::vector<double> &chlevels)
+change_levelByCode(int chcode, const VarList &varList1, int vlistID2, const std::vector<double> &chlevels)
 {
-  int varID;
-  auto nvars = vlistNvars(vlistID2);
-  for (varID = 0; varID < nvars; ++varID)
+  for (const auto &var1 : varList1.vars)
     {
-      auto code = vlistInqVarCode(vlistID2, varID);
-      if (code == chcode) break;
+      if (var1.code == chcode)
+        {
+          change_varLevel(var1.ID, vlistID2, chlevels);
+          return;
+        }
     }
-  if (varID == nvars) cdo_abort("Code %d not found!", chcode);
 
-  changeVarLevel(varID, vlistID2, chlevels);
+  cdo_abort("Code %d not found!", chcode);
 }
 
 static void
-changeLevelByName(const char *chname, const VarList &varList1, int vlistID2, const std::vector<double> &chlevels)
+change_levelByName(const char *chname, const VarList &varList1, int vlistID2, const std::vector<double> &chlevels)
 {
-  int varID;
-  auto nvars = vlistNvars(vlistID2);
-  for (varID = 0; varID < nvars; ++varID)
+  for (const auto &var1 : varList1.vars)
     {
-      if (varList1[varID].name == chname) break;
+      if (var1.name == chname)
+        {
+          change_varLevel(var1.ID, vlistID2, chlevels);
+          return;
+        }
     }
-  if (varID == nvars) cdo_abort("Variable name %s not found!", chname);
 
-  changeVarLevel(varID, vlistID2, chlevels);
+  cdo_abort("Variable name %s not found!", chname);
 }
 
 static void
-changeLtype(int vlistID2, int nch, const std::vector<int> &chltypes)
+change_ltype(int vlistID2, int nch, const std::vector<int> &chltypes)
 {
-  auto nzaxis = vlistNzaxis(vlistID2);
+  auto nzaxis = vlistNumZaxis(vlistID2);
   for (int index = 0; index < nzaxis; ++index)
     {
       auto zaxisID1 = vlistZaxis(vlistID2, index);
@@ -324,11 +318,6 @@ public:
   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;
-  std::vector<int> chints, chltypes;
-  std::vector<double> chlevels;
 
   CdoStreamID streamID1;
   int taxisID1;
@@ -337,11 +326,10 @@ public:
   int taxisID2;
 
   VarList varList1;
-  Field field;
 
 public:
   void
-  init()
+  init() override
   {
     CHCODE = module.get_id("chcode");
     CHTABNUM = module.get_id("chtabnum");
@@ -359,6 +347,12 @@ public:
 
     auto nch = cdo_operator_argc();
 
+    const char *chname = nullptr;
+    int chcode = 0;
+    std::vector<const char *> chnames;
+    std::vector<int> chints, chltypes;
+    std::vector<double> chlevels;
+
     if (operatorID == CHCODE || operatorID == CHTABNUM)
       {
         if (nch % 2) cdo_abort("Odd number of input arguments!");
@@ -411,18 +405,18 @@ public:
     taxisID2 = taxisDuplicate(taxisID1);
     vlistDefTaxis(vlistID2, taxisID2);
 
-    varList_init(varList1, vlistID1);
+    varList1 = VarList(vlistID1);
 
     // clang-format off
-    if      (operatorID == CHCODE)   changeCode(varList1, vlistID2, nch, chints);
-    else if (operatorID == CHTABNUM) changeTabnum(vlistID2, nch, chints);
-    else if (operatorID == CHPARAM)  changeParam(varList1, vlistID2, nch, chnames);
-    else if (operatorID == CHNAME)   changeName(varList1, vlistID2, nch, chnames);
-    else if (operatorID == CHUNIT)   changeUnit(varList1, vlistID2, nch, chnames);
-    else if (operatorID == CHLEVEL)  changeLevel(vlistID2, nch, chlevels);
-    else if (operatorID == CHLEVELC) changeLevelByCode(chcode, vlistID2, chlevels);
-    else if (operatorID == CHLEVELV) changeLevelByName(chname, varList1, vlistID2, chlevels);
-    else if (operatorID == CHLTYPE)  changeLtype(vlistID2, nch, chltypes);
+    if      (operatorID == CHCODE)   change_code(varList1, vlistID2, nch, chints);
+    else if (operatorID == CHTABNUM) change_tabnum(varList1, vlistID2, nch, chints);
+    else if (operatorID == CHPARAM)  change_param(varList1, vlistID2, nch, chnames);
+    else if (operatorID == CHNAME)   change_name(varList1, vlistID2, nch, chnames);
+    else if (operatorID == CHUNIT)   change_unit(varList1, vlistID2, nch, chnames);
+    else if (operatorID == CHLEVEL)  change_level(vlistID2, nch, chlevels);
+    else if (operatorID == CHLEVELC) change_levelByCode(chcode, varList1, vlistID2, chlevels);
+    else if (operatorID == CHLEVELV) change_levelByName(chname, varList1, vlistID2, chlevels);
+    else if (operatorID == CHLTYPE)  change_ltype(vlistID2, nch, chltypes);
     // clang-format on
 
     streamID2 = cdo_open_write(1);
@@ -430,8 +424,10 @@ public:
   }
 
   void
-  run()
+  run() override
   {
+    Field field;
+
     int tsID = 0;
     while (true)
       {
@@ -447,7 +443,7 @@ public:
             cdo_inq_record(streamID1, &varID, &levelID);
             cdo_def_record(streamID2, varID, levelID);
 
-            field.init(varList1[varID]);
+            field.init(varList1.vars[varID]);
             cdo_read_record(streamID1, field);
             cdo_write_record(streamID2, field);
           }
@@ -457,7 +453,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID1);
     cdo_stream_close(streamID2);
diff --git a/src/Change_e5slm.cc b/src/Change_e5slm.cc
index f0a07d1d2797bea68ce6fecfdfa38497e887636f..d521cdd63948acd914666a4c327417cf96c3c53d 100644
--- a/src/Change_e5slm.cc
+++ b/src/Change_e5slm.cc
@@ -22,13 +22,14 @@ public:
   using Process::Process;
   inline static CdoModule module = {
     .name = "Change_e5slm",
-    .operators = { { "change_e5slm"}, { "change_e5lsm"}, { "change_e5mask"} },
+    .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 numMissVals;
@@ -49,15 +50,16 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
-
     streamID1 = cdo_open_read(0);
 
-    const auto vlistID1 = cdo_stream_inq_vlist(streamID1);
+    auto vlistID1 = cdo_stream_inq_vlist(streamID1);
+    VarList varList1(vlistID1);
+
     taxisID1 = vlistInqTaxis(vlistID1);
 
-    const auto vlistID2 = vlistDuplicate(vlistID1);
+    auto vlistID2 = vlistDuplicate(vlistID1);
     taxisID2 = taxisDuplicate(taxisID1);
     vlistDefTaxis(vlistID2, taxisID2);
 
@@ -70,10 +72,13 @@ public:
     const char *fn_slm = cdo_operator_argv(0).c_str();
 
     /* read SLM */
-    const auto streamIDslm = stream_open_read_locked(fn_slm);
-    const auto vlistIDslm = streamInqVlist(streamIDslm);
+    auto streamIDslm = stream_open_read_locked(fn_slm);
+    auto vlistIDslm = streamInqVlist(streamIDslm);
 
-    gridsize = gridInqSize(vlistInqVarGrid(vlistIDslm, 0));
+    {
+      VarList varListSLM(vlistIDslm);
+      gridsize = varListSLM.vars[0].gridsize;
+    }
 
     array = Varray<double>(gridsize);
     cland = Varray<double>(gridsize);
@@ -86,46 +91,46 @@ public:
 
     if (numMissVals) cdo_abort("SLM with missing values are unsupported!");
 
-    const auto mm = varray_min_max(cland);
+    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);
 
     streamClose(streamIDslm);
 
     for (size_t i = 0; i < gridsize; ++i) lsea[i] = cland[i] <= 0;
 
-    const auto nvars = vlistNvars(vlistID1);
-    codes = std::vector<short>(nvars);
+    auto numVars = varList1.numVars();
+    codes = std::vector<short>(numVars);
 
-    for (varID = 0; varID < nvars; ++varID)
+    for (varID = 0; varID < numVars; ++varID)
       {
-        if (gridsize != gridInqSize(vlistInqVarGrid(vlistID1, varID))) cdo_abort("gridsize differ!");
-
-        auto code = vlistInqVarCode(vlistID1, varID);
-        auto varname = cdo::inq_var_name(vlistID1, varID);
+        const auto &var = varList1.vars[varID];
+        if (gridsize != var.gridsize) cdo_abort("gridsize differ!");
 
+        auto code = var.code;
         if (code < 0)
           {
             // clang-format off
-          if      (varname == "SLM")       code = 172;
-          else if (varname == "ALAKE")     code = 99;
-          else if (varname == "WS")        code = 140;
-          else if (varname == "AZ0")       code = 173;
-          else if (varname == "ALB")       code = 174;
-          else if (varname == "VGRAT")     code = 198;
-          else if (varname == "FOREST")    code = 212;
-          else if (varname == "FAO")       code = 226;
-          else if (varname == "WSMX")      code = 229;
-          else if (varname == "GLAC")      code = 232;
-          else if (varname == "VLTCLIM")   code = 71;
-          else if (varname == "VGRATCLIM") code = 70;
+            if      (var.name == "SLM")       code = 172;
+            else if (var.name == "ALAKE")     code = 99;
+            else if (var.name == "WS")        code = 140;
+            else if (var.name == "AZ0")       code = 173;
+            else if (var.name == "ALB")       code = 174;
+            else if (var.name == "VGRAT")     code = 198;
+            else if (var.name == "FOREST")    code = 212;
+            else if (var.name == "FAO")       code = 226;
+            else if (var.name == "WSMX")      code = 229;
+            else if (var.name == "GLAC")      code = 232;
+            else if (var.name == "VLTCLIM")   code = 71;
+            else if (var.name == "VGRATCLIM") code = 70;
             // clang-format on
           }
 
         codes[varID] = code;
       }
   }
+
   void
-  run()
+  run() override
   {
     int tsID = 0;
     while ((nrecs = cdo_stream_inq_timestep(streamID1, tsID)))
@@ -170,8 +175,9 @@ public:
         tsID++;
       }
   }
+
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID1);
     cdo_stream_close(streamID2);
diff --git a/src/Cloudlayer.cc b/src/Cloudlayer.cc
index 25cf9c84304a83537742cd34d278acc1fbe78b6e..870dfa8ce4a22412722065e7b36874440879a37f 100644
--- a/src/Cloudlayer.cc
+++ b/src/Cloudlayer.cc
@@ -92,7 +92,7 @@ public:
   using Process::Process;
   inline static CdoModule module = {
     .name = "Cloudlayer",
-    .operators = { { "cloudlayer"} },
+    .operators = { { "cloudlayer" } },
     .aliases = {},
     .mode = EXPOSED,     // Module mode: 0:intern 1:extern
     .number = CDI_REAL,  // Allowed number type
@@ -125,7 +125,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
 
     if (cdo_operator_argc() > 0)
@@ -140,22 +140,20 @@ public:
     streamID1 = cdo_open_read(0);
     auto vlistID1 = cdo_stream_inq_vlist(streamID1);
 
-    VarList varList1;
-    varList_init(varList1, vlistID1);
+    VarList varList1(vlistID1);
 
     gridsize = vlist_check_gridsize(vlistID1);
 
     auto aclcac_code = 223;
 
-    auto nvars = vlistNvars(vlistID1);
-    for (int varID = 0; varID < nvars; ++varID)
+    for (const auto &var1 : varList1.vars)
       {
-        zaxisID = varList1[varID].zaxisID;
-        auto code = varList1[varID].code;
+        zaxisID = var1.zaxisID;
+        auto code = var1.code;
 
         if (code <= 0)
           {
-            if (string_to_lower(varList1[varID].name) == "aclcac") code = 223;
+            if (string_to_lower(var1.name) == "aclcac") code = 223;
           }
 
         if (code == aclcac_code)
@@ -163,7 +161,7 @@ public:
             aclcac_code_found = 1;
             if (zaxisInqType(zaxisID) == ZAXIS_PRESSURE || zaxisInqType(zaxisID) == ZAXIS_HYBRID)
               {
-                aclcacID = varID;
+                aclcacID = var1.ID;
                 break;
               }
           }
@@ -177,11 +175,11 @@ public:
           cdo_abort("Cloud cover (parameter 223) not found!");
       }
 
-    missval = varList1[aclcacID].missval;
-    gridID = varList1[aclcacID].gridID;
-    zaxisID = varList1[aclcacID].zaxisID;
-
-    nlevels = varList1[aclcacID].nlevels;
+    const auto &aclcacVar = varList1.vars[aclcacID];
+    missval = aclcacVar.missval;
+    gridID = aclcacVar.gridID;
+    zaxisID = aclcacVar.zaxisID;
+    nlevels = aclcacVar.nlevels;
     auto nhlev = nlevels + 1;
 
     aclcac = std::vector<double>(gridsize * nlevels);
@@ -279,8 +277,9 @@ public:
     streamID2 = cdo_open_write(1);
     cdo_def_vlist(streamID2, vlistID2);
   }
+
   void
-  run()
+  run() override
   {
     int tsID = 0;
     while (true)
@@ -328,8 +327,9 @@ public:
         tsID++;
       }
   }
+
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
diff --git a/src/Collgrid.cc b/src/Collgrid.cc
index c8121c5eaf19b9455538d19088e40356333fdb2c..438e455f2fe48be0e27cfa76f05e6720a68152c2 100644
--- a/src/Collgrid.cc
+++ b/src/Collgrid.cc
@@ -9,7 +9,6 @@
 
 #include "cdo_rlimit.h"
 #include "process_int.h"
-#include "cdo_vlist.h"
 #include "param_conversion.h"
 #include <mpim_grid.h>
 #include "util_files.h"
@@ -52,7 +51,7 @@ cmpxy_gt(const xyinfoType &a, const xyinfoType &b)
 }
 
 static int
-gen_coll_grid(int ngrids, int nfiles, std::vector<CollgridInfo> &collgridInfo, int gindex, long nxblocks)
+gen_coll_grid(int ngrids, int numFiles, std::vector<CollgridInfo> &collgridInfo, int gindex, long nxblocks)
 {
   auto isSouthNorth = true;
   auto isRegular = false;
@@ -69,12 +68,12 @@ gen_coll_grid(int ngrids, int nfiles, std::vector<CollgridInfo> &collgridInfo, i
   auto withCenter = (globalGridType == CDI_UNDEFID && gridHasCoordinates(gridID));
   auto withBounds = (isUnstructured && globalGridType == CDI_UNDEFID && gridHasBounds(gridID));
 
-  std::vector<xyinfoType> xyinfo(nfiles);
-  std::vector<long> xsize(nfiles), ysize(nfiles);
-  Varray2D<double> xvals(nfiles), yvals(nfiles);
-  Varray2D<double> xbounds(nfiles), ybounds(nfiles);
+  std::vector<xyinfoType> xyinfo(numFiles);
+  std::vector<long> xsize(numFiles), ysize(numFiles);
+  Varray2D<double> xvals(numFiles), yvals(numFiles);
+  Varray2D<double> xbounds(numFiles), ybounds(numFiles);
 
-  for (int fileID = 0; fileID < nfiles; ++fileID)
+  for (int fileID = 0; fileID < numFiles; ++fileID)
     {
       gridID = vlistGrid(collgridInfo[fileID].vlistID, gindex);
       auto gridtype = (globalGridType != CDI_UNDEFID) ? globalGridType : gridInqType(gridID);
@@ -126,28 +125,29 @@ gen_coll_grid(int ngrids, int nfiles, std::vector<CollgridInfo> &collgridInfo, i
     }
 
   if (Options::cdoVerbose && isRegular)
-    for (int fileID = 0; fileID < nfiles; ++fileID) printf("1 %d %g %g \n", xyinfo[fileID].id, xyinfo[fileID].x, xyinfo[fileID].y);
+    for (int fileID = 0; fileID < numFiles; ++fileID)
+      printf("1 %d %g %g \n", xyinfo[fileID].id, xyinfo[fileID].x, xyinfo[fileID].y);
 
   if (isRegular)
     {
       ranges::sort(xyinfo, {}, &xyinfoType::x);
 
       if (Options::cdoVerbose)
-        for (int fileID = 0; fileID < nfiles; ++fileID)
+        for (int fileID = 0; fileID < numFiles; ++fileID)
           printf("2 %d %g %g \n", xyinfo[fileID].id, xyinfo[fileID].x, xyinfo[fileID].y);
 
       ranges::sort(xyinfo, isSouthNorth ? cmpxy_lt : cmpxy_gt);
 
       if (Options::cdoVerbose)
-        for (int fileID = 0; fileID < nfiles; ++fileID)
+        for (int fileID = 0; fileID < numFiles; ++fileID)
           printf("3 %d %g %g \n", xyinfo[fileID].id, xyinfo[fileID].x, xyinfo[fileID].y);
 
       if (nx <= 0)
         {
           nx = 1;
-          for (int fileID = 1; fileID < nfiles; ++fileID)
+          for (int fileID = 1; fileID < numFiles; ++fileID)
             {
-              if (DBL_IS_EQUAL(xyinfo[0].y, xyinfo[fileID].y))
+              if (dbl_is_equal(xyinfo[0].y, xyinfo[fileID].y))
                 nx++;
               else
                 break;
@@ -156,11 +156,11 @@ gen_coll_grid(int ngrids, int nfiles, std::vector<CollgridInfo> &collgridInfo, i
     }
   else
     {
-      if (nx <= 0) nx = nfiles;
+      if (nx <= 0) nx = numFiles;
     }
 
-  long ny = nfiles / nx;
-  if (nx * ny != nfiles) cdo_abort("Number of input files (%ld) and number of blocks (%ldx%ld) differ!", nfiles, nx, ny);
+  long ny = numFiles / nx;
+  if (nx * ny != numFiles) cdo_abort("Number of input files (%ld) and number of blocks (%ldx%ld) differ!", numFiles, nx, ny);
 
   long xsize2 = 0;
   for (long i = 0; i < nx; ++i) xsize2 += xsize[xyinfo[i].id];
@@ -210,7 +210,7 @@ gen_coll_grid(int ngrids, int nfiles, std::vector<CollgridInfo> &collgridInfo, i
       yoff[j + 1] = yoff[j] + ysize[idx];
     }
 
-  for (int fileID = 0; fileID < nfiles; ++fileID)
+  for (int fileID = 0; fileID < numFiles; ++fileID)
     {
       long idx = xyinfo[fileID].id;
       long iy = fileID / nx;
@@ -297,20 +297,19 @@ collect_cells(const Field &field1, Field &field2, const std::vector<long> &cellI
 }
 
 static std::vector<int>
-get_var_gridindex(int vlistID)
+get_var_gridindex(int vlistID, const VarList &varList)
 {
-  auto nvars = vlistNvars(vlistID);
-  auto ngrids = vlistNgrids(vlistID);
+  auto numVars = varList.numVars();
+  auto numGrids = vlistNumGrids(vlistID);
 
-  std::vector<int> varGridIndex(nvars, 0);
-  for (int varID = 0; varID < nvars; ++varID)
+  std::vector<int> varGridIndex(numVars, 0);
+  for (const auto &var : varList.vars)
     {
-      auto gridID = vlistInqVarGrid(vlistID, varID);
-      for (int index = 0; index < ngrids; ++index)
+      for (int index = 0; index < numGrids; ++index)
         {
-          if (gridID == vlistGrid(vlistID, index))
+          if (var.gridID == vlistGrid(vlistID, index))
             {
-              varGridIndex[varID] = index;
+              varGridIndex[var.ID] = index;
               break;
             }
         }
@@ -322,8 +321,8 @@ get_var_gridindex(int vlistID)
 static std::vector<GridInfo2>
 get_gridinfo(int vlistID, const VarList &varList, const std::vector<int> &varGridIndex, std::vector<bool> &selectedVars)
 {
-  auto nvars = vlistNvars(vlistID);
-  auto ngrids = vlistNgrids(vlistID);
+  auto numVars = varList.numVars();
+  auto ngrids = vlistNumGrids(vlistID);
 
   std::vector<GridInfo2> gridInfo(ngrids);
   for (int index = 0; index < ngrids; ++index)
@@ -335,12 +334,13 @@ get_gridinfo(int vlistID, const VarList &varList, const std::vector<int> &varGri
   int globalCellIndicesID = -1;
   int globalVertIndicesID = -1;
   int globalEdgeIndicesID = -1;
-  for (int varID = 0; varID < nvars; ++varID)
+  for (int varID = 0; varID < numVars; ++varID)
     {
+      const auto &var = varList.vars[varID];
       // clang-format off
-      if      (varList[varID].name == "global_cell_indices") globalCellIndicesID = varID;
-      else if (varList[varID].name == "global_vert_indices") globalVertIndicesID = varID;
-      else if (varList[varID].name == "global_edge_indices") globalEdgeIndicesID = varID;
+      if      (var.name == "global_cell_indices") globalCellIndicesID = varID;
+      else if (var.name == "global_vert_indices") globalVertIndicesID = varID;
+      else if (var.name == "global_edge_indices") globalEdgeIndicesID = varID;
       // clang-format on
     }
   if (globalCellIndicesID != -1) selectedVars[globalCellIndicesID] = false;
@@ -351,21 +351,21 @@ get_gridinfo(int vlistID, const VarList &varList, const std::vector<int> &varGri
   if (globalVertIndicesID != -1) gridInfo[varGridIndex[globalVertIndicesID]].globalIndicesID = globalVertIndicesID;
   if (globalEdgeIndicesID != -1) gridInfo[varGridIndex[globalEdgeIndicesID]].globalIndicesID = globalEdgeIndicesID;
 
-  for (int varID = 0; varID < nvars; ++varID)
+  for (int varID = 0; varID < numVars; ++varID)
     if (selectedVars[varID]) gridInfo[varGridIndex[varID]].needed = true;
 
   return gridInfo;
 }
 
 static std::vector<bool>
-get_selected_vars(int nsel, int noff, int vlistID1, const VarList &varList1)
+get_selected_vars(int nsel, int noff, const VarList &varList1)
 {
-  auto nvars = vlistNvars(vlistID1);
-  std::vector<bool> selectedVars(nvars, false);
+  auto numVars = varList1.numVars();
+  std::vector<bool> selectedVars(numVars, false);
 
   if (nsel == 0)
     {
-      for (int varID = 0; varID < nvars; ++varID) selectedVars[varID] = true;
+      for (int varID = 0; varID < numVars; ++varID) selectedVars[varID] = true;
     }
   else
     {
@@ -375,11 +375,11 @@ get_selected_vars(int nsel, int noff, int vlistID1, const VarList &varList1)
       std::vector<bool> selfound(nsel);
       for (int i = 0; i < nsel; ++i) selfound[i] = false;
 
-      for (int varID = 0; varID < nvars; ++varID)
+      for (int varID = 0; varID < numVars; ++varID)
         {
           for (int isel = 0; isel < nsel; isel++)
             {
-              if (cdo_operator_argv(noff + isel) == varList1[varID].name)
+              if (cdo_operator_argv(noff + isel) == varList1.vars[varID].name)
                 {
                   selfound[isel] = true;
                   selectedVars[varID] = true;
@@ -405,7 +405,7 @@ get_selected_vars(int nsel, int noff, int vlistID1, const VarList &varList1)
 static void
 select_vars(const std::vector<bool> &selectedVars, int vlistID1, const VarList &varList1)
 {
-  auto nvars = vlistNvars(vlistID1);
+  auto nvars = varList1.numVars();
   int numVars = 0;
 
   for (int varID = 0; varID < nvars; ++varID)
@@ -413,8 +413,8 @@ select_vars(const std::vector<bool> &selectedVars, int vlistID1, const VarList &
       if (selectedVars[varID])
         {
           numVars++;
-          auto nlevels = varList1[varID].nlevels;
-          for (int levID = 0; levID < nlevels; levID++) vlistDefFlag(vlistID1, varID, levID, true);
+          auto numLevels = varList1.vars[varID].nlevels;
+          for (int levelID = 0; levelID < numLevels; levelID++) vlistDefFlag(vlistID1, varID, levelID, true);
         }
     }
 
@@ -444,7 +444,7 @@ public:
   int taxisID2;
   int vlistID2;
 
-  int nfiles;
+  int numFiles;
 
   std::vector<GridInfo2> gridInfo;
   std::vector<CollgridInfo> collgridInfo;
@@ -453,29 +453,29 @@ public:
   std::vector<int> varGridIndex;
 
   VarList varList2;
-  Field field2;
+  std::vector<size_t> targetGridsize;
 
 public:
   void
-  init()
+  init() override
   {
-    nfiles = cdo_stream_cnt() - 1;
-    std::string ofilename = cdo_get_stream_name(nfiles);
+    numFiles = cdo_stream_cnt() - 1;
+    std::string ofilename = cdo_get_stream_name(numFiles);
 
     if (!Options::cdoOverwriteMode && FileUtils::file_exists(ofilename) && !FileUtils::user_file_overwrite(ofilename))
       cdo_abort("Outputfile %s already exists!", ofilename);
 
-    collgridInfo = std::vector<CollgridInfo>(nfiles);
+    collgridInfo = std::vector<CollgridInfo>(numFiles);
 
-    cdo::set_numfiles(nfiles + 8);
+    cdo::set_numfiles(numFiles + 8);
 
-    for (int fileID = 0; fileID < nfiles; ++fileID)
+    for (int fileID = 0; fileID < numFiles; ++fileID)
       {
         auto streamID = cdo_open_read(fileID);
         auto vlistID = cdo_stream_inq_vlist(streamID);
         collgridInfo[fileID].streamID = streamID;
         collgridInfo[fileID].vlistID = vlistID;
-        varList_init(collgridInfo[fileID].varList, vlistID);
+        collgridInfo[fileID].varList = VarList(vlistID);
       }
 
     const auto &varList1 = collgridInfo[0].varList;
@@ -483,8 +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 < numFiles; ++fileID)
+      varList_compare(varList1, collgridInfo[fileID].varList, CmpVarList::Name | CmpVarList::NumLevels);
 
     auto nsel = cdo_operator_argc();
     int noff = 0;
@@ -513,8 +513,8 @@ public:
           }
       }
 
-    auto selectedVars = get_selected_vars(nsel, noff, vlistID1, varList1);
-    varGridIndex = get_var_gridindex(vlistID1);
+    auto selectedVars = get_selected_vars(nsel, noff, varList1);
+    varGridIndex = get_var_gridindex(vlistID1, varList1);
     gridInfo = get_gridinfo(vlistID1, varList1, varGridIndex, selectedVars);
     select_vars(selectedVars, vlistID1, varList1);
 
@@ -526,13 +526,14 @@ public:
     taxisID2 = taxisDuplicate(taxisID1);
     vlistDefTaxis(vlistID2, taxisID2);
 
-    // if ( Options::cdoVerbose ) vlistPrint(vlistID1);
-    // if ( Options::cdoVerbose ) vlistPrint(vlistID2);
+    // if (Options::cdoVerbose) vlistPrint(vlistID1);
+    // if (Options::cdoVerbose) vlistPrint(vlistID2);
 
-    auto ngrids1 = vlistNgrids(vlistID1);
-    auto ngrids2 = vlistNgrids(vlistID2);
+    auto ngrids1 = vlistNumGrids(vlistID1);
+    auto ngrids2 = vlistNumGrids(vlistID2);
+    targetGridsize.resize(ngrids1, 0);
 
-    for (int fileID = 0; fileID < nfiles; ++fileID)
+    for (int fileID = 0; fileID < numFiles; ++fileID)
       {
         collgridInfo[fileID].cellIndex.resize(ngrids1);
         for (int gindex = 0; gindex < ngrids1; ++gindex)
@@ -553,7 +554,8 @@ public:
         for (i1 = 0; i1 < ngrids1; ++i1)
           if (vlistGrid(vlistID1, i1) == vlistGrid(vlistID2, i2)) break;
 
-        gridID2s[i2] = gen_coll_grid(ngrids2, nfiles, collgridInfo, i1, nxblocks);
+        gridID2s[i2] = gen_coll_grid(ngrids2, numFiles, collgridInfo, i1, nxblocks);
+        targetGridsize[i1] = gridInqSize(gridID2s[i2]);
       }
 
     for (int i = 0; i < ngrids2; ++i)
@@ -561,13 +563,13 @@ public:
         if (gridID2s[i] != -1) vlistChangeGridIndex(vlistID2, i, gridID2s[i]);
       }
 
-    varList_init(varList2, vlistID2);
+    varList2 = VarList(vlistID2);
+    auto numVars2 = varList2.numVars();
 
-    auto nvars2 = vlistNvars(vlistID2);
-    collectVars2 = std::vector<bool>(nvars2, false);
-    for (int varID = 0; varID < nvars2; ++varID)
+    collectVars2 = std::vector<bool>(numVars2, false);
+    for (int varID = 0; varID < numVars2; ++varID)
       {
-        auto gridID = varList2[varID].gridID;
+        auto gridID = varList2.vars[varID].gridID;
         for (int i = 0; i < ngrids2; ++i)
           {
             if (gridID2s[i] != -1 && gridID == vlistGrid(vlistID2, i))
@@ -578,18 +580,20 @@ public:
           }
       }
 
-    streamID2 = cdo_open_write(nfiles);
+    streamID2 = cdo_open_write(numFiles);
     cdo_def_vlist(streamID2, vlistID2);
   }
 
   void
-  run()
+  run() override
   {
+    Field field2;
+
     int nrecs0 = 0;
     int tsID = 0;
     do {
         nrecs0 = cdo_stream_inq_timestep(collgridInfo[0].streamID, tsID);
-        for (int fileID = 1; fileID < nfiles; ++fileID)
+        for (int fileID = 1; fileID < numFiles; ++fileID)
           {
             auto nrecs = cdo_stream_inq_timestep(collgridInfo[fileID].streamID, tsID);
             if (nrecs != nrecs0)
@@ -604,7 +608,7 @@ public:
         for (int recID = 0; recID < nrecs0; ++recID)
           {
             int varID = 0, levelID = 0;
-            for (int fileID = nfiles - 1; fileID >= 0; fileID--) cdo_inq_record(collgridInfo[fileID].streamID, &varID, &levelID);
+            for (int fileID = numFiles - 1; fileID >= 0; fileID--) cdo_inq_record(collgridInfo[fileID].streamID, &varID, &levelID);
 
             // if (Options::cdoVerbose && tsID == 0) printf(" tsID, recID, varID, levelID %d %d %d %d\n", tsID, recID, varID,
             // levelID);
@@ -613,14 +617,20 @@ public:
             if (gridInfo[gindex].needed && gridInfo[gindex].globalIndicesID == varID)
               {
                 Varray<double> cellIndex;
-                for (int fileID = 0; fileID < nfiles; ++fileID)
+                for (int fileID = 0; fileID < numFiles; ++fileID)
                   {
-                    auto patchSize = collgridInfo[fileID].varList[varID].gridsize;
+                    auto &collgrid = collgridInfo[fileID];
+                    auto patchSize = collgrid.varList.vars[varID].gridsize;
                     if (cellIndex.size() < patchSize) cellIndex.resize(patchSize);
                     size_t numMissVals;
-                    cdo_read_record(collgridInfo[fileID].streamID, cellIndex.data(), &numMissVals);
+                    cdo_read_record(collgrid.streamID, cellIndex.data(), &numMissVals);
                     for (size_t i = 0; i < patchSize; ++i)
-                      collgridInfo[fileID].cellIndex[gindex][i] = std::lround(cellIndex[i]) - 1;
+                      {
+                        auto index = std::lround(cellIndex[i]);
+                        if (index >= (long) targetGridsize[gindex])
+                          cdo_abort("global cell index out of range (%ld/%zu)", index, targetGridsize[gindex]);
+                        collgrid.cellIndex[gindex][i] = index - 1;
+                      }
                   }
               }
 
@@ -630,19 +640,20 @@ public:
                 auto levelID2 = vlistFindLevel(vlistID2, varID, levelID);
                 // if (Options::cdoVerbose && tsID == 0) printf("varID %d %d levelID %d %d\n", varID, varID2, levelID, levelID2);
 
-                field2.init(varList2[varID2]);
+                field2.init(varList2.vars[varID2]);
                 field_fill(field2, field2.missval);
 
 #ifdef _OPENMP
 #pragma omp parallel for default(shared)
 #endif
-                for (int fileID = 0; fileID < nfiles; ++fileID)
+                for (int fileID = 0; fileID < numFiles; ++fileID)
                   {
-                    auto &field1 = collgridInfo[fileID].field;
-                    field1.init(collgridInfo[fileID].varList[varID]);
-                    cdo_read_record(collgridInfo[fileID].streamID, field1);
+                    auto &collgrid = collgridInfo[fileID];
+                    auto &field1 = collgrid.field;
+                    field1.init(collgrid.varList.vars[varID]);
+                    cdo_read_record(collgrid.streamID, field1);
 
-                    if (collectVars2[varID2]) collect_cells(field1, field2, collgridInfo[fileID].cellIndex[gindex]);
+                    if (collectVars2[varID2]) collect_cells(field1, field2, collgrid.cellIndex[gindex]);
                   }
 
                 cdo_def_record(streamID2, varID2, levelID2);
@@ -662,9 +673,9 @@ public:
   }
 
   void
-  close()
+  close() override
   {
-    for (int fileID = 0; fileID < nfiles; ++fileID) cdo_stream_close(collgridInfo[fileID].streamID);
+    for (int fileID = 0; fileID < numFiles; ++fileID) cdo_stream_close(collgridInfo[fileID].streamID);
 
     cdo_stream_close(streamID2);
   }
diff --git a/src/Command.cc b/src/Command.cc
index 508744c9654aa9012ef850c3a40f17cd8d5789ca..4ac2ab826edd0e3001118ae517483ef93438cab1 100644
--- a/src/Command.cc
+++ b/src/Command.cc
@@ -134,7 +134,8 @@ com_stat(const std::string &arg)
 {
   (void) (arg);  // unused
 
-  auto name = varList[gl_varID].name.c_str();
+  const auto &var = varList.vars[gl_varID];
+  auto name = var.name.c_str();
 
   auto tsID2 = gl_tsID2;
   if (tsID2 == -1) tsID2 = (gl_ntsteps != -1) ? gl_ntsteps - 1 : INT_MAX - 1;
@@ -154,9 +155,9 @@ com_stat(const std::string &arg)
         {
           cdo::timer stepTimer;
 
-          auto nlevels = varList[gl_varID].nlevels;
-          auto gridsize = varList[gl_varID].gridsize;
-          auto missval = varList[gl_varID].missval;
+          auto nlevels = var.nlevels;
+          auto gridsize = var.gridsize;
+          auto missval = var.missval;
 
           auto levelID = (nlevels > 1) ? gl_levelID : 0;
 
@@ -165,8 +166,8 @@ com_stat(const std::string &arg)
 
           auto mmm = varray_min_max_mean_mv(gridsize, gl_data, missval);
 
-          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());
+          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());
         }
     }
 
@@ -214,7 +215,7 @@ set_tsID(int tsID1, int tsID2)
 static int
 check_levelID(int levelID)
 {
-  auto nlevels = varList[gl_varID].nlevels;
+  auto nlevels = varList.vars[gl_varID].nlevels;
   if (levelID >= nlevels)
     {
       fprintf(stdout, "z=%d out of range (max=%d)!\n", levelID + 1, nlevels);
@@ -236,9 +237,9 @@ set_levelID(int levelID)
 static int
 check_varID(int varID)
 {
-  if (varID < 0 || varID >= (int) varList.size())
+  if (varID < 0 || varID >= (int) varList.numVars())
     {
-      fprintf(stdout, "varID out of range (max=%d)!\n", (int) varList.size());
+      fprintf(stdout, "varID out of range (max=%d)!\n", (int) varList.numVars());
       return 1;
     }
 
@@ -312,7 +313,7 @@ set_var(const std::vector<std::string> &argv)
   auto lfound = false;
   for (int varID = 0; varID < gl_nvars; ++varID)
     {
-      if (name == varList[varID].name)
+      if (name == varList.vars[varID].name)
         {
           lfound = true;
           set_varID(varID);
@@ -360,10 +361,11 @@ com_vars(const std::string &arg)
 
   for (int varID = 0; varID < gl_nvars; ++varID)
     {
-      cdiParamToString(varList[varID].param, paramstr, sizeof(paramstr));
+      const auto &var = varList.vars[varID];
+      cdiParamToString(var.param, paramstr, sizeof(paramstr));
 
-      fprintf(stdout, "varID=%3d, param=%s, name=%s, longname=\"%s\", units=\"%s\"\n", varID + 1, paramstr,
-              varList[varID].name.c_str(), varList[varID].longname.c_str(), varList[varID].units.c_str());
+      fprintf(stdout, "varID=%3d, param=%s, name=%s, longname=\"%s\", units=\"%s\"\n", varID + 1, paramstr, var.name.c_str(),
+              var.longname.c_str(), var.units.c_str());
     }
 
   return 0;
@@ -477,7 +479,7 @@ command_init()
   gl_data.resize(gridsizemax);
 
   gl_nvars = vlistNvars(gl_vlistID);
-  varList_init(varList, gl_vlistID);
+  varList = VarList(gl_vlistID);
 
   set_varID(0);
 }
@@ -489,12 +491,13 @@ run_demo()
   gl_tsID2 = -1;
   for (int varID = 0; varID < gl_nvars; ++varID)
     {
-      auto nlevels = varList[varID].nlevels;
+      const auto &var = varList.vars[varID];
+      auto nlevels = var.nlevels;
       for (int levelID = 0; levelID < nlevels; ++levelID)
         {
           set_varID(varID);
           set_levelID(levelID);
-          com_stat(varList[varID].name);
+          com_stat(var.name);
         }
     }
 }
@@ -505,7 +508,7 @@ public:
   using Process::Process;
   inline static CdoModule module = {
     .name = "Command",
-    .operators = { { "command"}, { "com"}, { "cmd"} },
+    .operators = { { "command" }, { "com" }, { "cmd" } },
     .aliases = {},
     .mode = INTERNAL,    // Module mode: 0:intern 1:extern
     .number = CDI_REAL,  // Allowed number type
@@ -515,7 +518,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
 
     if (cdo_operator_argc() == 1)
@@ -532,8 +535,9 @@ public:
 
     command_init();
   }
+
   void
-  run()
+  run() override
   {
     if (mode_demo) { run_demo(); }
     else
@@ -548,8 +552,9 @@ public:
           }
       }
   }
+
   void
-  close()
+  close() override
   {
     streamClose(gl_streamID);
   }
diff --git a/src/Comp.cc b/src/Comp.cc
index 47069a33e792293239f5cbd88d16d8664505c7a2..5cd9d723d384522392a53b4780adbe4b38bab9af 100644
--- a/src/Comp.cc
+++ b/src/Comp.cc
@@ -45,11 +45,11 @@ static auto func_comp
 
 class Comp : public Process
 {
-  enum
+  enum class FillType
   {
-    FILL_NONE,
-    FILL_TS,
-    FILL_REC
+    NONE,
+    TS,
+    REC,
   };
 
 public:
@@ -68,7 +68,8 @@ public:
     .constraints = { 2, 1, NoRestriction },
   };
   inline static RegisterEntry<Comp> registration = RegisterEntry<Comp>(module);
-  int filltype = FILL_NONE;
+
+  FillType fillType{ FillType::NONE };
   Varray2D<double> vardata;
 
   CdoStreamID streamID1;
@@ -82,15 +83,15 @@ public:
   double *arrayx1;
   double *arrayx2;
 
-  VarList varList1, varList2;
+  VarList varList1;
+  VarList varList2;
 
   Varray<double> vaIn1, vaIn2, vaOut;
 
 public:
   void
-  init()
+  init() override
   {
-
     auto operatorID = cdo_operator_id();
     operFunc = cdo_operator_f1(operatorID);
 
@@ -112,15 +113,15 @@ public:
 
     auto fillstream1 = false;
 
-    if (vlistNrecs(vlistID1) != 1 && vlistNrecs(vlistID2) == 1)
+    if (vlistNumRecords(vlistID1) != 1 && vlistNumRecords(vlistID2) == 1)
       {
-        filltype = FILL_REC;
+        fillType = FillType::REC;
         cdo_print("Filling up stream2 >%s< by copying the first record.", cdo_get_stream_name(1));
         if (ntsteps2 != 1) cdo_abort("stream2 has more than 1 timestep!");
       }
-    else if (vlistNrecs(vlistID1) == 1 && vlistNrecs(vlistID2) != 1)
+    else if (vlistNumRecords(vlistID1) == 1 && vlistNumRecords(vlistID2) != 1)
       {
-        filltype = FILL_REC;
+        fillType = FillType::REC;
         cdo_print("Filling up stream1 >%s< by copying the first record.", cdo_get_stream_name(0));
         if (ntsteps1 != 1) cdo_abort("stream1 has more than 1 timestep!");
         fillstream1 = true;
@@ -129,7 +130,7 @@ public:
         std::swap(taxisID1, taxisID2);
       }
 
-    if (filltype == FILL_NONE) vlist_compare(vlistID1, vlistID2, CmpVlist::All);
+    if (fillType == FillType::NONE) vlist_compare(vlistID1, vlistID2, CmpVarList::All);
 
     auto gridsizemax = vlistGridsizeMax(vlistID1);
     vaIn1 = Varray<double>(gridsizemax);
@@ -141,16 +142,16 @@ public:
 
     if (Options::cdoVerbose) cdo_print("Number of timesteps: file1 %d, file2 %d", ntsteps1, ntsteps2);
 
-    if (filltype == FILL_NONE)
+    if (fillType == FillType::NONE)
       {
         if (ntsteps1 != 1 && ntsteps2 == 1)
           {
-            filltype = FILL_TS;
+            fillType = FillType::TS;
             cdo_print("Filling up stream2 >%s< by copying the first timestep.", cdo_get_stream_name(1));
           }
         else if (ntsteps1 == 1 && ntsteps2 != 1)
           {
-            filltype = FILL_TS;
+            fillType = FillType::TS;
             cdo_print("Filling up stream1 >%s< by copying the first timestep.", cdo_get_stream_name(0));
             fillstream1 = true;
             std::swap(streamID1, streamID2);
@@ -158,7 +159,7 @@ public:
             std::swap(taxisID1, taxisID2);
           }
 
-        if (filltype == FILL_TS) cdo_fill_ts(vlistID2, vardata);
+        if (fillType == FillType::TS) cdo_fill_ts(vlistID2, vardata);
       }
 
     if (fillstream1)
@@ -167,8 +168,8 @@ public:
         arrayx2 = vaIn1.data();
       }
 
-    varList_init(varList1, vlistID1);
-    varList_init(varList2, vlistID2);
+    varList1 = VarList(vlistID1);
+    varList2 = VarList(vlistID2);
 
     auto vlistID3 = vlistDuplicate(vlistID1);
 
@@ -182,7 +183,7 @@ public:
   }
 
   void
-  run()
+  run() override
   {
     int tsID = 0;
     while (true)
@@ -190,7 +191,7 @@ public:
         auto nrecs = cdo_stream_inq_timestep(streamID1, tsID);
         if (nrecs == 0) break;
 
-        if (tsID == 0 || filltype == FILL_NONE)
+        if (tsID == 0 || fillType == FillType::NONE)
           {
             auto nrecs2 = cdo_stream_inq_timestep(streamID2, tsID);
             if (nrecs2 == 0) cdo_abort("Input streams have different number of timesteps!");
@@ -206,34 +207,38 @@ public:
             cdo_inq_record(streamID1, &varID, &levelID);
             cdo_read_record(streamID1, arrayx1, &numMissVals1);
 
-            if (tsID == 0 || filltype == FILL_NONE)
+            if (tsID == 0 || fillType == FillType::NONE)
               {
-                if (recID == 0 || filltype != FILL_REC)
+                if (recID == 0 || fillType != FillType::REC)
                   {
                     cdo_inq_record(streamID2, &varID, &levelID);
                     cdo_read_record(streamID2, arrayx2, &numMissVals2);
                   }
 
-                if (filltype == FILL_TS)
+                if (fillType == FillType::TS)
                   {
-                    auto offset = varList2[varID].gridsize * levelID;
-                    array_copy(varList2[varID].gridsize, arrayx2, &vardata[varID][offset]);
+                    auto gridsize = varList2.vars[varID].gridsize;
+                    auto offset = gridsize * levelID;
+                    array_copy(gridsize, arrayx2, &vardata[varID][offset]);
                   }
               }
-            else if (filltype == FILL_TS)
+            else if (fillType == FillType::TS)
               {
-                auto offset = varList2[varID].gridsize * levelID;
-                array_copy(varList2[varID].gridsize, &vardata[varID][offset], arrayx2);
+                auto gridsize = varList2.vars[varID].gridsize;
+                auto offset = gridsize * levelID;
+                array_copy(gridsize, &vardata[varID][offset], arrayx2);
               }
 
-            auto datatype1 = varList1[varID].datatype;
-            auto gridsize1 = varList1[varID].gridsize;
-            auto missval1 = varList1[varID].missval;
+            const auto &var1 = varList1.vars[varID];
+            auto datatype1 = var1.dataType;
+            auto gridsize1 = var1.gridsize;
+            auto missval1 = var1.missval;
 
-            auto xvarID = (filltype == FILL_REC) ? 0 : varID;
-            auto datatype2 = varList2[xvarID].datatype;
-            auto gridsize2 = varList2[xvarID].gridsize;
-            auto missval2 = varList2[xvarID].missval;
+            auto xvarID = (fillType == FillType::REC) ? 0 : varID;
+            const auto &var2 = varList2.vars[xvarID];
+            auto datatype2 = var2.dataType;
+            auto gridsize2 = var2.gridsize;
+            auto missval2 = var2.missval;
 
             if (gridsize1 != gridsize2)
               cdo_abort("Streams have different gridsize (gridsize1 = %zu; gridsize2 = %zu)!", gridsize1, gridsize2);
@@ -254,8 +259,8 @@ public:
                   }
               }
 
-            if (numMissVals1 > 0) cdo_check_missval(missval1, varList1[varID].name);
-            // if (numMissVals2 > 0) cdo_check_missval(missval2, varList2[varID].name);
+            if (numMissVals1 > 0) cdo_check_missval(missval1, varList1.vars[varID].name);
+            // if (numMissVals2 > 0) cdo_check_missval(missval2, varList2.vars[varID].name);
 
             auto hasMissvals = (numMissVals1 > 0 || numMissVals2 > 0);
             // clang-format off
@@ -278,7 +283,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID3);
     cdo_stream_close(streamID2);
diff --git a/src/Compc.cc b/src/Compc.cc
index a6748e42a3bb00461976f5fa41147f73b5f9e5c3..d249d0c50c3496a959c64a796b4c03ea8921b549 100644
--- a/src/Compc.cc
+++ b/src/Compc.cc
@@ -80,6 +80,7 @@ public:
     .constraints = { 1, 1, NoRestriction },
   };
   inline static RegisterEntry<Compc> registration = RegisterEntry<Compc>(module);
+
   CdoStreamID streamID1;
   int taxisID1;
 
@@ -89,13 +90,12 @@ public:
   int operFunc;
   double rconst;
 
-  VarList varList;
+  VarList varList1;
 
 public:
   void
-  init()
+  init() override
   {
-
     auto operatorID = cdo_operator_id();
     operFunc = cdo_operator_f1(operatorID);
 
@@ -113,7 +113,7 @@ public:
     taxisID2 = taxisDuplicate(taxisID1);
     vlistDefTaxis(vlistID2, taxisID2);
 
-    varList_init(varList, vlistID1);
+    varList1 = VarList(vlistID1);
 
     streamID2 = cdo_open_write(1);
     cdo_def_vlist(streamID2, vlistID2);
@@ -122,7 +122,7 @@ public:
   }
 
   void
-  run()
+  run() override
   {
     Field field;
 
@@ -139,14 +139,15 @@ public:
           {
             int varID, levelID;
             cdo_inq_record(streamID1, &varID, &levelID);
-            field.init(varList[varID]);
+            const auto &var = varList1.vars[varID];
+            field.init(var);
             cdo_read_record(streamID1, field);
 
             auto missval = field.missval;
             auto numMissVals = field.numMissVals;
             auto hasMissvals = (numMissVals > 0 || dbl_is_equal(rconst, missval));
 
-            if (numMissVals > 0) cdo_check_missval(missval, varList[varID].name);
+            if (numMissVals > 0) cdo_check_missval(missval, var.name);
 
             comp_function(field, operFunc, hasMissvals, rconst);
 
@@ -161,7 +162,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
diff --git a/src/Complextorect.cc b/src/Complextorect.cc
index ffd26b59c0015974b80cad22e58598c64b44b34f..f71e54afc0d1d48f72642b4223069f981b497557 100644
--- a/src/Complextorect.cc
+++ b/src/Complextorect.cc
@@ -16,13 +16,14 @@ public:
   using Process::Process;
   inline static CdoModule module = {
     .name = "Complextorect",
-    .operators = { { "complextorect"}, { "complextopol"} },
+    .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;
@@ -45,9 +46,8 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
-
     COMPLEXTORECT = module.get_id("complextorect");
     COMPLEXTOPOL = module.get_id("complextopol");
 
@@ -81,7 +81,7 @@ public:
 
     cdo_def_vlist(streamID2, vlistID2);
     cdo_def_vlist(streamID3, vlistID3);
-    varList_init(varList1, vlistID1);
+    varList1 = VarList(vlistID1);
 
     auto gridsizemax = vlistGridsizeMax(vlistID1);
     array1 = Varray<double>(2 * gridsizemax);
@@ -90,7 +90,7 @@ public:
   }
 
   void
-  run()
+  run() override
   {
     int tsID = 0;
     while (true)
@@ -111,13 +111,14 @@ public:
             cdo_def_record(streamID2, varID, levelID);
             cdo_def_record(streamID3, varID, levelID);
 
-            auto gridsize = varList1[varID].gridsize;
+            const auto &var1 = varList1.vars[varID];
+            auto gridsize = var1.gridsize;
 
             size_t numMissVals;
             cdo_read_record(streamID1, array1.data(), &numMissVals);
 
             auto is_EQ = dbl_is_equal;
-            auto missval1 = varList1[varID].missval;
+            auto missval1 = var1.missval;
             auto missval2 = missval1;
 
             if (operatorID == COMPLEXTORECT)
@@ -148,7 +149,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID3);
     cdo_stream_close(streamID2);
diff --git a/src/Cond.cc b/src/Cond.cc
index cffc7beb029d2e2b7f747fcc37623752715f5d19..d1667a92038c3bc4ce114342daeddc769014777d 100644
--- a/src/Cond.cc
+++ b/src/Cond.cc
@@ -23,7 +23,7 @@ static void
 operator_IFTHEN(size_t n, double mv1, double mv2, const Varray<double> &vIn1, const Varray<double> &vIn2, Varray<double> &vOut)
 {
   if (std::isnan(mv1))
-    for (size_t i = 0; i < n; ++i) vOut[i] = (!dbl_is_equal(vIn1[i], mv1) && !dbl_is_equal(vIn1[i], 0.0)) ? vIn2[i] : mv2;
+    for (size_t i = 0; i < n; ++i) vOut[i] = (dbl_is_not_equal(vIn1[i], mv1) && dbl_is_not_equal(vIn1[i], 0.0)) ? vIn2[i] : mv2;
   else
     for (size_t i = 0; i < n; ++i) vOut[i] = (!is_equal(vIn1[i], mv1) && !is_equal(vIn1[i], 0.0)) ? vIn2[i] : mv2;
 }
@@ -32,7 +32,7 @@ static void
 operator_IFNOTTHEN(size_t n, double mv1, double mv2, const Varray<double> &vIn1, const Varray<double> &vIn2, Varray<double> &vOut)
 {
   if (std::isnan(mv1))
-    for (size_t i = 0; i < n; ++i) vOut[i] = (!dbl_is_equal(vIn1[i], mv1) && dbl_is_equal(vIn1[i], 0.0)) ? vIn2[i] : mv2;
+    for (size_t i = 0; i < n; ++i) vOut[i] = (dbl_is_not_equal(vIn1[i], mv1) && dbl_is_equal(vIn1[i], 0.0)) ? vIn2[i] : mv2;
   else
     for (size_t i = 0; i < n; ++i) vOut[i] = (!is_equal(vIn1[i], mv1) && is_equal(vIn1[i], 0.0)) ? vIn2[i] : mv2;
 }
@@ -73,18 +73,16 @@ public:
 
   int operatorID;
 
-  VarList varList1, varList2;
+  VarList varList1;
+  VarList varList2;
   Varray<double> vaIn1, vaIn2, vaOut;
 
 public:
   void
-  init()
+  init() override
   {
-
-    // clang-format off
-IFTHEN = module.get_id("ifthen");
-IFNOTTHEN = module.get_id("ifnotthen");
-    // clang-format on
+    IFTHEN = module.get_id("ifthen");
+    IFNOTTHEN = module.get_id("ifnotthen");
 
     operatorID = cdo_operator_id();
 
@@ -106,16 +104,16 @@ IFNOTTHEN = module.get_id("ifnotthen");
     if (ntsteps1 == 0) ntsteps1 = 1;
     if (ntsteps2 == 0) ntsteps2 = 1;
 
-    if (vlistNrecs(vlistID1) == 1 && vlistNrecs(vlistID2) != 1)
+    if (vlistNumRecords(vlistID1) == 1 && vlistNumRecords(vlistID2) != 1)
       {
         filltype = FILL_REC;
         cdo_print("Filling up stream1 >%s< by copying the first record.", cdo_get_stream_name(0));
       }
 
-    if (filltype == FILL_NONE) vlist_compare(vlistID1, vlistID2, CmpVlist::Dim);
+    varList1 = VarList(vlistID1);
+    varList2 = VarList(vlistID2);
 
-    varList_init(varList1, vlistID1);
-    varList_init(varList2, vlistID2);
+    if (filltype == FILL_NONE) varList_compare(varList1, varList2, CmpVarList::Dim);
 
     streamID3 = cdo_open_write(2);
     cdo_def_vlist(streamID3, vlistID3);
@@ -144,7 +142,7 @@ IFNOTTHEN = module.get_id("ifnotthen");
   }
 
   void
-  run()
+  run() override
   {
     int tsID = 0;
     while (true)
@@ -178,23 +176,27 @@ IFNOTTHEN = module.get_id("ifnotthen");
 
                 if (filltype == FILL_TS)
                   {
-                    auto offset = varList1[varID].gridsize * levelID;
-                    array_copy(varList1[varID].gridsize, &vaIn1[0], &vardata1[varID][offset]);
+                    auto gridsize = varList1.vars[varID].gridsize;
+                    auto offset = gridsize * levelID;
+                    array_copy(gridsize, &vaIn1[0], &vardata1[varID][offset]);
                     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]);
+                auto gridsize = varList1.vars[varID].gridsize;
+                auto offset = gridsize * levelID;
+                array_copy(gridsize, &vardata1[varID][offset], &vaIn1[0]);
                 numMissVals1 = varnumMissVals1[varID][levelID];
               }
 
-            auto ngp = varList2[varID].gridsize;
-            auto missval2 = varList2[varID].missval;
-            if (recID == 0 || filltype != FILL_REC) missval1 = varList1[varID].missval;
+            const auto &var1 = varList1.vars[varID];
+            const auto &var2 = varList2.vars[varID];
+            auto ngp = var2.gridsize;
+            auto missval2 = var2.missval;
+            if (recID == 0 || filltype != FILL_REC) missval1 = var1.missval;
 
-            if (numMissVals1 > 0) cdo_check_missval(missval1, varList1[varID].name);
+            if (numMissVals1 > 0) cdo_check_missval(missval1, var1.name);
 
             // clang-format off
             if      (operatorID == IFTHEN)    operator_IFTHEN(ngp, missval1, missval2, vaIn1, vaIn2, vaOut);
@@ -212,7 +214,7 @@ IFNOTTHEN = module.get_id("ifnotthen");
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID3);
     cdo_stream_close(streamID2);
diff --git a/src/Cond2.cc b/src/Cond2.cc
index ca1ffa20ef45d3fdbf43e0450204c2995fe62de1..f8bcd5435118957027f25094bf625604f6ed8acb 100644
--- a/src/Cond2.cc
+++ b/src/Cond2.cc
@@ -52,14 +52,14 @@ public:
   CdoStreamID streamID4;
   int taxisID4;
 
-  VarList varList1, varList2;
+  VarList varList1;
+  VarList varList2;
   Varray<double> array1, array2, array3, array4;
 
 public:
   void
-  init()
+  init() override
   {
-
     operator_check_argc(0);
 
     streamID1 = cdo_open_read(0);
@@ -80,22 +80,22 @@ public:
     if (ntsteps1 == 0) ntsteps1 = 1;
     if (ntsteps2 == 0) ntsteps2 = 1;
 
-    if (vlistNrecs(vlistID1) == 1 && vlistNrecs(vlistID2) != 1)
+    if (vlistNumRecords(vlistID1) == 1 && vlistNumRecords(vlistID2) != 1)
       {
         filltype = FILL_REC;
         cdo_print("Filling up stream1 >%s< by copying the first record.", cdo_get_stream_name(0));
       }
 
-    if (filltype == FILL_NONE) vlist_compare(vlistID1, vlistID2, CmpVlist::Dim);
+    varList1 = VarList(vlistID1);
+    varList2 = VarList(vlistID2);
+
+    if (filltype == FILL_NONE) varList_compare(varList1, varList2, CmpVarList::Dim);
 
-    vlist_compare(vlistID2, vlistID3, CmpVlist::Dim);
+    varList_compare(varList2, VarList(vlistID3), CmpVarList::Dim);
 
     streamID4 = cdo_open_write(3);
     cdo_def_vlist(streamID4, vlistID4);
 
-    varList_init(varList1, vlistID1);
-    varList_init(varList2, vlistID2);
-
     auto gridsizemax = vlistGridsizeMax(vlistID1);
 
     if (filltype == FILL_REC && gridsizemax != gridInqSize(vlistGrid(vlistID1, 0)))
@@ -122,7 +122,7 @@ public:
   }
 
   void
-  run()
+  run() override
   {
     int tsID = 0;
     while (true)
@@ -162,7 +162,7 @@ public:
 
                 if (filltype == FILL_TS)
                   {
-                    auto gridsize = varList1[varID].gridsize;
+                    auto gridsize = varList1.vars[varID].gridsize;
                     auto offset = gridsize * levelID;
                     array_copy(gridsize, &array1[0], &vardata1[varID][offset]);
                     varnumMissVals1[varID][levelID] = numMissVals1;
@@ -170,17 +170,19 @@ public:
               }
             else if (filltype == FILL_TS)
               {
-                auto gridsize = varList1[varID].gridsize;
+                auto gridsize = varList1.vars[varID].gridsize;
                 auto offset = gridsize * levelID;
                 array_copy(gridsize, &vardata1[varID][offset], &array1[0]);
                 numMissVals1 = varnumMissVals1[varID][levelID];
               }
 
-            auto gridsize = varList2[varID].gridsize;
-            auto missval2 = varList2[varID].missval;
-            if (recID == 0 || filltype != FILL_REC) missval1 = varList1[varID].missval;
+            const auto &var1 = varList1.vars[varID];
+            const auto &var2 = varList2.vars[varID];
+            auto gridsize = var2.gridsize;
+            auto missval2 = var2.missval;
+            if (recID == 0 || filltype != FILL_REC) missval1 = var1.missval;
 
-            if (numMissVals1 > 0) cdo_check_missval(missval1, varList1[varID].name);
+            if (numMissVals1 > 0) cdo_check_missval(missval1, var1.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];
@@ -195,7 +197,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID4);
     cdo_stream_close(streamID3);
diff --git a/src/Condc.cc b/src/Condc.cc
index 3acc2e29d9a5baeb3b50c09155d52a141ae7f23a..f62fe2ff5d68acfc6c6b74c2b269cf747addd512 100644
--- a/src/Condc.cc
+++ b/src/Condc.cc
@@ -24,7 +24,7 @@ operator_IFTHENC(size_t n, T mv, Varray<T> &v, T cVal)
 {
   constexpr T nullVal{ 0 };
   if (std::isnan(mv))
-    for (size_t i = 0; i < n; ++i) v[i] = (!dbl_is_equal(v[i], mv) && !dbl_is_equal(v[i], nullVal)) ? cVal : mv;
+    for (size_t i = 0; i < n; ++i) v[i] = (dbl_is_not_equal(v[i], mv) && dbl_is_not_equal(v[i], nullVal)) ? cVal : mv;
   else
     for (size_t i = 0; i < n; ++i) v[i] = (!is_equal(v[i], mv) && !is_equal(v[i], nullVal)) ? cVal : mv;
 }
@@ -35,7 +35,7 @@ operator_IFNOTTHENC(size_t n, T mv, Varray<T> &v, T cVal)
 {
   constexpr T nullVal{ 0 };
   if (std::isnan(mv))
-    for (size_t i = 0; i < n; ++i) v[i] = (!dbl_is_equal(v[i], mv) && dbl_is_equal(v[i], nullVal)) ? cVal : mv;
+    for (size_t i = 0; i < n; ++i) v[i] = (dbl_is_not_equal(v[i], mv) && dbl_is_equal(v[i], nullVal)) ? cVal : mv;
   else
     for (size_t i = 0; i < n; ++i) v[i] = (!is_equal(v[i], mv) && is_equal(v[i], nullVal)) ? cVal : mv;
 }
@@ -53,6 +53,7 @@ public:
     .constraints = { 1, 1, NoRestriction },
   };
   inline static RegisterEntry<Condc> registration = RegisterEntry<Condc>(module);
+
   int IFTHENC, IFNOTTHENC;
   CdoStreamID streamID1;
   int taxisID1;
@@ -63,17 +64,14 @@ public:
   int operatorID;
   double rconst;
 
-  VarList varList;
+  VarList varList1;
 
 public:
   void
-  init()
+  init() override
   {
-
-    // clang-format off
-IFTHENC = module.get_id("ifthenc");
-IFNOTTHENC = module.get_id("ifnotthenc");
-    // clang-format on
+    IFTHENC = module.get_id("ifthenc");
+    IFNOTTHENC = module.get_id("ifnotthenc");
 
     operatorID = cdo_operator_id();
 
@@ -91,14 +89,14 @@ IFNOTTHENC = module.get_id("ifnotthenc");
     taxisID2 = taxisDuplicate(taxisID1);
     vlistDefTaxis(vlistID2, taxisID2);
 
-    varList_init(varList, vlistID1);
+    varList1 = VarList(vlistID1);
 
     streamID2 = cdo_open_write(1);
     cdo_def_vlist(streamID2, vlistID2);
   }
 
   void
-  run()
+  run() override
   {
     Field field;
 
@@ -115,14 +113,15 @@ IFNOTTHENC = module.get_id("ifnotthenc");
           {
             int varID, levelID;
             cdo_inq_record(streamID1, &varID, &levelID);
-            field.init(varList[varID]);
+            const auto &var = varList1.vars[varID];
+            field.init(var);
             cdo_read_record(streamID1, field);
 
             auto missval = field.missval;
             auto ngp = field.size;
 
             auto numMissVals = field.numMissVals;
-            if (numMissVals > 0) cdo_check_missval(missval, varList[varID].name);
+            if (numMissVals > 0) cdo_check_missval(missval, var.name);
 
             // clang-format off
             if (field.memType == MemType::Float)
@@ -150,7 +149,7 @@ IFNOTTHENC = module.get_id("ifnotthenc");
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
diff --git a/src/Consecstat.cc b/src/Consecstat.cc
index 0739b689ed42b61cb9b114a83dc04f862aee07f6..73598a0f6fefac49ca46dec5d964a615767e2ec5 100644
--- a/src/Consecstat.cc
+++ b/src/Consecstat.cc
@@ -58,9 +58,9 @@ selEndOfPeriod(Field &periods, const Field &history, const Field &current, int i
 #endif
           for (size_t i = 0; i < len; ++i)
             {
-              if (!dbl_is_equal(harray[i], hmissval))
+              if (dbl_is_not_equal(harray[i], hmissval))
                 {
-                  if (!dbl_is_equal(carray[i], cmissval))
+                  if (dbl_is_not_equal(carray[i], cmissval))
                     parray[i] = (dbl_is_equal(carray[i], 0.0) && is_not_equal(harray[i], 0.0)) ? harray[i] : pmissval;
                   else  // DBL_IS_EQUAL(carray[i], cmissval)
                     parray[i] = (is_not_equal(harray[i], 0.0)) ? harray[i] : pmissval;
@@ -129,15 +129,15 @@ public:
   int otaxisID;
 
   int operatorID;
-  int nvars;
+  int numVars;
 
-  VarList varList;
+  VarList varList1;
   Field field;
-  FieldVector2D vars, hist, periods;
+  FieldVector2D varsData, histData, periodsData;
 
 public:
   void
-  init()
+  init() override
   {
     operatorID = cdo_operator_id();
 
@@ -152,32 +152,29 @@ public:
     otaxisID = taxisDuplicate(itaxisID);
     vlistDefTaxis(ovlistID, otaxisID);
 
-    varList_init(varList, ovlistID);
+    varList1 = VarList(ivlistID);
 
     field.resize(vlistGridsizeMax(ovlistID));
 
-    nvars = vlistNvars(ivlistID);
-    fields_from_vlist(ivlistID, vars, FIELD_VEC, 0);
-    if (operatorID == CONSECTS)
-      {
-        fields_from_vlist(ivlistID, hist, FIELD_VEC);
-        fields_from_vlist(ivlistID, periods, FIELD_VEC);
-      }
+    numVars = varList1.numVars();
+    field2D_init(varsData, varList1, FIELD_VEC, 0);
+    if (operatorID == CONSECTS) field2D_init(histData, varList1, FIELD_VEC);
+    if (operatorID == CONSECTS) field2D_init(periodsData, varList1, FIELD_VEC);
 
-    for (int varID = 0; varID < nvars; ++varID) cdiDefKeyString(ovlistID, varID, CDI_KEY_UNITS, "steps");  // TODO
+    for (int varID = 0; varID < numVars; ++varID) cdiDefKeyString(ovlistID, varID, CDI_KEY_UNITS, "steps");  // TODO
 
     ostreamID = cdo_open_write(1);
     cdo_def_vlist(ostreamID, ovlistID);
   }
+
   void
-  run()
+  run() override
   {
-
     int itsID = 0;
     int otsID = 0;
     while (true)
       {
-        const auto nrecs = cdo_stream_inq_timestep(istreamID, itsID);
+        auto nrecs = cdo_stream_inq_timestep(istreamID, itsID);
         if (nrecs == 0) break;
 
         vDateTime = taxisInqVdatetime(itaxisID);
@@ -202,31 +199,32 @@ public:
             int varID, levelID;
             cdo_inq_record(istreamID, &varID, &levelID);
             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;
+            const auto &var1 = varList1.vars[varID];
+            field.grid = var1.gridID;
+            field.size = var1.gridsize;
+            field.missval = var1.missval;
 
-            field2_sumtr(vars[varID][levelID], field, refval);
+            field2_sumtr(varsData[varID][levelID], field, refval);
 
             switch (operatorID)
               {
               case CONSECSUM:
                 cdo_def_record(ostreamID, varID, levelID);
-                cdo_write_record(ostreamID, vars[varID][levelID].vec_d.data(), vars[varID][levelID].numMissVals);
+                cdo_write_record(ostreamID, varsData[varID][levelID].vec_d.data(), varsData[varID][levelID].numMissVals);
                 break;
               case CONSECTS:
                 if (itsID != 0)
                   {
-                    selEndOfPeriod(periods[varID][levelID], hist[varID][levelID], vars[varID][levelID], false);
+                    selEndOfPeriod(periodsData[varID][levelID], histData[varID][levelID], varsData[varID][levelID], false);
                     cdo_def_record(ostreamID, varID, levelID);
-                    cdo_write_record(ostreamID, periods[varID][levelID].vec_d.data(), periods[varID][levelID].numMissVals);
+                    cdo_write_record(ostreamID, periodsData[varID][levelID].vec_d.data(), periodsData[varID][levelID].numMissVals);
                   }
 #ifdef _OPENMP
 #pragma omp parallel for default(shared) schedule(static)
-                for (size_t i = 0; i < vars[varID][levelID].size; ++i)
-                  hist[varID][levelID].vec_d[i] = vars[varID][levelID].vec_d[i];
+                for (size_t i = 0; i < varsData[varID][levelID].size; ++i)
+                  histData[varID][levelID].vec_d[i] = varsData[varID][levelID].vec_d[i];
 #else
-                hist[varID][levelID].vec_d = vars[varID][levelID].vec_d;
+                histData[varID][levelID].vec_d = varsData[varID][levelID].vec_d;
 #endif
                 break;
               default: printf(SWITCHWARN, __func__); break;
@@ -243,20 +241,21 @@ public:
         taxisDefVdatetime(otaxisID, vDateTime);
         cdo_def_timestep(ostreamID, otsID - 1);
 
-        for (int varID = 0; varID < nvars; ++varID)
+        for (int varID = 0; varID < numVars; ++varID)
           {
-            const auto nlevels = varList[varID].nlevels;
+            const auto nlevels = varList1.vars[varID].nlevels;
             for (int levelID = 0; levelID < nlevels; ++levelID)
               {
-                selEndOfPeriod(periods[varID][levelID], hist[varID][levelID], vars[varID][levelID], true);
+                selEndOfPeriod(periodsData[varID][levelID], histData[varID][levelID], varsData[varID][levelID], true);
                 cdo_def_record(ostreamID, varID, levelID);
-                cdo_write_record(ostreamID, periods[varID][levelID].vec_d.data(), periods[varID][levelID].numMissVals);
+                cdo_write_record(ostreamID, periodsData[varID][levelID].vec_d.data(), periodsData[varID][levelID].numMissVals);
               }
           }
       }
   }
+
   void
-  close()
+  close() override
   {
     cdo_stream_close(istreamID);
     cdo_stream_close(ostreamID);
diff --git a/src/Copy.cc b/src/Copy.cc
index acfe478e5346da4a9efe681a47bc432dd7b04593..8e6845032940bfc040b94bb87876589801265f49 100644
--- a/src/Copy.cc
+++ b/src/Copy.cc
@@ -66,14 +66,14 @@ public:
 
   bool dataIsUnchanged;
 
-  int nfiles;
+  int numFiles;
   bool isFdbCopy;
 
   int operatorID;
 
 public:
   void
-  init()
+  init() override
   {
     dataIsUnchanged = data_is_unchanged();
 
@@ -90,18 +90,18 @@ public:
     operator_check_argc(0);
 
     auto streamCnt = cdo_stream_cnt();
-    nfiles = streamCnt - 1;
+    numFiles = streamCnt - 1;
 
-    isFdbCopy = is_fdb_copy(dataIsUnchanged, nfiles);
-
-    progress::init();
+    isFdbCopy = is_fdb_copy(dataIsUnchanged, numFiles);
   }
 
   void
-  run()
+  run() override
   {
+    cdo::Progress progress;
+
     int tsID2 = 0;
-    for (int indf = 0; indf < nfiles; ++indf)
+    for (int indf = 0; indf < numFiles; ++indf)
       {
         if (Options::cdoVerbose) cdo_print("Process file: %s", cdo_get_stream_name(indf));
 
@@ -109,8 +109,7 @@ public:
         auto vlistID1 = cdo_stream_inq_vlist(streamID1);
         auto taxisID1 = vlistInqTaxis(vlistID1);
 
-        VarList varList1;
-        varList_init(varList1, vlistID1);
+        VarList varList1(vlistID1);
 
         if (indf == 0)
           {
@@ -118,46 +117,47 @@ public:
             taxisID2 = taxisDuplicate(taxisID1);
             vlistDefTaxis(vlistID2, taxisID2);
 
-            auto nvars = vlistNvars(vlistID1);
-
-            auto ntsteps = vlistNtsteps(vlistID1);
-            if (ntsteps == 1 && varList_numVaryingVars(varList1) == 0) ntsteps = 0;
+            auto numVars = varList1.numVars();
+            auto numSteps = varList1.numSteps();
+            if (numSteps == 1 && varList1.numVaryingVars() == 0) numSteps = 0;
 
-            if (ntsteps == 0 && nfiles > 1)
+            if (numSteps == 0 && numFiles > 1)
               {
                 hasConstantFields = false;
-                for (int varID = 0; varID < nvars; ++varID) vlistDefVarTimetype(vlistID2, varID, TIME_VARYING);
+                for (int varID = 0; varID < numVars; ++varID) vlistDefVarTimetype(vlistID2, varID, TIME_VARYING);
               }
           }
-        else { vlist_compare(vlistID1, vlistID2, CmpVlist::All); }
+        else
+          {
+            VarList varList2(vlistID2);
+            varList_compare(varList1, varList2);
+          }
 
         if (streamID2 == CDO_STREAM_UNDEF)
           {
-            streamID2 = cdo_open_write(nfiles);
+            streamID2 = cdo_open_write(numFiles);
             cdo_def_vlist(streamID2, vlistID2);
           }
 
-        auto ntsteps = vlistNtsteps(vlistID1);
-
+        auto numSteps = varList1.numSteps();
         int tsID1 = 0;
         while (true)
           {
-            auto nrecs = cdo_stream_inq_timestep(streamID1, tsID1);
-            if (nrecs == 0) break;
+            auto numRecords = cdo_stream_inq_timestep(streamID1, tsID1);
+            if (numRecords == 0) break;
 
             cdo_taxis_copy_timestep(taxisID2, taxisID1);
             cdo_def_timestep(streamID2, tsID2);
 
-            for (int recID = 0; recID < nrecs; ++recID)
+            for (int recID = 0; recID < numRecords; ++recID)
               {
-                double fstatus = indf + ((ntsteps >= 0) ? (tsID1 + (recID + 1.0) / nrecs) / ntsteps : 1.0);
-                if (!Options::cdoVerbose) progress::update(0, 1, fstatus / nfiles);
+                double fstatus = indf + ((numSteps >= 0) ? (tsID1 + (recID + 1.0) / numRecords) / numSteps : 1.0);
+                progress.update(fstatus / numFiles);
 
-                int varID, levelID;
-                cdo_inq_record(streamID1, &varID, &levelID);
+                auto [varID, levelID] = cdo_inq_record(streamID1);
 
-                if (hasConstantFields && tsID2 > 0 && tsID1 == 0)
-                  if (varList1[varID].isConstant) continue;
+                const auto &var1 = varList1.vars[varID];
+                if (hasConstantFields && tsID2 > 0 && tsID1 == 0 && var1.isConstant) continue;
 
                 cdo_def_record(streamID2, varID, levelID);
 
@@ -167,7 +167,7 @@ public:
                   }
                 else
                   {
-                    field.init(varList1[varID]);
+                    field.init(var1);
                     cdo_read_record(streamID1, field);
                     cdo_write_record(streamID2, field);
                   }
@@ -182,7 +182,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
 
diff --git a/src/DCW_util.cc b/src/DCW_util.cc
index 55b9101b974440c46c18dbfc59676b6d71b1dca1..965cdca77fb44547ed9a449720d226e4d8e50870 100644
--- a/src/DCW_util.cc
+++ b/src/DCW_util.cc
@@ -50,7 +50,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
 
     if (dcw_load_lists(dcw_lists)) cdo_abort("dcw_load_lists() failed!");
@@ -59,7 +59,7 @@ public:
   }
 
   void
-  run()
+  run() override
   {
 
     if (argc == 0) { cdo_abort("Parameter missing (available keywords are path/countries)"); }
@@ -82,7 +82,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
   }
 };
diff --git a/src/Dayarith.cc b/src/Dayarith.cc
index 2466d0c73f9623019553b111fbf87ce68095d2a1..e67c83f1b426e0b3ea20403d32b1c5bb4132eb7f 100644
--- a/src/Dayarith.cc
+++ b/src/Dayarith.cc
@@ -36,6 +36,7 @@ public:
     .constraints = { 2, 1, NoRestriction },
   };
   inline static RegisterEntry<Dayarith> registration = RegisterEntry<Dayarith>(module);
+
   CdoStreamID streamID1;
   int taxisID1;
 
@@ -49,13 +50,12 @@ public:
 
   VarList varList1;
   Field field;
-  FieldVector2D vars2;
+  FieldVector2D varsData2;
 
 public:
   void
-  init()
+  init() override
   {
-
     auto operatorID = cdo_operator_id();
     operfunc = cdo_operator_f1(operatorID);
 
@@ -70,9 +70,9 @@ public:
 
     vlist_unpack(vlistID3);
 
-    vlist_compare(vlistID1, vlistID2, CmpVlist::All);
-
-    varList_init(varList1, vlistID1);
+    varList1 = VarList(vlistID1);
+    VarList varList2(vlistID2);
+    varList_compare(varList1, varList2);
 
     taxisID1 = vlistInqTaxis(vlistID1);
     taxisID2 = vlistInqTaxis(vlistID2);
@@ -82,11 +82,11 @@ public:
     streamID3 = cdo_open_write(2);
     cdo_def_vlist(streamID3, vlistID3);
 
-    fields_from_vlist(vlistID2, vars2, FIELD_VEC | FIELD_NAT);
+    field2D_init(varsData2, varList2, FIELD_VEC | FIELD_NAT);
   }
 
   void
-  run()
+  run() override
   {
     CdiDate vDate2{};
     int tsID = 0;
@@ -120,7 +120,7 @@ public:
               {
                 int varID, levelID;
                 cdo_inq_record(streamID2, &varID, &levelID);
-                cdo_read_record(streamID2, vars2[varID][levelID]);
+                cdo_read_record(streamID2, varsData2[varID][levelID]);
               }
 
             tsID2++;
@@ -133,10 +133,10 @@ public:
           {
             int varID, levelID;
             cdo_inq_record(streamID1, &varID, &levelID);
-            field.init(varList1[varID]);
+            field.init(varList1.vars[varID]);
             cdo_read_record(streamID1, field);
 
-            field2_function(field, vars2[varID][levelID], operfunc);
+            field2_function(field, varsData2[varID][levelID], operfunc);
 
             cdo_def_record(streamID3, varID, levelID);
             cdo_write_record(streamID3, field);
@@ -147,7 +147,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID3);
     cdo_stream_close(streamID2);
diff --git a/src/Deltat.cc b/src/Deltat.cc
index 2f2533aefd4726ae36ce74ababedd9de89c8cdce..d53c16b2ede53f6ebd306580cb4a1e52d351c766 100644
--- a/src/Deltat.cc
+++ b/src/Deltat.cc
@@ -82,6 +82,8 @@ public:
     .constraints = { 1, 1, NoRestriction },
   };
   inline static RegisterEntry<Deltat> registration = RegisterEntry<Deltat>(module);
+
+private:
   CdoStreamID streamID1;
   CdoStreamID streamID2;
 
@@ -89,19 +91,15 @@ public:
   int taxisID2;
   int calendar;
   VarList varList1;
-  FieldVector2D vars;
+  FieldVector2D varsData;
 
   Field field1, field2;
   bool ldivdt;
 
 public:
   void
-  init()
+  init() override
   {
-
-    // clang-format off
-    // clang-format on
-
     auto operatorID = cdo_operator_id();
     ldivdt = cdo_operator_f2(operatorID);
 
@@ -118,15 +116,15 @@ public:
 
     calendar = taxisInqCalendar(taxisID1);
 
-    varList_init(varList1, vlistID1);
+    varList1 = VarList(vlistID1);
 
     streamID2 = cdo_open_write(1);
     cdo_def_vlist(streamID2, vlistID2);
 
-    fields_from_vlist(vlistID1, vars, FIELD_VEC | FIELD_NAT);
+    field2D_init(varsData, vlistID1, FIELD_VEC | FIELD_NAT);
   }
   void
-  run()
+  run() override
   {
     int tsID = 0;
     auto nrecs = cdo_stream_inq_timestep(streamID1, tsID);
@@ -137,7 +135,7 @@ public:
       {
         int varID, levelID;
         cdo_inq_record(streamID1, &varID, &levelID);
-        cdo_read_record(streamID1, vars[varID][levelID]);
+        cdo_read_record(streamID1, varsData[varID][levelID]);
       }
 
     tsID++;
@@ -158,12 +156,13 @@ public:
           {
             int varID, levelID;
             cdo_inq_record(streamID1, &varID, &levelID);
-            field1.init(varList1[varID]);
+            const auto &var1 = varList1.vars[varID];
+            field1.init(var1);
             cdo_read_record(streamID1, field1);
 
-            auto &field0 = vars[varID][levelID];
+            auto &field0 = varsData[varID][levelID];
 
-            field2.init(varList1[varID]);
+            field2.init(var1);
             field_deltat(field0, field1, field2, idtInSec);
 
             field_copy(field1, field0);
@@ -178,7 +177,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
diff --git a/src/Deltime.cc b/src/Deltime.cc
index 8369959cb2e4ee299d2bc7a95ee53060b3488e52..2641cb13bfb84f4f057a5809be85c27e7c6b913d 100644
--- a/src/Deltime.cc
+++ b/src/Deltime.cc
@@ -19,13 +19,14 @@ public:
   using Process::Process;
   inline static CdoModule module = {
     .name = "Deltime",
-    .operators = { { "delday"}, { "del29feb"} },
+    .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;
 
@@ -39,21 +40,16 @@ public:
   int nfound = 0;
   bool dataIsUnchanged;
 
-  Field field;
-
   VarList varList1;
 
 public:
   void
-  init()
+  init() override
   {
-
     dataIsUnchanged = data_is_unchanged();
 
-    // clang-format off
-DELDAY = module.get_id("delday");
-DEL29FEB = module.get_id("del29feb");
-    // clang-format on
+    DELDAY = module.get_id("delday");
+    DEL29FEB = module.get_id("del29feb");
 
     (void) (DELDAY);  // unused
 
@@ -103,11 +99,14 @@ DEL29FEB = module.get_id("del29feb");
     streamID2 = cdo_open_write(1);
     cdo_def_vlist(streamID2, vlistID2);
 
-    varList_init(varList1, vlistID1);
+    varList1 = VarList(vlistID1);
   }
+
   void
-  run()
+  run() override
   {
+    Field field;
+
     int tsID = 0;
     int tsID2 = 0;
     while (true)
@@ -141,7 +140,7 @@ DEL29FEB = module.get_id("del29feb");
                 if (dataIsUnchanged) { cdo_copy_record(streamID2, streamID1); }
                 else
                   {
-                    field.init(varList1[varID]);
+                    field.init(varList1.vars[varID]);
                     cdo_read_record(streamID1, field);
                     cdo_write_record(streamID2, field);
                   }
@@ -151,8 +150,9 @@ DEL29FEB = module.get_id("del29feb");
         tsID++;
       }
   }
+
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
diff --git a/src/Depth.cc b/src/Depth.cc
index 6cf1bd3cc1fc2ec2b4eb1f821b30a5211f4b1621..653f27c9478de7a7d13b08a0f65c3fe044abe4aa 100644
--- a/src/Depth.cc
+++ b/src/Depth.cc
@@ -9,13 +9,7 @@
 
 #include "cdo_options.h"
 #include "process_int.h"
-#include "cdo_vlist.h"
-#include "field_vinterp.h"
-#include "stdnametable.h"
 #include "util_string.h"
-#include "const.h"
-#include "cdo_zaxis.h"
-#include "param_conversion.h"
 #include "field_functions.h"
 
 template <typename T>
@@ -59,7 +53,7 @@ public:
   using Process::Process;
   inline static CdoModule module = {
     .name = "Depth",
-    .operators = { { "zsdepth"} },
+    .operators = { { "zsdepth" } },
     .aliases = {},
     .mode = EXPOSED,     // Module mode: 0:intern 1:extern
     .number = CDI_REAL,  // Allowed number type
@@ -82,7 +76,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
     streamID1 = cdo_open_read(0);
 
@@ -91,15 +85,14 @@ public:
     taxisID1 = vlistInqTaxis(vlistID1);
     taxisID2 = taxisDuplicate(taxisID1);
 
-    VarList varList1;
-    varList_init(varList1, vlistID1);
-    varListSetUniqueMemtype(varList1);
+    VarList varList1(vlistID1);
+    varList_set_unique_memtype(varList1);
 
     auto nvars = vlistNvars(vlistID1);
 
     for (int varID = 0; varID < nvars; ++varID)
       {
-        auto varname = string_to_lower(varList1[varID].name);
+        auto varname = string_to_lower(varList1.vars[varID].name);
 
         // clang-format off
         if      (varname == "prism_thick_c") thickID = varID;
@@ -113,10 +106,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.vars[thickID].name);
+        if (-1 != stretchID)  cdo_print("  %s -> %s", "zstar surface stretch at cell center", varList1.vars[stretchID].name);
+        if (-1 != zosID)      cdo_print("  %s -> %s", "zstar sfc elevation at cell center", varList1.vars[zosID].name);
+        if (-1 != draftaveID) cdo_print("  %s -> %s", "draftave", varList1.vars[draftaveID].name);
         // clang-format on
       }
 
@@ -125,10 +118,10 @@ public:
     if (-1 == zosID) cdo_abort("zos not found!");
     if (-1 == draftaveID) cdo_warning("draftave not found, set to zero!");
 
-    auto zaxisID = varList1[thickID].zaxisID;
-    nlevels = varList1[thickID].nlevels;
+    auto zaxisID = varList1.vars[thickID].zaxisID;
+    nlevels = varList1.vars[thickID].nlevels;
 
-    auto ngrids = vlistNgrids(vlistID1);
+    auto ngrids = vlistNumGrids(vlistID1);
     if (ngrids > 1) cdo_abort("Too many different grids!");
 
     int index = 0;
@@ -146,16 +139,16 @@ public:
 
     vardata1 = Field3DVector(nvars);
 
-    for (int varID = 0; varID < nvars; ++varID) vardata1[varID].init(varList1[varID]);
+    for (int varID = 0; varID < nvars; ++varID) vardata1[varID].init(varList1.vars[varID]);
 
-    fullDepth.init(varList1[thickID]);
+    fullDepth.init(varList1.vars[thickID]);
 
     streamID2 = cdo_open_write(1);
     cdo_def_vlist(streamID2, vlistID2);
   }
 
   void
-  run()
+  run() override
   {
     int tsID = 0;
     while (true)
@@ -189,7 +182,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
diff --git a/src/Derivepar.cc b/src/Derivepar.cc
index 6c9b8ec849740d501a9b48ec22503f213e4a6ae8..6d2ed748239c11008a442f2478b0c67c253f4f77 100644
--- a/src/Derivepar.cc
+++ b/src/Derivepar.cc
@@ -107,7 +107,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
     GHEIGHT_FULL = module.get_id("gheight");
     GHEIGHT_HALF = module.get_id("gheight_half");
@@ -138,24 +138,24 @@ public:
 
     if (zaxisID_ML == -1) cdo_abort("No 3D variable with hybrid sigma pressure coordinate found!");
 
-    varList_init(varList1, vlistID1);
-    varListSetUniqueMemtype(varList1);
+    varList1 = VarList(vlistID1);
+    varList_set_unique_memtype(varList1);
 
-    auto nvars = vlistNvars(vlistID1);
+    auto numVars = varList1.numVars();
 
-    varIDs = search_varIDs(varList1, vlistID1, numFullLevels);
+    varIDs = varList_search_varIDs(varList1, numFullLevels);
 
     if (Options::cdoVerbose)
       {
         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.vars[varIDs.humID].name);
+        if (-1 != varIDs.tempID)    cdo_print("  %s -> %s", var_stdname(air_temperature), varList1.vars[varIDs.tempID].name);
+        if (-1 != varIDs.psID)      cdo_print("  %s -> %s", var_stdname(surface_air_pressure), varList1.vars[varIDs.psID].name);
+        if (-1 != varIDs.lnpsID)    cdo_print("  LOG(%s) -> %s", var_stdname(surface_air_pressure), varList1.vars[varIDs.lnpsID].name);
+        if (-1 != varIDs.sgeopotID) cdo_print("  %s -> %s", var_stdname(surface_geopotential), varList1.vars[varIDs.sgeopotID].name);
+        if (-1 != varIDs.geopotID)  cdo_print("  %s -> %s", var_stdname(geopotential), varList1.vars[varIDs.geopotID].name);
+        if (-1 != varIDs.gheightID) cdo_print("  %s -> %s", var_stdname(geopotential_height), varList1.vars[varIDs.gheightID].name);
         // clang-format on
       }
 
@@ -164,13 +164,12 @@ public:
 
     if (varIDs.tempID == -1) cdo_abort("%s not found!", var_stdname(air_temperature));
 
-    for (int varID = 0; varID < nvars; ++varID)
+    for (int varID = 0; varID < numVars; ++varID)
       {
         if (operatorID == SEALEVELPRESSURE) varIDs.humID = -1;
 
-        const auto &var = varList1[varID];
-        if (var.gridType == GRID_SPECTRAL && var.zaxisType == ZAXIS_HYBRID)
-          cdo_abort("Spectral data on model level unsupported!");
+        const auto &var = varList1.vars[varID];
+        if (var.gridType == GRID_SPECTRAL && var.zaxisType == ZAXIS_HYBRID) cdo_abort("Spectral data on model level unsupported!");
 
         if (var.gridType == GRID_SPECTRAL) cdo_abort("Spectral data unsupported!");
       }
@@ -271,7 +270,7 @@ public:
   }
 
   void
-  run()
+  run() override
   {
     int tsID = 0;
     while (true)
@@ -321,9 +320,10 @@ public:
             check_range_var2d(tsID + 1, sgeopot, MIN_FIS, MAX_FIS, "Orography");
           }
 
-        check_range_var3d(tsID + 1, varList1[varIDs.tempID].nlevels, gridsize, temp, MIN_T, MAX_T, "Temperature");
+        check_range_var3d(tsID + 1, varList1.vars[varIDs.tempID].nlevels, gridsize, temp, MIN_T, MAX_T, "Temperature");
 
-        if (varIDs.humID != -1) check_range_var3d(tsID + 1, varList1[varIDs.humID].nlevels, gridsize, hum, -0.1, MAX_Q, "Humidity");
+        if (varIDs.humID != -1)
+          check_range_var3d(tsID + 1, varList1.vars[varIDs.humID].nlevels, gridsize, hum, -0.1, MAX_Q, "Humidity");
 
         if (operatorID == GHEIGHT_FULL || operatorID == GHEIGHT_HALF)
           {
@@ -360,7 +360,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
diff --git a/src/Detrend.cc b/src/Detrend.cc
index e2bf9f6c01aad82f129eccc45f561d0b901dc2ee..62b507e10fe9b6212c74a775ee201b66b4fd4496 100644
--- a/src/Detrend.cc
+++ b/src/Detrend.cc
@@ -17,69 +17,15 @@
 #include "process_int.h"
 #include "cdo_vlist.h"
 #include "cdo_options.h"
+#include "cdo_task.h"
+#include "field_trend.h"
 #include "datetime.h"
-#include "cimdOmp.h"
 #include "pmlist.h"
 #include "param_conversion.h"
+#include "progress.h"
 #include "field_functions.h"
 #include "arithmetic.h"
 
-static void
-detrend(long nts, const Varray<double> &deltaTS0, double missval1, const Varray<double> &array1, Varray<double> &array2)
-{
-  auto missval2 = missval1;
-  double sumj = 0.0, sumjj = 0.0;
-  double sumx = 0.0, sumjx = 0.0;
-  long n = 0;
-
-  auto detrend_sum = [&](auto j, auto is_EQ) {
-    if (!is_EQ(array1[j], missval1))
-      {
-        auto zj = deltaTS0[j];
-        sumj += zj;
-        sumjj += zj * zj;
-        sumjx += zj * array1[j];
-        sumx += array1[j];
-        n++;
-      }
-  };
-
-  if (std::isnan(missval1))
-    for (long j = 0; j < nts; ++j) detrend_sum(j, dbl_is_equal);
-  else
-    for (long j = 0; j < nts; ++j) detrend_sum(j, is_equal);
-
-  double work1 = 0, work2 = 0;
-
-  {
-    auto is_EQ = is_equal;
-    work1 = DIVM(SUBM(sumjx, DIVM(MULM(sumj, sumx), n)), SUBM(sumjj, DIVM(MULM(sumj, sumj), n)));
-    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]))); };
-
-  if (std::isnan(missval1))
-    for (long j = 0; j < nts; ++j) array2[j] = detrend_kernel(j, dbl_is_equal);
-  else
-    for (long j = 0; j < nts; ++j) array2[j] = detrend_kernel(j, is_equal);
-}
-
-static void
-computeDeltaTS0(bool tstepIsEqual, int nts, int calendar, DateTimeList &dtlist, Varray<double> &deltaTS0)
-{
-  CheckTimeIncr checkTimeIncr;
-  JulianDate julianDate0;
-  double deltat1 = 0.0;
-
-  for (int tsID = 0; tsID < nts; ++tsID)
-    {
-      auto vDateTime = dtlist.get_vDateTime(tsID);
-      if (tstepIsEqual) check_time_increment(tsID, calendar, vDateTime, checkTimeIncr);
-      deltaTS0[tsID] = tstepIsEqual ? (double) tsID : delta_time_step_0(tsID, calendar, vDateTime, julianDate0, deltat1);
-    }
-}
-
 static void
 detrendGetParameter(bool &tstepIsEqual)
 {
@@ -122,25 +68,25 @@ public:
   };
   inline static RegisterEntry<Detrend> registration = RegisterEntry<Detrend>(module);
 
-  int varID, levelID;
+  static const int numWork = 5;
+
   DateTimeList dtlist;
 
   CdoStreamID streamID1;
   CdoStreamID streamID2;
 
-  VarList varList;
-  FieldVector3D vars;
+  VarList varList1;
+  FieldVector3D varsData;
 
   int taxisID1;
   int taxisID2;
   int vlistID1;
-  int nvars;
 
   bool tstepIsEqual = true;
 
 public:
   void
-  init()
+  init() override
   {
     detrendGetParameter(tstepIsEqual);
 
@@ -158,93 +104,180 @@ public:
     streamID2 = cdo_open_write(1);
     cdo_def_vlist(streamID2, vlistID2);
 
-    varList_init(varList, vlistID1);
+    varList1 = VarList(vlistID1);
+  }
 
-    nvars = vlistNvars(vlistID1);
+  void
+  vars_calc_trend_param(FieldVector3D &work)
+  {
+    auto numVars = varList1.numVars();
+    for (int varID = 0; varID < numVars; ++varID)
+      {
+        const auto &var = varList1.vars[varID];
+        for (int levelID = 0; levelID < var.nlevels; ++levelID)
+          {
+            auto gridsize = var.gridsize;
+            auto missval1 = var.missval;
+            auto missval2 = var.missval;
+
+            auto &paramA = work[0][varID][levelID].vec_d;
+            auto &paramB = work[1][varID][levelID].vec_d;
+            auto &sumj = work[0][varID][levelID].vec_d;
+            auto &sumjj = work[1][varID][levelID].vec_d;
+            const auto &sumjx = work[2][varID][levelID].vec_d;
+            const auto &sumx = work[3][varID][levelID].vec_d;
+            const auto &zn = work[4][varID][levelID].vec_d;
+
+            auto trend_kernel = [&](auto i, auto is_EQ) {
+              auto temp1 = SUBM(sumjx[i], DIVM(MULM(sumj[i], sumx[i]), zn[i]));
+              auto temp2 = SUBM(sumjj[i], DIVM(MULM(sumj[i], sumj[i]), zn[i]));
+              auto temp3 = DIVM(temp1, temp2);
+
+              paramA[i] = SUBM(DIVM(sumx[i], zn[i]), MULM(DIVM(sumj[i], zn[i]), temp3));
+              paramB[i] = temp3;
+            };
+
+            if (std::isnan(var.missval))
+              for (size_t i = 0; i < gridsize; ++i) trend_kernel(i, dbl_is_equal);
+            else
+              for (size_t i = 0; i < gridsize; ++i) trend_kernel(i, is_equal);
+          }
+      }
+  }
+
+  static void
+  vars_sub_trend(FieldVector3D &work, FieldVector2D &varsData, const VarList &varList, double zj)
+  {
+    auto numVars = varList.numVars();
+    for (int varID = 0; varID < numVars; ++varID)
+      {
+        const auto &var = varList.vars[varID];
+        if (var.isConstant) continue;
+        for (int levelID = 0; levelID < var.nlevels; ++levelID)
+          {
+            auto &field = varsData[varID][levelID];
+            auto &paramA = work[0][varID][levelID];
+            auto &paramB = work[1][varID][levelID];
+            sub_trend(zj, field, paramA, paramB);
+          }
+      }
+  }
+
+  static void
+  vars_trend_sum(FieldVector3D &work, const FieldVector2D &varsData, const VarList &varList, double zj)
+  {
+    auto numVars = varList.numVars();
+    for (int varID = 0; varID < numVars; ++varID)
+      {
+        const auto &var = varList.vars[varID];
+        if (var.isConstant) continue;
+        for (int levelID = 0; levelID < var.nlevels; ++levelID)
+          {
+            auto &field = varsData[varID][levelID];
+            calc_trend_sum(work, field, zj, varID, levelID);
+          }
+      }
   }
 
   void
-  run()
+  run() override
   {
+    auto runAsync = (Options::CDO_Parallel_Read > 0);
+    auto task = runAsync ? std::make_unique<cdo::Task>() : nullptr;
+
+    auto calendar = taxisInqCalendar(taxisID1);
+    CheckTimeIncr checkTimeIncr;
+    JulianDate julianDate0;
+    double deltat1 = 0.0;
+    auto numSteps = varList1.numSteps();
+    cdo::Progress progress;
+
+    if (numSteps > 0) varsData.resize(numSteps);
+
+    FieldVector3D work(numWork);
+    for (auto &w : work) field2D_init(w, varList1, FIELD_VEC, 0);
+
     int tsID = 0;
     while (true)
       {
         auto nrecs = cdo_stream_inq_timestep(streamID1, tsID);
         if (nrecs == 0) break;
 
-        constexpr size_t NALLOC_INC = 1024;
-        if ((size_t) tsID >= vars.size()) vars.resize(vars.size() + NALLOC_INC);
+        if (numSteps > 1) progress.update((tsID + 1.0) / numSteps, 0.0, 0.5);
 
         dtlist.taxis_inq_timestep(taxisID1, tsID);
+        auto vDateTime = dtlist.get_vDateTime(tsID);
+        if (tstepIsEqual) check_time_increment(tsID, calendar, vDateTime, checkTimeIncr);
+        auto zj = tstepIsEqual ? (double) tsID : delta_time_step_0(tsID, calendar, vDateTime, julianDate0, deltat1);
 
-        fields_from_vlist(vlistID1, vars[tsID]);
+        constexpr size_t NALLOC_INC = 1024;
+        if ((size_t) tsID >= varsData.size()) varsData.resize(varsData.size() + NALLOC_INC);
+        field2D_init(varsData[tsID], varList1);
 
         for (int recID = 0; recID < nrecs; ++recID)
           {
-            cdo_inq_record(streamID1, &varID, &levelID);
-            auto &field = vars[tsID][varID][levelID];
-            field.init(varList[varID]);
+            auto [varID, levelID] = cdo_inq_record(streamID1);
+            auto &field = varsData[tsID][varID][levelID];
+            field.init(varList1.vars[varID]);
             cdo_read_record(streamID1, field);
           }
 
+        if (runAsync && tsID > 0) task->wait();
+
+        std::function<void()> vars_trend_sum_func
+            = std::bind(vars_trend_sum, std::ref(work), std::cref(varsData[tsID]), std::cref(varList1), zj);
+
+        runAsync ? task->doAsync(vars_trend_sum_func) : vars_trend_sum_func();
+
         tsID++;
       }
 
-    auto nts = tsID;
-    Varray<double> deltaTS0(nts);
-    Varray2D<double> array1_2D(Threading::ompNumThreads, Varray<double>(nts));
-    Varray2D<double> array2_2D(Threading::ompNumThreads, Varray<double>(nts));
+    if (runAsync) task->wait();
 
-    auto calendar = taxisInqCalendar(taxisID1);
-    computeDeltaTS0(tstepIsEqual, nts, calendar, dtlist, deltaTS0);
+    numSteps = tsID;
 
-    for (varID = 0; varID < nvars; ++varID)
+    vars_calc_trend_param(work);
+
+    if (runAsync)
       {
-        const auto &var = varList[varID];
-        auto nsteps = var.isConstant ? 1 : nts;
-        auto missval = var.missval;
-        auto fieldMemType = var.memType;
-        auto gridsize = var.gridsize;
-        for (levelID = 0; levelID < var.nlevels; ++levelID)
-          {
-#ifdef _OPENMP
-#pragma omp parallel for default(none) schedule(static) \
-    shared(fieldMemType, gridsize, nsteps, deltaTS0, missval, array1_2D, array2_2D, vars, varID, levelID)
-#endif
-            for (size_t i = 0; i < gridsize; ++i)
-              {
-                auto ompthID = cdo_omp_get_thread_num();
-                auto &array1 = array1_2D[ompthID];
-                auto &array2 = array2_2D[ompthID];
+        auto step = 0;
+        auto vDateTime = dtlist.get_vDateTime(step);
+        auto zj = tstepIsEqual ? (double) step : delta_time_step_0(step, calendar, vDateTime, julianDate0, deltat1);
+        std::function<void()> vars_sub_trend_func
+            = std::bind(vars_sub_trend, std::ref(work), std::ref(varsData[step]), std::ref(varList1), zj);
+        task->doAsync(vars_sub_trend_func);
+      }
 
-                if (fieldMemType == MemType::Float)
-                  for (int k = 0; k < nsteps; ++k) array1[k] = vars[k][varID][levelID].vec_f[i];
-                else
-                  for (int k = 0; k < nsteps; ++k) array1[k] = vars[k][varID][levelID].vec_d[i];
+    for (tsID = 0; tsID < numSteps; ++tsID)
+      {
+        progress.update((tsID + 1.0) / numSteps, 0.5, 0.5);
 
-                detrend(nsteps, deltaTS0, missval, array1, array2);
+        if (runAsync) task->wait();
+        auto step = runAsync ? tsID + 1 : tsID;
 
-                if (fieldMemType == MemType::Float)
-                  for (int k = 0; k < nsteps; ++k) vars[k][varID][levelID].vec_f[i] = array2[k];
-                else
-                  for (int k = 0; k < nsteps; ++k) vars[k][varID][levelID].vec_d[i] = array2[k];
-              }
+        if (step < numSteps)
+          {
+            auto vDateTime = dtlist.get_vDateTime(step);
+            auto zj = tstepIsEqual ? (double) step : delta_time_step_0(step, calendar, vDateTime, julianDate0, deltat1);
+
+            std::function<void()> vars_sub_trend_func
+                = std::bind(vars_sub_trend, std::ref(work), std::ref(varsData[step]), std::cref(varList1), zj);
+
+            runAsync ? task->doAsync(vars_sub_trend_func) : vars_sub_trend_func();
           }
-      }
 
-    for (tsID = 0; tsID < nts; ++tsID)
-      {
         dtlist.taxis_def_timestep(taxisID2, tsID);
         cdo_def_timestep(streamID2, tsID);
 
-        for (varID = 0; varID < nvars; ++varID)
+        auto numVars = varList1.numVars();
+        for (int varID = 0; varID < numVars; ++varID)
           {
-            const auto &var = varList[varID];
+            const auto &var = varList1.vars[varID];
             if (tsID && var.isConstant) continue;
-            for (levelID = 0; levelID < var.nlevels; ++levelID)
+            for (int levelID = 0; levelID < var.nlevels; ++levelID)
               {
+                auto &field = varsData[tsID][varID][levelID];
                 cdo_def_record(streamID2, varID, levelID);
-                auto &field = vars[tsID][varID][levelID];
                 cdo_write_record(streamID2, field);
               }
           }
@@ -252,9 +285,8 @@ public:
   }
 
   void
-  close()
+  close() override
   {
-
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
   }
diff --git a/src/Diff.cc b/src/Diff.cc
index fc14ca99dd1f0064c93c4a1c7bda710b2ea31cff..4bfc96d2ac492b1e5b28b0524822d419c4c8fb31 100644
--- a/src/Diff.cc
+++ b/src/Diff.cc
@@ -13,46 +13,48 @@
 
 #include <map>
 #include <algorithm>
+#include <climits>
 
 #include <cdi.h>
 
+#include "cdo_task.h"
 #include "process_int.h"
-#include "cdo_vlist.h"
-#include "param_conversion.h"
 #include "mpmo_color.h"
 #include "cdo_options.h"
 #include "printinfo.h"
-#include "pmlist.h"
 #include "cdo_zaxis.h"
+#include "param_conversion.h"
+#include "field_functions.h"
+#include "pmlist.h"
 
-struct DiffParam
+struct DiffResult
 {
-  size_t ndiff = 0;
-  double absm = 0.0;
-  double relm = 0.0;
-  bool dsgn = false;
-  bool zero = false;
+  size_t ndiff{ 0 };
+  double absm{ 0.0 };
+  double relm{ 0.0 };
+  bool dsgn{ false };
+  bool zero{ false };
 };
 
 static inline void
-diff_kernel(double v1, double v2, DiffParam &dp)
+diff_kernel(double v1, double v2, DiffResult &result)
 {
   auto absdiff = std::fabs(v1 - v2);
-  if (absdiff > 0.0) dp.ndiff++;
+  if (absdiff > 0.0) result.ndiff++;
 
-  dp.absm = std::max(dp.absm, absdiff);
+  result.absm = std::max(result.absm, absdiff);
 
   auto vv = v1 * v2;
   if (vv < 0.0)
-    dp.dsgn = true;
+    result.dsgn = true;
   else if (is_equal(vv, 0.0))
-    dp.zero = true;
+    result.zero = true;
   else
-    dp.relm = std::max(dp.relm, absdiff / std::max(std::fabs(v1), std::fabs(v2)));
+    result.relm = std::max(result.relm, absdiff / std::max(std::fabs(v1), std::fabs(v2)));
 }
 
 static void
-diff_kernel_mv(double v1, double v2, double missval1, double missval2, DiffParam &dp)
+diff_kernel_mv(double v1, double v2, double missval1, double missval2, DiffResult &result)
 {
   auto v1IsNan = std::isnan(v1);
   auto v2IsNan = std::isnan(v2);
@@ -60,42 +62,42 @@ diff_kernel_mv(double v1, double v2, double missval1, double missval2, DiffParam
   auto v2IsMissval = dbl_is_equal(v2, missval2);
   if (v1IsNan != v2IsNan)
     {
-      dp.ndiff++;
-      dp.relm = 1.0;
+      result.ndiff++;
+      result.relm = 1.0;
     }
-  else if (!v1IsMissval && !v2IsMissval) { diff_kernel(v1, v2, dp); }
+  else if (!v1IsMissval && !v2IsMissval) { diff_kernel(v1, v2, result); }
   else if (v1IsMissval != v2IsMissval)
     {
-      dp.ndiff++;
-      dp.relm = 1.0;
+      result.ndiff++;
+      result.relm = 1.0;
     }
 }
 
-static DiffParam
-diff(size_t gridsize, const Field &field1, const Field &field2)
+static DiffResult
+diff(size_t n, const Field &field1, const Field &field2)
 {
-  DiffParam diffParam;
+  DiffResult diffParam;
   auto hasMissvals = (field1.numMissVals || field2.numMissVals);
   if (hasMissvals)
     {
       if (memtype_is_float_float(field1.memType, field2.memType))
         {
-          for (size_t i = 0; i < gridsize; ++i)
+          for (size_t i = 0; i < n; ++i)
             diff_kernel_mv(field1.vec_f[i], field2.vec_f[i], field1.missval, field2.missval, diffParam);
         }
       else if (memtype_is_float_double(field1.memType, field2.memType))
         {
-          for (size_t i = 0; i < gridsize; ++i)
+          for (size_t i = 0; i < n; ++i)
             diff_kernel_mv(field1.vec_f[i], field2.vec_d[i], field1.missval, field2.missval, diffParam);
         }
       else if (memtype_is_double_float(field1.memType, field2.memType))
         {
-          for (size_t i = 0; i < gridsize; ++i)
+          for (size_t i = 0; i < n; ++i)
             diff_kernel_mv(field1.vec_d[i], field2.vec_f[i], field1.missval, field2.missval, diffParam);
         }
       else
         {
-          for (size_t i = 0; i < gridsize; ++i)
+          for (size_t i = 0; i < n; ++i)
             diff_kernel_mv(field1.vec_d[i], field2.vec_d[i], field1.missval, field2.missval, diffParam);
         }
     }
@@ -103,19 +105,19 @@ diff(size_t gridsize, const Field &field1, const Field &field2)
     {
       if (memtype_is_float_float(field1.memType, field2.memType))
         {
-          for (size_t i = 0; i < gridsize; ++i) diff_kernel(field1.vec_f[i], field2.vec_f[i], diffParam);
+          for (size_t i = 0; i < n; ++i) { diff_kernel(field1.vec_f[i], field2.vec_f[i], diffParam); }
         }
       else if (memtype_is_float_double(field1.memType, field2.memType))
         {
-          for (size_t i = 0; i < gridsize; ++i) diff_kernel(field1.vec_f[i], field2.vec_d[i], diffParam);
+          for (size_t i = 0; i < n; ++i) { diff_kernel(field1.vec_f[i], field2.vec_d[i], diffParam); }
         }
       else if (memtype_is_double_float(field1.memType, field2.memType))
         {
-          for (size_t i = 0; i < gridsize; ++i) diff_kernel(field1.vec_d[i], field2.vec_f[i], diffParam);
+          for (size_t i = 0; i < n; ++i) { diff_kernel(field1.vec_d[i], field2.vec_f[i], diffParam); }
         }
       else
         {
-          for (size_t i = 0; i < gridsize; ++i) diff_kernel(field1.vec_d[i], field2.vec_d[i], diffParam);
+          for (size_t i = 0; i < n; ++i) { diff_kernel(field1.vec_d[i], field2.vec_d[i], diffParam); }
         }
     }
 
@@ -131,9 +133,23 @@ use_real_part(size_t gridsize, Field &field)
     for (size_t i = 0; i < gridsize; ++i) field.vec_d[i] = field.vec_d[i * 2];
 }
 
-static void
-diff_get_parameter(double &abslim, double &abslim2, double &rellim, int &mapflag, int &maxcount)
+struct DiffParam
+{
+  double absLimit{ 0.0 };
+  double absLimit2{ 1.e-3 };
+  double relLimit{ 1.0 };
+  MapFlag mapFlag{ MapFlag::Undefined };
+  int maxDiffRecords{ INT_MAX };
+  int numDiffRecords{ 0 };
+  int numDiffRecords2{ 0 };
+  bool printHeader{ true };
+};
+
+static DiffParam
+get_parameter(void)
 {
+  DiffParam params;
+
   auto pargc = cdo_operator_argc();
   if (pargc)
     {
@@ -152,21 +168,93 @@ diff_get_parameter(double &abslim, double &abslim2, double &rellim, int &mapflag
           const auto &value = kv.values[0];
 
           // clang-format off
-          if      (key == "abslim")   abslim = parameter_to_double(value);
-          else if (key == "abslim2")  abslim2 = parameter_to_double(value);
-          else if (key == "rellim")   rellim = parameter_to_double(value);
-          else if (key == "maxcount") maxcount = parameter_to_int(value);
+          if      (key == "abslim")   params.absLimit = parameter_to_double(value);
+          else if (key == "abslim2")  params.absLimit2 = parameter_to_double(value);
+          else if (key == "rellim")   params.relLimit = parameter_to_double(value);
+          else if (key == "maxcount") params.maxDiffRecords = parameter_to_int(value);
           else if (key == "names")
             {
-              if      (value == "left")      mapflag = 1;
-              else if (value == "right")     mapflag = 2;
-              else if (value == "intersect") mapflag = 3;
+              if      (value == "left")      params.mapFlag = MapFlag::Left;
+              else if (value == "right")     params.mapFlag = MapFlag::Right;
+              else if (value == "intersect") params.mapFlag = MapFlag::Intersect;
               else cdo_abort("Invalid value for key >%s< (names=<left/right/intersect>)", key, value);
             }
           else cdo_abort("Invalid parameter key >%s<!", key);
           // clang-format on
         }
     }
+
+  return params;
+}
+
+static void
+print_header(int operfunc, DiffParam &params)
+{
+  params.printHeader = false;
+
+  fprintf(stdout, "               Date     Time   Level Gridsize    Miss ");
+  fprintf(stdout, "   Diff ");
+  fprintf(stdout, ": S Z  Max_Absdiff Max_Reldiff : ");
+  // clang-format off
+  if      (operfunc == Func_Name)  fprintf(stdout, "Parameter name");
+  else if (operfunc == Func_Param) fprintf(stdout, "Parameter ID");
+  else if (operfunc == Func_Code)  fprintf(stdout, "Code number");
+  // clang-format on
+  fprintf(stdout, "\n");
+}
+
+static void
+compare_fields(const Field &field1, const Field &field2, int recordNumber, const CdoVar &var, int levelID, int operfunc,
+               CdiDateTime vDateTime, DiffParam &params)
+{
+  auto dr = diff(var.gridsize, field1, field2);
+
+  auto checkRelativeLimit = true;
+  if (!Options::silentMode || Options::cdoVerbose)
+    {
+      if (dr.absm > params.absLimit || (checkRelativeLimit && dr.relm >= params.relLimit) || Options::cdoVerbose)
+        {
+          if (params.printHeader) print_header(operfunc, params);
+
+          fprintf(stdout, "%6d ", recordNumber);
+          fprintf(stdout, ":");
+
+          auto vdateString = date_to_string(vDateTime.date);
+          auto vtimeString = time_to_string(vDateTime.time);
+
+          set_text_color(stdout, MAGENTA);
+          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(var.zaxisID, levelID));
+          fprintf(stdout, "%8zu %7zu ", var.gridsize, std::max(field1.numMissVals, field2.numMissVals));
+          fprintf(stdout, "%7zu ", dr.ndiff);
+          reset_text_color(stdout);
+
+          fprintf(stdout, ":");
+          fprintf(stdout, " %c %c ", dr.dsgn ? 'T' : 'F', dr.zero ? 'T' : 'F');
+          set_text_color(stdout, BLUE);
+          fprintf(stdout, "%#12.5g%#12.5g", dr.absm, dr.relm);
+          reset_text_color(stdout);
+          fprintf(stdout, " : ");
+
+          char paramstr[32];
+          if (operfunc == Func_Param) cdiParamToString(var.param, paramstr, sizeof(paramstr));
+
+          set_text_color(stdout, BRIGHT, GREEN);
+          // clang-format off
+          if      (operfunc == Func_Name)  fprintf(stdout, "%-11s", var.name.c_str());
+          else if (operfunc == Func_Param) fprintf(stdout, "%-11s", paramstr);
+          else if (operfunc == Func_Code)  fprintf(stdout, "%4d", var.code);
+          // clang-format on
+          reset_text_color(stdout);
+
+          fprintf(stdout, "\n");
+        }
+    }
+
+  if (dr.absm > params.absLimit || (checkRelativeLimit && dr.relm >= params.relLimit)) params.numDiffRecords++;
+  if (dr.absm > params.absLimit2 || (checkRelativeLimit && dr.relm >= params.relLimit)) params.numDiffRecords2++;
 }
 
 class Diff : public Process
@@ -175,10 +263,12 @@ public:
   using Process::Process;
   inline static CdoModule module = {
     .name = "Diff",
-    .operators = { { "diff", DiffHelp },
-                   { "diffp", DiffHelp },
-                   { "diffn", DiffHelp },
-                   { "diffc", DiffHelp } },
+    // clang-format off
+    .operators = { { "diff",  Func_Param, 0, DiffHelp },
+                   { "diffp", Func_Param, 0, DiffHelp },
+                   { "diffn", Func_Name,  0, DiffHelp },
+                   { "diffc", Func_Code,  0, DiffHelp } },
+    // clang-format on
     .aliases = { { "diffv", "diffn" } },
     .mode = EXPOSED,     // Module mode: 0:intern 1:extern
     .number = CDI_BOTH,  // Allowed number type
@@ -186,42 +276,34 @@ public:
   };
   inline static RegisterEntry<Diff> registration = RegisterEntry<Diff>(module);
 
-  int DIFF, DIFFP, DIFFN, DIFFC;
-  bool printHeader = true;
-  int varID1, varID2 = -1;
-  int levelID;
-  int ndrec = 0, nd2rec = 0, ngrec = 0;
-  char paramstr[32];
-  int mapflag = 0, maxcount = 0;
-  double abslim = 0.0, abslim2 = 1.e-3, rellim = 1.0;
+  int operfunc;
+
+  DiffParam params;
 
   CdoStreamID streamID1;
   CdoStreamID streamID2;
 
   int taxisID;
-  int operatorID;
 
   std::map<int, int> mapOfVarIDs;
-  VarList varList1, varList2;
+
+  VarList varList1;
+  VarList varList2;
 
 public:
   void
-  init()
+  init() override
   {
-    DIFF = module.get_id("diff");
-    DIFFP = module.get_id("diffp");
-    DIFFN = module.get_id("diffn");
-    DIFFC = module.get_id("diffc");
+    auto operatorID = cdo_operator_id();
+    operfunc = cdo_operator_f1(operatorID);
 
-    operatorID = cdo_operator_id();
-
-    diff_get_parameter(abslim, abslim2, rellim, mapflag, maxcount);
+    params = get_parameter();
 
     constexpr double rangeMin = -1.e33;
     constexpr double rangeMax = 1.e33;
-    if (rellim < rangeMin || rellim > rangeMax) cdo_abort("Rel. limit out of range!");
-    if (abslim < rangeMin || abslim > rangeMax) cdo_abort("Abs. limit out of range!");
-    if (abslim2 < rangeMin || abslim2 > rangeMax) cdo_abort("Abs2. limit out of range!");
+    if (params.relLimit < rangeMin || params.relLimit > rangeMax) cdo_abort("Rel. limit out of range!");
+    if (params.absLimit < rangeMin || params.absLimit > rangeMax) cdo_abort("Abs. limit out of range!");
+    if (params.absLimit2 < rangeMin || params.absLimit2 > rangeMax) cdo_abort("Abs2. limit out of range!");
 
     streamID1 = cdo_open_read(0);
     streamID2 = cdo_open_read(1);
@@ -229,35 +311,38 @@ public:
     auto vlistID1 = cdo_stream_inq_vlist(streamID1);
     auto vlistID2 = cdo_stream_inq_vlist(streamID2);
 
-    varList_init(varList1, vlistID1);
-    varList_init(varList2, vlistID2);
+    varList1 = VarList(vlistID1);
+    varList2 = VarList(vlistID2);
 
-    if (mapflag == 0)
+    if (params.mapFlag == MapFlag::Undefined)
       {
-        varList_compare(varList1, varList2, CmpVlist::All);
-        for (int varID = 0, numVars = varList1.size(); varID < numVars; ++varID) mapOfVarIDs[varID] = varID;
+        varList_compare(varList1, varList2);
+        for (const auto &var : varList1.vars) mapOfVarIDs[var.ID] = var.ID;
       }
-    else { varList_map(varList1, varList2, CmpVlist::All, mapflag, mapOfVarIDs); }
+    else { varList_map(varList1, varList2, params.mapFlag, mapOfVarIDs); }
 
     taxisID = vlistInqTaxis(vlistID1);
   }
 
   void
-  run()
+  run() override
   {
-    Field field1, field2;
+    auto runAsync = (Options::CDO_Parallel_Read > 0);
+    auto task = runAsync ? std::make_unique<cdo::Task>() : nullptr;
+    auto numTasks = runAsync ? 2 : 1;
+
+    FieldVector fieldVector1(numTasks);
+    FieldVector fieldVector2(numTasks);
 
+    int numSets = 0;
     int nrecs, nrecs2;
-    int indg = 0;
     int tsID = 0;
     while (true)
       {
-        auto stopLoop = false;
+        auto stopRead = false;
 
         nrecs = cdo_stream_inq_timestep(streamID1, tsID);
         auto vDateTime = taxisInqVdatetime(taxisID);
-        auto vdateString = date_to_string(vDateTime.date);
-        auto vtimeString = time_to_string(vDateTime.time);
 
         nrecs2 = cdo_stream_inq_timestep(streamID2, tsID);
 
@@ -267,15 +352,16 @@ public:
 
         for (int recID = 0; recID < nrecs; ++recID)
           {
-            cdo_inq_record(streamID1, &varID1, &levelID);
+            auto [varID1, levelID] = cdo_inq_record(streamID1);
 
             auto it = mapOfVarIDs.find(varID1);
             if (it == mapOfVarIDs.end())
               {
-                if (mapflag == 2 || mapflag == 3) continue;
+                if (params.mapFlag == MapFlag::Right || params.mapFlag == MapFlag::Intersect) continue;
                 cdo_abort("Internal problem (tsID=%d recID=%d): varID1=%d not found!", tsID + 1, recID + 1, varID1);
               }
 
+            int varID2 = 0;
             for (; recID2next < nrecs2; ++recID2next)
               {
                 cdo_inq_record(streamID2, &varID2, &levelID);
@@ -290,15 +376,12 @@ public:
               cdo_abort("Internal problem (tsID=%d recID=%d): varID2=%d not found in second stream!", tsID + 1, recID + 1,
                         it->second);
 
-            indg += 1;
-
-            const auto &var1 = varList1[varID1];
-            const auto &var2 = varList2[varID2];
+            const auto &var1 = varList1.vars[varID1];
+            const auto &var2 = varList2.vars[varID2];
 
-            // checkrel = gridInqType(gridID) != GRID_SPECTRAL;
-            auto checkrel = true;
-
-            cdiParamToString(var1.param, paramstr, sizeof(paramstr));
+            auto taskNum = numSets % numTasks;
+            auto &field1 = fieldVector1[taskNum];
+            auto &field2 = fieldVector2[taskNum];
 
             field1.init(var1);
             cdo_read_record(streamID1, field1);
@@ -308,89 +391,47 @@ public:
             cdo_read_record(streamID2, field2);
             if (var2.nwpv == CDI_COMP) use_real_part(var1.gridsize, field2);
 
-            auto dp = diff(var1.gridsize, field1, field2);
-
-            if (!Options::silentMode || Options::cdoVerbose)
+            if (runAsync && numSets > 0)
               {
-                if (dp.absm > abslim || (checkrel && dp.relm >= rellim) || Options::cdoVerbose)
-                  {
-                    if (printHeader)
-                      {
-                        printHeader = false;
-
-                        fprintf(stdout, "               Date     Time   Level Gridsize    Miss ");
-                        fprintf(stdout, "   Diff ");
-                        fprintf(stdout, ": S Z  Max_Absdiff Max_Reldiff : ");
-
-                        if (operatorID == DIFFN)
-                          fprintf(stdout, "Parameter name");
-                        else if (operatorID == DIFF || operatorID == DIFFP)
-                          fprintf(stdout, "Parameter ID");
-                        else if (operatorID == DIFFC)
-                          fprintf(stdout, "Code number");
-
-                        fprintf(stdout, "\n");
-                      }
-
-                    fprintf(stdout, "%6d ", indg);
-                    fprintf(stdout, ":");
-
-                    set_text_color(stdout, MAGENTA);
-                    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(var1.zaxisID, levelID));
-                    fprintf(stdout, "%8zu %7zu ", var1.gridsize, std::max(field1.numMissVals, field2.numMissVals));
-                    fprintf(stdout, "%7zu ", dp.ndiff);
-                    reset_text_color(stdout);
-
-                    fprintf(stdout, ":");
-                    fprintf(stdout, " %c %c ", dp.dsgn ? 'T' : 'F', dp.zero ? 'T' : 'F');
-                    set_text_color(stdout, BLUE);
-                    fprintf(stdout, "%#12.5g%#12.5g", dp.absm, dp.relm);
-                    reset_text_color(stdout);
-                    fprintf(stdout, " : ");
-
-                    set_text_color(stdout, BRIGHT, GREEN);
-                    if (operatorID == DIFFN)
-                      fprintf(stdout, "%-11s", var1.name.c_str());
-                    else if (operatorID == DIFF || operatorID == DIFFP)
-                      fprintf(stdout, "%-11s", paramstr);
-                    else if (operatorID == DIFFC)
-                      fprintf(stdout, "%4d", var1.code);
-                    reset_text_color(stdout);
-
-                    fprintf(stdout, "\n");
-                  }
+                task->wait();
+                // clang-format off
+                if (params.numDiffRecords >= params.maxDiffRecords) { stopRead = true; break; }
+                // clang-format on
               }
 
-            ngrec++;
-            if (dp.absm > abslim || (checkrel && dp.relm >= rellim)) ndrec++;
-            if (dp.absm > abslim2 || (checkrel && dp.relm >= rellim)) nd2rec++;
+            std::function<void()> compare_fields_func = std::bind(compare_fields, std::cref(field1), std::cref(field2), numSets + 1,
+                                                                  std::cref(var1), levelID, operfunc, vDateTime, std::ref(params));
+
+            runAsync ? task->doAsync(compare_fields_func) : compare_fields_func();
 
-            if (maxcount > 0 && ndrec >= maxcount)
+            if (not runAsync)
               {
-                stopLoop = true;
-                break;
+                // clang-format off
+                if (params.numDiffRecords >= params.maxDiffRecords) { stopRead = true; break; }
+                // clang-format on
               }
+
+            numSets++;
           }
 
-        if (stopLoop) break;
+        if (stopRead) break;
 
         tsID++;
       }
 
-    if (ndrec > 0)
+    if (runAsync) task->wait();
+
+    if (params.numDiffRecords > 0)
       {
         Options::cdoExitStatus = 1;
 
         set_text_color(stdout, BRIGHT, RED);
-        fprintf(stdout, "  %d of %d records differ", ndrec, ngrec);
+        fprintf(stdout, "  %d of %d records differ", params.numDiffRecords, numSets);
         reset_text_color(stdout);
         fprintf(stdout, "\n");
 
-        if (ndrec != nd2rec && abslim < abslim2)
-          fprintf(stdout, "  %d of %d records differ more than %g\n", nd2rec, ngrec, abslim2);
+        if (params.numDiffRecords != params.numDiffRecords2 && params.absLimit < params.absLimit2)
+          fprintf(stdout, "  %d of %d records differ more than %g\n", params.numDiffRecords2, numSets, params.absLimit2);
         //  fprintf(stdout, "  %d of %d records differ more then one thousandth\n", nprec, ngrec);
       }
 
@@ -399,7 +440,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID1);
     cdo_stream_close(streamID2);
diff --git a/src/Distgrid.cc b/src/Distgrid.cc
index 7ad8d106a8933c70adf234af58b73c89cab57b98..6398a284c60cd5ac1db20050e15001650d974e0f 100644
--- a/src/Distgrid.cc
+++ b/src/Distgrid.cc
@@ -365,7 +365,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
     operator_input_arg("nxblocks, [nyblocks]");
     if (cdo_operator_argc() < 1) cdo_abort("Too few arguments!");
@@ -380,7 +380,7 @@ public:
     streamID1 = cdo_open_read(0);
     auto vlistID1 = cdo_stream_inq_vlist(streamID1);
 
-    ngrids = vlistNgrids(vlistID1);
+    ngrids = vlistNumGrids(vlistID1);
 
     gridInfo1 = std::vector<GridInfo1>(ngrids);
     for (int index = 0; index < ngrids; ++index)
@@ -443,7 +443,7 @@ public:
 
     cdo::set_numfiles(nsplit + 8);
 
-    varList_init(varList1, vlistID1);
+    varList1 = VarList(vlistID1);
 
     vlistIDs = std::vector<int>(nsplit);
     streamIDs = std::vector<CdoStreamID>(nsplit);
@@ -500,7 +500,7 @@ public:
   }
 
   void
-  run()
+  run() override
   {
     int tsID = 0;
     while (true)
@@ -514,7 +514,7 @@ public:
           {
             int varID, levelID;
             cdo_inq_record(streamID1, &varID, &levelID);
-            field1.init(varList1[varID]);
+            field1.init(varList1.vars[varID]);
             cdo_read_record(streamID1, field1);
 
 #ifdef _OPENMP
@@ -522,7 +522,7 @@ public:
 #endif
             for (size_t index = 0; index < nsplit; ++index)
               {
-                auto var = varList1[varID];
+                auto var = varList1.vars[varID];
                 var.gridID = distgridInfo[0][index].gridID;
                 var.gridsize = distgridInfo[0][index].gridsize;
                 field2.init(var);
@@ -544,7 +544,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID1);
 
diff --git a/src/Duplicate.cc b/src/Duplicate.cc
index 0c26b8ba2190ecf586571006fbb19878d9d9139b..82c87c375c2b5329091ef1c663b91d7777266986 100644
--- a/src/Duplicate.cc
+++ b/src/Duplicate.cc
@@ -9,7 +9,6 @@
 
 #include "cdo_options.h"
 #include "process_int.h"
-#include "cdo_vlist.h"
 #include "param_conversion.h"
 #include "field_functions.h"
 
@@ -26,7 +25,8 @@ public:
     .constraints = { 1, 1, NoRestriction },
   };
   inline static RegisterEntry<Duplicate> registration = RegisterEntry<Duplicate>(module);
-  FieldVector3D vars;
+
+  FieldVector3D varsData;
   std::vector<CdiDateTime> vDateTimes;
 
   CdoStreamID streamID1;
@@ -39,11 +39,11 @@ public:
   int nvars;
   int ndup;
 
-  VarList varList;
+  VarList varList1;
 
 public:
   void
-  init()
+  init() override
   {
 
     if (cdo_operator_argc() > 1) cdo_abort("Too many arguments!");
@@ -60,12 +60,12 @@ public:
     taxisID2 = taxisDuplicate(taxisID1);
     vlistDefTaxis(vlistID2, taxisID2);
 
-    varList_init(varList, vlistID1);
+    varList1 = VarList(vlistID1);
 
     nvars = vlistNvars(vlistID1);
     auto ntsteps = vlistNtsteps(vlistID1);
 
-    if (ntsteps == 1 && varList_numVaryingVars(varList) == 0) ntsteps = 0;
+    if (ntsteps == 1 && varList1.numVaryingVars() == 0) ntsteps = 0;
 
     if (ntsteps == 0)
       {
@@ -81,7 +81,7 @@ public:
   }
 
   void
-  run()
+  run() override
   {
     int tsID = 0;
     while (true)
@@ -91,18 +91,18 @@ public:
 
         constexpr size_t NALLOC_INC = 1024;
         if ((size_t) tsID >= vDateTimes.size()) vDateTimes.resize(vDateTimes.size() + NALLOC_INC);
-        if ((size_t) tsID >= vars.size()) vars.resize(vars.size() + NALLOC_INC);
+        if ((size_t) tsID >= varsData.size()) varsData.resize(varsData.size() + NALLOC_INC);
 
         vDateTimes[tsID] = taxisInqVdatetime(taxisID1);
 
-        fields_from_vlist(vlistID1, vars[tsID]);
+        field2D_init(varsData[tsID], varList1);
 
         for (int recID = 0; recID < nrecs; ++recID)
           {
             int varID, levelID;
             cdo_inq_record(streamID1, &varID, &levelID);
-            auto &field = vars[tsID][varID][levelID];
-            field.init(varList[varID]);
+            auto &field = varsData[tsID][varID][levelID];
+            field.init(varList1.vars[varID]);
             cdo_read_record(streamID1, field);
           }
 
@@ -121,10 +121,11 @@ public:
 
             for (int varID = 0; varID < nvars; ++varID)
               {
-                fieldOut.init(varList[varID]);
-                for (int levelID = 0; levelID < varList[varID].nlevels; ++levelID)
+                const auto &var1 = varList1.vars[varID];
+                fieldOut.init(var1);
+                for (int levelID = 0; levelID < var1.nlevels; ++levelID)
                   {
-                    const auto &field = vars[tsID][varID][levelID];
+                    const auto &field = varsData[tsID][varID][levelID];
                     if (field.hasData())
                       {
                         field_copy(field, fieldOut);
@@ -138,7 +139,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
diff --git a/src/EOFs.cc b/src/EOFs.cc
index 49e0a841fb688935a7818e73dcf8387ff8017ec6..2b235a7642479eb63d892169b9bb489b8c194940 100644
--- a/src/EOFs.cc
+++ b/src/EOFs.cc
@@ -26,12 +26,10 @@
 #include "julian_date.h"
 
 #include "cdo_options.h"
-#include "cdo_vlist.h"
 #include "process_int.h"
 #include "param_conversion.h"
 #include <mpim_grid.h>
 #include "eigen_solution.h"
-#include "datetime.h"
 #include "eof_mode.h"
 
 // No missing value support added so far!
@@ -108,9 +106,7 @@ 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 } },
+    .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
@@ -152,7 +148,7 @@ public:
 
   size_t num_eigen_functions = 0;
 
-  int nvars;
+  int numVars;
   int n_eig;
   int ngrids;
 
@@ -166,12 +162,8 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
-
-    // clang-format off
-    // clang-format on
-
     auto operatorID = cdo_operator_id();
     auto operfunc = cdo_operator_f1(operatorID);
 
@@ -185,13 +177,13 @@ public:
     vlistID1 = cdo_stream_inq_vlist(streamID1);
     taxisID1 = vlistInqTaxis(vlistID1);
 
-    varList_init(varList1, vlistID1);
+    varList1 = VarList(vlistID1);
 
-    auto gridID1 = varList1[0].gridID;
+    auto gridID1 = varList1.vars[0].gridID;
     auto gridsizemax = vlistGridsizeMax(vlistID1);
-    nvars = vlistNvars(vlistID1);
+    numVars = varList1.numVars();
 
-    ngrids = vlistNgrids(vlistID1);
+    ngrids = vlistNumGrids(vlistID1);
     for (int index = 1; index < ngrids; ++index)
       if (vlistGrid(vlistID1, 0) != vlistGrid(vlistID1, index)) cdo_abort("Too many different grids!");
 
@@ -285,11 +277,11 @@ public:
 
     pack = std::vector<size_t>(gridsizemax);
     in = Varray<double>(gridsizemax);
-    eofdata = std::vector<std::vector<eofdata_t>>(nvars);
+    eofdata = std::vector<std::vector<eofdata_t>>(numVars);
 
-    for (varID = 0; varID < nvars; ++varID)
+    for (varID = 0; varID < numVars; ++varID)
       {
-        auto nlevs = varList1[varID].nlevels;
+        auto nlevs = varList1.vars[varID].nlevels;
         eofdata[varID].resize(nlevs);
 
         if (time_space)
@@ -298,8 +290,9 @@ public:
 
     if (Options::cdoVerbose) cdo_print("Allocated eigenvalue/eigenvector structures with nts=%d gridsize=%zu", nts, gridsizemax);
   }
+
   void
-  run()
+  run() override
   {
     int tsID = 0;
 
@@ -314,8 +307,8 @@ public:
             cdo_inq_record(streamID1, &varID, &levelID);
             cdo_read_record(streamID1, in.data(), &numMissVals);
 
-            auto gridsize = varList1[varID].gridsize;
-            auto missval = varList1[varID].missval;
+            auto gridsize = varList1.vars[varID].gridsize;
+            auto missval = varList1.vars[varID].missval;
 
             if (npack == SIZE_MAX)
               {
@@ -440,12 +433,13 @@ public:
             cdo_def_timestep(streamID3, tsID);
           }
 
-        for (varID = 0; varID < nvars; ++varID)
+        for (varID = 0; varID < numVars; ++varID)
           {
-            const auto &vname = varList1[varID].name;
-            auto gridsize = varList1[varID].gridsize;
-            auto nlevs = varList1[varID].nlevels;
-            auto missval = varList1[varID].missval;
+            const auto &var1 = varList1.vars[varID];
+            auto &vname = var1.name;
+            auto gridsize = var1.gridsize;
+            auto nlevs = var1.nlevels;
+            auto missval = var1.missval;
 
             for (levelID = 0; levelID < nlevs; ++levelID)
               {
@@ -545,8 +539,9 @@ public:
           }      // loop nvars
       }
   }
+
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID3);
     cdo_stream_close(streamID2);
@@ -554,10 +549,5 @@ public:
 
     vlistDestroy(vlistID2);
     vlistDestroy(vlistID3);
-
-    gridDestroy(gridID2);
-
-    //  taxisDestroy(taxisID2);
-    //  taxisDestroy(taxisID3);
   }
 };
diff --git a/src/EcaEtccdi.cc b/src/EcaEtccdi.cc
index 401b7eb1a2e043e1d4686a8792ac7b337d109f2b..87f3d30634ab7ddfe53a14f605460f7651a3a855 100644
--- a/src/EcaEtccdi.cc
+++ b/src/EcaEtccdi.cc
@@ -107,11 +107,12 @@ writeTimesteps(int MaxMonths, int recentYear, FieldVector3D &cei, int frequency,
           for (int recID = 0; recID < maxRecs; ++recID)
             {
               auto [varIDo, levelIDo] = recList[recID].get();
-              if (*otsID && varList1[varIDo].isConstant) continue;
+              if (*otsID && varList1.vars[varIDo].isConstant) continue;
 
               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].numMissVals);
+              cdo_write_record(streamID4, cei[loopmonth - 2][0][levelIDo].vec_d.data(),
+                               cei[loopmonth - 2][0][levelIDo].numMissVals);
             }
           (*otsID)++;
         }
@@ -123,7 +124,7 @@ writeTimesteps(int MaxMonths, int recentYear, FieldVector3D &cei, int frequency,
       for (int recID = 0; recID < maxRecs; ++recID)
         {
           auto [varIDo, levelIDo] = recList[recID].get();
-          if (*otsID && varList1[varIDo].isConstant) continue;
+          if (*otsID && varList1.vars[varIDo].isConstant) continue;
 
           if (func2 == FieldFunc_Avg) fieldc_div(cei[0][0][levelIDo], (double) (tempdpy / 100.0));
           cdo_def_record(streamID4, varIDo, levelIDo);
@@ -142,8 +143,8 @@ calculateOuterPeriod(Field &field, int MaxMonths, int recentYear, int endOfCalc,
 
   auto cdiStream = streamOpenRead(cdo_get_stream_name(0));
 
-  const auto cdiVlistID = streamInqVlist(cdiStream);
-  const auto cdiTaxisID = vlistInqTaxis(cdiVlistID);
+  auto cdiVlistID = streamInqVlist(cdiStream);
+  auto cdiTaxisID = vlistInqTaxis(cdiVlistID);
   std::vector<int> tempdpm(MaxMonths);
   int tempdpy = 0;
   for (int i = 0; i < MaxMonths; ++i) tempdpm[i] = 0;
@@ -154,10 +155,10 @@ calculateOuterPeriod(Field &field, int MaxMonths, int recentYear, int endOfCalc,
 
   while (true)
     {
-      const auto nrecs = streamInqTimestep(cdiStream, tsID++);
+      auto nrecs = streamInqTimestep(cdiStream, tsID++);
       if (nrecs == 0) break;
 
-      const auto vDateTime = taxisInqVdatetime(cdiTaxisID);
+      auto vDateTime = taxisInqVdatetime(cdiTaxisID);
       cdiDate_decode(vDateTime.date, &year, &month, &day);
       if (!lHasStarted && year != recentYear)
         continue;
@@ -229,7 +230,7 @@ etccdi_op(ETCCDI_REQUEST &request)
 {
   constexpr int MaxDays = 373;
   constexpr int MaxMonths = 12;
-  FieldVector2D vars2[MaxDays];
+  FieldVector2D varsData2[MaxDays];
   HistogramSet hsets[MaxDays];
 
   const int operatorID = cdo_operator_id();
@@ -260,35 +261,38 @@ etccdi_op(ETCCDI_REQUEST &request)
       for (int month = 0; month < MaxMonths; ++month) dpm[month + year * MaxMonths] = 0;
     }
 
-  const auto streamID1 = cdo_open_read(0);
-  const auto streamID2 = cdo_open_read(1);
-  const auto streamID3 = cdo_open_read(2);
+  auto streamID1 = cdo_open_read(0);
+  auto streamID2 = cdo_open_read(1);
+  auto streamID3 = cdo_open_read(2);
 
-  const auto vlistID1 = cdo_stream_inq_vlist(streamID1);
-  const auto vlistID2 = cdo_stream_inq_vlist(streamID2);
-  const auto vlistID3 = cdo_stream_inq_vlist(streamID3);
-  const auto vlistID4 = vlistDuplicate(vlistID1);
+  auto vlistID1 = cdo_stream_inq_vlist(streamID1);
+  auto vlistID2 = cdo_stream_inq_vlist(streamID2);
+  auto vlistID3 = cdo_stream_inq_vlist(streamID3);
+  auto vlistID4 = vlistDuplicate(vlistID1);
 
   vlist_unpack(vlistID4);
 
-  vlist_compare(vlistID1, vlistID2, CmpVlist::All);
-  vlist_compare(vlistID1, vlistID3, CmpVlist::All);
+  VarList varList1(vlistID1);
+  VarList varList2(vlistID2);
 
-  const auto taxisID1 = vlistInqTaxis(vlistID1);
-  const auto taxisID2 = vlistInqTaxis(vlistID2);
-  const auto taxisID3 = vlistInqTaxis(vlistID3);
+  varList_compare(varList1, varList2);
+  varList_compare(varList1, VarList(vlistID3));
+
+  auto taxisID1 = vlistInqTaxis(vlistID1);
+  auto taxisID2 = vlistInqTaxis(vlistID2);
+  auto taxisID3 = vlistInqTaxis(vlistID3);
   // TODO - check that time axes 2 and 3 are equal
 
-  const auto taxisID4 = taxisDuplicate(taxisID1);
+  auto taxisID4 = taxisDuplicate(taxisID1);
   if (taxisHasBounds(taxisID4)) taxisDeleteBounds(taxisID4);
   vlistDefTaxis(vlistID4, taxisID4);
 
-  const auto streamID4 = cdo_open_write(3);
+  auto streamID4 = cdo_open_write(3);
 
-  const auto nvars = vlistNvars(vlistID1);
-  bool lOnlyOneVar = true;
+  auto numVars = varList1.numVars();
+  auto lOnlyOneVar = true;
 
-  if (nvars == 1)
+  if (numVars == 1)
     {
       cdiDefKeyString(vlistID4, 0, CDI_KEY_NAME, request.name);
       cdiDefKeyString(vlistID4, 0, CDI_KEY_LONGNAME, request.longname);
@@ -303,40 +307,40 @@ etccdi_op(ETCCDI_REQUEST &request)
 
   cdo_def_vlist(streamID4, vlistID4);
 
-  const auto maxrecs = vlistNrecs(vlistID1);
-  std::vector<RecordInfo> recList(maxrecs);
+  auto maxRecords = varList1.numRecords();
+  std::vector<RecordInfo> recordList(maxRecords);
 
-  const auto gridsizemax = vlistGridsizeMax(vlistID1);
+  auto gridsizemax = vlistGridsizeMax(vlistID1);
 
   Field field;
 
-  VarList varList1;
-  varList_init(varList1, vlistID1);
+  FieldVector3D varsData1(sumboot * (MaxDays - 1) + 1);
+  FieldVector3D cei(sumboot * MaxMonths);
+  FieldVector3D varsPtemp(MaxDays);
 
-  FieldVector3D vars1(sumboot * (MaxDays - 1) + 1), cei(sumboot * MaxMonths), varsPtemp(MaxDays);
   for (int dayOfYear = 0; dayOfYear < MaxDays; dayOfYear++)
     {
-      fields_from_vlist(vlistID1, vars1[dayOfYear], FIELD_VEC | FIELD_MEMTYPE);
+      field2D_init(varsData1[dayOfYear], varList1, FIELD_VEC | FIELD_MEMTYPE);
       wdaysSrc[dayOfYear] = false;
       for (int year = 0; year < sumboot; year++) wdaysRead[dayOfYear + year * (MaxDays - 1)] = false;
     }
 
   for (int dayOfYear = MaxDays; dayOfYear < sumboot * (MaxDays - 1) + 1; dayOfYear++)
-    fields_from_vlist(vlistID1, vars1[dayOfYear], FIELD_VEC | FIELD_MEMTYPE);
+    field2D_init(varsData1[dayOfYear], varList1, FIELD_VEC | FIELD_MEMTYPE);
 
   int tsID = 0;
 
   while (true)
     {
-      const auto nrecs = cdo_stream_inq_timestep(streamID2, tsID);
+      auto nrecs = cdo_stream_inq_timestep(streamID2, tsID);
       if (nrecs == 0) break;
 
       if (nrecs != cdo_stream_inq_timestep(streamID3, tsID))
         cdo_abort("Number of records at time step %d of %s and %s differ!", tsID + 1, cdo_get_stream_name(1),
                   cdo_get_stream_name(2));
 
-      const auto vDateTime2 = taxisInqVdatetime(taxisID2);
-      const auto vDateTime3 = taxisInqVdatetime(taxisID3);
+      auto vDateTime2 = taxisInqVdatetime(taxisID2);
+      auto vDateTime3 = taxisInqVdatetime(taxisID3);
 
       if (cdiDate_get(vDateTime2.date) != cdiDate_get(vDateTime3.date))
         cdo_abort("Verification dates at time step %d of %s and %s differ!", tsID + 1, cdo_get_stream_name(1),
@@ -348,36 +352,35 @@ etccdi_op(ETCCDI_REQUEST &request)
 
       if (dayOfYear < 0 || dayOfYear >= MaxDays) cdo_abort("Day %d out of range!", dayOfYear);
 
-      if (!vars2[dayOfYear].size())
+      if (!varsData2[dayOfYear].size())
         {
           wdaysSrc[dayOfYear] = true;
-          fields_from_vlist(vlistID2, vars2[dayOfYear], FIELD_VEC | FIELD_MEMTYPE);
-          fields_from_vlist(vlistID2, varsPtemp[dayOfYear], FIELD_VEC);
-          hsets[dayOfYear].create(nvars);
+          field2D_init(varsData2[dayOfYear], varList2, FIELD_VEC | FIELD_MEMTYPE);
+          field2D_init(varsPtemp[dayOfYear], varList2, FIELD_VEC);
+          hsets[dayOfYear].create(numVars);
 
-          for (int varID = 0; varID < nvars; ++varID)
-            hsets[dayOfYear].createVarLevels(varID, varList1[varID].nlevels, varList1[varID].gridsize);
+          for (const auto &var : varList1.vars) hsets[dayOfYear].createVarLevels(var.ID, var.nlevels, var.gridsize);
         }
 
       for (int recID = 0; recID < nrecs; ++recID)
         {
           int varID, levelID;
           cdo_inq_record(streamID2, &varID, &levelID);
-          cdo_read_record(streamID2, vars2[dayOfYear][varID][levelID]);
-          varsPtemp[dayOfYear][varID][levelID].numMissVals = vars2[dayOfYear][varID][levelID].numMissVals;
+          cdo_read_record(streamID2, varsData2[dayOfYear][varID][levelID]);
+          varsPtemp[dayOfYear][varID][levelID].numMissVals = varsData2[dayOfYear][varID][levelID].numMissVals;
         }
 
       for (int recID = 0; recID < nrecs; ++recID)
         {
           int varID, levelID;
           cdo_inq_record(streamID3, &varID, &levelID);
-          field.init(varList1[varID]);
+          field.init(varList1.vars[varID]);
           cdo_read_record(streamID3, field);
 
-          hsets[dayOfYear].defVarLevelBounds(varID, levelID, vars2[dayOfYear][varID][levelID], field);
+          hsets[dayOfYear].defVarLevelBounds(varID, levelID, varsData2[dayOfYear][varID][levelID], field);
         }
       // fieldsFree(vlistID2, vars2[dayOfYear]);
-      // fields_from_vlist(vlistID2, vars2[dayOfYear], FIELD_VEC);
+      // field2D_init(vars2[dayOfYear], varList2, FIELD_VEC);
 
       tsID++;
     }
@@ -389,10 +392,10 @@ etccdi_op(ETCCDI_REQUEST &request)
   int year;
   while (true)
     {
-      const auto nrecs = cdo_stream_inq_timestep(streamID1, tsID++);
+      auto nrecs = cdo_stream_inq_timestep(streamID1, tsID++);
       if (nrecs == 0) break;
 
-      const auto vDateTime = taxisInqVdatetime(taxisID1);
+      auto vDateTime = taxisInqVdatetime(taxisID1);
 
       int month, day;
       cdiDate_decode(vDateTime.date, &year, &month, &day);
@@ -419,9 +422,9 @@ etccdi_op(ETCCDI_REQUEST &request)
                 {
                   int varID, levelID;
                   cdo_inq_record(streamID1, &varID, &levelID);
-                  cdo_read_record(streamID1, vars1[dayOfYear + (year - request.startboot) * (MaxDays - 1)][varID][levelID]);
+                  cdo_read_record(streamID1, varsData1[dayOfYear + (year - request.startboot) * (MaxDays - 1)][varID][levelID]);
 
-                  if (tsID == 0) recList[recID].set(varID, levelID);
+                  if (tsID == 0) recordList[recID].set(varID, levelID);
                 }
             }
           else
@@ -436,9 +439,9 @@ etccdi_op(ETCCDI_REQUEST &request)
 
   for (year = 0; year < sumboot; year++)
     {
-      fields_from_vlist(vlistID1, cei[year * MaxMonths], FIELD_VEC);
+      field2D_init(cei[year * MaxMonths], varList1, FIELD_VEC);
       if (frequency == 8)
-        for (int month = 1; month < MaxMonths; ++month) fields_from_vlist(vlistID1, cei[year * MaxMonths + month], FIELD_VEC);
+        for (int month = 1; month < MaxMonths; ++month) field2D_init(cei[year * MaxMonths + month], varList1, FIELD_VEC);
     }
 
   // printf("Wir beginnen nun mit der Schleife.\n");
@@ -458,10 +461,11 @@ etccdi_op(ETCCDI_REQUEST &request)
     }
   if (Options::cdoVerbose) cdo_print("Calculated window days");
 
-  for (int varID = 0; varID < nvars; ++varID)
+  for (int varID = 0; varID < numVars; ++varID)
     {
-      if (varList1[varID].isConstant) continue;
-      for (int levelID = 0; levelID < varList1[varID].nlevels; ++levelID)
+      const auto &var1 = varList1.vars[varID];
+      if (var1.isConstant) continue;
+      for (int levelID = 0; levelID < var1.nlevels; ++levelID)
         {
           if (request.func2 == FieldFunc_Sum)
             {
@@ -471,7 +475,7 @@ etccdi_op(ETCCDI_REQUEST &request)
                     {
                       if (wdaysRead[loopdoy + ytoadd * (MaxDays - 1)])
                         {
-                          auto &source = vars1[loopdoy + ytoadd * (MaxDays - 1)][varID][levelID];
+                          auto &source = varsData1[loopdoy + ytoadd * (MaxDays - 1)][varID][levelID];
                           auto &hset = hsets[1];
                           hset.addVarLevelValues(varID, levelID, source);
                         }
@@ -481,7 +485,7 @@ etccdi_op(ETCCDI_REQUEST &request)
           else
             {
 #ifdef _OPENMP
-#pragma omp parallel for shared(sumboot, vars1, request, varID, levelID, hsets, wdays, wdaysRead) schedule(dynamic)
+#pragma omp parallel for shared(sumboot, varsData1, request, varID, levelID, hsets, wdays, wdaysRead) schedule(dynamic)
 #endif
               for (int loopdoy = 1; loopdoy < MaxDays; loopdoy++)
                 {
@@ -491,9 +495,8 @@ etccdi_op(ETCCDI_REQUEST &request)
                         {
                           for (int ano = 0; ano < request.ndates; ano++)
                             {
-                              auto &source
-                                  = vars1[wdays[ytoadd * request.ndates * (MaxDays - 1) + (loopdoy - 1) * request.ndates + ano + 1]]
-                                         [varID][levelID];
+                              auto &source = varsData1[wdays[ytoadd * request.ndates * (MaxDays - 1)
+                                                             + (loopdoy - 1) * request.ndates + ano + 1]][varID][levelID];
                               auto &hset = hsets[loopdoy];
                               hset.addVarLevelValues(varID, levelID, source);
                             }
@@ -508,10 +511,11 @@ etccdi_op(ETCCDI_REQUEST &request)
   if (Options::cdoVerbose) cdo_print("Added 30 years to histograms");
   if (lOnlyOneVar && ((!lOnlyRefPeriod && firstYear != request.startboot) || request.func2 == FieldFunc_Sum))
     {
-      for (int varID = 0; varID < nvars; ++varID)
+      for (int varID = 0; varID < numVars; ++varID)
         {
-          if (varList1[varID].isConstant) continue;
-          for (int levelID = 0; levelID < varList1[varID].nlevels; ++levelID)
+          const auto &var1 = varList1.vars[varID];
+          if (var1.isConstant) continue;
+          for (int levelID = 0; levelID < var1.nlevels; ++levelID)
             {
               if (request.func2 == FieldFunc_Sum)
                 {
@@ -536,7 +540,7 @@ etccdi_op(ETCCDI_REQUEST &request)
         }
       field.resize(gridsizemax);
       calculateOuterPeriod(field, MaxMonths, firstYear, request.startboot, cei, varsPtemp, frequency, taxisID4, streamID4, &otsID,
-                           varList1, recList, selection, request.func2);
+                           varList1, recordList, selection, request.func2);
     }
   else if (!lOnlyRefPeriod && firstYear != request.startboot)
     cdo_warning("Since you have more than one variable in the input file, only the bootstrapping period can be calculated");
@@ -546,13 +550,13 @@ etccdi_op(ETCCDI_REQUEST &request)
       for (bootsyear = request.startboot; bootsyear < request.endboot + 1; bootsyear++)
         {
           if (Options::cdoVerbose) cdo_print("Bootsyear: %d", bootsyear);
-          for (int varID = 0; varID < nvars; ++varID)
+          for (int varID = 0; varID < numVars; ++varID)
             {
-              if (varList1[varID].isConstant) continue;
-              for (int levelID = 0; levelID < varList1[varID].nlevels; ++levelID)
+              if (varList1.vars[varID].isConstant) continue;
+              for (int levelID = 0; levelID < varList1.vars[varID].nlevels; ++levelID)
                 {
 #ifdef _OPENMP
-#pragma omp parallel for shared(sumboot, wdaysRead, request, vars1, varID, levelID, hsets, wdays, cei) schedule(dynamic)
+#pragma omp parallel for shared(sumboot, wdaysRead, request, varsData1, varID, levelID, hsets, wdays, cei) schedule(dynamic)
 #endif
                   for (int loopdoy = 1; loopdoy < MaxDays; loopdoy++)
                     {
@@ -566,14 +570,14 @@ etccdi_op(ETCCDI_REQUEST &request)
                                       = ytoadd * request.ndates * (MaxDays - 1) + (loopdoy - 1) * request.ndates + ano + 1;
                                   if ((int((wdays[recentWday] - 1) / (MaxDays - 1)) + request.startboot) == bootsyear)
                                     {
-                                      auto &source = vars1[wdays[recentWday]][varID][levelID];
+                                      auto &source = varsData1[wdays[recentWday]][varID][levelID];
                                       auto &hset = hsets[loopdoy];
                                       hset.subVarLevelValues(varID, levelID, source);
                                     }
                                   // percyear cannot be smaller than request.startboot
                                   if ((int((wdays[recentWday] - 1) / (MaxDays - 1)) + request.startboot) == bootsyear - 1)
                                     {
-                                      auto &source = vars1[wdays[recentWday]][varID][levelID];
+                                      auto &source = varsData1[wdays[recentWday]][varID][levelID];
                                       auto &hset = hsets[loopdoy];
                                       hset.addVarLevelValues(varID, levelID, source);
                                     }
@@ -587,7 +591,7 @@ etccdi_op(ETCCDI_REQUEST &request)
                       if (subyear != bootsyear)
                         {
 #ifdef _OPENMP
-#pragma omp parallel for shared(sumboot, request, vars1, varID, levelID, hsets, wdaysRead, varsPtemp, vars2, cei, subyear, \
+#pragma omp parallel for shared(sumboot, request, varsData1, varID, levelID, hsets, wdaysRead, varsPtemp, varsData2, cei, subyear, \
                                 bootsyear, wdays, frequency) schedule(dynamic)
 #endif
                           for (int loopdoy = 1; loopdoy < MaxDays; loopdoy++)
@@ -602,7 +606,7 @@ etccdi_op(ETCCDI_REQUEST &request)
                                               = ytoadd * request.ndates * (MaxDays - 1) + (loopdoy - 1) * request.ndates + ano + 1;
                                           if ((int((wdays[recentWday] - 1) / (MaxDays - 1)) + request.startboot) == subyear)
                                             {
-                                              auto &source = vars1[wdays[recentWday]][varID][levelID];
+                                              auto &source = varsData1[wdays[recentWday]][varID][levelID];
                                               auto &hset = hsets[loopdoy];
                                               if (hset.addVarLevelValues(varID, levelID, source) == 1)
                                                 cdo_print("'%d', '%d", loopdoy, wdays[recentWday]);
@@ -618,8 +622,9 @@ etccdi_op(ETCCDI_REQUEST &request)
                                   auto &pctls = varsPtemp[loopdoy][varID][levelID];
                                   hsets[loopdoy].getVarLevelPercentiles(pctls, varID, levelID, request.pn);
                                   /*** Compare data with percentile ***/
-                                  auto &source = vars1[loopdoy + (bootsyear - request.startboot) * (MaxDays - 1)][varID][levelID];
-                                  auto &toCompare = vars2[loopdoy][varID][levelID];
+                                  auto &source
+                                      = varsData1[loopdoy + (bootsyear - request.startboot) * (MaxDays - 1)][varID][levelID];
+                                  auto &toCompare = varsData2[loopdoy][varID][levelID];
                                   field_copy(source, toCompare);
                                   if (selection == func_selle)
                                     vfarselle(toCompare, pctls);
@@ -662,7 +667,7 @@ etccdi_op(ETCCDI_REQUEST &request)
                                               = ytoadd * request.ndates * (MaxDays - 1) + (loopdoy - 1) * request.ndates + ano + 1;
                                           if ((int((wdays[recentWday] - 1) / (MaxDays - 1)) + request.startboot) == subyear)
                                             {
-                                              auto &source = vars1[wdays[recentWday]][varID][levelID];
+                                              auto &source = varsData1[wdays[recentWday]][varID][levelID];
                                               auto &hset = hsets[loopdoy];
                                               if (hset.subVarLevelValues(varID, levelID, source) == 1)
                                                 cdo_print("'%d', '%d", loopdoy, wdays[recentWday]);
@@ -697,10 +702,10 @@ etccdi_op(ETCCDI_REQUEST &request)
                   define_mid_of_time(frequency, taxisID4, bootsyear, month, MaxMonths);
                   cdo_def_timestep(streamID4, otsID);
 
-                  for (int recID = 0; recID < maxrecs; ++recID)
+                  for (int recID = 0; recID < maxRecords; ++recID)
                     {
-                      auto [varIDo, levelIDo] = recList[recID].get();
-                      if (otsID && varList1[varIDo].isConstant) continue;
+                      auto [varIDo, levelIDo] = recordList[recID].get();
+                      if (otsID && varList1.vars[varIDo].isConstant) continue;
 
                       cdo_def_record(streamID4, varIDo, levelIDo);
                       cdo_write_record(
@@ -715,10 +720,10 @@ etccdi_op(ETCCDI_REQUEST &request)
               define_mid_of_time(frequency, taxisID4, bootsyear, 0, MaxMonths);
               cdo_def_timestep(streamID4, otsID);
 
-              for (int recID = 0; recID < maxrecs; ++recID)
+              for (int recID = 0; recID < maxRecords; ++recID)
                 {
-                  auto [varIDo, levelIDo] = recList[recID].get();
-                  if (otsID && varList1[varIDo].isConstant) continue;
+                  auto [varIDo, levelIDo] = recordList[recID].get();
+                  if (otsID && varList1.vars[varIDo].isConstant) continue;
 
                   cdo_def_record(streamID4, varIDo, levelIDo);
                   cdo_write_record(streamID4, cei[(bootsyear - request.startboot) * MaxMonths][varIDo][levelIDo].vec_d.data(),
@@ -735,10 +740,10 @@ etccdi_op(ETCCDI_REQUEST &request)
       if (frequency == 8)
         for (int loopmonth = 1; loopmonth < MaxMonths; loopmonth++) field_fill(cei[loopmonth][0][0], 0.);
 
-      for (int varID = 0; varID < nvars; ++varID)
+      for (int varID = 0; varID < numVars; ++varID)
         {
-          if (varList1[varID].isConstant) continue;
-          for (int levelID = 0; levelID < varList1[varID].nlevels; ++levelID)
+          if (varList1.vars[varID].isConstant) continue;
+          for (int levelID = 0; levelID < varList1.vars[varID].nlevels; ++levelID)
             {
 #ifdef _OPENMP
 #pragma omp parallel for shared(request, wdaysRead, varID, levelID, hsets, varsPtemp) schedule(dynamic)
@@ -755,7 +760,7 @@ etccdi_op(ETCCDI_REQUEST &request)
                               // percyear cannot be smaller than request.startboot
                               if ((int((wdays[recentWday] - 1) / (MaxDays - 1)) + request.startboot) == bootsyear - 1)
                                 {
-                                  auto &source = vars1[wdays[recentWday]][varID][levelID];
+                                  auto &source = varsData1[wdays[recentWday]][varID][levelID];
                                   auto &hset = hsets[loopdoy];
                                   hset.addVarLevelValues(varID, levelID, source);
                                 }
@@ -771,11 +776,11 @@ etccdi_op(ETCCDI_REQUEST &request)
             }
         }
       field.resize(gridsizemax);
-      field.missval = vars1[1][0][0].missval;
-      field.size = vars1[1][0][0].size;
-      field.grid = vars1[1][0][0].grid;
+      field.missval = varsData1[1][0][0].missval;
+      field.size = varsData1[1][0][0].size;
+      field.grid = varsData1[1][0][0].grid;
       calculateOuterPeriod(field, MaxMonths, request.endboot + 1, lastYear + 1, cei, varsPtemp, frequency, taxisID4, streamID4,
-                           &otsID, varList1, recList, selection, request.func2);
+                           &otsID, varList1, recordList, selection, request.func2);
     }
 
   cdo_stream_close(streamID4);
@@ -803,14 +808,14 @@ public:
     .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()
+  init() override
   {
-
     if (cdo_operator_argc() < 3) cdo_abort("Too few arguments!");
     if (cdo_operator_argc() > 4) cdo_abort("Too many arguments!");
 
@@ -824,7 +829,7 @@ public:
     request.ndates = parameter_to_int(cdo_operator_argv(0));
     request.startboot = parameter_to_int(cdo_operator_argv(1));
 
-    const auto operatorID = cdo_operator_id();
+    auto operatorID = cdo_operator_id();
     request.compare_type = cdo_operator_f2(cdo_operator_id());
     if (cdo_operator_argc() == 4 && 'm' == cdo_operator_argv(3)[0]) { request.compare_type = CMP_MONTH; }
 
@@ -898,15 +903,17 @@ public:
         request.pn = 10;
       }
   }
+
   void
-  run()
+  run() override
   {
     etccdi_op(request);
     /*  else
         EcaEtccdi(-1, ndates, startboot, endboot); */
   }
+
   void
-  close()
+  close() override
   {
   }
 };
diff --git a/src/EcaIndices.cc b/src/EcaIndices.cc
index a034e378b6468821294011274d3a6450e6b70874..fd7c2f2fbb7a139ba81ab68beb642c0c4f67c2cc 100644
--- a/src/EcaIndices.cc
+++ b/src/EcaIndices.cc
@@ -353,18 +353,18 @@ protected:
   }
   std::function<void(Request p_request)> eca_func;
   Request request;
-  virtual void init() = 0;
+  // virtual void init() = 0;
 
 public:
   void
-  run()
+  run() override
   {
     assert(request.compare_type != -1);
     eca_func(request);
   }
 
   void
-  close()
+  close() override
   {
   }
 };
@@ -419,6 +419,8 @@ public:
   };
   inline static RegisterEntry<EcaCfd> registration = RegisterEntry<EcaCfd>(module);
   int ndays = 5;
+  char cfd_name2[1024];
+  char cfd_longname2[1024];
 
 public:
   void
@@ -438,10 +440,8 @@ public:
         if (cdo_operator_argc() > 0) ndays = parameter_to_int(cdo_operator_argv(0));
       }
 
-    char cfd_longname2[1024];
-    char cfd_name2[1024];
-    std::snprintf(cfd_longname2, sizeof(cfd_longname2), CFD_LONGNAME2, ndays);
     std::snprintf(cfd_name2, sizeof(cfd_name2), CFD_NAME2, ndays);
+    std::snprintf(cfd_longname2, sizeof(cfd_longname2), CFD_LONGNAME2, ndays);
 
     request.var1.name = CFD_NAME;
     request.var1.longname = CFD_LONGNAME;
@@ -474,10 +474,12 @@ public:
   inline static RegisterEntry<EcaCsu> registration = RegisterEntry<EcaCsu>(module);
   double argT = 25.0;
   int ndays = 5;
+  char csu_name2[1024];
+  char csu_longname2[1024];
 
 public:
   void
-  init()
+  init() override
   {
     set_default_compare_type(request.compare_type);
 
@@ -494,10 +496,8 @@ public:
         if (cdo_operator_argc() == 2) ndays = parameter_to_int(cdo_operator_argv(1));
       }
 
-    char csu_longname2[1024];
-    char csu_name2[1024];
-    std::snprintf(csu_longname2, sizeof(csu_longname2), CSU_LONGNAME2, ndays);
     std::snprintf(csu_name2, sizeof(csu_name2), CSU_NAME2, ndays);
+    std::snprintf(csu_longname2, sizeof(csu_longname2), CSU_LONGNAME2, ndays);
 
     request.var1.name = CSU_NAME;
     request.var1.longname = CSU_LONGNAME;
@@ -534,7 +534,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
     set_default_compare_type(request.compare_type);
 
@@ -594,7 +594,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
     ECA_CWFI = module.get_id("eca_cwfi");
     ETCCDI_CSDI = module.get_id("etccdi_csdi");
@@ -657,7 +657,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
 
     set_default_compare_type(request.compare_type);
@@ -688,7 +688,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
     ECA_FD = module.get_id("eca_fd");
     ETCCDI_FD = module.get_id("etccdi_fd");
@@ -750,7 +750,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
     set_default_compare_type(request.compare_type);
 
@@ -794,7 +794,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
 
     set_default_compare_type(request.compare_type);
@@ -837,7 +837,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
     set_default_compare_type(request.compare_type);
 
@@ -895,7 +895,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
     ECA_HWFI = module.get_id("eca_hwfi");
     ETCCDI_WSDI = module.get_id("etccdi_wsdi");
@@ -959,7 +959,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
 
     ECA_ID = module.get_id("eca_id");
@@ -1011,7 +1011,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
 
     ECA_SU = module.get_id("eca_su");
@@ -1062,7 +1062,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
 
     set_default_compare_type(request.compare_type);
@@ -1093,7 +1093,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
 
     set_default_compare_type(request.compare_type);
@@ -1124,7 +1124,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
 
     set_default_compare_type(request.compare_type);
@@ -1155,7 +1155,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
 
     set_default_compare_type(request.compare_type);
@@ -1189,7 +1189,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
     set_default_compare_type(request.compare_type);
 
@@ -1241,7 +1241,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
 
     set_default_compare_type(request.compare_type);
@@ -1272,7 +1272,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
     set_default_compare_type(request.compare_type);
 
@@ -1320,7 +1320,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
 
     ECA_CDD = module.get_id("eca_cdd");
@@ -1396,7 +1396,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
     ECA_CWD = module.get_id("eca_cwd");
     ETCCDI_CWD = module.get_id("etccdi_cwd");
@@ -1472,7 +1472,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
     ECA_PD = module.get_id("eca_pd");
     ETCCDI_R1MM = module.get_id("etccdi_r1mm");
@@ -1588,7 +1588,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
 
     set_default_compare_type(request.compare_type);
@@ -1620,7 +1620,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
 
     set_default_compare_type(request.compare_type);
@@ -1652,7 +1652,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
 
     set_default_compare_type(request.compare_type);
@@ -1684,7 +1684,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
 
     set_default_compare_type(request.compare_type);
@@ -1716,7 +1716,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
 
     set_default_compare_type(request.compare_type);
@@ -1736,7 +1736,7 @@ public:
 //{
 // public:
 //   void
-//   init()
+//   init() override
 //   {
 // //
 //     cdo_operator_add("eca_r95p", 0, CMP_DATE, nullptr);
@@ -1779,7 +1779,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
 
     set_default_compare_type(request.compare_type);
@@ -1811,7 +1811,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
 
     set_default_compare_type(request.compare_type);
@@ -1843,7 +1843,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
 
     set_default_compare_type(request.compare_type);
@@ -1875,7 +1875,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
     double threshold = 1;
 
@@ -1924,7 +1924,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
 
     ECA_RX1DAY = module.get_id("eca_rx1day");
@@ -1975,7 +1975,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
     double argX = 50.0;
 
@@ -2035,7 +2035,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
     ECA_SDII = module.get_id("eca_sdii");
     ETCCDI_SDII = module.get_id("etccdi_sdii");
@@ -2092,7 +2092,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
     set_default_compare_type(request.compare_type);
 
@@ -2127,7 +2127,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
     set_default_compare_type(request.compare_type);
 
@@ -2180,7 +2180,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
     set_default_compare_type(request.compare_type);
 
@@ -2224,7 +2224,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
     set_default_compare_type(request.compare_type);
 
@@ -2268,7 +2268,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
     set_default_compare_type(request.compare_type);
 
diff --git a/src/Echam5ini.cc b/src/Echam5ini.cc
index acca9422effad4776cee5fcc3eb1fb3d1c380268..9af42fd71ce88971fedd3bb9cc0ecc6eeeaccf55 100644
--- a/src/Echam5ini.cc
+++ b/src/Echam5ini.cc
@@ -622,19 +622,19 @@ public:
     auto vlistID1 = cdo_stream_inq_vlist(streamID1);
     auto taxisID = vlistInqTaxis(vlistID1);
 
-    VarList varList1;
-    varList_init(varList1, vlistID1);
-    auto nvars = vlistNvars(vlistID1);
+    VarList varList1(vlistID1);
+    auto numVars = varList1.numVars();
 
-    std::vector<VAR> vars(nvars);
+    std::vector<VAR> vars(numVars);
 
     int ntr = 0;
-    for (int varID = 0; varID < nvars; ++varID)
+    for (int varID = 0; varID < numVars; ++varID)
       {
-        auto code = varList1[varID].code;
-        auto &name = varList1[varID].name;
-        auto &longname = varList1[varID].longname;
-        auto &units = varList1[varID].units;
+        const auto &var1 = varList1.vars[varID];
+        auto code = var1.code;
+        auto name = var1.name;
+        auto longname = var1.longname;
+        auto units = var1.units;
 
         if (code < 0) code = 0;
         if (name.substr(0, 3) == "var")
@@ -675,27 +675,18 @@ public:
         else if (name.substr(0, 3) == "LSP")
           code = 152;
 
-        auto gridID = varList1[varID].gridID;
-        auto zaxisID = varList1[varID].zaxisID;
+        if (var1.zaxisType == GRID_SPECTRAL && ntr == 0) ntr = gridInqTrunc(var1.gridID);
 
-        auto gridtype = gridInqType(gridID);
-        auto zaxistype = zaxisInqType(zaxisID);
+        auto zaxisType = var1.zaxisType;
+        if (zaxisType == ZAXIS_HYBRID && var1.nlevels == 1) zaxisType = ZAXIS_SURFACE;
 
-        if (gridtype == GRID_SPECTRAL && ntr == 0) ntr = gridInqTrunc(gridID);
+        inivar(vars[varID], var1.gridType, zaxisType, code, name, longname, units);
 
-        auto gridsize = gridInqSize(gridID);
-        auto nlev = zaxisInqSize(zaxisID);
-
-        if (zaxistype == ZAXIS_HYBRID && nlev == 1) zaxistype = ZAXIS_SURFACE;
-
-        inivar(vars[varID], gridtype, zaxistype, code, name, longname, units);
-
-        vars[varID].gridID = gridID;
-        vars[varID].zaxisID = zaxisID;
-        vars[varID].gridsize = gridsize;
-        vars[varID].nlev = nlev;
-
-        vars[varID].ptr = new double[nlev * gridsize];
+        vars[varID].gridID = var1.gridID;
+        vars[varID].zaxisID = var1.zaxisID;
+        vars[varID].gridsize = var1.gridsize;
+        vars[varID].nlev = var1.nlevels;
+        vars[varID].ptr = new double[var1.nlevels * var1.gridsize];
       }
 
     auto nrecs = cdo_stream_inq_timestep(streamID1, 0);
@@ -714,14 +705,14 @@ public:
         int varID, levelID;
         cdo_inq_record(streamID1, &varID, &levelID);
 
-        auto gridsize = gridInqSize(vlistInqVarGrid(vlistID1, varID));
+        auto gridsize = vars[varID].gridsize;
         size_t numMissVals;
         cdo_read_record(streamID1, vars[varID].ptr + levelID * gridsize, &numMissVals);
       }
 
     cdo_stream_close(streamID1);
 
-    export_e5ml(cdo_get_stream_name(1), vars, nvars, vdate, vtime, ntr);
+    export_e5ml(cdo_get_stream_name(1), vars, numVars, vdate, vtime, ntr);
   }
 
   void
@@ -731,13 +722,13 @@ public:
     iniatts(&atts);
 
     std::vector<VAR> vars;
-    auto nvars = import_e5ml(cdo_get_stream_name(0), vars);
-    if (nvars == 0) cdo_abort("Unsupported file type!");
+    auto numVars = import_e5ml(cdo_get_stream_name(0), vars);
+    if (numVars == 0) cdo_abort("Unsupported file type!");
 
     auto vlistID2 = vlistCreate();
     vlistDefNtsteps(vlistID2, 0);
 
-    for (int iv = 0; iv < nvars; iv++)
+    for (int iv = 0; iv < numVars; iv++)
       {
         int varID = vlistDefVar(vlistID2, vars[iv].gridID, vars[iv].zaxisID, TIME_CONSTANT);
         if (vars[iv].code > 0) vlistDefVarCode(vlistID2, varID, vars[iv].code);
@@ -765,7 +756,7 @@ public:
     int tsID = 0;
     cdo_def_timestep(streamID2, tsID);
 
-    for (int varID = 0; varID < nvars; ++varID)
+    for (int varID = 0; varID < numVars; ++varID)
       {
         auto gridsize = vars[varID].gridsize;
         auto nlev = vars[varID].nlev;
@@ -783,11 +774,11 @@ public:
   }
 
   void
-  init()
+  init() override
   {
   }
   void
-  close()
+  close() override
   {
     // vlistDestroy(vlistID2);
   }
@@ -799,7 +790,7 @@ public:
   using Echam5ini::Echam5ini;
   inline static CdoModule module = {
     .name = "Echam5ini",
-    .operators = { { "import_e5ml"} },
+    .operators = { { "import_e5ml" } },
     .aliases = {},
     .mode = EXPOSED,     // Module mode: 0:intern 1:extern
     .number = CDI_REAL,  // Allowed number type
@@ -809,7 +800,7 @@ public:
 
 public:
   void
-  run()
+  run() override
   {
     im_e5ml();
   }
@@ -820,7 +811,7 @@ public:
   using Echam5ini::Echam5ini;
   inline static CdoModule module = {
     .name = "Echam5ini",
-    .operators = { { "import_e5ml"}, { "export_e5ml"} },
+    .operators = { { "import_e5ml" }, { "export_e5ml" } },
     .aliases = {},
     .mode = EXPOSED,     // Module mode: 0:intern 1:extern
     .number = CDI_REAL,  // Allowed number type
@@ -830,7 +821,7 @@ public:
 
 public:
   void
-  run()
+  run() override
   {
     ex_e5ml();
   }
diff --git a/src/Enlarge.cc b/src/Enlarge.cc
index f9fbf7836798ebebd0b8298bde80f681a00593d2..f0865af4390062834efbf46553a0ba7d3f47a86d 100644
--- a/src/Enlarge.cc
+++ b/src/Enlarge.cc
@@ -30,6 +30,7 @@ public:
     .constraints = { 1, 1, NoRestriction },
   };
   inline static RegisterEntry<Enlarge> registration = RegisterEntry<Enlarge>(module);
+
   bool linfo = true;
 
   CdoStreamID streamID1;
@@ -48,11 +49,12 @@ public:
   Varray<double> array1;
   Varray<double> array2;
 
+  VarList varList1;
+
 public:
   void
-  init()
+  init() override
   {
-
     operator_check_argc(1);
 
     auto gridID2 = cdo_define_grid(cdo_operator_argv(0));
@@ -66,6 +68,8 @@ public:
     vlistID1 = cdo_stream_inq_vlist(streamID1);
     vlistID2 = vlistDuplicate(vlistID1);
 
+    varList1 = VarList(vlistID1);
+
     taxisID1 = vlistInqTaxis(vlistID1);
     taxisID2 = taxisDuplicate(taxisID1);
     vlistDefTaxis(vlistID2, taxisID2);
@@ -73,8 +77,8 @@ public:
     gridsize2 = gridInqSize(gridID2);
     if (gridsize2 < vlistGridsizeMax(vlistID1)) cdo_abort("Gridsize of input stream is greater than new gridsize!");
 
-    auto ngrids = vlistNgrids(vlistID1);
-    for (int index = 0; index < ngrids; ++index) vlistChangeGridIndex(vlistID2, index, gridID2);
+    auto numGrids = vlistNumGrids(vlistID1);
+    for (int index = 0; index < numGrids; ++index) vlistChangeGridIndex(vlistID2, index, gridID2);
 
     streamID2 = cdo_open_write(1);
 
@@ -85,7 +89,7 @@ public:
   }
 
   void
-  run()
+  run() override
   {
     int tsID = 0;
     while (true)
@@ -103,9 +107,9 @@ public:
             size_t numMissVals;
             cdo_read_record(streamID1, array1.data(), &numMissVals);
 
-            auto missval = vlistInqVarMissval(vlistID1, varID);
-            auto gridID1 = vlistInqVarGrid(vlistID1, varID);
-            auto gridsize1 = gridInqSize(gridID1);
+            const auto &var = varList1.vars[varID];
+            auto gridID1 = var.gridID;
+            auto gridsize1 = var.gridsize;
 
             auto xsize1 = gridInqXsize(gridID1);
             auto ysize1 = gridInqYsize(gridID1);
@@ -143,7 +147,7 @@ public:
                 varray_copy(gridsize1, array1, array2);
                 for (size_t i = gridsize1; i < gridsize2; ++i) array2[i] = array1[gridsize1 - 1];
 
-                if (numMissVals && DBL_IS_EQUAL(array1[gridsize1 - 1], missval)) numMissVals += (gridsize2 - gridsize1);
+                if (numMissVals && dbl_is_equal(array1[gridsize1 - 1], var.missval)) numMissVals += (gridsize2 - gridsize1);
               }
 
             cdo_def_record(streamID2, varID, levelID);
@@ -155,7 +159,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
 
     cdo_stream_close(streamID2);
diff --git a/src/Enlargegrid.cc b/src/Enlargegrid.cc
index 070e57194c6ed0a065c65d1e3ed1d9f0892ada61..4496b5fb61c0b0aab35d79062decb479bdf905c8 100644
--- a/src/Enlargegrid.cc
+++ b/src/Enlargegrid.cc
@@ -111,13 +111,14 @@ public:
   using Process::Process;
   inline static CdoModule module = {
     .name = "Enlargegrid",
-    .operators = { { "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;
 
@@ -136,9 +137,8 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
-
     operator_input_arg("grid description file or name");
     if (cdo_operator_argc() < 1) cdo_abort("Too few arguments!");
     if (cdo_operator_argc() > 2) cdo_abort("Too many arguments!");
@@ -152,7 +152,7 @@ public:
     taxisID2 = taxisDuplicate(taxisID1);
 
     int ndiffgrids = 0;
-    for (int index = 1; index < vlistNgrids(vlistID1); ++index)
+    for (int index = 1; index < vlistNumGrids(vlistID1); ++index)
       if (vlistGrid(vlistID1, 0) != vlistGrid(vlistID1, index)) ndiffgrids++;
 
     if (ndiffgrids > 0) cdo_abort("Too many different grids in %s!", cdo_get_stream_name(0));
@@ -170,8 +170,8 @@ public:
 
     auto vlistID2 = vlistDuplicate(vlistID1);
 
-    auto ngrids = vlistNgrids(vlistID1);
-    for (int index = 0; index < ngrids; ++index) vlistChangeGridIndex(vlistID2, index, gridID2);
+    auto numGrids = vlistNumGrids(vlistID1);
+    for (int index = 0; index < numGrids; ++index) vlistChangeGridIndex(vlistID2, index, gridID2);
 
     streamID2 = cdo_open_write(1);
 
@@ -180,7 +180,7 @@ public:
   }
 
   void
-  run()
+  run() override
   {
     int tsID = 0;
     while (true)
@@ -214,7 +214,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
diff --git a/src/Ensstat.cc b/src/Ensstat.cc
index 6db973f17a0e45fc48a4352fd2f02b6873236646..f289527d556cb991434db21893e521001691de44 100644
--- a/src/Ensstat.cc
+++ b/src/Ensstat.cc
@@ -39,51 +39,23 @@ struct EnsstatFile
 {
   VarList varList;
   CdoStreamID streamID;
-  int vlistID;
-  double missval[2];
-  Field field[2];
 };
 
-struct EnsstatArg
+static void
+ensstat(const std::vector<EnsstatFile> &ef, FieldVector &fieldVector, CdoStreamID streamID2, int varID, int levelID,
+        FieldVector &workFields, Varray<double> &array2, Varray<double> &count2, int operfunc, double pn)
 {
-  int t{ 0 };
-  int varID[2];
-  int levelID[2];
-  CdoStreamID streamID2;
-  int nfiles;
-  EnsstatFile *efData;
-  double *array2Data;
-  double *count2Data;
-  FieldVector fields;
-  int operfunc;
-  double pn;
-  bool lpctl;
-  bool withCountData;
-  int nvars;
-};
-
-static void *
-ensstat_func(void *ensarg)
-{
-  if (Options::CDO_task) cdo_omp_set_num_threads(Threading::ompNumThreads);
-
-  auto arg = (EnsstatArg *) ensarg;
-  auto t = arg->t;
-  auto nfiles = arg->nfiles;
-  auto ef = arg->efData;
-  auto &fields = arg->fields;
-  auto array2 = arg->array2Data;
-  auto count2 = arg->count2Data;
-  auto withCountData = arg->withCountData;
+  int numFiles = ef.size();
+  auto withCountData = (count2.size() > 0);
 
   auto hasMissvals = false;
-  for (int fileID = 0; fileID < nfiles; ++fileID)
-    if (ef[fileID].field[t].numMissVals > 0) hasMissvals = true;
+  for (int fileID = 0; fileID < numFiles; ++fileID)
+    if (fieldVector[fileID].numMissVals > 0) hasMissvals = true;
 
-  auto varID = arg->varID[t];
-  auto gridsize = ef[0].varList[varID].gridsize;
-  auto missval = ef[0].varList[varID].missval;
-  auto memType = ef[0].field[t].memType;
+  auto numVars = ef[0].varList.numVars();
+  auto gridsize = ef[0].varList.vars[varID].gridsize;
+  auto missval = ef[0].varList.vars[varID].missval;
+  auto memType = fieldVector[0].memType;
 
   std::atomic<size_t> atomicNumMiss{ 0 };
 #ifdef _OPENMP
@@ -93,43 +65,42 @@ ensstat_func(void *ensarg)
     {
       auto ompthID = cdo_omp_get_thread_num();
 
-      auto &field = fields[ompthID];
-      field.missval = missval;
-      field.numMissVals = 0;
+      auto &work = workFields[ompthID];
+      work.missval = missval;
+      work.numMissVals = 0;
       if (memType == MemType::Float)
-        for (int fileID = 0; fileID < nfiles; ++fileID) field.vec_d[fileID] = ef[fileID].field[t].vec_f[i];
+        for (int fileID = 0; fileID < numFiles; ++fileID) work.vec_d[fileID] = fieldVector[fileID].vec_f[i];
       else
-        for (int fileID = 0; fileID < nfiles; ++fileID) field.vec_d[fileID] = ef[fileID].field[t].vec_d[i];
+        for (int fileID = 0; fileID < numFiles; ++fileID) work.vec_d[fileID] = fieldVector[fileID].vec_d[i];
 
       if (hasMissvals)
-        for (int fileID = 0; fileID < nfiles; ++fileID)
+        for (int fileID = 0; fileID < numFiles; ++fileID)
           {
-            if (dbl_is_equal(field.vec_d[fileID], ef[fileID].missval[t]))
+            if (dbl_is_equal(work.vec_d[fileID], ef[fileID].varList.vars[varID].missval))
               {
-                field.vec_d[fileID] = missval;
-                field.numMissVals++;
+                work.vec_d[fileID] = missval;
+                work.numMissVals++;
               }
           }
 
-      array2[i] = arg->lpctl ? field_pctl(field, arg->pn) : field_function(field, arg->operfunc);
+      auto lpctl = (operfunc == FieldFunc_Pctl);
+      array2[i] = lpctl ? field_pctl(work, pn) : field_function(work, operfunc);
 
-      if (dbl_is_equal(array2[i], field.missval)) atomicNumMiss++;
+      if (dbl_is_equal(array2[i], work.missval)) atomicNumMiss++;
 
-      if (withCountData) count2[i] = nfiles - field.numMissVals;
+      if (withCountData) count2[i] = numFiles - work.numMissVals;
     }
 
   size_t numMissVals = atomicNumMiss;
 
-  cdo_def_record(arg->streamID2, arg->varID[t], arg->levelID[t]);
-  cdo_write_record(arg->streamID2, array2, numMissVals);
+  cdo_def_record(streamID2, varID, levelID);
+  cdo_write_record(streamID2, array2.data(), numMissVals);
 
   if (withCountData)
     {
-      cdo_def_record(arg->streamID2, arg->varID[t] + arg->nvars, arg->levelID[t]);
-      cdo_write_record(arg->streamID2, count2, 0);
+      cdo_def_record(streamID2, varID + numVars, levelID);
+      cdo_write_record(streamID2, count2.data(), 0);
     }
-
-  return nullptr;
 }
 
 class Ensstat : public Process
@@ -158,38 +129,36 @@ public:
     .constraints = { -1, 1, NoRestriction },
   };
   inline static RegisterEntry<Ensstat> registration = RegisterEntry<Ensstat>(module);
-  cdo::Task task;
-  int nrecs0;
+
+  int operfunc{ 0 };
+  double pn{ 0 };
 
   std::vector<EnsstatFile> ef;
 
   bool printWarning = false;
   bool printError = false;
 
-  int tsID = 0;
-  int nfiles;
+  int tsID{ 0 };
+  int numFiles;
   int taxisID1;
   int taxisID2;
   CdoStreamID streamID2;
 
   Varray<double> array2;
   Varray<double> count2;
-  EnsstatArg ensstatArg;
 
 public:
   void
-  init()
+  init() override
   {
-
     auto operatorID = cdo_operator_id();
-    auto operfunc = cdo_operator_f1(operatorID);
+    operfunc = cdo_operator_f1(operatorID);
 
     auto lpctl = (operfunc == FieldFunc_Pctl);
 
     auto argc = cdo_operator_argc();
     auto nargc = argc;
 
-    double pn = 0.0;
     if (lpctl)
       {
         operator_input_arg("percentile number");
@@ -206,33 +175,32 @@ public:
           cdo_abort("Unknown parameter: >%s<", cdo_operator_argv(nargc - 1));
       }
 
-    nfiles = cdo_stream_cnt() - 1;
+    numFiles = cdo_stream_cnt() - 1;
 
-    if (Options::cdoVerbose) cdo_print("Ensemble over %d files.", nfiles);
+    if (Options::cdoVerbose) cdo_print("Ensemble over %d files.", numFiles);
 
-    cdo::set_numfiles(nfiles + 8);
+    cdo::set_numfiles(numFiles + 8);
 
-    std::string ofilename = cdo_get_stream_name(nfiles);
+    std::string ofilename = cdo_get_stream_name(numFiles);
 
     if (!Options::cdoOverwriteMode && FileUtils::file_exists(ofilename) && !FileUtils::user_file_overwrite(ofilename))
       cdo_abort("Outputfile %s already exists!", ofilename);
 
-    ef = std::vector<EnsstatFile>(nfiles);
-
-    FieldVector fields(Threading::ompNumThreads);
-    for (int i = 0; i < Threading::ompNumThreads; ++i) fields[i].resize(nfiles);
+    ef.resize(numFiles);
 
-    for (int fileID = 0; fileID < nfiles; ++fileID)
+    int vlistID1 = -1;
+    for (int fileID = 0; fileID < numFiles; ++fileID)
       {
-        ef[fileID].streamID = cdo_open_read(fileID);
-        ef[fileID].vlistID = cdo_stream_inq_vlist(ef[fileID].streamID);
-        varList_init(ef[fileID].varList, ef[fileID].vlistID);
+        auto streamID = cdo_open_read(fileID);
+        auto vlistID = cdo_stream_inq_vlist(streamID);
+        ef[fileID].streamID = streamID;
+        ef[fileID].varList = VarList(vlistID);
+        if (fileID == 0) vlistID1 = vlistID;
       }
 
     // check that the contents is always the same
-    for (int fileID = 1; fileID < nfiles; ++fileID) vlist_compare(ef[0].vlistID, ef[fileID].vlistID, CmpVlist::All);
+    for (int fileID = 1; fileID < numFiles; ++fileID) varList_compare(ef[0].varList, ef[fileID].varList);
 
-    auto vlistID1 = ef[0].vlistID;
     auto vlistID2 = vlistDuplicate(vlistID1);
 
     vlist_unpack(vlistID2);
@@ -244,44 +212,44 @@ public:
     auto gridsizemax = vlistGridsizeMax(vlistID1);
     array2.resize(gridsizemax);
 
-    auto nvars = vlistNvars(vlistID2);
+    auto numVars = ef[0].varList.numVars();
     if (withCountData)
       {
         count2.resize(gridsizemax);
-        for (int varID = 0; varID < nvars; ++varID)
+        for (int varID = 0; varID < numVars; ++varID)
           {
-            auto &var = ef[0].varList[varID];
+            auto &var = ef[0].varList.vars[varID];
             auto name = var.name + "_count";
-            auto cvarID = vlistDefVar(vlistID2, var.gridID, var.zaxisID, var.timetype);
+            auto cvarID = vlistDefVar(vlistID2, var.gridID, var.zaxisID, var.timeType);
             cdiDefKeyString(vlistID2, cvarID, CDI_KEY_NAME, name.c_str());
             vlistDefVarDatatype(vlistID2, cvarID, CDI_DATATYPE_INT16);
-            if (cvarID != (varID + nvars)) cdo_abort("Internal error, varIDs do not match!");
+            if (cvarID != (varID + numVars)) cdo_abort("Internal error, varIDs do not match!");
           }
       }
 
-    streamID2 = cdo_open_write(nfiles);
+    streamID2 = cdo_open_write(numFiles);
     cdo_def_vlist(streamID2, vlistID2);
-
-    ensstatArg.streamID2 = streamID2;
-    ensstatArg.nfiles = nfiles;
-    ensstatArg.array2Data = array2.data();
-    ensstatArg.count2Data = count2.data();
-    ensstatArg.fields.resize(Threading::ompNumThreads);
-    for (int i = 0; i < Threading::ompNumThreads; ++i) ensstatArg.fields[i].resize(nfiles);
-    ensstatArg.operfunc = operfunc;
-    ensstatArg.pn = pn;
-    ensstatArg.lpctl = lpctl;
-    ensstatArg.withCountData = withCountData;
-    ensstatArg.nvars = nvars;
   }
 
   void
-  run()
+  run() override
   {
-    int t = 0;
+    auto task = Options::CDO_task ? std::make_unique<cdo::Task>() : nullptr;
+    const int numTasks = Options::CDO_task ? 2 : 1;
+
+    FieldVector workFields(Threading::ompNumThreads);
+    for (auto &work : workFields) work.resize(numFiles);
+
+    FieldVector fieldVector[2];
+    fieldVector[0].resize(numFiles);
+    if (Options::CDO_task) fieldVector[1].resize(numFiles);
+
+    int recordNum = 0;
+    int nrecs0;
     do {
         nrecs0 = cdo_stream_inq_timestep(ef[0].streamID, tsID);
-        for (int fileID = 1; fileID < nfiles; ++fileID)
+
+        for (int fileID = 1; fileID < numFiles; ++fileID)
           {
             auto streamID = ef[fileID].streamID;
             auto nrecs = cdo_stream_inq_timestep(streamID, tsID);
@@ -315,33 +283,31 @@ public:
 
         for (int recID = 0; recID < nrecs0; ++recID)
           {
-            int varID = -1, levelID = -1;
+            auto taskNum = recordNum % numTasks;
+            auto &fields = fieldVector[taskNum];
 
-            for (int fileID = 0; fileID < nfiles; ++fileID)
+            int varID = -1, levelID = -1;
+            for (int fileID = 0; fileID < numFiles; ++fileID)
               {
                 cdo_inq_record(ef[fileID].streamID, &varID, &levelID);
-                ef[fileID].missval[t] = ef[fileID].varList[varID].missval;
-                ef[fileID].field[t].init(ef[fileID].varList[varID]);
+                fields[fileID].init(ef[fileID].varList.vars[varID]);
               }
 #ifdef _OPENMP
 #pragma omp parallel for default(shared)
 #endif
-            for (int fileID = 0; fileID < nfiles; ++fileID) { cdo_read_record(ef[fileID].streamID, ef[fileID].field[t]); }
+            for (int fileID = 0; fileID < numFiles; ++fileID) { cdo_read_record(ef[fileID].streamID, fields[fileID]); }
 
-            ensstatArg.efData = ef.data();
-            ensstatArg.varID[t] = varID;
-            ensstatArg.levelID[t] = levelID;
-            ensstatArg.t = t;
-            if (Options::CDO_task)
-              {
-                task.wait();
-                task.start(ensstat_func, &ensstatArg);
-                t = !t;
-              }
-            else { ensstat_func(&ensstatArg); }
+            if (Options::CDO_task) task->wait();
+
+            std::function<void()> ensstat_func = std::bind(ensstat, std::cref(ef), std::ref(fields), streamID2, varID, levelID,
+                                                           std::ref(workFields), std::ref(array2), std::ref(count2), operfunc, pn);
+
+            Options::CDO_task ? task->doAsync(ensstat_func) : ensstat_func();
+
+            recordNum++;
           }
 
-        if (Options::CDO_task) task.wait();
+        if (Options::CDO_task) task->wait();
 
         tsID++;
       }
@@ -349,12 +315,12 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     if (printWarning) cdo_warning("Inconsistent ensemble, processed only the first %d timesteps!", tsID);
     if (printError) cdo_abort("Inconsistent ensemble, processed only the first %d timesteps!", tsID);
 
-    for (int fileID = 0; fileID < nfiles; ++fileID) cdo_stream_close(ef[fileID].streamID);
+    for (int fileID = 0; fileID < numFiles; ++fileID) cdo_stream_close(ef[fileID].streamID);
 
     cdo_stream_close(streamID2);
   }
diff --git a/src/Ensstat3.cc b/src/Ensstat3.cc
index 08dad8cb093b0b2babaca08b5020f56df68e3c83..3b77fc2650a4ab258076b2c2a82b741f933da7eb 100644
--- a/src/Ensstat3.cc
+++ b/src/Ensstat3.cc
@@ -15,7 +15,6 @@
 #include <cdi.h>
 
 #include "process_int.h"
-#include "cdo_vlist.h"
 #include "param_conversion.h"
 #include "cdo_options.h"
 #include "util_files.h"
@@ -90,12 +89,12 @@ public:
     .constraints = { -1, 1, NoRestriction },
   };
   inline static RegisterEntry<Ensstat3> registration = RegisterEntry<Ensstat3>(module);
+
   int j;
   int nrecs = 0, nrecs0;
   size_t numMissVals = 0;
   int chksum = 0;  // for check of histogram population
   int levelID, varID, binID = 0;
-  int gridID, gridID2;
   int have_miss = 0;
   CdoStreamID streamID2 = 0;
 
@@ -104,6 +103,7 @@ public:
     Varray<double> array;
     CdoStreamID streamID;
     int vlistID;
+    VarList varList;
   };
 
   std::vector<ens_file_t> ef;
@@ -113,7 +113,6 @@ public:
 
   int taxisID1;
   int taxisID2;
-  int vlistID1;
 
   int nbins = 0;
 
@@ -132,12 +131,8 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
-
-    // clang-format off
-    // clang-format on
-
     auto operatorID = cdo_operator_id();
     operfunc = cdo_operator_f1(operatorID);
     datafunc = cdo_operator_f2(operatorID);
@@ -178,17 +173,19 @@ public:
         auto streamID = cdo_open_read(fileID);
         ef[fileID].streamID = streamID;
         ef[fileID].vlistID = cdo_stream_inq_vlist(streamID);
+        ef[fileID].varList = VarList(ef[fileID].vlistID);
       }
 
     // check for identical contents of all ensemble members
-    for (int fileID = 1; fileID < nfiles; ++fileID) vlist_compare(ef[0].vlistID, ef[fileID].vlistID, CmpVlist::All);
+    for (int fileID = 1; fileID < nfiles; ++fileID) varList_compare(ef[0].varList, ef[fileID].varList);
 
-    vlistID1 = ef[0].vlistID;
+    auto vlistID1 = ef[0].vlistID;
+    const auto &varList1 = ef[0].varList;
     auto vlistID2 = vlistCreate();
-    vlistDefNtsteps(vlistID2, vlistNtsteps(vlistID1));
+    vlistDefNtsteps(vlistID2, varList1.numSteps());
 
-    auto nvars = vlistNvars(vlistID1);
-    varIDs2 = std::vector<int>(nvars);
+    auto numVars = varList1.numVars();
+    varIDs2 = std::vector<int>(numVars);
 
     auto zaxisID2 = zaxisCreate(ZAXIS_GENERIC, nfiles);
     {
@@ -200,8 +197,9 @@ public:
 
     int time_mode = (datafunc == TIME) ? TIME_VARYING : TIME_CONSTANT;
 
-    for (varID = 0; varID < nvars; ++varID)
+    for (varID = 0; varID < numVars; ++varID)
       {
+        int gridID2;
         /* **************************************************************** */
         /* nfiles includes the observation, so there are nfiles-1 ensembles */
         /* and exactly nfiles bins, in which the observation could fall     */
@@ -216,7 +214,7 @@ public:
             gridDefYvals(gridID2, &val);
           }
         else  // datafunc == SPACE
-          gridID2 = vlistInqVarGrid(vlistID1, varID);
+          gridID2 = varList1.vars[varID].gridID;
 
         varIDs2[varID] = vlistDefVar(vlistID2, gridID2, zaxisID2, time_mode);
       }
@@ -225,9 +223,9 @@ public:
     taxisID2 = taxisDuplicate(taxisID1);
     vlistDefTaxis(vlistID2, taxisID2);
 
-    for (varID = 0; varID < nvars; ++varID)
+    for (varID = 0; varID < numVars; ++varID)
       {
-        if (zaxisInqSize(vlistInqVarZaxis(vlistID1, varID)) > 1)
+        if (varList1.vars[varID].nlevels > 1)
           {
             cdo_warning("More than one level not supported when processing ranked histograms.");
             cdo_warning("Try to use `cdo splitlevel` to split the dataset into levels and apply");
@@ -277,9 +275,12 @@ public:
           }
       }
   }
+
   void
-  run()
+  run() override
   {
+    const auto &varList1 = ef[0].varList;
+
     int tsID = 0;
     do {
         nrecs0 = cdo_stream_inq_timestep(ef[0].streamID, tsID);
@@ -317,9 +318,8 @@ public:
                 cdo_read_record(streamID, ef[fileID].array.data(), &numMissVals);
               }
 
-            gridID = vlistInqVarGrid(vlistID1, varID);
-            gridsize = gridInqSize(gridID);
-            auto missval = vlistInqVarMissval(vlistID1, varID);
+            gridsize = varList1.vars[varID].gridsize;
+            auto missval = varList1.vars[varID].missval;
 
             numMissVals = 0;
             if (datafunc == TIME && operfunc == func_rank)
@@ -502,7 +502,7 @@ public:
       }
   }
   void
-  close()
+  close() override
   {
     for (int fileID = 0; fileID < nfiles; ++fileID) cdo_stream_close(ef[fileID].streamID);
 
diff --git a/src/Ensval.cc b/src/Ensval.cc
index 7732a4f431e677d5d802e1dc3f36f74bfab0a865..78454d078d9ce23c74618b615d72774905275ef1 100644
--- a/src/Ensval.cc
+++ b/src/Ensval.cc
@@ -18,7 +18,6 @@
 #include <cdi.h>
 
 #include "process_int.h"
-#include "cdo_vlist.h"
 #include "cdo_options.h"
 #include "param_conversion.h"
 #include "util_files.h"
@@ -79,6 +78,7 @@ public:
   {
     CdoStreamID streamID;
     int vlistID;
+    VarList varList;
     Varray<double> array;
   };
 
@@ -107,7 +107,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
     auto operatorID = cdo_operator_id();
     operfunc = cdo_operator_f1(operatorID);
@@ -160,6 +160,7 @@ public:
 
         ef[fileID].streamID = streamID;
         ef[fileID].vlistID = vlistID;
+        ef[fileID].varList = VarList(ef[fileID].vlistID);
       }
 
     auto streamID1 = ef[0].streamID;
@@ -170,7 +171,7 @@ public:
     auto nvars = vlistNvars(ef[0].vlistID);
     if (Options::cdoVerbose) cdo_print("nvars %i", nvars);
 
-    for (fileID = 1; fileID < nfiles; ++fileID) vlist_compare(ef[0].vlistID, ef[fileID].vlistID, CmpVlist::All);
+    for (fileID = 1; fileID < nfiles; ++fileID) varList_compare(ef[0].varList, ef[fileID].varList);
 
     auto vlistID1 = ef[0].vlistID;
     taxisID1 = vlistInqTaxis(vlistID1);
@@ -220,7 +221,7 @@ public:
         taxisID2[stream] = taxisDuplicate(taxisID1);
         vlistID2[stream] = vlistDuplicate(vlistID1);
 
-        ngrids = vlistNgrids(vlistID2[stream]);
+        ngrids = vlistNumGrids(vlistID2[stream]);
         for (int i = 0; i < ngrids; ++i) vlistChangeGridIndex(vlistID2[stream], i, gridID2);
 
         vlistDefTaxis(vlistID2[stream], taxisID2[stream]);
@@ -229,11 +230,11 @@ public:
 
     if (Options::cdoVerbose) cdo_print(" sum_weights %10.6f", sum_weights);
 
-    varList_init(varList1, vlistID1);
+    varList1 = VarList(vlistID1);
   }
 
   void
-  run()
+  run() override
   {
     int tsID = 0;
     do {
@@ -262,8 +263,8 @@ public:
 
                 if (fileID == 0)
                   {
-                    gridsize = varList1[varID].gridsize;
-                    missval = varList1[varID].missval;
+                    gridsize = varList1.vars[varID].gridsize;
+                    missval = varList1.vars[varID].missval;
                     weights.resize(gridsize);
                   }
 
@@ -397,7 +398,7 @@ public:
                   {
                     p = (double) k / (double) nens;
 
-                    if (!dbl_is_equal(sum_weights, 1.0))
+                    if (dbl_is_not_equal(sum_weights, 1.0))
                       {
                         alpha[k] /= sum_weights;
                         beta[k] /= sum_weights;
@@ -510,7 +511,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     for (fileID = 0; fileID < nfiles; ++fileID)
       {
@@ -525,7 +526,5 @@ public:
         vlistDestroy(vlistID2[stream]);
         taxisDestroy(taxisID2[stream]);
       }
-
-    gridDestroy(gridID2);
   }
 };
diff --git a/src/Eof3d.cc b/src/Eof3d.cc
index 3ba52a16ece8156b6f78276860e2805cd42318ac..9c29d81158ed0adcb4daf32ce0b43ba3106451ed 100644
--- a/src/Eof3d.cc
+++ b/src/Eof3d.cc
@@ -27,12 +27,10 @@
 #include "julian_date.h"
 
 #include "cdo_options.h"
-#include "cdo_vlist.h"
 #include "process_int.h"
 #include "param_conversion.h"
 #include <mpim_grid.h>
 #include "eigen_solution.h"
-#include "datetime.h"
 #include "eof_mode.h"
 
 // NO MISSING VALUE SUPPORT ADDED SO FAR
@@ -78,7 +76,7 @@ public:
   int taxisID1;
 
   int nrecs;
-  int nvars;
+  int numVars;
 
   T_WEIGHT_MODE weight_mode;
   T_EIGEN_MODE eigen_mode;
@@ -100,14 +98,10 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
-
-    // clang-format off
-    // clang-format on
-
-    const auto operatorID = cdo_operator_id();
-    const auto operfunc = cdo_operator_f1(operatorID);
+    auto operatorID = cdo_operator_id();
+    auto operfunc = cdo_operator_f1(operatorID);
 
     operator_input_arg("Number of eigen functions to write out");
     n_eig = parameter_to_int(cdo_operator_argv(0));
@@ -122,10 +116,10 @@ public:
     streamID1 = cdo_open_read(0);
     vlistID1 = cdo_stream_inq_vlist(streamID1);
 
-    varList_init(varList1, vlistID1);
+    varList1 = VarList(vlistID1);
 
     // COUNT NUMBER OF TIMESTEPS if EOF3D_ or EOF3D_TIME
-    nts = vlistNtsteps(vlistID1);
+    nts = varList1.numSteps();
     if (nts == -1)
       {
         nts = 0;
@@ -156,26 +150,26 @@ public:
 
     if (Options::cdoVerbose) cdo_print("counted %d timesteps", n);
 
-    nvars = vlistNvars(vlistID1);
+    numVars = varList1.numVars();
 
-    auto gridID1 = varList1[0].gridID;
+    auto gridID1 = varList1.vars[0].gridID;
     gridsizemax = vlistGridsizeMax(vlistID1);
 
     // allocation of temporary fields and output structures
 
     in = Varray<double>(gridsizemax);
-    datacounts = Varray2D<int>(nvars);
-    datafields = Varray3D<double>(nvars);
-    eigenvectors = Varray3D<double>(nvars);
-    eigenvalues = Varray3D<double>(nvars);
+    datacounts = Varray2D<int>(numVars);
+    datafields = Varray3D<double>(numVars);
+    eigenvectors = Varray3D<double>(numVars);
+    eigenvalues = Varray3D<double>(numVars);
 
     size_t maxlevs = 0;
-    for (varID = 0; varID < nvars; ++varID)
+    for (varID = 0; varID < numVars; ++varID)
       {
-        const auto gridsize = vlistGridsizeMax(vlistID1);
-        const size_t nlevs = varList1[varID].nlevels;
+        auto gridsize = vlistGridsizeMax(vlistID1);
+        size_t nlevs = varList1.vars[varID].nlevels;
         temp_size = gridsize * nlevs;
-        const auto missval = varList1[varID].missval;
+        auto missval = varList1.vars[varID].missval;
 
         if (nlevs > maxlevs) maxlevs = nlevs;
 
@@ -203,7 +197,7 @@ public:
 
     if (weight_mode == WEIGHT_ON)
       {
-        const auto wstatus = gridcell_weights(gridID1, weights);
+        auto wstatus = gridcell_weights(gridID1, weights);
         if (wstatus != 0)
           {
             weight_mode = WEIGHT_OFF;
@@ -216,8 +210,9 @@ public:
           }
       }
   }
+
   void
-  run()
+  run() override
   {
     int tsID = 0;
 
@@ -231,15 +226,15 @@ public:
           {
             cdo_inq_record(streamID1, &varID, &levelID);
 
-            const auto gridsize = varList1[varID].gridsize;
-            const auto missval = varList1[varID].missval;
+            auto gridsize = varList1.vars[varID].gridsize;
+            auto missval = varList1.vars[varID].missval;
 
             cdo_read_record(streamID1, in.data(), &numMissVals);
 
-            const auto offset = gridsize * levelID;
+            auto offset = gridsize * levelID;
             for (size_t i = 0; i < gridsize; ++i)
               {
-                if (!DBL_IS_EQUAL(in[i], missval))
+                if (dbl_is_not_equal(in[i], missval))
                   {
                     datafields[varID][tsID][offset + i] = in[i];
                     datacounts[varID][offset + i]++;
@@ -260,21 +255,22 @@ public:
         tsID++;
       }
 
-    if (Options::cdoVerbose) cdo_print("Read data for %d variables", nvars);
+    if (Options::cdoVerbose) cdo_print("Read data for %d variables", numVars);
 
     Varray<size_t> pack(temp_size);  // TODO
 
-    for (varID = 0; varID < nvars; ++varID)
+    for (varID = 0; varID < numVars; ++varID)
       {
-        const auto gridsize = varList1[varID].gridsize;
-        const auto nlevs = varList1[varID].nlevels;
-        auto missval = varList1[varID].missval;
+        const auto &var1 = varList1.vars[varID];
+        auto gridsize = var1.gridsize;
+        auto nlevs = var1.nlevels;
+        auto missval = var1.missval;
         temp_size = gridsize * nlevs;
 
         if (Options::cdoVerbose)
           {
             cdo_print("============================================================================");
-            cdo_print("Calculating covariance matrix and SVD for var%d (%s)", varID + 1, varList1[varID].name);
+            cdo_print("Calculating covariance matrix and SVD for var%d (%s)", varID + 1, var1.name);
           }
 
         npack = 0;  // TODO already set to 0
@@ -297,7 +293,7 @@ public:
 
         if (npack < 1)
           {
-            cdo_warning("Refusing to calculate EOF from a single time step for var%d (%s)", varID + 1, varList1[varID].name);
+            cdo_warning("Refusing to calculate EOF from a single time step for var%d (%s)", varID + 1, var1.name);
             continue;
           }
 
@@ -381,43 +377,43 @@ public:
                 for (size_t i = 0; i < npack; ++i) eigenvec[pack[i]] = missval;
               }
           }  // for ( eofID = 0; eofID < n_eig; eofID++ )
-      }      // for ( varID = 0; varID < nvars; varID++ )
+      }      // for ( varID = 0; varID < numVars; varID++ )
 
     // write files with eigenvalues (ID3) and eigenvectors (ID2)
 
     // eigenvalues
     streamID2 = cdo_open_write(1);
 
-    const auto vlistID2 = vlistDuplicate(vlistID1);
-    const auto taxisID2 = taxisDuplicate(taxisID1);
+    auto vlistID2 = vlistDuplicate(vlistID1);
+    auto taxisID2 = taxisDuplicate(taxisID1);
     taxisDefRdate(taxisID2, 0);
     taxisDefRtime(taxisID2, 0);
     vlistDefTaxis(vlistID2, taxisID2);
 
-    const auto gridID2 = gridCreate(GRID_LONLAT, 1);
+    auto gridID2 = gridCreate(GRID_LONLAT, 1);
     gridDefXsize(gridID2, 1);
     gridDefYsize(gridID2, 1);
     double xvals = 0.0, yvals = 0.0;
     gridDefXvals(gridID2, &xvals);
     gridDefYvals(gridID2, &yvals);
 
-    ngrids = vlistNgrids(vlistID2);
+    ngrids = vlistNumGrids(vlistID2);
     for (int i = 0; i < ngrids; ++i) vlistChangeGridIndex(vlistID2, i, gridID2);
 
-    const auto zaxisID2 = zaxisCreate(ZAXIS_GENERIC, 1);
+    auto zaxisID2 = zaxisCreate(ZAXIS_GENERIC, 1);
     double zvals = 0;
     zaxisDefLevels(zaxisID2, &zvals);
     cdiDefKeyString(zaxisID2, CDI_GLOBAL, CDI_KEY_NAME, "zaxis_Reduced");
     cdiDefKeyString(zaxisID2, CDI_GLOBAL, CDI_KEY_LONGNAME, "Reduced zaxis from EOF3D - only one eigen value per 3D eigen vector");
 
-    const auto nzaxis = vlistNzaxis(vlistID2);
+    auto nzaxis = vlistNumZaxis(vlistID2);
     for (int i = 0; i < nzaxis; ++i) vlistChangeZaxisIndex(vlistID2, i, zaxisID2);
 
     // eigenvectors
     streamID3 = cdo_open_write(2);
 
-    const auto vlistID3 = vlistDuplicate(vlistID1);
-    const auto taxisID3 = taxisDuplicate(taxisID1);
+    auto vlistID3 = vlistDuplicate(vlistID1);
+    auto taxisID3 = taxisDuplicate(taxisID1);
     taxisDefRdate(taxisID3, 0);
     taxisDefRtime(taxisID3, 0);
     vlistDefTaxis(vlistID3, taxisID3);
@@ -429,7 +425,7 @@ public:
     for (tsID = 0; tsID < (int) n; ++tsID)
       {
         julianDate = julianDate_add_seconds(julianDate, 60);
-        const auto vDateTime = julianDate_decode(calendar, julianDate);
+        auto vDateTime = julianDate_decode(calendar, julianDate);
 
         taxisDefVdatetime(taxisID2, vDateTime);
         cdo_def_timestep(streamID2, tsID);
@@ -440,13 +436,13 @@ public:
             cdo_def_timestep(streamID3, tsID);
           }
 
-        for (varID = 0; varID < nvars; ++varID)
+        for (varID = 0; varID < numVars; ++varID)
           {
-            const auto missval = varList1[varID].missval;
-            const auto nlevs = varList1[varID].nlevels;
+            auto missval = varList1.vars[varID].missval;
+            auto nlevs = varList1.vars[varID].nlevels;
             for (levelID = 0; levelID < (int) nlevs; ++levelID)
               {
-                const auto offset = levelID * gridsizemax;
+                auto offset = levelID * gridsizemax;
                 if (tsID < n_eig)
                   {
                     numMissVals = array_num_mv(gridsizemax, &eigenvectors[varID][tsID][offset], missval);
@@ -462,10 +458,10 @@ public:
           }  // for ( varID = 0; ... )
       }      // for ( tsID = 0; ... )
   }
+
   void
-  close()
+  close() override
   {
-
     cdo_stream_close(streamID3);
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
diff --git a/src/Eofcoeff.cc b/src/Eofcoeff.cc
index 0488508dbbe567a306a636477c5fafdbdc32d0c2..584542a79ec4f801db2eb123fe577a468166020c 100644
--- a/src/Eofcoeff.cc
+++ b/src/Eofcoeff.cc
@@ -50,12 +50,15 @@ public:
   int vlistID2;
   int vlistID3;
 
+  VarList varList1;
+  VarList varList2;
+
   int gridID1;
   int eofID;
   size_t gridsize;
   int neof;
-  int nvars;
-  int nlevs;
+  int numVars;
+  int numLevels;
 
   std::vector<CdoStreamID> streamIDs;
 
@@ -64,7 +67,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
     streamID1 = cdo_open_read(0);
     streamID2 = cdo_open_read(1);
@@ -73,34 +76,37 @@ public:
     vlistID2 = cdo_stream_inq_vlist(streamID2);
     vlistID3 = vlistDuplicate(vlistID2);
 
+    varList1 = VarList(vlistID1);
+    varList2 = VarList(vlistID2);
+
     // taxisID1 = vlistInqTaxis(vlistID1);
     taxisID2 = vlistInqTaxis(vlistID2);
     taxisID3 = taxisDuplicate(taxisID2);
 
-    gridID1 = vlistInqVarGrid(vlistID1, 0);
-    int gridID2 = vlistInqVarGrid(vlistID2, 0);
+    gridID1 = varList1.vars[0].gridID;
+    int gridID2 = varList2.vars[0].gridID;
 
     gridsize = vlistGridsizeMax(vlistID1);
     if (gridsize != vlistGridsizeMax(vlistID2))
       cdo_abort("Gridsize of input files does not match! %zu and %zu", gridsize, vlistGridsizeMax(vlistID2));
 
-    if (vlistNgrids(vlistID2) > 1 || vlistNgrids(vlistID1) > 1) cdo_abort("Too many different grids in input!");
+    if (vlistNumGrids(vlistID2) > 1 || vlistNumGrids(vlistID1) > 1) cdo_abort("Too many different grids in input!");
 
-    nvars = vlistNvars(vlistID1) == vlistNvars(vlistID2) ? vlistNvars(vlistID1) : -1;
-    nlevs = zaxisInqSize(vlistInqVarZaxis(vlistID1, 0));
+    numVars = (varList1.numVars() == varList2.numVars()) ? varList1.numVars() : -1;
+    numLevels = varList1.vars[0].nlevels;
 
     if (gridID1 != gridID2) cdo_compare_grids(gridID1, gridID2);
 
     fileSuffix = FileUtils::gen_suffix(cdo_inq_filetype(streamID1), vlistID1, cdo_get_stream_name(0));
 
-    eof = FieldVector3D(nvars);
-    for (varID = 0; varID < nvars; ++varID) eof[varID].resize(nlevs);
+    eof = FieldVector3D(numVars);
+    for (varID = 0; varID < numVars; ++varID) eof[varID].resize(numLevels);
 
     eofID = 0;
   }
 
   void
-  run()
+  run() override
   {
     while (1)
       {
@@ -110,15 +116,15 @@ public:
         for (int recID = 0; recID < nrecs; ++recID)
           {
             cdo_inq_record(streamID1, &varID, &levelID);
-            missval1 = vlistInqVarMissval(vlistID1, varID);
+            missval1 = varList1.vars[varID].missval;
             eof[varID][levelID].resize((eofID == 0) ? 1 : eofID + 1);
             eof[varID][levelID][eofID].grid = gridID1;
             eof[varID][levelID][eofID].missval = missval1;
             eof[varID][levelID][eofID].resize(gridsize);
             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");
+            if (varID >= numVars) cdo_abort("Internal error - varID >= nvars");
+            if (levelID >= numLevels) cdo_abort("Internal error - levelID >= nlevs");
 
             cdo_read_record(streamID1, eof[varID][levelID][eofID].vec_d.data(), &numMissVals);
             eof[varID][levelID][eofID].numMissVals = numMissVals;
@@ -139,11 +145,11 @@ public:
 
     // Create var-list and time-axis for output
 
-    auto ngrids = vlistNgrids(vlistID3);
+    auto ngrids = vlistNumGrids(vlistID3);
     for (int i = 0; i < ngrids; ++i) vlistChangeGridIndex(vlistID3, i, gridID3);
 
     vlistDefTaxis(vlistID3, taxisID3);
-    for (varID = 0; varID < nvars; ++varID) vlistDefVarTimetype(vlistID3, varID, TIME_VARYING);
+    for (varID = 0; varID < numVars; ++varID) vlistDefVarTimetype(vlistID3, varID, TIME_VARYING);
 
     // open streams for eofcoeff output
     streamIDs = std::vector<CdoStreamID>(neof);
@@ -184,7 +190,7 @@ public:
         for (int recID = 0; recID < nrecs; ++recID)
           {
             cdo_inq_record(streamID2, &varID, &levelID);
-            missval2 = vlistInqVarMissval(vlistID2, varID);
+            missval2 = varList2.vars[varID].missval;
             cdo_read_record(streamID2, in.vec_d.data(), &in.numMissVals);
 
             for (eofID = 0; eofID < neof; eofID++)
@@ -214,8 +220,8 @@ public:
                 // 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(), numMissVals);
               }
-            if (varID >= nvars) cdo_abort("Internal error - varID >= nvars");
-            if (levelID >= nlevs) cdo_abort("Internal error - levelID >= nlevs");
+            if (varID >= numVars) cdo_abort("Internal error - varID >= nvars");
+            if (levelID >= numLevels) cdo_abort("Internal error - levelID >= nlevs");
           }
 
         tsID++;
@@ -223,7 +229,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     for (eofID = 0; eofID < neof; eofID++) cdo_stream_close(streamIDs[eofID]);
 
diff --git a/src/Eofcoeff3d.cc b/src/Eofcoeff3d.cc
index 0768bf594337954a7935fab09cb7b3e35fc33f2d..32748d379a7f1cbe027a4cd7f1ec85b33e8496c5 100644
--- a/src/Eofcoeff3d.cc
+++ b/src/Eofcoeff3d.cc
@@ -46,14 +46,17 @@ public:
   int vlistID1;
   int vlistID2;
 
+  VarList varList1;
+  VarList varList2;
+
   int gridID1;
 
   int taxisID2;
 
   size_t gridsize;
-  int nvars;
+  int numVars;
   int neof;
-  int nlevs;
+  int numLevels;
   int taxisID3;
 
   std::string fileSuffix;
@@ -65,7 +68,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
     streamID1 = cdo_open_read(0);
     streamID2 = cdo_open_read(1);
@@ -73,31 +76,34 @@ public:
     vlistID1 = cdo_stream_inq_vlist(streamID1);
     vlistID2 = cdo_stream_inq_vlist(streamID2);
 
+    varList1 = VarList(vlistID1);
+    varList2 = VarList(vlistID2);
+
     // taxisID1 = vlistInqTaxis(vlistID1);
     taxisID2 = vlistInqTaxis(vlistID2);
     taxisID3 = taxisDuplicate(taxisID2);
 
-    gridID1 = vlistInqVarGrid(vlistID1, 0);
-    int gridID2 = vlistInqVarGrid(vlistID2, 0);
+    gridID1 = varList1.vars[0].gridID;
+    int gridID2 = varList2.vars[0].gridID;
 
     gridsize = vlistGridsizeMax(vlistID1);
     if (gridsize != vlistGridsizeMax(vlistID2)) cdo_abort("Gridsize of input files does not match!");
 
-    if (vlistNgrids(vlistID2) > 1 || vlistNgrids(vlistID1) > 1) cdo_abort("Too many different grids in input");
+    if (vlistNumGrids(vlistID2) > 1 || vlistNumGrids(vlistID1) > 1) cdo_abort("Too many different grids in input");
 
-    nvars = (vlistNvars(vlistID1) == vlistNvars(vlistID2)) ? vlistNvars(vlistID1) : -1;
-    nlevs = zaxisInqSize(vlistInqVarZaxis(vlistID1, 0));
+    numVars = (varList1.numVars() == varList2.numVars()) ? varList1.numVars() : -1;
+    numLevels = varList1.vars[0].nlevels;
 
     if (gridID1 != gridID2) cdo_compare_grids(gridID1, gridID2);
 
     fileSuffix = FileUtils::gen_suffix(cdo_inq_filetype(streamID1), vlistID1, cdo_get_stream_name(0));
 
-    eof = FieldVector3D(nvars);
-    for (varID = 0; varID < nvars; ++varID) eof[varID].resize(nlevs);
+    eof = FieldVector3D(numVars);
+    for (varID = 0; varID < numVars; ++varID) eof[varID].resize(numLevels);
   }
 
   void
-  run()
+  run() override
   {
     while (1)
       {
@@ -107,15 +113,15 @@ public:
         for (int recID = 0; recID < nrecs; ++recID)
           {
             cdo_inq_record(streamID1, &varID, &levelID);
-            missval1 = vlistInqVarMissval(vlistID1, varID);
+            missval1 = varList1.vars[varID].missval;
             eof[varID][levelID].resize((eofID == 0) ? 1 : eofID + 1);
             eof[varID][levelID][eofID].grid = gridID1;
             eof[varID][levelID][eofID].missval = missval1;
             eof[varID][levelID][eofID].resize(gridsize);
             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");
+            if (varID >= numVars) cdo_abort("Internal error - varID >= nvars");
+            if (levelID >= numLevels) cdo_abort("Internal error - levelID >= nlevs");
 
             cdo_read_record(streamID1, eof[varID][levelID][eofID].vec_d.data(), &numMissVals);
             eof[varID][levelID][eofID].numMissVals = numMissVals;
@@ -145,14 +151,14 @@ public:
 
     auto vlistID3 = vlistDuplicate(vlistID2);
 
-    auto ngrids = vlistNgrids(vlistID3);
+    auto ngrids = vlistNumGrids(vlistID3);
     for (int i = 0; i < ngrids; ++i) vlistChangeGridIndex(vlistID3, i, gridID3);
 
-    auto nzaxis = vlistNzaxis(vlistID3);
+    auto nzaxis = vlistNumZaxis(vlistID3);
     for (int i = 0; i < nzaxis; ++i) vlistChangeZaxisIndex(vlistID3, i, zaxisID3);
 
     vlistDefTaxis(vlistID3, taxisID3);
-    for (varID = 0; varID < nvars; ++varID) vlistDefVarTimetype(vlistID3, varID, TIME_VARYING);
+    for (varID = 0; varID < numVars; ++varID) vlistDefVarTimetype(vlistID3, varID, TIME_VARYING);
 
     // open streams for eofcoeff output
 
@@ -174,8 +180,8 @@ public:
     Field in;
     in.resize(gridsize);
     in.grid = gridID1;
-    FieldVector2D out(nvars);
-    for (varID = 0; varID < nvars; ++varID)
+    FieldVector2D out(numVars);
+    for (varID = 0; varID < numVars; ++varID)
       {
         out[varID].resize(neof);
         for (eofID = 0; eofID < neof; eofID++)
@@ -192,7 +198,7 @@ public:
         nrecs = cdo_stream_inq_timestep(streamID2, tsID);
         if (nrecs == 0) break;
 
-        for (varID = 0; varID < nvars; ++varID)
+        for (varID = 0; varID < numVars; ++varID)
           for (eofID = 0; eofID < neof; eofID++)
             {
               out[varID][eofID].vec_d[0] = 0;
@@ -206,7 +212,7 @@ public:
           {
             cdo_inq_record(streamID2, &varID, &levelID);
             cdo_read_record(streamID2, in.vec_d.data(), &in.numMissVals);
-            missval2 = vlistInqVarMissval(vlistID2, varID);
+            missval2 = varList2.vars[varID].missval;
 
             for (eofID = 0; eofID < neof; eofID++)
               {
@@ -231,13 +237,13 @@ public:
                 */
               }
 
-            if (varID >= nvars) cdo_abort("Internal error - varID >= nvars");
-            if (levelID >= nlevs) cdo_abort("Internal error - levelID >= nlevs");
+            if (varID >= numVars) cdo_abort("Internal error - varID >= nvars");
+            if (levelID >= numLevels) cdo_abort("Internal error - levelID >= nlevs");
           }
 
         for (eofID = 0; eofID < neof; eofID++)
           {
-            for (varID = 0; varID < nvars; ++varID)
+            for (varID = 0; varID < numVars; ++varID)
               {
                 cdo_def_record(streamIDs[eofID], varID, 0);
                 cdo_write_record(streamIDs[eofID], out[varID][eofID].vec_d.data(), out[varID][eofID].numMissVals);
@@ -249,7 +255,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     for (eofID = 0; eofID < neof; eofID++) cdo_stream_close(streamIDs[eofID]);
 
diff --git a/src/EstFreq.cc b/src/EstFreq.cc
index 022bc532a5ece867ed60f2ccce93456d2d15ad0e..0b759e1e6dd39cef4d0737cfa20b6e6e2195fdf6 100644
--- a/src/EstFreq.cc
+++ b/src/EstFreq.cc
@@ -16,14 +16,14 @@ public:
   using Process::Process;
   inline static CdoModule module = {
     .name = "EstFreq",
-    .operators = { { "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;
 
@@ -45,9 +45,8 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
-
     operator_check_argc(0);
 
     dataIsUnchanged = data_is_unchanged();
@@ -67,12 +66,13 @@ public:
 
     ntsteps = vlistNtsteps(vlistID1);
 
-    varList_init(varList1, vlistID1);
+    varList1 = VarList(vlistID1);
   }
+
   void
-  run()
+  run() override
   {
-
+    int tsID = 0;
     while (true)
       {
         auto nrecs = cdo_stream_inq_timestep(streamID1, tsID);
@@ -104,7 +104,7 @@ public:
             if (dataIsUnchanged) { cdo_copy_record(streamID2, streamID1); }
             else
               {
-                field.init(varList1[varID]);
+                field.init(varList1.vars[varID]);
                 cdo_read_record(streamID1, field);
                 cdo_write_record(streamID2, field);
               }
@@ -168,8 +168,9 @@ public:
     if (Options::cdoVerbose) printf("Your file indicates a frequency of '%s'.\n", frequency);
     cdiDefAttTxt(vlistID2, CDI_GLOBAL, "frequency", 3, frequency);
   }
+
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID1);
     cdo_stream_close(streamID2);
diff --git a/src/Exprf.cc b/src/Exprf.cc
index 3ed3617b63a6ddbcb60e511e7cd68b2ca6e1b550..0a2becdefe001219295e154dbd736e6e1c4165ed 100644
--- a/src/Exprf.cc
+++ b/src/Exprf.cc
@@ -95,9 +95,9 @@ exprs_expand(std::string &exprString, const VarList &varList)
   if (exprString.find(templateName) != std::string::npos)
     {
       replaceTemplate = true;
-      for (size_t varID = 0, n = varList.size(); varID < n; ++varID)
+      for (const auto &var : varList.vars)
         {
-          if (templateName == varList[varID].name)
+          if (templateName == var.name)
             {
               replaceTemplate = false;
               break;
@@ -114,10 +114,10 @@ exprs_expand(std::string &exprString, const VarList &varList)
           if (string.find(templateName) == std::string::npos) { exprStringNew += string + ";"; }
           else
             {
-              for (size_t varID = 0, n = varList.size(); varID < n; ++varID)
+              for (const auto &var : varList.vars)
                 {
                   auto tmpString = string;
-                  replace_all(tmpString, templateName, varList[varID].name);
+                  replace_all(tmpString, templateName, var.name);
                   exprStringNew += tmpString + ";";
                 }
             }
@@ -130,76 +130,61 @@ exprs_expand(std::string &exprString, const VarList &varList)
 }
 
 static void
-params_init(std::vector<ParamEntry> &params, const VarList &varList, int vlistID)
+params_init(std::vector<ParamEntry> &params, const VarList &varList)
 {
-  auto nvars = vlistNvars(vlistID);
-  for (int varID = 0; varID < nvars; ++varID)
+  for (const auto &var : varList.vars)
     {
-      const auto &var = varList[varID];
-
-      auto stdname = cdo::inq_key_string(vlistID, varID, CDI_KEY_STDNAME);
-
-      params[varID].type = ParamType::VAR;
-      params[varID].isValid = true;
-      params[varID].hasMV = true;
-      params[varID].gridID = var.gridID;
-      params[varID].zaxisID = var.zaxisID;
-      params[varID].datatype = var.datatype;
-      params[varID].steptype = var.timetype;
-      params[varID].nlat = gridInqYsize(var.gridID);
-      params[varID].ngp = var.gridsize;
-      params[varID].nlev = var.nlevels;
-      params[varID].missval = var.missval;
-      params[varID].name = var.name;
-      if (var.longname.size()) params[varID].longname = var.longname;
-      if (var.units.size()) params[varID].units = var.units;
-      if (stdname.size()) params[varID].stdname = stdname;
+      auto &param = params[var.ID];
+      auto stdname = cdo::inq_key_string(varList.vlistID, var.ID, CDI_KEY_STDNAME);
+
+      param.type = ParamType::VAR;
+      param.isValid = true;
+      param.hasMV = true;
+      param.gridID = var.gridID;
+      param.zaxisID = var.zaxisID;
+      param.datatype = var.dataType;
+      param.steptype = var.timeType;
+      param.nlat = gridInqYsize(var.gridID);
+      param.ngp = var.gridsize;
+      param.nlev = var.nlevels;
+      param.missval = var.missval;
+      param.name = var.name;
+      if (var.longname.size()) param.longname = var.longname;
+      if (var.units.size()) param.units = var.units;
+      if (stdname.size()) param.stdname = stdname;
     }
 }
 
 static void
 params_delete(const std::vector<ParamEntry> &params)
 {
-  for (int varID = 0; varID < MaxParams; ++varID)
+  for (auto &param : params)
     {
-      if (params[varID].data) delete[] params[varID].data;
+      if (param.data) delete[] param.data;
     }
 }
 
 static void
 params_add_coord(ParseParamType &parseArg, int coord, int cdiID, size_t size, const std::string &units, const std::string &longname)
 {
-  auto ncoords = parseArg.ncoords;
+  auto ncoords = parseArg.numCoords;
   if (ncoords >= parseArg.maxCoords) cdo_abort("Too many coordinates (limit=%d)", parseArg.maxCoords);
 
-  parseArg.coords[ncoords].needed = false;
-  parseArg.coords[ncoords].coord = coord;
-  parseArg.coords[ncoords].cdiID = cdiID;
-  parseArg.coords[ncoords].size = size;
-  if (units.size()) parseArg.coords[ncoords].units = units;
-  if (longname.size()) parseArg.coords[ncoords].longname = longname;
+  auto &coords = parseArg.coords[ncoords];
+  coords.needed = false;
+  coords.coord = coord;
+  coords.cdiID = cdiID;
+  coords.size = size;
+  if (units.size()) coords.units = units;
+  if (longname.size()) coords.longname = longname;
 
-  parseArg.ncoords++;
-}
-
-int
-params_get_coord_ID(const ParseParamType &parseArg, int coord, int cdiID)
-{
-  auto ncoords = parseArg.ncoords;
-  for (int coordID = 0; coordID < ncoords; ++coordID)
-    {
-      if (parseArg.coords[coordID].coord == coord && parseArg.coords[coordID].cdiID == cdiID) return coordID;
-    }
-
-  cdo_abort("%s: coordinate %c not found!", __func__, coord);
-
-  return -1;
+  parseArg.numCoords++;
 }
 
 static void
 params_add_coordinates(int vlistID, ParseParamType &parseArg)
 {
-  auto ngrids = vlistNgrids(vlistID);
+  auto ngrids = vlistNumGrids(vlistID);
   for (int index = 0; index < ngrids; ++index)
     {
       auto gridID = vlistGrid(vlistID, index);
@@ -214,7 +199,7 @@ params_add_coordinates(int vlistID, ParseParamType &parseArg)
       params_add_coord(parseArg, 'g', gridID, size, "", "grid cell indices");
     }
 
-  auto nzaxis = vlistNzaxis(vlistID);
+  auto nzaxis = vlistNumZaxis(vlistID);
   for (int index = 0; index < nzaxis; ++index)
     {
       auto zaxisID = vlistZaxis(vlistID, index);
@@ -235,12 +220,13 @@ params_add_ts(ParseParamType &parseArg)
   auto varID = parseArg.nparams;
   if (varID >= parseArg.maxparams) cdo_abort("Too many parameter (limit=%d)", parseArg.maxparams);
 
-  params[varID].name = "_timestep_info";
-  params[varID].gridID = parseArg.pointID;
-  params[varID].zaxisID = parseArg.surfaceID;
-  params[varID].steptype = TIME_VARYING;
-  params[varID].ngp = CoordIndex::LEN;
-  params[varID].nlev = 1;
+  auto &param = params[varID];
+  param.name = "_timestep_info";
+  param.gridID = parseArg.pointID;
+  param.zaxisID = parseArg.surfaceID;
+  param.steptype = TIME_VARYING;
+  param.ngp = CoordIndex::LEN;
+  param.nlev = 1;
 
   parseArg.nparams++;
   parseArg.cnparams++;
@@ -251,25 +237,25 @@ params_add_ts(ParseParamType &parseArg)
 static void
 parse_param_init(ParseParamType &parseArg, int vlistID, int pointID, int zonalID, int surfaceID)
 {
-  auto nvars = vlistNvars(vlistID);
-  auto ngrids = vlistNgrids(vlistID);
-  auto nzaxis = vlistNzaxis(vlistID);
+  auto numVars = vlistNvars(vlistID);
+  auto ngrids = vlistNumGrids(vlistID);
+  auto nzaxis = vlistNumZaxis(vlistID);
   auto maxCoords = ngrids * 5 + nzaxis * 3;
 
   parseArg.maxparams = MaxParams;
   parseArg.params.resize(MaxParams);
-  parseArg.nparams = nvars;
-  parseArg.cnparams = nvars;
-  parseArg.nvars1 = nvars;
+  parseArg.nparams = numVars;
+  parseArg.cnparams = numVars;
+  parseArg.numVars1 = numVars;
   parseArg.init = true;
   parseArg.debug = (Options::cdoVerbose != 0);
   parseArg.pointID = pointID;
   parseArg.zonalID = zonalID;
   parseArg.surfaceID = surfaceID;
-  parseArg.needed.resize(nvars);
+  parseArg.needed.resize(numVars);
   parseArg.coords.resize(maxCoords);
   parseArg.maxCoords = maxCoords;
-  parseArg.ncoords = 0;
+  parseArg.numCoords = 0;
 }
 
 static int
@@ -277,7 +263,7 @@ genZonalID(int vlistID)
 {
   int zonalID = -1;
 
-  auto ngrids = vlistNgrids(vlistID);
+  auto ngrids = vlistNumGrids(vlistID);
   for (int index = 0; index < ngrids; ++index)
     {
       auto gridID = vlistGrid(vlistID, index);
@@ -354,8 +340,8 @@ public:
 
   int vartsID;
 
-  int nvars1;
-  int nvars2;
+  int numVars1;
+  int numVars2;
 
   ParseParamType parseArg;
   CdiDateTime vDateTime0{};
@@ -370,7 +356,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
     auto operatorID = cdo_operator_id();
     bool replacesVariables = cdo_operator_f1(operatorID);
@@ -385,13 +371,12 @@ public:
     streamID1 = cdo_open_read(0);
     auto vlistID1 = cdo_stream_inq_vlist(streamID1);
 
-    VarList varList1;
-    varList_init(varList1, vlistID1);
+    VarList varList1(vlistID1);
 
     exprString = exprs_expand(exprString, varList1);
     if (Options::cdoVerbose) cdo_print(exprString);
 
-    nvars1 = vlistNvars(vlistID1);
+    numVars1 = varList1.numVars();
 
     pointID = gridCreate(GRID_GENERIC, 1);
     zonalID = genZonalID(vlistID1);
@@ -400,10 +385,10 @@ public:
     parse_param_init(parseArg, vlistID1, pointID, zonalID, surfaceID);
 
     auto &params = parseArg.params;
-    params_init(parseArg.params, varList1, vlistID1);
+    params_init(parseArg.params, varList1);
 
     // Set all input variables to 'needed' if replacing is switched off
-    for (int varID = 0; varID < nvars1; ++varID) parseArg.needed[varID] = !replacesVariables;
+    for (int varID = 0; varID < numVars1; ++varID) parseArg.needed[varID] = !replacesVariables;
 
     // init function rand()
     std::srand(Options::Random_Seed);
@@ -426,7 +411,7 @@ public:
     parseArg.init = false;
 
     if (Options::cdoVerbose)
-      for (int varID = 0; varID < nvars1; ++varID)
+      for (int varID = 0; varID < numVars1; ++varID)
         if (parseArg.needed[varID]) cdo_print("Needed var: %d %s", varID, params[varID].name);
 
     if (Options::cdoVerbose)
@@ -442,13 +427,13 @@ public:
     if (!replacesVariables)
       {
         int pidx = 0;
-        for (int varID = 0; varID < nvars1; ++varID)
+        for (int varID = 0; varID < numVars1; ++varID)
           {
             params[varID].select = false;
             if (!params[varID].remove)
               {
                 varIDmap[pidx++] = varID;
-                auto nlevels = varList1[varID].nlevels;
+                auto nlevels = varList1.vars[varID].nlevels;
                 // printf("Replace %d nlevs %d\n", varID, nlevels);
                 for (int levID = 0; levID < nlevels; levID++) vlistDefFlag(vlistID1, varID, levID, true);
               }
@@ -460,8 +445,8 @@ public:
     for (int pidx = 0, n = parseArg.nparams; pidx < n; pidx++)
       {
         const auto &param = params[pidx];
-        if (pidx < nvars1 && !param.select) continue;
-        if (pidx >= nvars1)
+        if (pidx < numVars1 && !param.select) continue;
+        if (pidx >= numVars1)
           {
             if (param.type == ParamType::CONST) continue;
             if (param.name[0] == '_') continue;
@@ -490,15 +475,15 @@ public:
 
     if (Options::cdoVerbose)
       {
-        for (int varID = 0; varID < nvars1; ++varID)
+        for (int varID = 0; varID < numVars1; ++varID)
           if (parseArg.needed[varID]) cdo_print("needed: %d %s", varID, parseArg.params[varID].name);
-        cdo_print("vlistNvars(vlistID1)=%d, vlistNvars(vlistID2)=%d", vlistNvars(vlistID1), vlistNvars(vlistID2));
+        cdo_print("numVars1=%d, numVars2=%d", numVars1, numVars2);
       }
 
-    nvars2 = vlistNvars(vlistID2);
-    if (nvars2 == 0) cdo_abort("No output variable found!");
+    numVars2 = vlistNvars(vlistID2);
+    if (numVars2 == 0) cdo_abort("No output variable found!");
 
-    for (int varID = 0; varID < nvars1; ++varID)
+    for (int varID = 0; varID < numVars1; ++varID)
       {
         if (parseArg.needed[varID])
           {
@@ -507,13 +492,13 @@ public:
           }
       }
 
-    for (int varID = parseArg.nvars1, n = parseArg.nparams; varID < n; ++varID)
+    for (int varID = parseArg.numVars1, n = parseArg.nparams; varID < n; ++varID)
       {
         auto nItems = std::max((size_t) 4, params[varID].ngp * params[varID].nlev);
         params[varID].data = new double[nItems];
       }
 
-    for (int i = 0, n = parseArg.ncoords; i < n; ++i)
+    for (int i = 0, n = parseArg.numCoords; i < n; ++i)
       {
         if (parseArg.coords[i].needed)
           {
@@ -579,7 +564,7 @@ public:
           }
       }
 
-    for (int varID = parseArg.nvars1, n = parseArg.nparams; varID < n; ++varID)
+    for (int varID = parseArg.numVars1, n = parseArg.nparams; varID < n; ++varID)
       {
         auto coord = params[varID].coord;
         if (coord)
@@ -624,7 +609,7 @@ public:
   }
 
   void
-  run()
+  run() override
   {
     int tsID = 0;
     while (true)
@@ -643,9 +628,9 @@ public:
 
         cdo_def_timestep(streamID2, tsID);
 
-        // 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)
+        // for (int varID = 0; varID < numVars1; ++varID) printf(">>> %s %d\n", params[varID].name.c_str(), params[varID].isValid);
+        for (int varID = 0; varID < numVars1; ++varID) params[varID].isValid = true;
+        for (int varID = 0; varID < numVars1; ++varID)
           if (tsID == 0 || params[varID].steptype != TIME_CONSTANT) params[varID].numMissVals = 0;
 
         for (int recID = 0; recID < nrecs; ++recID)
@@ -664,10 +649,10 @@ public:
               }
           }
 
-        for (int varID = 0; varID < nvars2; ++varID)
+        for (int varID = 0; varID < numVars2; ++varID)
           {
             auto pidx = varIDmap[varID];
-            if (pidx < nvars1) continue;
+            if (pidx < numVars1) continue;
 
             auto &param = params[pidx];
             param.numMissVals = 0;
@@ -685,7 +670,7 @@ public:
           yylex_destroy(scanner);
         }
 
-        for (int varID = 0; varID < nvars2; ++varID)
+        for (int varID = 0; varID < numVars2; ++varID)
           {
             auto pidx = varIDmap[varID];
 
@@ -710,7 +695,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
@@ -718,8 +703,5 @@ public:
     vlistDestroy(vlistID2);
 
     params_delete(parseArg.params);
-
-    gridDestroy(pointID);
-    if (zonalID != -1) gridDestroy(zonalID);
   }
 };
diff --git a/src/FC.cc b/src/FC.cc
index c9d9725d9bc3c501e0978605415903d209bc349d..fbe8db34c4bfcaadc51514e64397bdeb8f0c2da1 100644
--- a/src/FC.cc
+++ b/src/FC.cc
@@ -36,7 +36,7 @@ static int
 vlistGetFirstReg2DGrid(int vlistID)
 {
   // find first gaussian grid
-  auto ngrids = vlistNgrids(vlistID);
+  auto ngrids = vlistNumGrids(vlistID);
   for (int index = 0; index < ngrids; ++index)
     {
       auto gridID = vlistGrid(vlistID, index);
@@ -53,18 +53,15 @@ public:
   using Process::Process;
   inline static CdoModule module = {
     .name = "FC",
-    .operators = { { "fc2sp"},
-                   { "sp2fc"},
-                   { "fc2gp"},
-                   { "gp2fc"},
-                   { "fourier2grid", 1, 0, nullptr},
-                   { "grid2fourier", 1, 0, nullptr} },
+    .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;
@@ -76,6 +73,7 @@ public:
   CdoStreamID streamID2;
 
   int vlistID1;
+  VarList varList1;
 
   int taxisID1;
   int taxisID2;
@@ -92,7 +90,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
 
     operator_check_argc(0);
@@ -114,6 +112,8 @@ public:
     vlistID1 = cdo_stream_inq_vlist(streamID1);
     auto vlistID2 = vlistDuplicate(vlistID1);
 
+    varList1 = VarList(vlistID1);
+
     taxisID1 = vlistInqTaxis(vlistID1);
     taxisID2 = taxisDuplicate(taxisID1);
     vlistDefTaxis(vlistID2, taxisID2);
@@ -276,19 +276,19 @@ public:
 
     // printf("nfc %d, ntr %d, nlat %zu, nlon %zu\n", nfc, ntr, nlat, nlon);
 
-    auto nvars = vlistNvars(vlistID2);
-    vars = std::vector<bool>(nvars);
-    for (int varID = 0; varID < nvars; ++varID) vars[varID] = gridID1 == vlistInqVarGrid(vlistID1, varID);
+    auto numVars = varList1.numVars();
+    vars = std::vector<bool>(numVars);
+    for (const auto &var : varList1.vars) vars[var.ID] = (gridID1 == var.gridID);
 
     if (gridID1 != -1) vlistChangeGrid(vlistID2, gridID1, gridID2);
     if (operatorID == GRID2FOURIER)
       {
-        for (int varID = 0; varID < nvars; ++varID)
+        for (int varID = 0; varID < numVars; ++varID)
           if (vars[varID]) vlistDefVarDatatype(vlistID2, varID, CDI_DATATYPE_CPX64);
       }
     else if (operatorID == FOURIER2GRID)
       {
-        for (int varID = 0; varID < nvars; ++varID)
+        for (int varID = 0; varID < numVars; ++varID)
           if (vars[varID]) vlistDefVarDatatype(vlistID2, varID, CDI_DATATYPE_FLT32);
       }
 
@@ -309,7 +309,7 @@ public:
   }
 
   void
-  run()
+  run() override
   {
     int tsID = 0;
     while (true)
@@ -331,7 +331,7 @@ public:
                 cdo_read_record(streamID1, array1.data(), &numMissVals);
                 if (numMissVals) cdo_abort("Missing values unsupported for spectral/fourier data!");
 
-                gridID1 = vlistInqVarGrid(vlistID1, varID);
+                gridID1 = varList1.vars[varID].gridID;
                 if (operatorID == FC2SP)
                   four2spec(spTrans, gridID1, array1, gridID2, array2);
                 else if (operatorID == SP2FC)
@@ -365,7 +365,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
diff --git a/src/Filedes.cc b/src/Filedes.cc
index 7fedd603db48426419d963049e7a9222a268f79b..c1909c921f2f8c945eef19dc32e7bf8fe9e749fa 100644
--- a/src/Filedes.cc
+++ b/src/Filedes.cc
@@ -18,64 +18,9 @@
 #include "cdo_options.h"
 #include "process_int.h"
 #include <mpim_grid.h>
-#include "util_string.h"
 
 void cdoPrintZaxis(int zaxisID);
 
-void
-cdoPrintAttributes(FILE *fp, int cdiID, int varID, int nblanks)
-{
-  int natts;
-  cdiInqNatts(cdiID, varID, &natts);
-
-  for (int ia = 0; ia < natts; ++ia)
-    {
-      char attname[CDI_MAX_NAME];
-      int atttype, attlen;
-      cdiInqAtt(cdiID, varID, ia, attname, &atttype, &attlen);
-
-      if (atttype == CDI_DATATYPE_INT8 || atttype == CDI_DATATYPE_UINT8 || atttype == CDI_DATATYPE_INT16
-          || atttype == CDI_DATATYPE_UINT16 || atttype == CDI_DATATYPE_INT32 || atttype == CDI_DATATYPE_UINT32)
-        {
-          std::vector<int> attint(attlen);
-          cdiInqAttInt(cdiID, varID, attname, attlen, attint.data());
-          fprintf(fp, "%*s", nblanks, "");
-          fprintf(fp, "%s = ", attname);
-          for (int i = 0; i < attlen; ++i)
-            {
-              if (i) fprintf(fp, ", ");
-              fprintf(fp, "%d", attint[i]);
-            }
-          fprintf(fp, "\n");
-        }
-      else if (atttype == CDI_DATATYPE_FLT32 || atttype == CDI_DATATYPE_FLT64)
-        {
-          char fltstr[128];
-          std::vector<double> attflt(attlen);
-          cdiInqAttFlt(cdiID, varID, attname, attlen, attflt.data());
-          fprintf(fp, "%*s", nblanks, "");
-          fprintf(fp, "%s = ", attname);
-          for (int i = 0; i < attlen; ++i)
-            {
-              if (i) fprintf(fp, ", ");
-              if (atttype == CDI_DATATYPE_FLT32)
-                fprintf(fp, "%sf", double_to_att_str(Options::CDO_flt_digits, fltstr, sizeof(fltstr), attflt[i]));
-              else
-                fprintf(fp, "%s", double_to_att_str(Options::CDO_dbl_digits, fltstr, sizeof(fltstr), attflt[i]));
-            }
-          fprintf(fp, "\n");
-        }
-      else if (atttype == CDI_DATATYPE_TXT)
-        {
-          std::vector<char> atttxt(attlen + 1);
-          cdiInqAttTxt(cdiID, varID, attname, attlen, atttxt.data());
-          atttxt[attlen] = 0;
-          fprintf(fp, "%*s", nblanks, "");
-          fprintf(fp, "%s = \"%s\"\n", attname, atttxt.data());
-        }
-    }
-}
-
 static void
 printSource(FILE *fp, int vlistID, int varID)
 {
@@ -91,7 +36,7 @@ printSource(FILE *fp, int vlistID, int varID)
 static void
 printVCT(int vlistID, bool lvct)
 {
-  auto nzaxis = vlistNzaxis(vlistID);
+  auto nzaxis = vlistNumZaxis(vlistID);
   for (int index = 0; index < nzaxis; ++index)
     {
       auto zaxisID = vlistZaxis(vlistID, index);
@@ -136,14 +81,15 @@ printVCT(int vlistID, bool lvct)
 static void
 printCodeTable(const VarList &varList)
 {
-  int nvars = varList.size();
+  int nvars = varList.numVars();
   for (int varID = 0; varID < nvars; ++varID)
     {
-      fprintf(stdout, "%4d  %-12s", varList[varID].code, varList[varID].name.c_str());
-      if (varList[varID].longname.size())
+      const auto &var = varList.vars[varID];
+      fprintf(stdout, "%4d  %-12s", var.code, var.name.c_str());
+      if (var.longname.size())
         {
-          fprintf(stdout, "  %s", varList[varID].longname.c_str());
-          if (varList[varID].units.size()) fprintf(stdout, " [%s]", varList[varID].units.c_str());
+          fprintf(stdout, "  %s", var.longname.c_str());
+          if (var.units.size()) fprintf(stdout, " [%s]", var.units.c_str());
         }
       fprintf(stdout, "\n");
     }
@@ -155,7 +101,7 @@ partab(FILE *fp, int vlistID, const VarList &varList, int option)
   int varID, datatype = -1;
   char paramstr[32];
 
-  int nvars = varList.size();
+  int numVars = varList.numVars();
   auto linebreak = (option != 4);
 
   if (option == 2)
@@ -172,12 +118,12 @@ partab(FILE *fp, int vlistID, const VarList &varList, int option)
         }
     }
 
-  if (nvars > 1)
+  if (numVars > 1)
     {
-      datatype = varList[0].datatype;
-      for (varID = 1; varID < nvars; ++varID)
+      datatype = varList.vars[0].dataType;
+      for (varID = 1; varID < numVars; ++varID)
         {
-          if (datatype != varList[varID].datatype)
+          if (datatype != varList.vars[varID].dataType)
             {
               datatype = -1;
               break;
@@ -200,16 +146,16 @@ partab(FILE *fp, int vlistID, const VarList &varList, int option)
         }
     }
 
-  for (varID = 0; varID < nvars; ++varID)
+  for (varID = 0; varID < numVars; ++varID)
     {
-      const auto &var = varList[varID];
+      const auto &var = varList.vars[varID];
 
       fprintf(fp, "&parameter");
       if (linebreak) fprintf(fp, "\n");
 
       auto stdname = cdo::inq_key_string(vlistID, varID, CDI_KEY_STDNAME);
 
-      fprintf(fp, "  name = %s", varList[varID].name.c_str());
+      fprintf(fp, "  name = %s", var.name.c_str());
       if (linebreak) fprintf(fp, "\n");
 
       if (var.param >= 0)
@@ -236,7 +182,7 @@ partab(FILE *fp, int vlistID, const VarList &varList, int option)
 
       if (datatype == -1)
         {
-          auto datatypestr = cdo::datatype_to_cstr(varList[varID].datatype);
+          auto datatypestr = cdo::datatype_to_cstr(var.dataType);
           if (*datatypestr)
             {
               fprintf(fp, "  datatype = %s", datatypestr);
@@ -328,6 +274,7 @@ public:
     .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;
 
@@ -339,22 +286,19 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
-
-    // clang-format off
-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
+    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");
 
     operatorID = cdo_operator_id();
 
@@ -363,15 +307,16 @@ PARTAB2 = module.get_id("partab2");
     streamID = cdo_open_read(0);
     vlistID = cdo_stream_inq_vlist(streamID);
 
-    varList_init(varList, vlistID);
+    varList = VarList(vlistID);
   }
+
   void
-  run()
+  run() override
   {
     if (operatorID == GRIDDES || operatorID == GRIDDES2)
       {
         auto opt = (operatorID == GRIDDES) ? 1 : 0;
-        auto ngrids = vlistNgrids(vlistID);
+        auto ngrids = vlistNumGrids(vlistID);
         for (int index = 0; index < ngrids; ++index)
           {
             printf("#\n# gridID %d\n#\n", index + 1);
@@ -382,7 +327,7 @@ PARTAB2 = module.get_id("partab2");
       }
     else if (operatorID == ZAXISDES)
       {
-        auto nzaxis = vlistNzaxis(vlistID);
+        auto nzaxis = vlistNumZaxis(vlistID);
         for (int index = 0; index < nzaxis; ++index)
           {
             printf("#\n# zaxisID %d\n#\n", index + 1);
@@ -399,8 +344,9 @@ PARTAB2 = module.get_id("partab2");
       }
     else if (operatorID == FILEDES) { filedes(streamID); }
   }
+
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID);
 
diff --git a/src/Fillmiss.cc b/src/Fillmiss.cc
index 0eac20c544104b65a9aa96b7749d4174ede5fb46..b4e6fe1a188f90e6e64eaa16773e45ab56657e0d 100644
--- a/src/Fillmiss.cc
+++ b/src/Fillmiss.cc
@@ -27,9 +27,9 @@
 
 template <typename T, typename CMP_FUNC>
 T
-fillmiss_kernel(int nfill, bool globgrid, long nx, long ny, long i, long j, T missval, MatrixView<T> &matrix1, CMP_FUNC is_EQ)
+fillmiss_kernel(int nfill, bool globgrid, long nx, long ny, long i, long j, T missval, MatrixView<T> &matrix1, CMP_FUNC is_NE)
 {
-  if (!is_EQ(matrix1[j][i], missval)) return matrix1[j][i];
+  if (is_NE(matrix1[j][i], missval)) return matrix1[j][i];
 
   T rval = missval;
   long ir, iu, il, io;
@@ -40,7 +40,7 @@ fillmiss_kernel(int nfill, bool globgrid, long nx, long ny, long i, long j, T mi
   double xr = 0.0, xu = 0.0, xl = 0.0, xo = 0.0;
 
   for (ir = i + 1; ir < nx; ir++)
-    if (!is_EQ(matrix1[j][ir], missval))
+    if (is_NE(matrix1[j][ir], missval))
       {
         kr = ir - i;
         xr = matrix1[j][ir];
@@ -50,7 +50,7 @@ fillmiss_kernel(int nfill, bool globgrid, long nx, long ny, long i, long j, T mi
   if (globgrid && ir == nx)
     {
       for (ir = 0; ir < i; ir++)
-        if (!is_EQ(matrix1[j][ir], missval))
+        if (is_NE(matrix1[j][ir], missval))
           {
             kr = nx + ir - i;
             xr = matrix1[j][ir];
@@ -59,7 +59,7 @@ fillmiss_kernel(int nfill, bool globgrid, long nx, long ny, long i, long j, T mi
     }
 
   for (il = i - 1; il >= 0; il--)
-    if (!is_EQ(matrix1[j][il], missval))
+    if (is_NE(matrix1[j][il], missval))
       {
         kl = i - il;
         xl = matrix1[j][il];
@@ -69,7 +69,7 @@ fillmiss_kernel(int nfill, bool globgrid, long nx, long ny, long i, long j, T mi
   if (globgrid && il == -1)
     {
       for (il = nx - 1; il > i; il--)
-        if (!is_EQ(matrix1[j][il], missval))
+        if (is_NE(matrix1[j][il], missval))
           {
             kl = nx + i - il;
             xl = matrix1[j][il];
@@ -78,7 +78,7 @@ fillmiss_kernel(int nfill, bool globgrid, long nx, long ny, long i, long j, T mi
     }
 
   for (iu = j + 1; iu < ny; iu++)
-    if (!is_EQ(matrix1[iu][i], missval))
+    if (is_NE(matrix1[iu][i], missval))
       {
         ku = iu - j;
         xu = matrix1[iu][i];
@@ -86,7 +86,7 @@ fillmiss_kernel(int nfill, bool globgrid, long nx, long ny, long i, long j, T mi
       }
 
   for (io = j - 1; io >= 0; io--)
-    if (!is_EQ(matrix1[io][i], missval))
+    if (is_NE(matrix1[io][i], missval))
       {
         ko = j - io;
         xo = matrix1[io][i];
@@ -125,7 +125,7 @@ fillmiss_kernel(int nfill, bool globgrid, long nx, long ny, long i, long j, T mi
 
 template <typename T, typename CMP_FUNC>
 void
-fillmiss_x(int gridID, Varray<T> &vIn, Varray<T> &vOut, T missval, int nfill, CMP_FUNC is_EQ)
+fillmiss_x(int gridID, Varray<T> &vIn, Varray<T> &vOut, T missval, int nfill, CMP_FUNC is_NE)
 {
   long nx = gridInqXsize(gridID);
   long ny = gridInqYsize(gridID);
@@ -141,7 +141,7 @@ fillmiss_x(int gridID, Varray<T> &vIn, Varray<T> &vOut, T missval, int nfill, CM
 #pragma omp parallel for default(shared) schedule(static)
 #endif
   for (long j = 0; j < ny; ++j)
-    for (long i = 0; i < nx; ++i) { matrix2[j][i] = fillmiss_kernel(nfill, globgrid, nx, ny, i, j, missval, matrix1, is_EQ); }
+    for (long i = 0; i < nx; ++i) { matrix2[j][i] = fillmiss_kernel(nfill, globgrid, nx, ny, i, j, missval, matrix1, is_NE); }
 }
 
 static void
@@ -152,24 +152,24 @@ fillmiss(Field &field1, Field &field2, int nfill)
   if (std::isnan(field1.missval))
     {
       if (field1.memType == MemType::Float)
-        fillmiss_x(field1.grid, field1.vec_f, field2.vec_f, (float) field1.missval, nfill, dbl_is_equal);
+        fillmiss_x(field1.grid, field1.vec_f, field2.vec_f, (float) field1.missval, nfill, dbl_is_not_equal);
       else
-        fillmiss_x(field1.grid, field1.vec_d, field2.vec_d, field1.missval, nfill, dbl_is_equal);
+        fillmiss_x(field1.grid, field1.vec_d, field2.vec_d, field1.missval, nfill, dbl_is_not_equal);
     }
   else
     {
       if (field1.memType == MemType::Float)
-        fillmiss_x(field1.grid, field1.vec_f, field2.vec_f, (float) field1.missval, nfill, is_equal);
+        fillmiss_x(field1.grid, field1.vec_f, field2.vec_f, (float) field1.missval, nfill, is_not_equal);
       else
-        fillmiss_x(field1.grid, field1.vec_d, field2.vec_d, field1.missval, nfill, is_equal);
+        fillmiss_x(field1.grid, field1.vec_d, field2.vec_d, field1.missval, nfill, is_not_equal);
     }
 }
 
 template <typename T, typename CMP_FUNC>
 T
-fillmiss_one_step_kernel(long nx, long ny, long i, long j, T missval, MatrixView<T> &matrix1, CMP_FUNC is_EQ)
+fillmiss_one_step_kernel(long nx, long ny, long i, long j, T missval, MatrixView<T> &matrix1, CMP_FUNC is_NE)
 {
-  if (!is_EQ(matrix1[j][i], missval)) return matrix1[j][i];
+  if (is_NE(matrix1[j][i], missval)) return matrix1[j][i];
 
   T rval = missval;
   long ir, iu, il, io;
@@ -180,7 +180,7 @@ fillmiss_one_step_kernel(long nx, long ny, long i, long j, T missval, MatrixView
   T xr = 0.0, xu = 0.0, xl = 0.0, xo = 0.0;
 
   for (ir = i + 1; ir < nx; ir++)
-    if (!is_EQ(matrix1[j][ir], missval))
+    if (is_NE(matrix1[j][ir], missval))
       {
         kr = ir - i;
         xr = matrix1[j][ir];
@@ -188,7 +188,7 @@ fillmiss_one_step_kernel(long nx, long ny, long i, long j, T missval, MatrixView
       }
 
   for (il = i - 1; il >= 0; il--)
-    if (!is_EQ(matrix1[j][il], missval))
+    if (is_NE(matrix1[j][il], missval))
       {
         kl = i - il;
         xl = matrix1[j][il];
@@ -196,7 +196,7 @@ fillmiss_one_step_kernel(long nx, long ny, long i, long j, T missval, MatrixView
       }
 
   for (iu = j + 1; iu < ny; iu++)
-    if (!is_EQ(matrix1[iu][i], missval))
+    if (is_NE(matrix1[iu][i], missval))
       {
         ku = iu - j;
         xu = matrix1[iu][i];
@@ -204,7 +204,7 @@ fillmiss_one_step_kernel(long nx, long ny, long i, long j, T missval, MatrixView
       }
 
   for (io = j - 1; io >= 0; io--)
-    if (!is_EQ(matrix1[io][i], missval))
+    if (is_NE(matrix1[io][i], missval))
       {
         ko = j - io;
         xo = matrix1[io][i];
@@ -266,16 +266,16 @@ fillmiss_one_step(Field &field1, Field &field2, int maxfill)
   if (std::isnan(field1.missval))
     {
       if (field1.memType == MemType::Float)
-        fillmiss_one_step_x(field1.grid, field1.vec_f, field2.vec_f, (float) field1.missval, maxfill, dbl_is_equal);
+        fillmiss_one_step_x(field1.grid, field1.vec_f, field2.vec_f, (float) field1.missval, maxfill, dbl_is_not_equal);
       else
-        fillmiss_one_step_x(field1.grid, field1.vec_d, field2.vec_d, field1.missval, maxfill, dbl_is_equal);
+        fillmiss_one_step_x(field1.grid, field1.vec_d, field2.vec_d, field1.missval, maxfill, dbl_is_not_equal);
     }
   else
     {
       if (field1.memType == MemType::Float)
-        fillmiss_one_step_x(field1.grid, field1.vec_f, field2.vec_f, (float) field1.missval, maxfill, is_equal);
+        fillmiss_one_step_x(field1.grid, field1.vec_f, field2.vec_f, (float) field1.missval, maxfill, is_not_equal);
       else
-        fillmiss_one_step_x(field1.grid, field1.vec_d, field2.vec_d, field1.missval, maxfill, is_equal);
+        fillmiss_one_step_x(field1.grid, field1.vec_d, field2.vec_d, field1.missval, maxfill, is_not_equal);
     }
 }
 
@@ -350,7 +350,7 @@ setmisstodis(size_t numMissVals, int gridID, Varray<T> &vIn, Varray<T> &vOut, T
 
   if (Options::cdoVerbose) cdo_print("Point search created: %.2f seconds", timer.elapsed());
 
-  progress::init();
+  cdo::Progress progress;
 
   timer.reset();
 
@@ -362,7 +362,7 @@ setmisstodis(size_t numMissVals, int gridID, Varray<T> &vIn, Varray<T> &vOut, T
   for (size_t i = 0; i < numMissVals; ++i)
     {
       atomicCount++;
-      if (cdo_omp_get_thread_num() == 0) progress::update(0, 1, (double) atomicCount / numMissVals);
+      if (cdo_omp_get_thread_num() == 0) progress.update((double) atomicCount / numMissVals);
 
       auto ompthID = cdo_omp_get_thread_num();
 
@@ -379,8 +379,6 @@ setmisstodis(size_t numMissVals, int gridID, Varray<T> &vIn, Varray<T> &vOut, T
         }
     }
 
-  progress::update(0, 1, 1);
-
   if (Options::cdoVerbose) cdo_print("Point search nearest: %.2f seconds", timer.elapsed());
 
   grid_point_search_delete(gps);
@@ -405,8 +403,8 @@ public:
   using Process::Process;
   inline static CdoModule module = {
     .name = "Fillmiss",
-    .operators = { { "fillmiss", 0, 0, "nfill"},
-                   { "fillmiss2", 0, 0, "nfill"},
+    .operators = { { "fillmiss", 0, 0, "nfill" },
+                   { "fillmiss2", 0, 0, "nfill" },
                    { "setmisstonn", 0, 0, "", SetmissHelp },
                    { "setmisstodis", 0, 0, "numberofneighbors", SetmissHelp } },
     .aliases = {},
@@ -415,6 +413,7 @@ public:
     .constraints = { 1, 1, NoRestriction },
   };
   inline static RegisterEntry<Fillmiss> registration = RegisterEntry<Fillmiss>(module);
+
   int FILLMISS, FILLMISS2, SETMISSTONN, SETMISSTODIS;
   CdoStreamID streamID1;
   CdoStreamID streamID2;
@@ -432,21 +431,20 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
-
-    // clang-format off
-FILLMISS = module.get_id("fillmiss");
-FILLMISS2 = module.get_id("fillmiss2");
-SETMISSTONN = module.get_id("setmisstonn");
-SETMISSTODIS = module.get_id("setmisstodis");
+    FILLMISS = module.get_id("fillmiss");
+    FILLMISS2 = module.get_id("fillmiss2");
+    SETMISSTONN = module.get_id("setmisstonn");
+    SETMISSTODIS = module.get_id("setmisstodis");
 
     operatorID = cdo_operator_id();
 
-    if      (operatorID == FILLMISS)        fill_method = &fillmiss;
-    else if (operatorID == FILLMISS2) fill_method = &fillmiss_one_step;
-    else if (operatorID == SETMISSTONN)     fill_method = &setmisstodis;
-    else if (operatorID == SETMISSTODIS)    fill_method = &setmisstodis;
+    // clang-format off
+    if      (operatorID == FILLMISS)      fill_method = &fillmiss;
+    else if (operatorID == FILLMISS2)     fill_method = &fillmiss_one_step;
+    else if (operatorID == SETMISSTONN)   fill_method = &setmisstodis;
+    else if (operatorID == SETMISSTODIS)  fill_method = &setmisstodis;
     // clang-format on
 
     nfill = (operatorID == SETMISSTODIS) ? 4 : 1;
@@ -473,11 +471,11 @@ SETMISSTODIS = module.get_id("setmisstodis");
     streamID2 = cdo_open_write(1);
     cdo_def_vlist(streamID2, vlistID2);
 
-    varList_init(varList1, vlistID1);
+    varList1 = VarList(vlistID1);
   }
 
   void
-  run()
+  run() override
   {
     int tsID = 0;
     while (true)
@@ -493,19 +491,20 @@ SETMISSTODIS = module.get_id("setmisstodis");
           {
             int varID, levelID;
             cdo_inq_record(streamID1, &varID, &levelID);
-            field1.init(varList1[varID]);
+            const auto &var1 = varList1.vars[varID];
+            field1.init(var1);
             cdo_read_record(streamID1, field1);
 
             cdo_def_record(streamID2, varID, levelID);
 
-            if (field1.numMissVals == 0 || field1.numMissVals == varList1[varID].gridsize) { cdo_write_record(streamID2, field1); }
+            if (field1.numMissVals == 0 || field1.numMissVals == var1.gridsize) { cdo_write_record(streamID2, field1); }
             else
               {
-                auto gridtype = gridInqType(varList1[varID].gridID);
+                auto gridtype = var1.gridType;
                 if ((operatorID == FILLMISS || operatorID == FILLMISS2) && (gridtype == GRID_GME || gridtype == GRID_UNSTRUCTURED))
                   cdo_abort("%s data unsupported!", gridNamePtr(gridtype));
 
-                field2.init(varList1[varID]);
+                field2.init(var1);
 
                 fill_method(field1, field2, nfill);
 
@@ -520,7 +519,7 @@ SETMISSTODIS = module.get_id("setmisstodis");
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
diff --git a/src/Filter.cc b/src/Filter.cc
index f0510deb05475dc3633c86f94e9701bac37783a6..95eed8f847c48969cc8c016bfcd438ab76aa14c4 100644
--- a/src/Filter.cc
+++ b/src/Filter.cc
@@ -27,7 +27,6 @@ static std::mutex fftwMutex;
 #include "cdi.h"
 #include "julian_date.h"
 #include "process_int.h"
-#include "cdo_vlist.h"
 #include "param_conversion.h"
 #include "cdo_fft.h"
 #include "cdo_fftw3.h"
@@ -161,17 +160,15 @@ public:
   int vlistID1;
   int vlistID2;
 
-  VarList varList;
+  VarList varList1;
 
   bool useFFTW = false;
   int operfunc;
   int calendar;
 
-  int nvars;
-
 public:
   void
-  init()
+  init() override
   {
     tunits = { "second", "minute", "hour", "day", "month", "year" };
     iunits = { 31536000, 525600, 8760, 365, 12, 1 };
@@ -202,15 +199,13 @@ public:
 
     calendar = taxisInqCalendar(taxisID1);
 
-    varList_init(varList, vlistID1);
-
-    nvars = vlistNvars(vlistID1);
+    varList1 = VarList(vlistID1);
   }
 
   void
-  run()
+  run() override
   {
-    FieldVector3D vars;
+    FieldVector3D varsData;
 
     int tsID = 0;
     while (true)
@@ -219,18 +214,18 @@ public:
         if (nrecs == 0) break;
 
         constexpr size_t NALLOC_INC = 1024;
-        if ((size_t) tsID >= vars.size()) vars.resize(vars.size() + NALLOC_INC);
+        if ((size_t) tsID >= varsData.size()) varsData.resize(varsData.size() + NALLOC_INC);
 
         dtlist.taxis_inq_timestep(taxisID1, tsID);
 
-        fields_from_vlist(vlistID1, vars[tsID]);
+        field2D_init(varsData[tsID], varList1);
 
         for (int recID = 0; recID < nrecs; ++recID)
           {
             int varID, levelID;
             cdo_inq_record(streamID1, &varID, &levelID);
-            auto &field = vars[tsID][varID][levelID];
-            field.init(varList[varID]);
+            auto &field = varsData[tsID][varID][levelID];
+            field.init(varList1.vars[varID]);
             cdo_read_record(streamID1, field);
             if (field.numMissVals) cdo_abort("Missing value support for operators in module Filter not added yet!");
           }
@@ -278,8 +273,8 @@ 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;
+    auto numArrays = vars_numArrays(varsData);
+    auto allocatedMem = vars_allocatedMem(varsData) * nts;
     if (Options::cdoVerbose)
       cdo_print("Allocate %zu array%s over %zu steps: size=%zu Bytes", numArrays, numArrays > 1 ? "s" : "", nts, allocatedMem);
 
@@ -341,9 +336,10 @@ public:
     std::vector<int> fmasc(nts, 0);
     create_fmasc(nts, fdata, fmin, fmax, fmasc);
 
-    for (int varID = 0; varID < nvars; ++varID)
+    auto numVars = varList1.numVars();
+    for (int varID = 0; varID < numVars; ++varID)
       {
-        const auto &var = varList[varID];
+        const auto &var = varList1.vars[varID];
         for (int levelID = 0; levelID < var.nlevels; ++levelID)
           {
             if (useFFTW)
@@ -360,22 +356,22 @@ public:
                     if (var.memType == MemType::Float)
                       for (int t = 0; t < nts; ++t)
                         {
-                          fm.in_fft[t][0] = vars[t][varID][levelID].vec_f[i];
+                          fm.in_fft[t][0] = varsData[t][varID][levelID].vec_f[i];
                           fm.in_fft[t][1] = 0.0;
                         }
                     else
                       for (int t = 0; t < nts; ++t)
                         {
-                          fm.in_fft[t][0] = vars[t][varID][levelID].vec_d[i];
+                          fm.in_fft[t][0] = varsData[t][varID][levelID].vec_d[i];
                           fm.in_fft[t][1] = 0.0;
                         }
 
                     filter_fftw(nts, fmasc, fm.out_fft, &fm.p_T2S, &fm.p_S2T);
 
                     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;
+                      for (int t = 0; t < nts; ++t) varsData[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;
+                      for (int t = 0; t < nts; ++t) varsData[t][varID][levelID].vec_d[i] = fm.in_fft[t][0] / nts;
                   }
 #endif
               }
@@ -390,18 +386,18 @@ public:
                     auto &fm = fourierMemory[ompthID];
 
                     if (var.memType == MemType::Float)
-                      for (int t = 0; t < nts; ++t) fm.real[t] = vars[t][varID][levelID].vec_f[i];
+                      for (int t = 0; t < nts; ++t) fm.real[t] = varsData[t][varID][levelID].vec_f[i];
                     else
-                      for (int t = 0; t < nts; ++t) fm.real[t] = vars[t][varID][levelID].vec_d[i];
+                      for (int t = 0; t < nts; ++t) fm.real[t] = varsData[t][varID][levelID].vec_d[i];
 
                     ranges::fill(fm.imag, 0.0);
 
                     filter_intrinsic(nts, fmasc, fm.real.data(), fm.imag.data());
 
                     if (var.memType == MemType::Float)
-                      for (int t = 0; t < nts; ++t) vars[t][varID][levelID].vec_f[i] = fm.real[t];
+                      for (int t = 0; t < nts; ++t) varsData[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];
+                      for (int t = 0; t < nts; ++t) varsData[t][varID][levelID].vec_d[i] = fm.real[t];
                   }
               }
           }
@@ -430,12 +426,12 @@ public:
         dtlist.taxis_def_timestep(taxisID2, tsID);
         cdo_def_timestep(streamID2, tsID);
 
-        for (int varID = 0; varID < nvars; ++varID)
+        for (int varID = 0; varID < numVars; ++varID)
           {
-            const auto &var = varList[varID];
+            const auto &var = varList1.vars[varID];
             for (int levelID = 0; levelID < var.nlevels; ++levelID)
               {
-                auto &field = vars[tsID][varID][levelID];
+                auto &field = varsData[tsID][varID][levelID];
                 if (field.hasData())
                   {
                     cdo_def_record(streamID2, varID, levelID);
@@ -447,7 +443,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
diff --git a/src/Fldrms.cc b/src/Fldrms.cc
index 1a55061754705827b6b8751347318921f426d598..79658c00399de68fbc28ec6196385f07a3dc50fc 100644
--- a/src/Fldrms.cc
+++ b/src/Fldrms.cc
@@ -22,13 +22,14 @@ public:
   using Process::Process;
   inline static CdoModule module = {
     .name = "Fldrms",
-    .operators = { { "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;
@@ -38,16 +39,16 @@ public:
   int taxisID1;
   int taxisID3;
 
-  VarList varList1, varList2;
+  VarList varList1;
+  VarList varList2;
   Field field1, field2, field3;
 
   bool needWeights;
 
 public:
   void
-  init()
+  init() override
   {
-
     operator_check_argc(0);
 
     needWeights = true;
@@ -70,7 +71,7 @@ public:
     gridDefXvals(gridID3, &slon);
     gridDefYvals(gridID3, &slat);
 
-    auto ngrids = vlistNgrids(vlistID1);
+    auto ngrids = vlistNumGrids(vlistID1);
     int ndiffgrids = 0;
     for (int index = 1; index < ngrids; ++index)
       if (vlistGrid(vlistID1, 0) != vlistGrid(vlistID1, index)) ndiffgrids++;
@@ -92,8 +93,8 @@ public:
 
     auto gridsizemax = vlistGridsizeMax(vlistID1);
 
-    varList_init(varList1, vlistID1);
-    varList_init(varList2, vlistID2);
+    varList1 = VarList(vlistID1);
+    varList2 = VarList(vlistID2);
 
     field1.resize(gridsizemax);
     if (needWeights) field1.weightv.resize(gridsizemax);
@@ -103,8 +104,9 @@ public:
     field3.resize(1);
     field3.grid = gridID3;
   }
+
   void
-  run()
+  run() override
   {
     int tsID = 0;
     while (true)
@@ -127,8 +129,10 @@ public:
             cdo_inq_record(streamID2, &varID, &levelID);
             cdo_read_record(streamID2, field2.vec_d.data(), &field2.numMissVals);
 
-            field1.grid = varList1[varID].gridID;
-            field2.grid = varList2[varID].gridID;
+            const auto &var1 = varList1.vars[varID];
+            const auto &var2 = varList2.vars[varID];
+            field1.grid = var1.gridID;
+            field2.grid = var2.gridID;
 
             if (needWeights && field1.grid != lastgrid)
               {
@@ -139,13 +143,13 @@ public:
                     auto wstatus = gridcell_weights(field1.grid, field1.weightv);
                     if (wstatus != 0 && tsID == 0 && levelID == 0)
                       cdo_warning("Grid cell bounds not available, using constant grid cell area weights for variable %s!",
-                                  varList1[varID].name);
+                                  var1.name);
                   }
               }
 
-            field1.missval = varList1[varID].missval;
-            field2.missval = varList1[varID].missval;
-            field3.missval = varList1[varID].missval;
+            field1.missval = var1.missval;
+            field2.missval = var1.missval;
+            field3.missval = var1.missval;
 
             field_rms(field1, field2, field3);
 
@@ -156,8 +160,9 @@ public:
         tsID++;
       }
   }
+
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID3);
     cdo_stream_close(streamID2);
diff --git a/src/Fldstat.cc b/src/Fldstat.cc
index c58068d282bbeb4b411bbca4d8c333fe92bf7d49..29c89c71ee11893dd129ac0308a97450e0090ba5 100644
--- a/src/Fldstat.cc
+++ b/src/Fldstat.cc
@@ -91,7 +91,7 @@ field_mul_weights(Varray<T> &v1, const Varray<double> &v2, size_t numMissVals, T
   if (numMissVals)
     {
       for (size_t i = 0; i < gridSize; ++i)
-        if (!dbl_is_equal(v1[i], missval)) v1[i] *= v2[i];
+        if (dbl_is_not_equal(v1[i], missval)) v1[i] *= v2[i];
     }
   else
     {
@@ -262,16 +262,14 @@ public:
 
   int operfunc;
   int ngrids;
-  int ntsteps;
 
   double pn = 0.0;
 
-  Field field;
   VarList varList1;
 
 public:
   void
-  init()
+  init() override
   {
     FLDINT = module.get_id("fldint");
 
@@ -303,7 +301,7 @@ public:
     taxisID2 = taxisDuplicate(taxisID1);
     vlistDefTaxis(vlistID2, taxisID2);
 
-    ngrids = vlistNgrids(vlistID1);
+    ngrids = vlistNumGrids(vlistID1);
 
     for (int index = 0; index < ngrids; ++index)
       {
@@ -316,15 +314,15 @@ public:
 
     cdo_def_vlist(streamID2, vlistID2);
 
-    varList_init(varList1, vlistID1);
-
-    ntsteps = vlistNtsteps(vlistID1);
+    varList1 = VarList(vlistID1);
   }
 
   void
-  run()
+  run() override
   {
-    if (!Options::cdoVerbose) progress::init();
+    Field field;
+    auto numSteps = varList1.numSteps();
+    cdo::Progress progress;
 
     int lastgrid = -1;
     int tsID = 0;
@@ -340,18 +338,20 @@ public:
 
         for (int recID = 0; recID < nrecs; ++recID)
           {
-            double fstatus = (ntsteps > 1) ? ((tsID + (recID + 1.0) / nrecs) / ntsteps) : 1.0;
-            if (!Options::cdoVerbose && tsID > 0) progress::update(0, 1, fstatus);
+            auto fstatus = ((tsID + (recID + 1.0) / nrecs) / numSteps);
+            if (numSteps > 1) progress.update(fstatus);
 
             int varID, levelID;
             cdo_inq_record(streamID1, &varID, &levelID);
-            auto &var = varList1[varID];
+            auto &var = varList1.vars[varID];
             field.init(var);
             cdo_read_record(streamID1, field);
 
             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 (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);
 
@@ -367,12 +367,10 @@ public:
 
         tsID++;
       }
-
-    if (!Options::cdoVerbose) progress::update(0, 1, 1);
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
diff --git a/src/Fldstat2.cc b/src/Fldstat2.cc
index bb643b758dea4ac811e89b47fafa07b6e2797f5a..13649cd6e83335e9f5688f09d5b26460c7c5cc09 100644
--- a/src/Fldstat2.cc
+++ b/src/Fldstat2.cc
@@ -16,15 +16,14 @@
 
 #include "arithmetic.h"
 #include "process_int.h"
-#include "cdo_vlist.h"
 #include <mpim_grid.h>
 #include "field_functions.h"
 
 // routine corr copied from PINGO
 // correclation in space
 auto correlation_kernel = [](auto v1, auto mv1, auto v2, auto mv2, auto w, auto &sum0, auto &sum1, auto &sum00, auto &sum01,
-                             auto &sum11, auto &wsum0, auto is_EQ) {
-  if (!is_EQ(w, mv1) && !is_EQ(v1, mv1) && !is_EQ(v2, mv2))
+                             auto &sum11, auto &wsum0, auto is_NE) {
+  if (is_NE(w, mv1) && is_NE(v1, mv1) && is_NE(v2, mv2))
     {
       sum0 += w * v1;
       sum1 += w * v2;
@@ -45,12 +44,12 @@ correlation(const Varray<T1> &v1, const Varray<T2> &v2, const Varray<double> &we
   if (std::isnan(missval1) || std::isnan(missval2))
     {
       for (size_t i = 0; i < gridsize; ++i)
-        correlation_kernel(v1[i], missval1, v2[i], missval2, weight[i], sum0, sum1, sum00, sum01, sum11, wsum0, dbl_is_equal);
+        correlation_kernel(v1[i], missval1, v2[i], missval2, weight[i], sum0, sum1, sum00, sum01, sum11, wsum0, dbl_is_not_equal);
     }
   else
     {
       for (size_t i = 0; i < gridsize; ++i)
-        correlation_kernel(v1[i], missval1, v2[i], missval2, weight[i], sum0, sum1, sum00, sum01, sum11, wsum0, is_equal);
+        correlation_kernel(v1[i], missval1, v2[i], missval2, weight[i], sum0, sum1, sum00, sum01, sum11, wsum0, is_not_equal);
     }
 
   auto is_EQ = dbl_is_equal;
@@ -76,8 +75,8 @@ correlation(const Field &field1, const Field &field2, const Varray<double> &weig
 
 // covariance in space
 auto covariance_kernel
-    = [](auto v1, auto mv1, auto v2, auto mv2, auto w, auto &sum0, auto &sum1, auto &sum01, auto &wsum0, auto is_EQ) {
-        if (!is_EQ(w, mv1) && !is_EQ(v1, mv1) && !is_EQ(v2, mv2))
+    = [](auto v1, auto mv1, auto v2, auto mv2, auto w, auto &sum0, auto &sum1, auto &sum01, auto &wsum0, auto is_NE) {
+        if (is_NE(w, mv1) && is_NE(v1, mv1) && is_NE(v2, mv2))
           {
             sum0 += w * v1;
             sum1 += w * v2;
@@ -96,12 +95,12 @@ covariance(const Varray<T1> &v1, const Varray<T2> &v2, const Varray<double> &wei
   if (std::isnan(missval1) || std::isnan(missval2))
     {
       for (size_t i = 0; i < gridsize; ++i)
-        covariance_kernel(v1[i], missval1, v2[i], missval2, weight[i], sum0, sum1, sum01, wsum0, dbl_is_equal);
+        covariance_kernel(v1[i], missval1, v2[i], missval2, weight[i], sum0, sum1, sum01, wsum0, dbl_is_not_equal);
     }
   else
     {
       for (size_t i = 0; i < gridsize; ++i)
-        covariance_kernel(v1[i], missval1, v2[i], missval2, weight[i], sum0, sum1, sum01, wsum0, is_equal);
+        covariance_kernel(v1[i], missval1, v2[i], missval2, weight[i], sum0, sum1, sum01, wsum0, is_not_equal);
     }
 
   auto out = is_not_equal(wsum0, 0.0) ? (sum01 * wsum0 - sum0 * sum1) / (wsum0 * wsum0) : missval1;
@@ -147,19 +146,15 @@ public:
   bool wstatus = false;
   bool needWeights = true;
 
-  VarList varList1, varList2;
-  Field field1, field2;
+  VarList varList1;
+  VarList varList2;
 
   Varray<double> weight;
 
 public:
   void
-  init()
+  init() override
   {
-
-    // clang-format off
-    // clang-format on
-
     auto operatorID = cdo_operator_id();
     operfunc = cdo_operator_f1(operatorID);
 
@@ -170,15 +165,14 @@ public:
     auto vlistID2 = cdo_stream_inq_vlist(streamID2);
     auto vlistID3 = vlistDuplicate(vlistID1);
 
-    vlist_compare(vlistID1, vlistID2, CmpVlist::All);
+    varList1 = VarList(vlistID1);
+    varList2 = VarList(vlistID2);
+    varList_compare(varList1, varList2);
 
     taxisID1 = vlistInqTaxis(vlistID1);
     taxisID3 = taxisDuplicate(taxisID1);
     vlistDefTaxis(vlistID3, taxisID3);
 
-    varList_init(varList1, vlistID1);
-    varList_init(varList2, vlistID2);
-
     double slon = 0.0, slat = 0.0;
     auto gridID3 = gridCreate(GRID_LONLAT, 1);
     gridDefXsize(gridID3, 1);
@@ -186,7 +180,7 @@ public:
     gridDefXvals(gridID3, &slon);
     gridDefYvals(gridID3, &slat);
 
-    auto ngrids = vlistNgrids(vlistID1);
+    auto ngrids = vlistNumGrids(vlistID1);
 
     for (int index = 0; index < ngrids; ++index) vlistChangeGridIndex(vlistID3, index, gridID3);
 
@@ -198,8 +192,10 @@ public:
   }
 
   void
-  run()
+  run() override
   {
+    Field field1, field2;
+
     int lastgridID = -1;
     int tsID = 0;
     while (true)
@@ -221,20 +217,22 @@ public:
           {
             int varID, levelID;
             cdo_inq_record(streamID1, &varID, &levelID);
-            field1.init(varList1[varID]);
+            const auto &var1 = varList1.vars[varID];
+            const auto &var2 = varList1.vars[varID];
+            field1.init(var1);
             cdo_inq_record(streamID2, &varID, &levelID);
-            field2.init(varList2[varID]);
+            field2.init(var2);
             cdo_read_record(streamID1, field1);
             cdo_read_record(streamID2, field2);
 
-            auto gridID = varList1[varID].gridID;
+            auto gridID = var1.gridID;
             if (needWeights && gridID != lastgridID)
               {
                 lastgridID = gridID;
                 wstatus = (gridcell_weights(gridID, weight) != 0);
               }
             if (wstatus && tsID == 0 && levelID == 0)
-              cdo_warning("Using constant grid cell area weights for variable %s!", varList1[varID].name);
+              cdo_warning("Using constant grid cell area weights for variable %s!", var1.name);
 
             double sglval = 0.0;
             if (operfunc == FieldFunc_Cor)
@@ -242,7 +240,7 @@ public:
             else if (operfunc == FieldFunc_Covar)
               sglval = covariance(field1, field2, weight);
 
-            auto numMissVals3 = DBL_IS_EQUAL(sglval, varList1[varID].missval) ? 1 : 0;
+            auto numMissVals3 = DBL_IS_EQUAL(sglval, var1.missval) ? 1 : 0;
 
             cdo_def_record(streamID3, varID, levelID);
             cdo_write_record(streamID3, &sglval, numMissVals3);
@@ -253,7 +251,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID3);
     cdo_stream_close(streamID2);
diff --git a/src/Fourier.cc b/src/Fourier.cc
index be138003cda37a853633fe6a922c0a60b740e20d..3cd4b6e36c87a1f288deaea85e7d0c91a5c1500c 100644
--- a/src/Fourier.cc
+++ b/src/Fourier.cc
@@ -18,7 +18,6 @@ static std::mutex fftwMutex;
 #include <cdi.h>
 
 #include "process_int.h"
-#include "cdo_vlist.h"
 #include "param_conversion.h"
 #include "cdo_fft.h"
 #include "cdo_options.h"
@@ -159,16 +158,16 @@ public:
   int vlistID1;
 
   bool use_fftw = false;
-  int nvars;
+  int numVars;
   int sign;
 
   VarList varList;
-  FieldVector3D vars;
+  FieldVector3D varsData;
   std::vector<CdiDateTime> vDateTimes;
 
 public:
   void
-  init()
+  init() override
   {
     if (Options::Use_FFTW)
       {
@@ -197,13 +196,13 @@ public:
     streamID2 = cdo_open_write(1);
     cdo_def_vlist(streamID2, vlistID2);
 
-    varList_init(varList, vlistID1);
+    varList = VarList(vlistID1);
 
-    nvars = vlistNvars(vlistID1);
+    numVars = vlistNvars(vlistID1);
   }
 
   void
-  run()
+  run() override
   {
     int tsID = 0;
     while (true)
@@ -216,22 +215,22 @@ public:
             constexpr size_t NALLOC_INC = 1024;
             nalloc += NALLOC_INC;
             vDateTimes.resize(nalloc);
-            vars.resize(nalloc);
+            varsData.resize(nalloc);
           }
 
         vDateTimes[tsID] = taxisInqVdatetime(taxisID1);
 
-        fields_from_vlist(vlistID1, vars[tsID]);
+        field2D_init(varsData[tsID], varList);
 
         for (int recID = 0; recID < nrecs; ++recID)
           {
             int varID, levelID;
             cdo_inq_record(streamID1, &varID, &levelID);
-            auto gridsize = varList[varID].gridsize;
-            vars[tsID][varID][levelID].resize(2 * gridsize);
+            auto gridsize = varList.vars[varID].gridsize;
+            varsData[tsID][varID][levelID].resize(2 * gridsize);
             size_t numMissVals;
-            cdo_read_record(streamID1, vars[tsID][varID][levelID].vec_d.data(), &numMissVals);
-            vars[tsID][varID][levelID].numMissVals = numMissVals;
+            cdo_read_record(streamID1, varsData[tsID][varID][levelID].vec_d.data(), &numMissVals);
+            varsData[tsID][varID][levelID].numMissVals = numMissVals;
           }
 
         tsID++;
@@ -266,17 +265,18 @@ public:
           }
       }
 
-    for (int varID = 0; varID < nvars; ++varID)
+    for (int varID = 0; varID < numVars; ++varID)
       {
-        auto missval = varList[varID].missval;
-        auto gridsize = varList[varID].gridsize;
-        auto nlevels = varList[varID].nlevels;
+        const auto &var = varList.vars[varID];
+        auto missval = var.missval;
+        auto gridsize = var.gridsize;
+        auto nlevels = var.nlevels;
         for (int levelID = 0; levelID < nlevels; ++levelID)
           {
             if (use_fftw)
-              fourier_fftw(sign, varID, levelID, nts, gridsize, missval, vars, fourierMemory);
+              fourier_fftw(sign, varID, levelID, nts, gridsize, missval, varsData, fourierMemory);
             else
-              fourier_intrinsic(sign, varID, levelID, nts, gridsize, missval, vars, fourierMemory);
+              fourier_intrinsic(sign, varID, levelID, nts, gridsize, missval, varsData, fourierMemory);
           }
       }
 
@@ -299,16 +299,16 @@ public:
         taxisDefVdatetime(taxisID2, vDateTimes[tsID]);
         cdo_def_timestep(streamID2, tsID);
 
-        for (int varID = 0; varID < nvars; ++varID)
+        for (int varID = 0; varID < numVars; ++varID)
           {
-            auto nlevels = varList[varID].nlevels;
+            auto nlevels = varList.vars[varID].nlevels;
             for (int levelID = 0; levelID < nlevels; ++levelID)
               {
-                if (!vars[tsID][varID][levelID].empty())
+                if (!varsData[tsID][varID][levelID].empty())
                   {
-                    auto numMissVals = vars[tsID][varID][levelID].numMissVals;
+                    auto numMissVals = varsData[tsID][varID][levelID].numMissVals;
                     cdo_def_record(streamID2, varID, levelID);
-                    cdo_write_record(streamID2, vars[tsID][varID][levelID].vec_d.data(), numMissVals);
+                    cdo_write_record(streamID2, varsData[tsID][varID][levelID].vec_d.data(), numMissVals);
                   }
               }
           }
@@ -316,7 +316,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
diff --git a/src/Gengrid.cc b/src/Gengrid.cc
index a71350c173f216b40cb2dc504ab53e92b4a8ecd8..9ddfed531a2884a1dca917e2bcc6cc5f3a01f061 100644
--- a/src/Gengrid.cc
+++ b/src/Gengrid.cc
@@ -40,7 +40,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
 
     operator_check_argc(0);
@@ -124,7 +124,7 @@ public:
     cdo_def_vlist(streamID3, vlistID3);
   }
   void
-  run()
+  run() override
   {
     int tsID = 0;
     cdo_def_timestep(streamID3, tsID);
@@ -135,7 +135,7 @@ public:
     cdo_write_record(streamID3, array3.data(), gridsize);
   }
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID3);
   }
diff --git a/src/Getgridcell.cc b/src/Getgridcell.cc
index bc5f591a7db9b573588461822b6aefcbca6ec928..a00f8bccaa34d3dd10e2bd3e625bdcad0c333295 100644
--- a/src/Getgridcell.cc
+++ b/src/Getgridcell.cc
@@ -138,7 +138,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
     gridPoint = get_parameter();
 
@@ -151,14 +151,14 @@ public:
 
     auto vlistID1 = cdo_stream_inq_vlist(streamID1);
 
-    auto ngrids = vlistNgrids(vlistID1);
+    auto ngrids = vlistNumGrids(vlistID1);
     if (ngrids != 1) cdo_abort("Too many different grids!");
 
     gridID1 = vlistGrid(vlistID1, 0);
   }
 
   void
-  run()
+  run() override
   {
     int64_t cellIdx = -1;
     if (is_healpix_grid(gridID1))
@@ -172,7 +172,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID1);
   }
diff --git a/src/Gradsdes.cc b/src/Gradsdes.cc
index a0836343dc211526be4afc301db1a2ad366be20c..e3ebe936816b37681764ee60903f1c98ad6f2e64 100644
--- a/src/Gradsdes.cc
+++ b/src/Gradsdes.cc
@@ -551,7 +551,7 @@ ctl_zdef(FILE *gdp, int vlistID, bool *zrev)
   double levinc = 0;
 
   *zrev = false;
-  int nzaxis = vlistNzaxis(vlistID);
+  int nzaxis = vlistNumZaxis(vlistID);
 
   int nlevmax = 0;
   for (int index = 0; index < nzaxis; ++index)
@@ -663,12 +663,12 @@ ctl_vars(FILE *gdp, int filetype, int vlistID, const VarList &varList, int nvars
 
   fprintf(gdp, "VARS  %d\n", nvarsout);
 
-  int nvars = varList.size();
-  for (int varID = 0; varID < nvars; ++varID)
+  int numVars = varList.numVars();
+  for (int varID = 0; varID < numVars; ++varID)
     {
       if (vars[varID] == true)
         {
-          const auto &var = varList[varID];
+          const auto &var = varList.vars[varID];
           int zaxisID = var.zaxisID;
           int ltype = 0;
           cdiInqKeyInt(zaxisID, CDI_GLOBAL, CDI_KEY_TYPEOFFIRSTFIXEDSURFACE, &ltype);
@@ -1097,7 +1097,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
     GRADSDES = module.get_id("gradsdes");
     DUMPMAP = module.get_id("dumpmap");
@@ -1140,7 +1140,7 @@ public:
 
     auto nvars = vlistNvars(vlistID);
     auto ntsteps = vlistNtsteps(vlistID);
-    auto ngrids = vlistNgrids(vlistID);
+    auto ngrids = vlistNumGrids(vlistID);
 
     filetype = cdo_inq_filetype(streamID);
     auto byteorder = cdo_inq_byteorder(streamID);
@@ -1175,19 +1175,19 @@ public:
 
     if (index == ngrids) cdo_abort("No Lon/Lat, Gaussian or Lambert grid found (%s data unsupported)!", gridNamePtr(gridtype));
 
-    varList_init(varList, vlistID);
+    varList = VarList(vlistID);
 
     // select all variables with used gridID
     vars = std::vector<int>(nvars);
     recoffset = std::vector<int>(nvars);
     for (int varID = 0; varID < nvars; ++varID)
       {
-        const auto &var = varList[varID];
+        const auto &var = varList.vars[varID];
         if (var.gridID == gridID)
           {
             if (filetype == CDI_FILETYPE_SRV || filetype == CDI_FILETYPE_EXT || filetype == CDI_FILETYPE_IEG)
               {
-                prec = var.datatype;
+                prec = var.dataType;
                 if (prec == CDI_DATATYPE_FLT64) flt64 = true;
               }
             vars[varID] = true;
@@ -1308,7 +1308,7 @@ public:
   }
 
   void
-  run()
+  run() override
   {
     if (operatorID == DUMPMAP) return;
 
@@ -1357,7 +1357,7 @@ public:
     ctl_options(gdp, yrev, zrev, sequential, bigendian, littleendian, flt64, cal365day);
 
     // UNDEF
-    ctl_undef(gdp, varList[0].missval);
+    ctl_undef(gdp, varList.vars[0].missval);
 
     // VARS
     ctl_vars(gdp, filetype, vlistID, varList, nvarsout, vars.data());
@@ -1377,7 +1377,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     if (operatorID == DUMPMAP) return;
 
diff --git a/src/Gridboxstat.cc b/src/Gridboxstat.cc
index 163c26bdc039c78be98d278530e9624fcd8cd036..66dd86739dd50003d5b7a62dda9145704deeea84 100644
--- a/src/Gridboxstat.cc
+++ b/src/Gridboxstat.cc
@@ -26,7 +26,6 @@
 #include "param_conversion.h"
 #include <mpim_grid.h>
 #include "cdo_options.h"
-#include "progress.h"
 #include "cimdOmp.h"
 #include "field_functions.h"
 
@@ -315,6 +314,7 @@ public:
     .constraints = { 1, 1, NoRestriction },
   };
   inline static RegisterEntry<Gridboxstat> registration = RegisterEntry<Gridboxstat>(module);
+
   int lastgrid = -1;
   bool wstatus = false;
 
@@ -327,7 +327,8 @@ public:
   int taxisID2;
 
   Field field1, field2;
-  VarList varList1, varList2;
+  VarList varList1;
+  VarList varList2;
 
   int xinc;
   int yinc;
@@ -336,9 +337,8 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
-
     operator_input_arg("xinc, yinc");
     operator_check_argc(2);
     xinc = parameter_to_int(cdo_operator_argv(0));
@@ -361,7 +361,7 @@ public:
     taxisID2 = taxisDuplicate(taxisID1);
     vlistDefTaxis(vlistID2, taxisID2);
 
-    auto ngrids = vlistNgrids(vlistID1);
+    auto ngrids = vlistNumGrids(vlistID1);
     if (ngrids > 1) cdo_abort("Too many different grids!");
 
     auto gridID1 = vlistGrid(vlistID1, 0);
@@ -372,8 +372,8 @@ public:
     streamID2 = cdo_open_write(1);
     cdo_def_vlist(streamID2, vlistID2);
 
-    varList_init(varList1, vlistID1);
-    varList_init(varList2, vlistID2);
+    varList1 = VarList(vlistID1);
+    varList2 = VarList(vlistID2);
 
     auto gridsize1 = gridInqSize(gridID1);
 
@@ -381,7 +381,7 @@ public:
   }
 
   void
-  run()
+  run() override
   {
     int tsID = 0;
     while (true)
@@ -396,7 +396,7 @@ public:
           {
             int varID, levelID;
             cdo_inq_record(streamID1, &varID, &levelID);
-            field1.init(varList1[varID]);
+            field1.init(varList1.vars[varID]);
             cdo_read_record(streamID1, field1);
 
             if (needWeights && field1.grid != lastgrid)
@@ -406,9 +406,9 @@ public:
               }
             if (wstatus != 0 && tsID == 0 && levelID == 0)
               cdo_warning("Grid cell bounds not available, using constant grid cell area weights for variable %s!",
-                          varList1[varID].name);
+                          varList1.vars[varID].name);
 
-            field2.init(varList2[varID]);
+            field2.init(varList2.vars[varID]);
             gridbox_stat(field1, field2, xinc, yinc, operfunc);
 
             cdo_def_record(streamID2, varID, levelID);
@@ -419,7 +419,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
diff --git a/src/Gridcell.cc b/src/Gridcell.cc
index ecf4d60197817c105436a814e27ba84d2139e8f4..798443668e9af434c19522348ff3f40bf8f786ec 100644
--- a/src/Gridcell.cc
+++ b/src/Gridcell.cc
@@ -256,7 +256,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
     // clang-format off
     GRIDAREA    = module.get_id("gridarea");
@@ -286,7 +286,7 @@ public:
     streamID1 = cdo_open_read(0);
     auto vlistID1 = cdo_stream_inq_vlist(streamID1);
 
-    auto ngrids = vlistNgrids(vlistID1);
+    auto ngrids = vlistNumGrids(vlistID1);
 
     if (ngrids > 1) cdo_warning("Found more than 1 grid, using the first one!");
 
@@ -342,7 +342,7 @@ public:
   }
 
   void
-  run()
+  run() override
   {
     if (operatorID == GRIDAREA) { gridcell_areas(gridID, array, planetRadiusInMeter); }
     else if (operatorID == GRIDWEIGHTS)
@@ -382,7 +382,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID1);
   }
diff --git a/src/Gridsearch.cc b/src/Gridsearch.cc
index 3af169b697002be467d4aa9f5bd93ffdaa569562..c642f2030eddb939694b1ed800abeda7ed7187a0 100644
--- a/src/Gridsearch.cc
+++ b/src/Gridsearch.cc
@@ -237,7 +237,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
 
     TESTPOINTSEARCH = module.get_id("testpointsearch");
@@ -253,13 +253,13 @@ public:
   }
 
   void
-  run()
+  run() override
   {
     if (operatorID == TESTCELLSEARCH) cell_search(gridID1, gridID2);
   }
 
   void
-  close()
+  close() override
   {
   }
 };
diff --git a/src/Harmonic.cc b/src/Harmonic.cc
index ce4846a323066065330805f7a5034e5a484d7ec2..737c90acd6bebec39e16a3c39e8d8927aee71268 100644
--- a/src/Harmonic.cc
+++ b/src/Harmonic.cc
@@ -26,7 +26,7 @@ public:
   using Process::Process;
   inline static CdoModule module = {
     .name = "Harmonic",
-    .operators = { { "harmonic"} },
+    .operators = { { "harmonic" } },
     .aliases = {},
     .mode = EXPOSED,     // Module mode: 0:intern 1:extern
     .number = CDI_REAL,  // Allowed number type
@@ -52,11 +52,12 @@ public:
   Varray3D<double> work;
   Varray<double> array;
 
-  VarList varList1, varList2;
+  VarList varList1;
+  VarList varList2;
 
 public:
   void
-  init()
+  init() override
   {
     operator_input_arg("wave number and wave length of first harmonic in number of timesteps");
 
@@ -75,8 +76,8 @@ public:
     auto vlistID1 = cdo_stream_inq_vlist(streamID1);
     vlistID2 = vlistDuplicate(vlistID1);
 
-    varList_init(varList1, vlistID1);
-    varList_init(varList2, vlistID2);
+    varList1 = VarList(vlistID1);
+    varList2 = VarList(vlistID2);
 
     taxisID1 = vlistInqTaxis(vlistID1);
     taxisID2 = cdo_taxis_create(TAXIS_ABSOLUTE);
@@ -104,7 +105,8 @@ public:
     for (int j = 0; j < n_out; ++j)
       {
         out[j].resize(nvars);
-        for (int varID = 0; varID < nvars; ++varID) out[j][varID].resize(varList1[varID].nlevels * varList1[varID].gridsize);
+        for (int varID = 0; varID < nvars; ++varID)
+          out[j][varID].resize(varList1.vars[varID].nlevels * varList1.vars[varID].gridsize);
       }
 
     for (int j = 0; j < n_out * 2; ++j)
@@ -112,7 +114,7 @@ public:
         work[j].resize(nvars);
         for (int varID = 0; varID < nvars; ++varID)
           {
-            const auto &var = varList1[varID];
+            const auto &var = varList1.vars[varID];
             work[j][varID].resize(var.gridsize * var.nlevels, 0);
           }
       }
@@ -122,7 +124,7 @@ public:
   }
 
   void
-  run()
+  run() override
   {
     int tsID = 0;
     while (true)
@@ -141,7 +143,7 @@ public:
 
             if (numMissVals) cdo_abort("Missing values are not allowed!");
 
-            auto gridsize = varList1[varID].gridsize;
+            auto gridsize = varList1.vars[varID].gridsize;
             auto offset = gridsize * levelID;
 
             for (int j = 0; j < n_out; ++j)
@@ -170,7 +172,7 @@ public:
       {
         for (int varID = 0; varID < nvars; ++varID)
           {
-            const auto &var2 = varList2[varID];
+            const auto &var2 = varList2.vars[varID];
             for (int levelID = 0; levelID < var2.nlevels; ++levelID)
               {
                 auto offset = var2.gridsize * levelID;
@@ -188,7 +190,7 @@ public:
       {
         for (int varID = 0; varID < nvars; ++varID)
           {
-            const auto &var2 = varList2[varID];
+            const auto &var2 = varList2.vars[varID];
             for (int levelID = 0; levelID < var2.nlevels; ++levelID)
               {
                 auto offset = var2.gridsize * levelID;
@@ -208,7 +210,7 @@ public:
 
         for (int varID = 0; varID < nvars; ++varID)
           {
-            const auto &var2 = varList2[varID];
+            const auto &var2 = varList2.vars[varID];
             for (int levelID = 0; levelID < var2.nlevels; ++levelID)
               {
                 auto offset = var2.gridsize * levelID;
@@ -222,7 +224,7 @@ public:
       {
         for (int varID = 0; varID < nvars; ++varID)
           {
-            const auto &var2 = varList2[varID];
+            const auto &var2 = varList2.vars[varID];
             for (int levelID = 0; levelID < var2.nlevels; ++levelID)
               {
                 auto offset = var2.gridsize * levelID;
@@ -249,7 +251,7 @@ public:
 
         for (int varID = 0; varID < nvars; ++varID)
           {
-            const auto &var2 = varList2[varID];
+            const auto &var2 = varList2.vars[varID];
             for (int levelID = 0; levelID < var2.nlevels; ++levelID)
               {
                 auto offset = var2.gridsize * levelID;
@@ -262,7 +264,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     for (int j = 0; j < n_out; ++j) cdo_stream_close(streamIDs[j]);
   }
diff --git a/src/Healpix.cc b/src/Healpix.cc
index fe27e5b54042291d453f5e6003c347bb091c76e1..a780ca7b6a736467e8968cff17b6a0bae62d0fce 100644
--- a/src/Healpix.cc
+++ b/src/Healpix.cc
@@ -14,8 +14,6 @@
 
 #include "process_int.h"
 #include "cdo_math.h"
-#include "cdo_vlist.h"
-#include "progress.h"
 #include "cdo_options.h"
 #include "grid_healpix.h"
 #include "param_conversion.h"
@@ -47,7 +45,7 @@ stat_avg_mv(const T *v, size_t n, T missval, double scale)
   double sum = 0.0;
   size_t nOut = 0;
   for (size_t i = 0; i < n; ++i)
-    if (!dbl_is_equal(v[i], missval))
+    if (dbl_is_not_equal(v[i], missval))
       {
         sum += v[i];
         nOut++;
@@ -63,7 +61,7 @@ stat_mean_mv(const T *v, size_t n, T missval, double scale)
   double sum = 0.0;
   size_t nOut = 0;
   for (size_t i = 0; i < n; ++i)
-    if (!dbl_is_equal(v[i], missval))
+    if (dbl_is_not_equal(v[i], missval))
       {
         sum += v[i];
         nOut++;
@@ -310,9 +308,8 @@ public:
 
   int HPDEGRADE;
   CdoStreamID streamID1;
-  int taxisID1;
-
   CdoStreamID streamID2;
+  int taxisID1;
   int taxisID2;
   int vlistID2;
 
@@ -328,7 +325,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
     // clang-format off
     HPDEGRADE = module.get_id("hpdegrade");
@@ -345,7 +342,7 @@ public:
     auto vlistID1 = cdo_stream_inq_vlist(streamID1);
     taxisID1 = vlistInqTaxis(vlistID1);
 
-    auto ngrids = vlistNgrids(vlistID1);
+    auto ngrids = vlistNumGrids(vlistID1);
     if (ngrids > 1) cdo_abort("Too many different grids!");
 
     auto gridID = vlistGrid(vlistID1, 0);
@@ -358,15 +355,15 @@ public:
     auto gridID2 = hp_define_grid(gridID, params);
     for (int index = 0; index < ngrids; ++index) vlistChangeGridIndex(vlistID2, index, gridID2);
 
-    varList_init(varList1, vlistID1);
-    varList_init(varList2, vlistID2);
+    varList1 = VarList(vlistID1);
+    varList2 = VarList(vlistID2);
 
     streamID2 = cdo_open_write(1);
     cdo_def_vlist(streamID2, vlistID2);
   }
 
   void
-  run()
+  run() override
   {
     int tsID1 = 0;
     while (true)
@@ -379,12 +376,11 @@ public:
 
         for (int recID = 0; recID < nrecs; ++recID)
           {
-            int varID, levelID;
-            cdo_inq_record(streamID1, &varID, &levelID);
-            field1.init(varList1[varID]);
+            auto[varID, levelID] = cdo_inq_record(streamID1);
+            field1.init(varList1.vars[varID]);
             cdo_read_record(streamID1, field1);
 
-            field2.init(varList2[varID]);
+            field2.init(varList2.vars[varID]);
 
             if (params.orderIn == HpOrder::Ring) ring_to_nested(field1, params.nsideIn);
 
@@ -401,7 +397,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
 
     cdo_stream_close(streamID1);
diff --git a/src/Hi.cc b/src/Hi.cc
index 7ad878a86827665e4a9c26ca04fcec70c9c4016d..b1199d29118b49ccdc3c2f97d6ba685cd48f3698 100644
--- a/src/Hi.cc
+++ b/src/Hi.cc
@@ -76,13 +76,14 @@ public:
   using Process::Process;
   inline static CdoModule module = {
     .name = "Hi",
-    .operators = { { "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;
@@ -100,12 +101,14 @@ public:
   int varID4;
 
   Field field1, field2, field3;
+  VarList varList1;
+  VarList varList2;
+  VarList varList3;
 
 public:
   void
-  init()
+  init() override
   {
-
     operator_check_argc(0);
 
     streamID1 = cdo_open_read(0);
@@ -120,8 +123,12 @@ public:
     // taxisID2 = vlistInqTaxis(vlistID2);
     // taxisID3 = vlistInqTaxis(vlistID3);
 
-    vlist_compare(vlistID1, vlistID2, CmpVlist::Dim);
-    vlist_compare(vlistID1, vlistID3, CmpVlist::Dim);
+    varList1 = VarList(vlistID1);
+    varList2 = VarList(vlistID2);
+    varList3 = VarList(vlistID3);
+
+    varList_compare(varList1, varList2);
+    varList_compare(varList1, varList3);
 
     auto gridsizemax = vlistGridsizeMax(vlistID1);
 
@@ -130,12 +137,11 @@ public:
     field3.resize(gridsizemax);
 
     if (Options::cdoVerbose)
-      cdo_print("Number of timesteps: file1 %d, file2 %d, file3 %d", vlistNtsteps(vlistID1), vlistNtsteps(vlistID2),
-                vlistNtsteps(vlistID3));
+      cdo_print("Number of timesteps: file1 %d, file2 %d, file3 %d", varList1.numSteps(), varList2.numSteps(), varList3.numSteps());
 
     vlistID4 = vlistCreate();
-    auto gridID = vlistInqVarGrid(vlistID1, FIRST_VAR);
-    auto zaxisID = vlistInqVarZaxis(vlistID1, FIRST_VAR);
+    auto gridID = varList1.vars[FIRST_VAR].gridID;
+    auto zaxisID = varList1.vars[FIRST_VAR].zaxisID;
     varID4 = vlistDefVar(vlistID4, gridID, zaxisID, TIME_VARYING);
 
     taxisID4 = cdo_taxis_create(TAXIS_RELATIVE);
@@ -155,7 +161,7 @@ public:
   }
 
   void
-  run()
+  run() override
   {
     int tsID = 0;
     while (true)
@@ -189,14 +195,14 @@ public:
 
             if (varID1 != FIRST_VAR) continue;
 
-            field1.grid = vlistInqVarGrid(vlistID1, varID1);
-            field1.missval = vlistInqVarMissval(vlistID1, varID1);
+            field1.grid = varList1.vars[varID1].gridID;
+            field1.missval = varList1.vars[varID1].missval;
 
-            field2.grid = vlistInqVarGrid(vlistID2, varID2);
-            field2.missval = vlistInqVarMissval(vlistID2, varID2);
+            field2.grid = varList2.vars[varID2].gridID;
+            field2.missval = varList2.vars[varID2].missval;
 
-            field3.grid = vlistInqVarGrid(vlistID3, varID3);
-            field3.missval = vlistInqVarMissval(vlistID3, varID3);
+            field3.grid = varList3.vars[varID3].gridID;
+            field3.missval = varList3.vars[varID3].missval;
 
             farexpr(field1, field2, field3, humidityIndex);
 
@@ -209,7 +215,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID4);
     cdo_stream_close(streamID3);
diff --git a/src/Histogram.cc b/src/Histogram.cc
index df1b7ba3000516bb1762dfa7d240251ccf6a24b7..a6eb572ba2e4c8a59987d68b0c6997df4b731ef2 100644
--- a/src/Histogram.cc
+++ b/src/Histogram.cc
@@ -13,7 +13,6 @@
 #include <cdi.h>
 
 #include "cdo_options.h"
-#include "cdo_vlist.h"
 #include "process_int.h"
 #include "param_conversion.h"
 
@@ -33,6 +32,7 @@ public:
     .constraints = { 1, 1, NoRestriction },
   };
   inline static RegisterEntry<Histogram> registration = RegisterEntry<Histogram>(module);
+
   int HISTCOUNT, HISTSUM, HISTMEAN, HISTFREQ;
 
 private:
@@ -47,7 +47,8 @@ private:
   Varray2D<double> vartcount;
 
   Varray<double> array;
-  VarList varList1, varList2;
+  VarList varList1;
+  VarList varList2;
 
   int nbins;
 
@@ -57,15 +58,12 @@ private:
 
 public:
   void
-  init()
+  init() override
   {
-
-    // clang-format off
-HISTCOUNT = module.get_id("histcount");
-HISTSUM = module.get_id("histsum");
-HISTMEAN = module.get_id("histmean");
-HISTFREQ = module.get_id("histfreq");
-    // clang-format on
+    HISTCOUNT = module.get_id("histcount");
+    HISTSUM = module.get_id("histsum");
+    HISTMEAN = module.get_id("histmean");
+    HISTFREQ = module.get_id("histfreq");
 
     (void) (HISTSUM);  // unused
 
@@ -84,14 +82,14 @@ HISTFREQ = module.get_id("histfreq");
       }
 
     streamID1 = cdo_open_read(0);
-    const auto vlistID1 = cdo_stream_inq_vlist(streamID1);
+    auto vlistID1 = cdo_stream_inq_vlist(streamID1);
 
     taxisID1 = vlistInqTaxis(vlistID1);
 
-    const auto vlistID2 = vlistDuplicate(vlistID1);
+    auto vlistID2 = vlistDuplicate(vlistID1);
 
     /* create zaxis for output bins */
-    const auto zaxisID2 = zaxisCreate(ZAXIS_GENERIC, nbins);
+    auto zaxisID2 = zaxisCreate(ZAXIS_GENERIC, nbins);
 
     zaxisDefLevels(zaxisID2, &fltarr[0]);
     zaxisDefLbounds(zaxisID2, &fltarr[0]);
@@ -102,11 +100,11 @@ HISTFREQ = module.get_id("histfreq");
     cdiDefKeyString(zaxisID2, CDI_GLOBAL, CDI_KEY_UNITS, "level");
 
     /* check zaxis: only 2D fields allowed */
-    const auto nzaxis = vlistNzaxis(vlistID1);
+    auto nzaxis = vlistNumZaxis(vlistID1);
     for (int index = 0; index < nzaxis; ++index)
       {
-        const auto zaxisID = vlistZaxis(vlistID1, index);
-        const auto nlevel = zaxisInqSize(zaxisID);
+        auto zaxisID = vlistZaxis(vlistID1, index);
+        auto nlevel = zaxisInqSize(zaxisID);
         if (nlevel > 1) cdo_abort("Found 3D field with %d levels. Only 2D fields allowed!", nlevel);
         vlistChangeZaxisIndex(vlistID2, index, zaxisID2);
       }
@@ -118,8 +116,8 @@ HISTFREQ = module.get_id("histfreq");
 
     cdo_def_vlist(streamID2, vlistID2);
 
-    varList_init(varList1, vlistID1);
-    varList_init(varList2, vlistID2);
+    varList1 = VarList(vlistID1);
+    varList2 = VarList(vlistID2);
 
     nvars = vlistNvars(vlistID2);
 
@@ -129,22 +127,23 @@ HISTFREQ = module.get_id("histfreq");
 
     for (int varID = 0; varID < nvars; ++varID)
       {
-        const auto gridsize = varList1[varID].gridsize;
+        auto gridsize = varList1.vars[varID].gridsize;
         vardata[varID].resize(nbins * gridsize, 0);
         varcount[varID].resize(nbins * gridsize, 0);
         vartcount[varID].resize(gridsize, 0);
       }
 
-    const auto gridsizemax = vlistGridsizeMax(vlistID1);
+    auto gridsizemax = vlistGridsizeMax(vlistID1);
     array = Varray<double>(gridsizemax);
   }
+
   void
-  run()
+  run() override
   {
     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);
@@ -156,8 +155,8 @@ HISTFREQ = module.get_id("histfreq");
             size_t numMissVals;
             cdo_read_record(streamID1, array.data(), &numMissVals);
 
-            const auto gridsize = varList1[varID].gridsize;
-            const auto missval = varList1[varID].missval;
+            auto gridsize = varList1.vars[varID].gridsize;
+            auto missval = varList1.vars[varID].missval;
 
             numMissVals = 0;
             for (size_t i = 0; i < gridsize; ++i)
@@ -168,7 +167,7 @@ HISTFREQ = module.get_id("histfreq");
                     int index = 0;
                     while (index < nbins)
                       {
-                        const auto offset = gridsize * index;
+                        auto offset = gridsize * index;
                         if (!DBL_IS_EQUAL(vardata[varID][offset + i], missval) && array[i] >= fltarr[index]
                             && array[i] < fltarr[index + 1])
                           {
@@ -191,15 +190,15 @@ HISTFREQ = module.get_id("histfreq");
 
     for (int varID = 0; varID < nvars; ++varID)
       {
-        const auto gridsize = varList2[varID].gridsize;
-        const auto missval = varList2[varID].missval;
+        auto gridsize = varList2.vars[varID].gridsize;
+        auto missval = varList2.vars[varID].missval;
 
         // fix missing values
 
         for (int index = 0; index < nbins; ++index)
           {
             size_t numMissVals = 0;
-            const auto offset = gridsize * index;
+            auto offset = gridsize * index;
 
             for (size_t i = 0; i < gridsize; ++i)
               {
@@ -235,7 +234,7 @@ HISTFREQ = module.get_id("histfreq");
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID1);
     cdo_stream_close(streamID2);
diff --git a/src/Importamsr.cc b/src/Importamsr.cc
index bb2f57d6f86fe6555fb4387bd92fe78254ceda90..34ea716a3dbd66d849cc30c962bcb70b9bddc3d8 100644
--- a/src/Importamsr.cc
+++ b/src/Importamsr.cc
@@ -14,7 +14,7 @@
 #define MAX_VARS 6
 
 static void
-init_amsr_day(int vlistID, int gridID, int zaxisID, int nvars)
+init_amsr_day(int vlistID, int gridID, int zaxisID, int numVars)
 {
   /*
     Version-5 RSS AMSR-E or AMSR-J daily files
@@ -37,7 +37,7 @@ init_amsr_day(int vlistID, int gridID, int zaxisID, int nvars)
   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)
+  for (int i = 0; i < numVars; ++i)
     {
       int varID = vlistDefVar(vlistID, gridID, zaxisID, TIME_VARYING);
       cdiDefKeyString(vlistID, varID, CDI_KEY_NAME, name[i]);
@@ -95,30 +95,28 @@ 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 *numMissVals)
+read_amsr(FILE *fp, const VarList &varList, Varray2D<double> &data, size_t *numMissVals)
 {
   std::vector<unsigned char> amsr_data;
 
-  for (int varID = 0; varID < nvars; ++varID)
+  for (const auto &var : varList.vars)
     {
-      size_t gridsize = gridInqSize(vlistInqVarGrid(vlistID, varID));
-      amsr_data.resize(gridsize);
-      size_t size = fread(amsr_data.data(), 1, gridsize, fp);
-      if (size != gridsize) cdo_abort("Read error!");
+      amsr_data.resize(var.gridsize);
+      size_t size = fread(amsr_data.data(), 1, var.gridsize, fp);
+      if (size != var.gridsize) cdo_abort("Read error!");
 
-      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);
+      cdiInqKeyFloat(varList.vlistID, var.ID, CDI_KEY_SCALEFACTOR, &xscale);
+      cdiInqKeyFloat(varList.vlistID, var.ID, CDI_KEY_ADDOFFSET, &xminval);
 
-      numMissVals[varID] = 0;
-      for (size_t i = 0; i < gridsize; ++i)
+      numMissVals[var.ID] = 0;
+      for (size_t i = 0; i < var.gridsize; ++i)
         {
-          if (amsr_data[i] <= 250) { data[varID][i] = amsr_data[i] * xscale + xminval; }
+          if (amsr_data[i] <= 250) { data[var.ID][i] = amsr_data[i] * xscale + xminval; }
           else
             {
-              data[varID][i] = missval;
-              numMissVals[varID]++;
+              data[var.ID][i] = var.missval;
+              numMissVals[var.ID]++;
             }
         }
     }
@@ -157,7 +155,7 @@ public:
   inline static RegisterEntry<Importamsr> registration = RegisterEntry<Importamsr>(module);
 
   int tsID;
-  int nvars;
+  int numVars;
   int vtime = 0;
   double xvals[NLON], yvals[NLAT];
   Varray2D<double> data;
@@ -179,10 +177,11 @@ public:
   void
   A()
   {
-    nvars = 6;
-    for (int i = 0; i < nvars; ++i) data[i].resize(gridsize);
+    numVars = 6;
+    for (int i = 0; i < numVars; ++i) data[i].resize(gridsize);
 
-    init_amsr_day(vlistID, gridID, zaxisID, nvars);
+    init_amsr_day(vlistID, gridID, zaxisID, numVars);
+    VarList varList(vlistID);
 
     cdo_def_vlist(streamID, vlistID);
 
@@ -196,19 +195,20 @@ public:
         vtime += 120000;  // 13:30:00
         cdo_def_timestep(streamID, tsID);
 
-        read_amsr(fp, vlistID, nvars, data, numMissVals);
+        read_amsr(fp, varList, data, numMissVals);
 
-        write_data(streamID, nvars, data, numMissVals);
+        write_data(streamID, numVars, data, numMissVals);
       }
   }
 
   void
   B()
   {
-    nvars = 5;
-    for (int i = 0; i < nvars; ++i) data[i].resize(gridsize);
+    numVars = 5;
+    for (int i = 0; i < numVars; ++i) data[i].resize(gridsize);
 
-    init_amsr_averaged(vlistID, gridID, zaxisID, nvars);
+    init_amsr_averaged(vlistID, gridID, zaxisID, numVars);
+    VarList varList(vlistID);
 
     // vlistDefNtsteps(vlistID, 0);
     cdo_def_vlist(streamID, vlistID);
@@ -220,14 +220,14 @@ public:
     tsID = 0;
     cdo_def_timestep(streamID, tsID);
 
-    read_amsr(fp, vlistID, nvars, data, numMissVals);
+    read_amsr(fp, varList, data, numMissVals);
 
-    write_data(streamID, nvars, data, numMissVals);
+    write_data(streamID, numVars, data, numMissVals);
   }
 
 public:
   void
-  init()
+  init() override
   {
     operator_check_argc(0);
 
@@ -270,7 +270,7 @@ public:
   }
 
   void
-  run()
+  run() override
   {
     if (fsize == 12441600) { A(); }
     else if (fsize == 5184000) { B(); }
@@ -279,15 +279,11 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID);
+    vlistDestroy(vlistID);
 
     std::fclose(fp);
-
-    vlistDestroy(vlistID);
-    gridDestroy(gridID);
-    zaxisDestroy(zaxisID);
-    taxisDestroy(taxisID);
   }
 };
diff --git a/src/Importbinary.cc b/src/Importbinary.cc
index 096a1845134b12524f9b6e2414c867f761bb100c..abe6b96169b3bdbf998456e45f07aabd67189148 100644
--- a/src/Importbinary.cc
+++ b/src/Importbinary.cc
@@ -153,7 +153,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
 
     operator_check_argc(0);
@@ -301,7 +301,7 @@ public:
     array = Varray<double>(gridsize);
   }
   void
-  run()
+  run() override
   {
     pfi.infile = nullptr;
     int tcur = 0;
@@ -488,16 +488,12 @@ public:
       }  // end of while (1) loop
   }
   void
-  close()
+  close() override
   {
     process_def_var_num(vlistNvars(vlistID));
 
     cdo_stream_close(streamID);
-
     vlistDestroy(vlistID);
-    gridDestroy(gridID);
-    zaxisDestroy(zaxisID);
-    taxisDestroy(taxisID);
 
     if (pfi.infile) std::fclose(pfi.infile);
   }
diff --git a/src/Importcmsaf.cc b/src/Importcmsaf.cc
index 5dcc9846517083933097b793a7fe4fa9f8bafcc4..0621e1b444bc7be52ddd8342b2efd4a32c026a43 100644
--- a/src/Importcmsaf.cc
+++ b/src/Importcmsaf.cc
@@ -16,10 +16,9 @@
 #endif
 
 #include <cdi.h>
-#include <stdlib.h> // realloc()
+#include <stdlib.h>  // realloc()
 
 #include "cdo_options.h"
-#include "compare.h"
 #include "varray.h"
 #include "process_int.h"
 #include "cdo_default_values.h"
@@ -993,13 +992,13 @@ read_dataset(hid_t loc_id, const char *name, void *opdata)
         {
           if (((datasets_t *) opdata)->obj[nset].dtype != dtype) cdo_warning("Data type changes over levels!");
 
-          if (haveAddoffset && !dbl_is_equal(((datasets_t *) opdata)->obj[nset].addoffset, addoffset))
+          if (haveAddoffset && dbl_is_not_equal(((datasets_t *) opdata)->obj[nset].addoffset, addoffset))
             cdo_warning("Offset changes over levels!");
 
-          if (haveScalefactor && !dbl_is_equal(((datasets_t *) opdata)->obj[nset].scalefactor, scalefactor))
+          if (haveScalefactor && dbl_is_not_equal(((datasets_t *) opdata)->obj[nset].scalefactor, scalefactor))
             cdo_warning("Scalefactor changes over levels!");
 
-          if (haveMissvals && !dbl_is_equal(((datasets_t *) opdata)->obj[nset].missval, missval))
+          if (haveMissvals && dbl_is_not_equal(((datasets_t *) opdata)->obj[nset].missval, missval))
             cdo_warning("Missing value changes over levels!");
         }
 
@@ -1032,7 +1031,7 @@ read_dataset(hid_t loc_id, const char *name, void *opdata)
       if (haveAddoffset || haveScalefactor)
         {
           for (size_t i = 0; i < gridsize * nt; ++i)
-            if (!dbl_is_equal(array[i], missval))
+            if (dbl_is_not_equal(array[i], missval))
               {
                 mask[i] = false;
 
@@ -1284,7 +1283,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
 
     operator_check_argc(0);
@@ -1307,7 +1306,7 @@ public:
   }
 
   void
-  run()
+  run() override
   {
 
 #ifdef HAVE_LIBHDF5
@@ -1436,8 +1435,8 @@ public:
                 numMissVals = gridsize - mm.n;
 
                 if (Options::cdoVerbose)
-                  cdo_print(" Write var %d,  level %d, numMissVals %zu, missval %g, minval %g, maxval %g", varID, levelID, numMissVals, missval,
-                            mm.min, mm.max);
+                  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, numMissVals);
@@ -1450,7 +1449,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
 #ifdef HAVE_LIBHDF5
     // Close file
@@ -1459,11 +1458,7 @@ public:
     process_def_var_num(vlistNvars(vlistID));
 
     cdo_stream_close(streamID);
-
     vlistDestroy(vlistID);
-    gridDestroy(gridID);
-    zaxisDestroy(zaxisID);
-    taxisDestroy(taxisID);
 
     for (ivar = 0; ivar < dsets.numSets; ++ivar) free(dsets.obj[ivar].array);
 
diff --git a/src/Importfv3grid.cc b/src/Importfv3grid.cc
index 42b4783eb5a848c29eb1ffb611af1ed9b1922165..966e9c66fb0fac619af2c5611caa18be05b36bf1 100644
--- a/src/Importfv3grid.cc
+++ b/src/Importfv3grid.cc
@@ -15,7 +15,7 @@ public:
   using Process::Process;
   inline static CdoModule module = {
     .name = "Importfv3grid",
-    .operators = { { "import_fv3grid"} },
+    .operators = { { "import_fv3grid" } },
     .aliases = {},
     .mode = EXPOSED,     // Module mode: 0:intern 1:extern
     .number = CDI_REAL,  // Allowed number type
@@ -39,7 +39,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
 
     operator_check_argc(0);
@@ -63,7 +63,7 @@ public:
         if (varname == vars[varID]) cdo_abort("Found variable %s, expected variable %s!", varname, vars[varID]);
       }
 
-    auto ngrids = vlistNgrids(vlistID1);
+    auto ngrids = vlistNumGrids(vlistID1);
     if (ngrids != 2) cdo_abort("Found %d grids, expected 2 grids!", nvars);
 
     auto gridIDi1 = vlistGrid(vlistID1, 0);
@@ -81,7 +81,7 @@ public:
   }
 
   void
-  run()
+  run() override
   {
     Varray<double> buffer(gridsize1);
     int varID, levelID;
@@ -153,13 +153,10 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
     vlistDestroy(vlistID2);
-    gridDestroy(gridIDo);
-    zaxisDestroy(surfaceID);
   }
 };
diff --git a/src/Importobs.cc b/src/Importobs.cc
index 5943c9b2f9cd9ebea72051d3b0ac6e7c6e266c64..3e08b15c47340a0adee3244e7c5476fc42e3f897 100644
--- a/src/Importobs.cc
+++ b/src/Importobs.cc
@@ -33,26 +33,22 @@ init_vars(int vlistID, int gridID, int zaxisID, int nvars)
 }
 
 static void
-init_data(int vlistID, int nvars, Varray2D<double> &data)
+init_data(const VarList &varList, Varray2D<double> &data)
 {
-  for (int varID = 0; varID < nvars; ++varID)
+  for (const auto &var : varList.vars)
     {
-      auto gridsize = gridInqSize(vlistInqVarGrid(vlistID, varID));
-      auto missval = vlistInqVarMissval(vlistID, varID);
-      for (size_t i = 0; i < gridsize; ++i) data[varID][i] = missval;
+      for (size_t i = 0; i < var.gridsize; ++i) data[var.ID][i] = var.missval;
     }
 }
 
 static void
-write_data(CdoStreamID streamID, int vlistID, int nvars, Varray2D<double> &data)
+write_data(CdoStreamID streamID, const VarList &varList, Varray2D<double> &data)
 {
-  for (int varID = 0; varID < nvars; ++varID)
+  for (const auto &var : varList.vars)
     {
-      auto gridsize = gridInqSize(vlistInqVarGrid(vlistID, varID));
-      auto missval = vlistInqVarMissval(vlistID, varID);
-      auto numMissVals = varray_num_mv(gridsize, data[varID], missval);
-      cdo_def_record(streamID, varID, 0);
-      cdo_write_record(streamID, data[varID].data(), numMissVals);
+      auto numMissVals = varray_num_mv(var.gridsize, data[var.ID], var.missval);
+      cdo_def_record(streamID, var.ID, 0);
+      cdo_write_record(streamID, data[var.ID].data(), numMissVals);
     }
 }
 
@@ -70,7 +66,7 @@ public:
   using Process::Process;
   inline static CdoModule module = {
     .name = "Importobs",
-    .operators = { { "import_obs", 0, 0, "grid description file or name"} },
+    .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
@@ -78,7 +74,7 @@ public:
   };
   inline static auto registration = RegisterEntry<Importobs>(module);
 
-  int nvars = 6;
+  int numVars = 6;
   int vtime = 0;
   char dummy[32], station[32], datetime[32];
   float lat, lon, height1, pressure, height2, value;
@@ -94,8 +90,6 @@ public:
 
   int vdate;
 
-  FILE *fp;
-
   Varray2D<double> data;
 
   Varray<double> xvals;
@@ -105,7 +99,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
     auto operatorID = cdo_operator_id();
 
@@ -132,24 +126,24 @@ public:
     if (vdate <= 999999) vdate = vdate * 100 + 1;
 
     streamID = cdo_open_write(1);
-
     zaxisID = zaxisCreate(ZAXIS_SURFACE, 1);
-
     vlistID = vlistCreate();
 
     taxisID = cdo_taxis_create(TAXIS_ABSOLUTE);
     vlistDefTaxis(vlistID, taxisID);
-    data = Varray2D<double>(nvars);
-    for (int i = 0; i < nvars; ++i) data[i].resize(gridsize);
+    data.resize(numVars);
+    for (int i = 0; i < numVars; ++i) data[i].resize(gridsize);
 
-    init_vars(vlistID, gridID, zaxisID, nvars);
+    init_vars(vlistID, gridID, zaxisID, numVars);
 
     cdo_def_vlist(streamID, vlistID);
   }
 
   void
-  run()
+  run() override
   {
+    VarList varList(vlistID);
+
     std::ifstream file(cdo_get_stream_name(0));
     if (!file.is_open()) cdo_abort("Open failed on: %s\n", cdo_get_stream_name(0));
 
@@ -168,7 +162,7 @@ public:
 
         if (vdate != vdate0 || vtime != vtime0)
           {
-            if (tsID > 0) write_data(streamID, vlistID, nvars, data);
+            if (tsID > 0) write_data(streamID, varList, data);
 
             vdate0 = vdate;
             vtime0 = vtime;
@@ -179,7 +173,7 @@ public:
             taxisDefVdatetime(taxisID, vDateTime);
             cdo_def_timestep(streamID, tsID);
 
-            init_data(vlistID, nvars, data);
+            init_data(varList, data);
 
             tsID++;
           }
@@ -225,7 +219,7 @@ public:
 
     file.close();
 
-    write_data(streamID, vlistID, nvars, data);
+    write_data(streamID, varList, data);
 
     if (Options::cdoVerbose) printf("lonmin=%g, lonmax=%g, latmin=%g, latmax=%g\n", lonmin, lonmax, latmin, latmax);
 
@@ -233,13 +227,9 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID);
-
     vlistDestroy(vlistID);
-    gridDestroy(gridID);
-    zaxisDestroy(zaxisID);
-    taxisDestroy(taxisID);
   }
 };
diff --git a/src/Info.cc b/src/Info.cc
index 57241d7dfcd8e28fc9d8ed41217ccb3854424654..289f0bd9e363ee04bb642553757bba70188c2f68 100644
--- a/src/Info.cc
+++ b/src/Info.cc
@@ -16,24 +16,25 @@
 
 #include <cdi.h>
 
+#include "cdo_task.h"
 #include "cdo_options.h"
-#include "cdo_vlist.h"
 #include "process_int.h"
 #include "mpmo_color.h"
 #include "varray.h"
 #include "datetime.h"
 #include "printinfo.h"
+#include "field_functions.h"
 #include "cdo_zaxis.h"
 
-struct Infostat
+struct InfoStat
 {
-  double min = DBL_MAX;
-  double max = -DBL_MAX;
-  double sum = 0.0;
-  double sumi = 0.0;
-  size_t nvals = 0;
-  size_t numMissVals = 0;
-  int nlevels = 0;
+  double min{ DBL_MAX };
+  double max{ -DBL_MAX };
+  double sum{ 0.0 };
+  double sumi{ 0.0 };
+  size_t nvals{ 0 };
+  size_t numMissVals{ 0 };
+  int nlevels{ 0 };
 };
 
 static void
@@ -79,7 +80,7 @@ print_grid_index(int nlon, int nlat, int i)
 
 template <typename T>
 static void
-print_map(int nlon, int nlat, const Varray<T> &varray, double missval, double min, double max)
+print_map(int nlon, int nlat, const Varray<T> &varray, T missval, double min, double max)
 {
   // source code from PINGO
   double level[10] = {};
@@ -138,9 +139,9 @@ print_map(int nlon, int nlat, const Varray<T> &varray, double missval, double mi
           auto x = varray[ilat * nlon + ilon];
           if (dbl_is_equal(x, missval))
             c = '.';
-          else if (dbl_is_equal(x, min) && !dbl_is_equal(min, max))
+          else if (dbl_is_equal(x, min) && dbl_is_not_equal(min, max))
             c = 'm';
-          else if (dbl_is_equal(x, max) && !dbl_is_equal(min, max))
+          else if (dbl_is_equal(x, max) && dbl_is_not_equal(min, max))
             c = 'M';
           else if (dbl_is_equal(x, 0.0))
             c = '*';
@@ -219,12 +220,12 @@ print_map(int nlon, int nlat, const Varray<T> &varray, double missval, double mi
 }
 
 static void
-print_map(int nlon, int nlat, const Field &field, const Infostat &infostat)
+print_map(int nlon, int nlat, const Field &field, const InfoStat &infoStat)
 {
   if (field.memType == MemType::Float)
-    print_map(nlon, nlat, field.vec_f, static_cast<float>(field.missval), infostat.min, infostat.max);
+    print_map(nlon, nlat, field.vec_f, static_cast<float>(field.missval), infoStat.min, infoStat.max);
   else
-    print_map(nlon, nlat, field.vec_d, field.missval, infostat.min, infostat.max);
+    print_map(nlon, nlat, field.vec_d, field.missval, infoStat.min, infoStat.max);
 }
 
 template <typename T>
@@ -234,7 +235,7 @@ complex_sum(size_t gridsize, const Varray<T> &varray, double missval, double &su
   size_t n = 0;
   for (size_t i = 0; i < gridsize; ++i)
     {
-      if (!dbl_is_equal(varray[i * 2], missval) && !dbl_is_equal(varray[i * 2 + 1], missval))
+      if (dbl_is_not_equal(varray[i * 2], missval) && dbl_is_not_equal(varray[i * 2 + 1], missval))
         {
           sum += varray[i * 2];
           sumi += varray[i * 2 + 1];
@@ -255,100 +256,189 @@ field_complex_sum(const Field &field, double &sum, double &sumi)
 }
 
 static void
-infostat_init(Infostat &infostat)
+infostat_init(InfoStat &infoStat)
 {
-  infostat.nvals = 0;
-  infostat.numMissVals = 0;
-  infostat.nlevels = 0;
-  infostat.min = DBL_MAX;
-  infostat.max = -DBL_MAX;
-  infostat.sum = 0.0;
-  infostat.sumi = 0.0;
+  infoStat.nvals = 0;
+  infoStat.numMissVals = 0;
+  infoStat.nlevels = 0;
+  infoStat.min = DBL_MAX;
+  infoStat.max = -DBL_MAX;
+  infoStat.sum = 0.0;
+  infoStat.sumi = 0.0;
 }
 
 static void
-print_header(int fileIndex, bool lvinfo, const std::string &e, const std::string &v)
+print_header(int fileIndex, bool lvinfo, int operfunc)
 {
+  auto e = (operfunc == Func_Name) ? "Parameter name" : ((operfunc == Func_Code) ? "Code number" : "Parameter ID");
+  auto v = (Options::cdoVerbose) ? " : Extra" : "";
+
   set_text_color(stdout, BRIGHT);
   if (fileIndex)
     fprintf(stdout, "%6d :       Date     Time   %s Gridsize    Miss :     Minimum        Mean     Maximum : %s%s\n", fileIndex,
-            lvinfo ? "Nlevs" : "Level", e.c_str(), v.c_str());
+            lvinfo ? "Nlevs" : "Level", e, v);
   else
     fprintf(stdout, "       :       Date     Time   %s Gridsize    Miss :     Minimum        Mean     Maximum : %s%s\n",
-            lvinfo ? "Nlevs" : "Level", e.c_str(), v.c_str());
+            lvinfo ? "Nlevs" : "Level", e, v);
   reset_text_color(stdout);
 }
 
 static void
-compute_stat_real(const Field &field, Infostat &infostat, size_t &imiss, size_t gridsize)
+compute_stat_real(const Field &field, InfoStat &infoStat, size_t &imiss, size_t gridsize)
 {
-  if (infostat.numMissVals)
+  if (infoStat.numMissVals)
     {
-      auto nvals = field_min_max_sum_mv(field, infostat.min, infostat.max, infostat.sum);
+      auto nvals = field_min_max_sum_mv(field, infoStat.min, infoStat.max, infoStat.sum);
       imiss = gridsize - nvals;
-      infostat.nvals += nvals;
+      infoStat.nvals += nvals;
     }
   else if (gridsize == 1)
     {
       double val = (field.memType == MemType::Float) ? field.vec_f[0] : field.vec_d[0];
-      infostat.sum = (infostat.nvals == 0) ? val : infostat.sum + val;
-      infostat.nvals += 1;
+      infoStat.sum = (infoStat.nvals == 0) ? val : infoStat.sum + val;
+      infoStat.nvals += 1;
     }
   else
     {
-      field_min_max_sum(field, infostat.min, infostat.max, infostat.sum);
-      infostat.nvals += gridsize;
+      field_min_max_sum(field, infoStat.min, infoStat.max, infoStat.sum);
+      infoStat.nvals += gridsize;
     }
 }
 
 static void
-compute_stat_comp(const Field &field, Infostat &infostat, size_t &imiss, size_t gridsize)
+compute_stat_comp(const Field &field, InfoStat &infoStat, size_t &imiss, size_t gridsize)
 {
-  auto nvals = field_complex_sum(field, infostat.sum, infostat.sumi);
+  auto nvals = field_complex_sum(field, infoStat.sum, infoStat.sumi);
 
   imiss = gridsize - nvals;
-  infostat.nvals += nvals;
+  infoStat.nvals += nvals;
 }
 
 static void
-print_stat_real(const Infostat &infostat)
+print_stat_real(const InfoStat &infoStat)
 {
-  if (infostat.nvals == 0)
+  if (infoStat.nvals == 0)
     fprintf(stdout, "                     nan            ");
-  else if (infostat.nvals == 1)
-    fprintf(stdout, "            %#12.5g            ", infostat.sum);
+  else if (infoStat.nvals == 1)
+    fprintf(stdout, "            %#12.5g            ", infoStat.sum);
   else
-    fprintf(stdout, "%#12.5g%#12.5g%#12.5g", infostat.min, infostat.sum / infostat.nvals, infostat.max);
+    fprintf(stdout, "%#12.5g%#12.5g%#12.5g", infoStat.min, infoStat.sum / infoStat.nvals, infoStat.max);
 }
 
 static void
-print_stat_comp(const Infostat &infostat)
+print_stat_comp(const InfoStat &infoStat)
 {
-  auto arrmean_r = (infostat.nvals > 0) ? infostat.sum / infostat.nvals : 0.0;
-  auto arrmean_i = (infostat.nvals > 0) ? infostat.sumi / infostat.nvals : 0.0;
+  auto arrmean_r = (infoStat.nvals > 0) ? infoStat.sum / infoStat.nvals : 0.0;
+  auto arrmean_i = (infoStat.nvals > 0) ? infoStat.sumi / infoStat.nvals : 0.0;
   fprintf(stdout, "   -  (%#12.5g,%#12.5g)  -", arrmean_r, arrmean_i);
 }
 
-class Info : public Process
+static void
+info(const Field &field, int indg, int indf, int tsID, int recID, int levelID, CdiDateTime vDateTime, const CdoVar &var,
+     int operfunc, bool printMap, bool lvinfo, InfoStat &infoStat)
 {
-  enum
-  {
-    E_NAME,
-    E_CODE,
-    E_PARAM
-  };
+  if ((tsID == 0 && recID == 0) || printMap) print_header(-(indf + 1), lvinfo, operfunc);
+
+  auto numMissVals = field.numMissVals;
+
+  auto loutput = !lvinfo;
+
+  if (loutput) infostat_init(infoStat);
+
+  infoStat.nlevels += 1;
+  infoStat.numMissVals += numMissVals;
+
+  if (var.nlevels == infoStat.nlevels) loutput = true;
+
+  char paramstr[32];
+  if (loutput)
+    {
+      cdiParamToString(var.param, paramstr, sizeof(paramstr));
+
+      fprintf(stdout, "%6d ", indg);
+      fprintf(stdout, ":");
+
+      auto vdateString = date_to_string(vDateTime.date);
+      auto vtimeString = time_to_string(vDateTime.time);
+
+      set_text_color(stdout, MAGENTA);
+      fprintf(stdout, "%s %s ", vdateString.c_str(), vtimeString.c_str());
+      reset_text_color(stdout);
+
+      set_text_color(stdout, GREEN);
+      if (lvinfo)
+        fprintf(stdout, "%7d ", var.nlevels);
+      else
+        fprintf(stdout, "%7g ", cdo_zaxis_inq_level(var.zaxisID, levelID));
+
+      fprintf(stdout, "%8zu %7zu ", var.gridsize, infoStat.numMissVals);
+      reset_text_color(stdout);
+
+      fprintf(stdout, ":");
+
+      set_text_color(stdout, BLUE);
+    }
+
+  size_t imiss = 0;
+  // clang-format off
+  if (var.nwpv == CDI_REAL) compute_stat_real(field, infoStat, imiss, var.gridsize);
+  else                      compute_stat_comp(field, infoStat, imiss, var.gridsize);
+  // clang-format on
+
+  if (loutput)
+    {
+      // clang-format off
+      if (var.nwpv == CDI_REAL) print_stat_real(infoStat);
+      else                      print_stat_comp(infoStat);
+      // clang-format on
+
+      reset_text_color(stdout);
+
+      fprintf(stdout, " : ");
+
+      // set_text_color(stdout, GREEN);
+      // clang-format off
+      if      (operfunc == Func_Name) fprintf(stdout, "%-14s", var.name.c_str());
+      else if (operfunc == Func_Code) fprintf(stdout, "%4d   ", var.code);
+      else                            fprintf(stdout, "%-14s", paramstr);
+      // clang-format on
+      // reset_text_color(stdout);
+
+      fprintf(stdout, "\n");
+    }
+
+  if (imiss != numMissVals && numMissVals) cdo_warning("Found %zu of %zu missing values!", imiss, numMissVals);
+
+  if (printMap)
+    {
+      auto gridID = var.gridID;
+      auto gridtype = var.gridType;
+      auto nlon = gridInqXsize(gridID);
+      auto nlat = gridInqYsize(gridID);
 
+      if (gridtype == GRID_GAUSSIAN || gridtype == GRID_LONLAT || gridtype == GRID_CURVILINEAR
+          || (gridtype == GRID_GENERIC && nlon * nlat == var.gridsize && nlon < 2048))
+        {
+          print_map(nlon, nlat, field, infoStat);
+        }
+    }
+}
+
+class Info : public Process
+{
 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 } },
+    // clang-format off
+    .operators = { { "info", Func_Param, 0, InfoHelp },
+                   { "infop", Func_Param, 0, InfoHelp },
+                   { "infon", Func_Name, 0, InfoHelp },
+                   { "infoc", Func_Code, 0, InfoHelp },
+                   { "vinfon", Func_Name, 0, InfoHelp },
+                   { "xinfon", Func_Name, 0, InfoHelp },
+                   { "map", Func_Param, 0, InfoHelp } },
+    // clang-format on
     .aliases = {},
     .mode = EXPOSED,     // Module mode: 0:intern 1:extern
     .number = CDI_BOTH,  // Allowed number type
@@ -356,176 +446,97 @@ public:
   };
   inline static RegisterEntry<Info> registration = RegisterEntry<Info>(module);
 
-  int VINFON, XINFON, MAP;
-  size_t imiss = 0;
-  char paramstr[32];
-
-  int operatorID;
   int operfunc;
-  DateTimeList dtlist;
 
+  bool printMap;
   bool lvinfo;
 
   std::string e;  // parameter_name
-  std::string v;  // verbose;
 
 public:
   void
-  init()
+  init() override
   {
-    VINFON = module.get_id("vinfon");
-    XINFON = module.get_id("xinfon");
-    MAP = module.get_id("map");
+    auto VINFON = module.get_id("vinfon");
+    auto XINFON = module.get_id("xinfon");
+    auto MAP = module.get_id("map");
 
-    operatorID = cdo_operator_id();
+    auto operatorID = cdo_operator_id();
     operfunc = cdo_operator_f1(operatorID);
 
     operator_check_argc(0);
 
+    printMap = (operatorID == MAP);
     lvinfo = (operatorID == VINFON || operatorID == XINFON);
-
-    e = (operfunc == E_NAME) ? "Parameter name" : ((operfunc == E_CODE) ? "Code number" : "Parameter ID");
-
-    v = (Options::cdoVerbose) ? " : Extra" : "";
   }
 
   void
-  run()
+  run() override
   {
-    int indg = 0;
+    int numSets = 0;
+    auto numStreams = cdo_stream_cnt();
 
-    for (int indf = 0; indf < cdo_stream_cnt(); indf++)
+    for (int indf = 0; indf < numStreams; indf++)
       {
         auto streamID = cdo_open_read(indf);
         auto vlistID = cdo_stream_inq_vlist(streamID);
         auto taxisID = vlistInqTaxis(vlistID);
 
-        auto nvars = vlistNvars(vlistID);
-        if (nvars == 0) continue;
-
-        std::vector<Infostat> infostat(nvars);
+        VarList varList(vlistID);
+        auto numVars = varList.numVars();
+        if (numVars == 0) continue;
 
-        VarList varList;
-        varList_init(varList, vlistID);
+        auto runAsync = (Options::CDO_Parallel_Read > 0);
+        auto task = runAsync ? std::make_unique<cdo::Task>() : nullptr;
+        auto numTasks = runAsync ? 2 : 1;
 
-        Field field;
+        FieldVector fieldVector(numTasks);
+        std::vector<InfoStat> infoStatList(numVars);
 
-        indg = 0;
+        numSets = 0;
         int tsID = 0;
         while (true)
           {
             auto nrecs = cdo_stream_inq_timestep(streamID, tsID);
             if (nrecs == 0) break;
 
-            dtlist.taxis_inq_timestep(taxisID, 0);
-            auto vdateString = date_to_string(dtlist.get_vDateTime(0).date);
-            auto vtimeString = time_to_string(dtlist.get_vDateTime(0).time);
+            auto vDateTime = taxisInqVdatetime(taxisID);
 
-            for (int varID = 0; varID < nvars; ++varID) infostat_init(infostat[varID]);
+            for (int varID = 0; varID < numVars; ++varID) infostat_init(infoStatList[varID]);
 
             for (int recID = 0; recID < nrecs; ++recID)
               {
-                if ((tsID == 0 && recID == 0) || operatorID == MAP) print_header(-(indf + 1), lvinfo, e, v);
-
-                int varID, levelID;
-                cdo_inq_record(streamID, &varID, &levelID);
-                const auto &var = varList[varID];
+                auto [varID, levelID] = cdo_inq_record(streamID);
+                const auto &var = varList.vars[varID];
+                auto taskNum = numSets % numTasks;
+                auto &field = fieldVector[taskNum];
                 field.init(var);
                 cdo_read_record(streamID, field);
-                auto numMissVals = field.numMissVals;
-
-                indg = lvinfo ? varID + 1 : indg + 1;
 
-                auto loutput = !lvinfo;
+                if (runAsync && numSets > 0) { task->wait(); }
 
-                if (loutput) infostat_init(infostat[varID]);
-
-                auto &infostatr = infostat[varID];
-                infostatr.nlevels += 1;
-                infostatr.numMissVals += numMissVals;
-
-                if (var.nlevels == infostatr.nlevels) loutput = true;
-
-                if (loutput)
-                  {
-                    cdiParamToString(var.param, paramstr, sizeof(paramstr));
+                numSets = lvinfo ? varID + 1 : numSets + 1;
 
-                    fprintf(stdout, "%6d ", indg);
-                    fprintf(stdout, ":");
+                std::function<void()> info_func
+                    = std::bind(info, std::cref(field), numSets, indf, tsID, recID, levelID, vDateTime, std::cref(var), operfunc,
+                                printMap, lvinfo, std::ref(infoStatList[varID]));
 
-                    set_text_color(stdout, MAGENTA);
-                    fprintf(stdout, "%s %s ", vdateString.c_str(), vtimeString.c_str());
-                    reset_text_color(stdout);
-
-                    set_text_color(stdout, GREEN);
-                    if (lvinfo)
-                      fprintf(stdout, "%7d ", var.nlevels);
-                    else
-                      fprintf(stdout, "%7g ", cdo_zaxis_inq_level(var.zaxisID, levelID));
-
-                    fprintf(stdout, "%8zu %7zu ", var.gridsize, infostatr.numMissVals);
-                    reset_text_color(stdout);
-
-                    fprintf(stdout, ":");
-
-                    set_text_color(stdout, BLUE);
-                  }
-
-                // clang-format off
-                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);
-                    // clang-format on
-
-                    reset_text_color(stdout);
-
-                    fprintf(stdout, " : ");
-
-                    // 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);
-                    // clang-format on
-                    // reset_text_color(stdout);
-
-                    fprintf(stdout, "\n");
-                  }
-
-                if (imiss != numMissVals && numMissVals) cdo_warning("Found %zu of %zu missing values!", imiss, numMissVals);
-
-                if (operatorID == MAP)
-                  {
-                    auto gridID = var.gridID;
-                    auto gridtype = gridInqType(gridID);
-                    auto nlon = gridInqXsize(gridID);
-                    auto nlat = gridInqYsize(gridID);
-
-                    if (gridtype == GRID_GAUSSIAN || gridtype == GRID_LONLAT || gridtype == GRID_CURVILINEAR
-                        || (gridtype == GRID_GENERIC && nlon * nlat == gridInqSize(gridID) && nlon < 1024))
-                      {
-                        print_map(nlon, nlat, field, infostatr);
-                      }
-                  }
+                runAsync ? task->doAsync(info_func) : info_func();
               }
 
             tsID++;
           }
 
+        if (runAsync) task->wait();
+
         cdo_stream_close(streamID);
       }
 
-    if (indg > 36 && operatorID != MAP) print_header(0, lvinfo, e, v);
+    if (numSets > 36 && !printMap) print_header(0, lvinfo, operfunc);
   }
 
   void
-  close()
+  close() override
   {
   }
 };
diff --git a/src/Input.cc b/src/Input.cc
index 1dd086fde53c11009db73263846dd7fe2aa4b8b4..75c004595d6b86af6326bd531c7ee51b969a8395 100644
--- a/src/Input.cc
+++ b/src/Input.cc
@@ -77,7 +77,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
     INPUT = module.get_id("input");
     INPUTSRV = module.get_id("inputsrv");
@@ -99,7 +99,7 @@ public:
   }
 
   void
-  run()
+  run() override
   {
     double dlevel = 0;
     int nrecs = 0;
@@ -245,7 +245,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     if (streamID)
       {
diff --git a/src/Intgrid.cc b/src/Intgrid.cc
index 9d50f086864e16f8f155e844508a8736c522df10..2bf1fe9db97e7a867c5dfa8f296021d21bae4e5c 100644
--- a/src/Intgrid.cc
+++ b/src/Intgrid.cc
@@ -246,18 +246,14 @@ public:
   using Process::Process;
   inline static CdoModule module = {
     .name = "Intgrid",
-    .operators = { { "intgridbil"},
-                   { "intgriddis"},
-                   { "intgridnn"},
-                   { "interpolate"},
-                   { "boxavg"},
-                   { "thinout"} },
+    .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;
@@ -271,15 +267,15 @@ public:
   int taxisID2;
 
   bool lfieldmem;
-
-  VarList varList1, varList2;
   Field field1, field2;
 
+  VarList varList1;
+  VarList varList2;
+
 public:
   void
-  init()
+  init() override
   {
-
     INTGRIDBIL = module.get_id("intgridbil");
     INTGRIDDIS = module.get_id("intgriddis");
     INTGRIDNN = module.get_id("intgridnn");
@@ -313,7 +309,7 @@ public:
     taxisID2 = taxisDuplicate(taxisID1);
     vlistDefTaxis(vlistID2, taxisID2);
 
-    auto ngrids = vlistNgrids(vlistID1);
+    auto ngrids = vlistNumGrids(vlistID1);
     for (int index = 0; index < ngrids; ++index)
       {
         auto gridID1 = vlistGrid(vlistID1, index);
@@ -359,8 +355,8 @@ public:
         vlistChangeGridIndex(vlistID2, index, gridID2);
       }
 
-    varList_init(varList1, vlistID1);
-    varList_init(varList2, vlistID2);
+    varList1 = VarList(vlistID1);
+    varList2 = VarList(vlistID2);
 
     auto gridsizemax = vlistGridsizeMax(vlistID1);
     if (!lfieldmem) field1.resize(gridsizemax);
@@ -373,7 +369,7 @@ public:
   }
 
   void
-  run()
+  run() override
   {
     int tsID = 0;
     while (true)
@@ -390,29 +386,29 @@ public:
             cdo_inq_record(streamID1, &varID, &levelID);
             if (lfieldmem)
               {
-                field1.init(varList1[varID]);
+                field1.init(varList1.vars[varID]);
                 cdo_read_record(streamID1, field1);
 
-                field2.init(varList2[varID]);
+                field2.init(varList2.vars[varID]);
               }
             else
               {
                 cdo_read_record(streamID1, field1.vec_d.data(), &field1.numMissVals);
 
-                field1.grid = varList1[varID].gridID;
-                field1.missval = varList1[varID].missval;
+                field1.grid = varList1.vars[varID].gridID;
+                field1.missval = varList1.vars[varID].missval;
                 field2.grid = gridID2;
                 field2.missval = field1.missval;
                 field2.numMissVals = 0;
               }
 
             // clang-format off
-  	    if      (operatorID == INTGRIDBIL)  intgridbil(field1, field2);
-	    else if (operatorID == INTGRIDNN)   intgridnn(field1, field2);
-	    else if (operatorID == INTGRIDDIS)  intgriddis(field1, field2, 4);
-	    else if (operatorID == INTERPOLATE) interpolate(field1, field2);
-	    else if (operatorID == BOXAVG)      boxavg(field1, field2, xinc, yinc);
-	    else if (operatorID == THINOUT)     thinout(field1, field2, xinc, yinc);
+       	    if      (operatorID == INTGRIDBIL)  intgridbil(field1, field2);
+            else if (operatorID == INTGRIDNN)   intgridnn(field1, field2);
+            else if (operatorID == INTGRIDDIS)  intgriddis(field1, field2, 4);
+            else if (operatorID == INTERPOLATE) interpolate(field1, field2);
+            else if (operatorID == BOXAVG)      boxavg(field1, field2, xinc, yinc);
+            else if (operatorID == THINOUT)     thinout(field1, field2, xinc, yinc);
             // clang-format on
 
             cdo_def_record(streamID2, varID, levelID);
@@ -426,7 +422,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
diff --git a/src/Intgridtraj.cc b/src/Intgridtraj.cc
index 1cddbb4cb95f0dba504ca562fa017b8555c43ed1..198d4892ba548c93851c366ce33d39fb1c64c7c5 100644
--- a/src/Intgridtraj.cc
+++ b/src/Intgridtraj.cc
@@ -13,10 +13,8 @@
 #include "cdi.h"
 #include "julian_date.h"
 
-#include "cdo_vlist.h"
 #include "process_int.h"
 #include "interpol.h"
-#include "datetime.h"
 #include "varray.h"
 #include "printinfo.h"
 
@@ -48,13 +46,14 @@ public:
   using Process::Process;
   inline static CdoModule module = {
     .name = "Intgridtraj",
-    .operators = { { "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;
@@ -67,11 +66,10 @@ public:
 
   int vlistID2;
   int gridID2;
-  int nvars;
+  int numVars;
 
   Field field1, field2;
   VarList varList1;
-  std::vector<RecordInfo> recList;
 
   Varray2D<double> vardata1;
   Varray2D<double> vardata2;
@@ -82,9 +80,8 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
-
     operator_input_arg("filename with grid trajectories");
     operator_check_argc(1);
 
@@ -95,25 +92,21 @@ public:
     read_nextpos(fp, calendar, julianDate, xpos, ypos);
 
     streamID1 = cdo_open_read(0);
-    const auto vlistID1 = cdo_stream_inq_vlist(streamID1);
-
-    varList_init(varList1, vlistID1);
+    auto vlistID1 = cdo_stream_inq_vlist(streamID1);
 
-    nvars = vlistNvars(vlistID1);
+    varList1 = VarList(vlistID1);
+    numVars = varList1.numVars();
 
-    const auto maxrecs = vlistNrecs(vlistID1);
-    recList = std::vector<RecordInfo>(maxrecs);
-
-    const auto gridsizemax = vlistGridsizeMax(vlistID1);
+    auto gridsizemax = vlistGridsizeMax(vlistID1);
     field1.resize(gridsizemax);
     field2.resize(1);
 
-    vardata1 = Varray2D<double>(nvars);
-    vardata2 = Varray2D<double>(nvars);
-    for (int varID = 0; varID < nvars; ++varID)
+    vardata1 = Varray2D<double>(numVars);
+    vardata2 = Varray2D<double>(numVars);
+    for (int varID = 0; varID < numVars; ++varID)
       {
-        const auto gridsize = varList1[varID].gridsize;
-        const auto nlevels = varList1[varID].nlevels;
+        auto gridsize = varList1.vars[varID].gridsize;
+        auto nlevels = varList1.vars[varID].nlevels;
         vardata1[varID].resize(gridsize * nlevels);
         vardata2[varID].resize(gridsize * nlevels);
       }
@@ -126,10 +119,10 @@ public:
 
     vlistID2 = vlistDuplicate(vlistID1);
 
-    const auto ngrids = vlistNgrids(vlistID1);
+    auto ngrids = vlistNumGrids(vlistID1);
     for (int index = 0; index < ngrids; ++index)
       {
-        const auto gridID1 = vlistGrid(vlistID1, index);
+        auto gridID1 = vlistGrid(vlistID1, index);
 
         if (gridInqType(gridID1) != GRID_LONLAT && gridInqType(gridID1) != GRID_GAUSSIAN)
           cdo_abort("Unsupported grid type: %s", gridNamePtr(gridInqType(gridID1)));
@@ -143,8 +136,11 @@ public:
   }
 
   void
-  run()
+  run() override
   {
+    auto maxRecords = varList1.numRecords();
+    std::vector<RecordInfo> recordList(maxRecords);
+
     int tsID = 0;
     auto nrecs = cdo_stream_inq_timestep(streamID1, tsID++);
     auto julianDate1 = julianDate_encode(calendar, taxisInqVdatetime(taxisID1));
@@ -152,8 +148,8 @@ public:
       {
         int varID, levelID;
         cdo_inq_record(streamID1, &varID, &levelID);
-        const auto gridsize = varList1[varID].gridsize;
-        const auto offset = gridsize * levelID;
+        auto gridsize = varList1.vars[varID].gridsize;
+        auto offset = gridsize * levelID;
         auto single1 = &vardata1[varID][offset];
         cdo_read_record(streamID1, single1, &numMissVals);
         if (numMissVals) cdo_abort("Missing values unsupported for this operator!");
@@ -171,10 +167,10 @@ public:
             int varID, levelID;
             cdo_inq_record(streamID1, &varID, &levelID);
 
-            recList[recID].set(varID, levelID);
+            recordList[recID].set(varID, levelID);
 
-            const auto gridsize = varList1[varID].gridsize;
-            const auto offset = gridsize * levelID;
+            auto gridsize = varList1.vars[varID].gridsize;
+            auto offset = gridsize * levelID;
             auto single2 = &vardata2[varID][offset];
             cdo_read_record(streamID1, single2, &numMissVals);
             if (numMissVals) cdo_abort("Missing values unsupported for this operator!");
@@ -194,24 +190,25 @@ public:
                 taxisDefVdatetime(taxisID2, julianDate_decode(calendar, julianDate));
                 cdo_def_timestep(streamID2, tsIDo++);
 
-                const auto deltat = julianDate_to_seconds(julianDate_sub(julianDate2, julianDate1));
-                const auto fac1 = julianDate_to_seconds(julianDate_sub(julianDate2, julianDate)) / deltat;
-                const auto fac2 = julianDate_to_seconds(julianDate_sub(julianDate, julianDate1)) / deltat;
+                auto deltat = julianDate_to_seconds(julianDate_sub(julianDate2, julianDate1));
+                auto fac1 = julianDate_to_seconds(julianDate_sub(julianDate2, julianDate)) / deltat;
+                auto fac2 = julianDate_to_seconds(julianDate_sub(julianDate, julianDate1)) / deltat;
                 // printf("      %f %f %f %f %f\n", julianDate_to_seconds(julianDate), julianDate_to_seconds(julianDate1),
                 // julianDate_to_seconds(julianDate2), fac1, fac2);
                 for (int recID = 0; recID < nrecs; ++recID)
                   {
-                    auto [varID, levelID] = recList[recID].get();
+                    auto [varID, levelID] = recordList[recID].get();
 
-                    const auto gridsize = varList1[varID].gridsize;
-                    const auto offset = gridsize * levelID;
+                    auto &var1 = varList1.vars[varID];
+                    auto gridsize = var1.gridsize;
+                    auto offset = gridsize * levelID;
                     auto single1 = &vardata1[varID][offset];
                     auto single2 = &vardata2[varID][offset];
 
                     for (size_t i = 0; i < gridsize; ++i) field1.vec_d[i] = single1[i] * fac1 + single2[i] * fac2;
 
-                    field1.grid = varList1[varID].gridID;
-                    field1.missval = varList1[varID].missval;
+                    field1.grid = var1.gridID;
+                    field1.missval = var1.missval;
                     field1.numMissVals = numMissVals;
                     field2.grid = gridID2;
                     field2.numMissVals = 0;
@@ -229,7 +226,7 @@ public:
           }
 
         julianDate1 = julianDate2;
-        for (int varID = 0; varID < nvars; ++varID)
+        for (int varID = 0; varID < numVars; ++varID)
           {
             auto vardatap = vardata1[varID];
             vardata1[varID] = vardata2[varID];
@@ -239,12 +236,13 @@ public:
 
     if (tsIDo == 0)
       {
-        const auto dt = julianDate_decode(calendar, julianDate);
+        auto dt = julianDate_decode(calendar, julianDate);
         cdo_warning("Date/time %s %s not found!", date_to_string(dt.date), time_to_string(dt.time));
       }
   }
+
   void
-  close()
+  close() override
   {
     std::fclose(fp);
     if (streamID2 != CDO_STREAM_UNDEF) cdo_stream_close(streamID2);
diff --git a/src/Intlevel.cc b/src/Intlevel.cc
index d5e87c999a67cca7d1fe4b9c50d9cad862baf02f..869a447fdc6e4074a52fa2bb03de7efb4a6b6208 100644
--- a/src/Intlevel.cc
+++ b/src/Intlevel.cc
@@ -16,9 +16,7 @@
 #include "cimdOmp.h"
 #include "cdo_options.h"
 #include "process_int.h"
-#include "cdo_vlist.h"
 #include "cdo_zaxis.h"
-#include "parse_literals.h"
 #include "pmlist.h"
 #include "param_conversion.h"
 
@@ -352,30 +350,31 @@ intlevel_get_parameter(std::string &zdescription, std::string &zvarname)
 }
 
 static void
-handle_zvar(size_t &zvarGridsize, size_t &wisize, int nvars, const std::string &zvarname, const VarList &varList1, int &zvarID,
+handle_zvar(size_t &zvarGridsize, size_t &wisize, const std::string &zvarname, const VarList &varList1, int &zvarID,
             bool &zvarIsVarying, int &nlevel, const std::vector<double> &lev2, int &nlev1, const int &nlev2, const int &vlistID1,
             int &zaxisID2, int &zaxisID1)
 {
-  for (int varID = 0; varID < nvars; ++varID)
+  for (const auto &var : varList1.vars)
     {
-      if (zvarname == varList1[varID].name)
+      if (zvarname == var.name)
         {
-          zvarID = varID;
+          zvarID = var.ID;
           break;
         }
     }
 
+  const auto &zvar = varList1.vars[zvarID];
   if (zvarID == CDI_UNDEFID) cdo_abort("Variable %s not found!", zvarname);
-  zvarIsVarying = (varList1[zvarID].timetype == TIME_VARYING);
-  zvarGridsize = varList1[zvarID].gridsize;
-  nlev1 = varList1[zvarID].nlevels;
+  zvarIsVarying = (zvar.timeType == TIME_VARYING);
+  zvarGridsize = zvar.gridsize;
+  nlev1 = zvar.nlevels;
 
   if (zaxisID2 == CDI_UNDEFID) zaxisID2 = create_zaxis_from_zvar(lev2, vlistID1, zvarID);
 
   wisize = zvarGridsize * nlev2;
 
   int i;
-  auto nzaxis = vlistNzaxis(vlistID1);
+  auto nzaxis = vlistNumZaxis(vlistID1);
   for (i = 0; i < nzaxis; ++i)
     {
       auto zaxisID = vlistZaxis(vlistID1, i);
@@ -394,7 +393,7 @@ handle_empty_zvar(size_t &wisize, int &nlevel, std::vector<double> &lev1, const
                   const int &nlev2, const int &vlistID1, int &zaxisID2, int &zaxisID1)
 {
   int i;
-  auto nzaxis = vlistNzaxis(vlistID1);
+  auto nzaxis = vlistNumZaxis(vlistID1);
   for (i = 0; i < nzaxis; ++i)
     {
       auto zaxisID = vlistZaxis(vlistID1, i);
@@ -466,7 +465,7 @@ public:
   int nlev1 = 0;
   bool zvarIsVarying = false;
 
-  int nvars;
+  int numVars;
 
   bool expol;
 
@@ -486,7 +485,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
     INTLEVEL = module.get_id("intlevel");
     INTLEVELX = module.get_id("intlevelx");
@@ -535,11 +534,11 @@ public:
     taxisID2 = taxisDuplicate(taxisID1);
     vlistDefTaxis(vlistID2, taxisID2);
 
-    varList_init(varList1, vlistID1);
-    varListSetUniqueMemtype(varList1);
-    memType = varList1[0].memType;
+    varList1 = VarList(vlistID1);
+    varList_set_unique_memtype(varList1);
+    memType = varList1.vars[0].memType;
 
-    nvars = vlistNvars(vlistID1);
+    numVars = varList1.numVars();
 
     // Find z-variable
     size_t wisize = 0;
@@ -547,11 +546,11 @@ public:
     if (zvarname.empty()) { handle_empty_zvar(wisize, nlevel, lev1, lev2, nlev1, nlev2, vlistID1, zaxisID2, zaxisID1); }
     else
       {
-        handle_zvar(zvarGridsize, wisize, nvars, zvarname, varList1, zvarID, zvarIsVarying, nlevel, lev2, nlev1, nlev2, vlistID1,
-                    zaxisID2, zaxisID1);
+        handle_zvar(zvarGridsize, wisize, zvarname, varList1, zvarID, zvarIsVarying, nlevel, lev2, nlev1, nlev2, vlistID1, zaxisID2,
+                    zaxisID1);
       }
 
-    auto nzaxis = vlistNzaxis(vlistID1);
+    auto nzaxis = vlistNumZaxis(vlistID1);
     for (int index = 0; index < nzaxis; ++index)
       {
         auto zaxisID = vlistZaxis(vlistID1, index);
@@ -559,8 +558,8 @@ public:
         if (zaxisID == zaxisID1 || nlevels == nlev1) vlistChangeZaxisIndex(vlistID2, index, zaxisID2);
       }
 
-    varList_init(varList2, vlistID2);
-    varListSetMemtype(varList2, memType);
+    varList2 = VarList(vlistID2);
+    varList_set_memtype(varList2, memType);
 
     lev_idx = Varray<int>(wisize);
     lev_wgt = Varray<float>(wisize);
@@ -568,33 +567,34 @@ public:
     streamID2 = cdo_open_write(1);
     cdo_def_vlist(streamID2, vlistID2);
 
-    processVars = std::vector<bool>(nvars);
-    interpVars = std::vector<bool>(nvars, false);
-    varnumMissVals = std::vector<std::vector<size_t>>(nvars);
-    vardata1 = Field3DVector(nvars);
-    vardata2 = Field3DVector(nvars);
+    processVars = std::vector<bool>(numVars);
+    interpVars = std::vector<bool>(numVars, false);
+    varnumMissVals = std::vector<std::vector<size_t>>(numVars);
+    vardata1 = Field3DVector(numVars);
+    vardata2 = Field3DVector(numVars);
 
     int maxlev = (nlev1 > nlev2) ? nlev1 : nlev2;
 
-    for (int varID = 0; varID < nvars; ++varID)
+    for (int varID = 0; varID < numVars; ++varID)
       {
-        auto nlevels = varList1[varID].nlevels;
+        const auto &var1 = varList1.vars[varID];
+        auto nlevels = var1.nlevels;
 
-        vardata1[varID].init(varList1[varID]);
+        vardata1[varID].init(var1);
 
-        interpVars[varID] = (varList1[varID].zaxisID == zaxisID1 || nlevels == nlev1);
+        interpVars[varID] = (var1.zaxisID == zaxisID1 || nlevels == nlev1);
 
         if (interpVars[varID])
           {
             varnumMissVals[varID].resize(maxlev, 0);
-            vardata2[varID].init(varList2[varID]);
+            vardata2[varID].init(varList2.vars[varID]);
           }
         else { varnumMissVals[varID].resize(nlevels); }
       }
   }
 
   void
-  run()
+  run() override
   {
     int tsID = 0;
     while (true)
@@ -602,7 +602,7 @@ public:
         auto nrecs = cdo_stream_inq_timestep(streamID1, tsID);
         if (nrecs == 0) break;
 
-        for (int varID = 0; varID < nvars; ++varID) processVars[varID] = false;
+        for (int varID = 0; varID < numVars; ++varID) processVars[varID] = false;
 
         cdo_taxis_copy_timestep(taxisID2, taxisID1);
         cdo_def_timestep(streamID2, tsID);
@@ -623,12 +623,14 @@ public:
               vert_gen_weights(expol, nlev1 + 2, lev1, nlev2, lev2, lev_idx, lev_wgt);
           }
 
-        for (int varID = 0; varID < nvars; ++varID)
+        for (int varID = 0; varID < numVars; ++varID)
           {
             if (processVars[varID] && interpVars[varID])
               {
-                auto missval = varList1[varID].missval;
-                auto gridsize = varList1[varID].gridsize;
+                const auto &var1 = varList1.vars[varID];
+
+                auto missval = var1.missval;
+                auto gridsize = var1.gridsize;
 
                 if (!zvarname.empty())
                   vert_interp_lev3d(gridsize, nlev1, missval, vardata1[varID], vardata2[varID], nlev2, lev_idx, lev_wgt);
@@ -646,11 +648,11 @@ public:
               }
           }
 
-        for (int varID = 0; varID < nvars; ++varID)
+        for (int varID = 0; varID < numVars; ++varID)
           {
             if (processVars[varID])
               {
-                for (int levelID = 0; levelID < varList2[varID].nlevels; ++levelID)
+                for (int levelID = 0; levelID < varList2.vars[varID].nlevels; ++levelID)
                   {
                     cdo_def_record(streamID2, varID, levelID);
                     cdo_write_record(streamID2, interpVars[varID] ? vardata2[varID] : vardata1[varID], levelID,
@@ -664,7 +666,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
diff --git a/src/Intlevel3d.cc b/src/Intlevel3d.cc
index e4225db4ad2f3860a67f8dce389079201b59a6d1..0e7323f774e44b096019dc873686655214ac3266 100644
--- a/src/Intlevel3d.cc
+++ b/src/Intlevel3d.cc
@@ -18,7 +18,6 @@
 #include "cimdOmp.h"
 #include "cdo_options.h"
 #include "process_int.h"
-#include "cdo_vlist.h"
 #include "cdi_lockedIO.h"
 
 void vert_interp_lev3d(size_t gridsize, int nlev1, double missval, const Field3D &field1, Field3D &field2, int nlev2,
@@ -74,28 +73,26 @@ vert_gen_weights3d(bool expol, size_t gridsize, int nlev1, const Varray<float> &
 }
 
 static void
-vlist_copy_var_attributes(const VarList &varList0, int varID0, int vlistID3, int oz3dvarID)
+vlist_copy_var_attributes(const CdoVar &var0, int vlistID3, int oz3dvarID)
 {
-  const auto &var0 = varList0[varID0];
   cdiDefKeyString(vlistID3, oz3dvarID, CDI_KEY_NAME, var0.name.c_str());
   if (var0.longname.size()) cdiDefKeyString(vlistID3, oz3dvarID, CDI_KEY_LONGNAME, var0.longname.c_str());
   if (var0.units.size()) cdiDefKeyString(vlistID3, oz3dvarID, CDI_KEY_UNITS, var0.units.c_str());
 }
 
-static void
-read_source_coordinate(int streamNumber, VarList &varList2, Varray<float> &zlevelsIn)
+static CdoVar
+read_source_coordinate(int streamNumber, Varray<float> &zlevelsIn)
 {
   auto streamID2 = cdo_open_read(streamNumber);  // 3d vertical source coordinate
   auto vlistID2 = cdo_stream_inq_vlist(streamID2);
 
-  varList_init(varList2, vlistID2);
+  VarList varList(vlistID2);
 
-  if (vlistNvars(vlistID2) != 1) cdo_abort("infile2: Only one single variable is allowed!");
+  if (varList.numVars() != 1) cdo_abort("infile2: Only one single variable is allowed!");
 
-  auto gridsize = varList2[0].gridsize;
-  auto nlevels = varList2[0].nlevels;
+  const auto &var0 = varList.vars[0];
 
-  zlevelsIn.resize(gridsize * nlevels);
+  zlevelsIn.resize(var0.gridsize * var0.nlevels);
 
   auto nrecs = cdo_stream_inq_timestep(streamID2, 0);
   if (Options::cdoVerbose) cdo_print("%d records input 3d vertical height", nrecs);
@@ -104,44 +101,47 @@ read_source_coordinate(int streamNumber, VarList &varList2, Varray<float> &zleve
     {
       int varID, levelID;
       cdo_inq_record(streamID2, &varID, &levelID);
-      auto offset = gridsize * levelID;
+      auto offset = var0.gridsize * levelID;
       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);
+
+  return var0;
 }
 
-static void
-read_target_coordinate(const std::string &fileName, VarList &varList0, Varray<float> &zlevelsOut)
+static CdoVar
+read_target_coordinate(const std::string &fileName, Varray<float> &zlevelsOut)
 {
   auto streamID0 = stream_open_read_locked(fileName.c_str());  // 3d vertical target coordinate
   auto vlistID0 = streamInqVlist(streamID0);
 
-  varList_init(varList0, vlistID0);
+  VarList varList(vlistID0);
 
-  if (vlistNvars(vlistID0) != 1) cdo_abort("tgtcoordinate: Only one single variable is allowed!");
+  if (varList.numVars() != 1) cdo_abort("tgtcoordinate: Only one single variable is allowed!");
 
-  auto gridsize = varList0[0].gridsize;
-  auto nlevels = varList0[0].nlevels;
+  const auto var0 = varList.vars[0];
 
-  zlevelsOut.resize(gridsize * nlevels);
+  zlevelsOut.resize(var0.gridsize * var0.nlevels);
 
   auto nrecs = streamInqTimestep(streamID0, 0);
-  if (Options::cdoVerbose) cdo_print("%d records target 3d vertical height and gridsize %zu", nrecs, gridsize);
+  if (Options::cdoVerbose) cdo_print("%d records target 3d vertical height and gridsize %zu", nrecs, var0.gridsize);
 
   for (int recID = 0; recID < nrecs; ++recID)
     {
       int varID, levelID;
       streamInqRecord(streamID0, &varID, &levelID);
-      auto offset = gridsize * levelID;
+      auto offset = var0.gridsize * levelID;
       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);
+
+  return var0;
 }
 
 class Intlevel3d : public Process
@@ -167,7 +167,7 @@ public:
   int taxisID3;
   int vlistID3;
 
-  int nvars;
+  int numVars;
 
   int nlevi;
   int nlevo;
@@ -194,13 +194,10 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
-
-    // clang-format off
-INTLEVEL3D = module.get_id("intlevel3d");
-INTLEVELX3D = module.get_id("intlevelx3d");
-    // clang-format on
+    INTLEVEL3D = module.get_id("intlevel3d");
+    INTLEVELX3D = module.get_id("intlevelx3d");
 
     (void) INTLEVEL3D;  // unused
 
@@ -222,28 +219,23 @@ INTLEVELX3D = module.get_id("intlevelx3d");
     taxisID3 = taxisDuplicate(taxisID1);
     vlistDefTaxis(vlistID3, taxisID1);
 
-    varList_init(varList1, vlistID1);
-    varListSetUniqueMemtype(varList1);
-    memType = varList1[0].memType;
+    varList1 = VarList(vlistID1);
+    varList_set_unique_memtype(varList1);
+    memType = varList1.vars[0].memType;
 
     // Read 3d source coordinate (streamID2)
-    VarList varList2;
     Varray<float> zlevelsIn;
-
-    read_source_coordinate(1, varList2, zlevelsIn);
-
-    auto gridsizei = varList2[0].gridsize;  // horizontal gridsize of input z coordinate
-    nlevi = varList2[0].nlevels;            // number of input levels for later use
+    auto zvari = read_source_coordinate(1, zlevelsIn);
+    auto gridsizei = zvari.gridsize;
+    nlevi = zvari.nlevels;
 
     // Read 3d target coordinate (streamID0)
-    VarList varList0;
-
-    read_target_coordinate(cdo_operator_argv(0), varList0, zlevelsOut);
+    auto zvaro = read_target_coordinate(cdo_operator_argv(0), zlevelsOut);
 
-    auto gridsizeo = varList0[0].gridsize;  // horizontal gridsize of output z coordinate
-    nlevo = varList0[0].nlevels;            // number of output levels for later use
-    auto gridID3 = varList0[0].gridID;
-    auto zaxisID3 = varList0[0].zaxisID;
+    auto gridsizeo = zvaro.gridsize;  // horizontal gridsize of output z coordinate
+    nlevo = zvaro.nlevels;            // number of output levels for later use
+    auto gridID3 = zvaro.gridID;
+    auto zaxisID3 = zvaro.zaxisID;
 
     // gridsize of input and output vertical coordinate must be equal (later use of gridsizeo ONLY)
     if (gridsizei != gridsizeo) cdo_abort("Input and output vertical coordinate must have the same gridsize!");
@@ -256,7 +248,7 @@ INTLEVELX3D = module.get_id("intlevelx3d");
      * Variables with a different z-axis should be copied into output.
      */
     int zaxisID1 = -1;
-    auto nzaxis = vlistNzaxis(vlistID1);
+    auto nzaxis = vlistNumZaxis(vlistID1);
     int i;
     for (i = 0; i < nzaxis; ++i)
       {
@@ -270,7 +262,7 @@ INTLEVELX3D = module.get_id("intlevelx3d");
       }
     if (i == nzaxis) cdo_abort("No processable variable found (vertical coordinate differ)!");
 
-    auto ngrids = vlistNgrids(vlistID1);
+    auto ngrids = vlistNumGrids(vlistID1);
     for (i = 0; i < ngrids; ++i)
       {
         auto gridsize = gridInqSize(vlistGrid(vlistID1, i));
@@ -290,30 +282,31 @@ INTLEVELX3D = module.get_id("intlevelx3d");
 
     // add the vertical output field to the output stream
     oz3dvarID = vlistDefVar(vlistID3, gridID3, zaxisID3, TIME_VARYING);
-    vlist_copy_var_attributes(varList0, 0, vlistID3, oz3dvarID);
+    vlist_copy_var_attributes(zvaro, vlistID3, oz3dvarID);
 
     streamID3 = cdo_open_write(2);  // output stream
     cdo_def_vlist(streamID3, vlistID3);
 
-    varList_init(varList3, vlistID3);
-    varListSetMemtype(varList3, memType);
+    varList3 = VarList(vlistID3);
+    varList_set_memtype(varList3, memType);
 
     auto maxlev = std::max(nlevi, nlevo);
-    nvars = vlistNvars(vlistID1);
+    numVars = varList1.numVars();
 
-    processVars = std::vector<bool>(nvars);
-    interpVars = std::vector<bool>(nvars, false);        // marker for variables to be interpolated
-    varnumMissVals = std::vector<std::vector<size_t>>(nvars);  // can for missing values of arbitrary variables
-    vardata1 = Field3DVector(nvars);
-    vardata2 = Field3DVector(nvars);
+    processVars = std::vector<bool>(numVars);
+    interpVars = std::vector<bool>(numVars, false);              // marker for variables to be interpolated
+    varnumMissVals = std::vector<std::vector<size_t>>(numVars);  // can for missing values of arbitrary variables
+    vardata1 = Field3DVector(numVars);
+    vardata2 = Field3DVector(numVars);
 
-    for (int varID = 0; varID < nvars; ++varID)
+    for (int varID = 0; varID < numVars; ++varID)
       {
-        auto zaxisID = varList1[varID].zaxisID;
-        auto gridsize = varList1[varID].gridsize;
-        auto nlevel = varList1[varID].nlevels;
+        const auto &var1 = varList1.vars[varID];
+        auto zaxisID = var1.zaxisID;
+        auto gridsize = var1.gridsize;
+        auto nlevel = var1.nlevels;
 
-        vardata1[varID].init(varList1[varID]);
+        vardata1[varID].init(var1);
 
         /*  variabls for interpolation:
          *  * have the required vertical axis, i.e. the correct number of levels (nlevi)
@@ -324,25 +317,24 @@ INTLEVELX3D = module.get_id("intlevelx3d");
         if (interpVars[varID])
           {
             varnumMissVals[varID].resize(maxlev, 0);
-            vardata2[varID].init(varList3[varID]);
+            vardata2[varID].init(varList3.vars[varID]);
           }
         else
           {
             varnumMissVals[varID].resize(nlevel);
-            if (Options::cdoVerbose)
-              cdo_print("Ignore variable %s (levels=%d gridsize=%zu)!", varList1[varID].name, nlevel, gridsize);
+            if (Options::cdoVerbose) cdo_print("Ignore variable %s (levels=%d gridsize=%zu)!", var1.name, nlevel, gridsize);
           }
       }
 
     {
       int varID;
-      for (varID = 0; varID < nvars; ++varID)
+      for (varID = 0; varID < numVars; ++varID)
         if (interpVars[varID]) break;
-      if (varID == nvars) cdo_abort("No processable variable found!");
+      if (varID == numVars) cdo_abort("No processable variable found!");
     }
   }
   void
-  run()
+  run() override
   {
 
     int tsID = 0;
@@ -351,7 +343,7 @@ INTLEVELX3D = module.get_id("intlevelx3d");
         auto nrecs = cdo_stream_inq_timestep(streamID1, tsID);
         if (nrecs == 0) break;
 
-        for (int varID = 0; varID < nvars; ++varID) processVars[varID] = false;
+        for (int varID = 0; varID < numVars; ++varID) processVars[varID] = false;
 
         cdo_taxis_copy_timestep(taxisID3, taxisID1);
         cdo_def_timestep(streamID3, tsID);
@@ -366,12 +358,13 @@ INTLEVELX3D = module.get_id("intlevelx3d");
           }
 
         // Perform the interpolation on all valid data variables
-        for (int varID = 0; varID < nvars; ++varID)
+        for (int varID = 0; varID < numVars; ++varID)
           {
+            const auto &var1 = varList1.vars[varID];
             if (processVars[varID] && interpVars[varID])
               {
-                auto gridsize = varList1[varID].gridsize;
-                auto missval = varList1[varID].missval;
+                auto gridsize = var1.gridsize;
+                auto missval = var1.missval;
 
                 vert_interp_lev3d(gridsize, nlevi, missval, vardata1[varID], vardata2[varID], nlevo, lev_idx, lev_wgt);
 
@@ -386,16 +379,16 @@ INTLEVELX3D = module.get_id("intlevelx3d");
               }
             else
               {
-                if (Options::cdoVerbose && tsID <= 1) cdo_print("Perform no interpolation on variable %s", varList1[varID].name);
+                if (Options::cdoVerbose && tsID <= 1) cdo_print("Perform no interpolation on variable %s", var1.name);
               }
           }
 
         // write the output
-        for (int varID = 0; varID < nvars; ++varID)
+        for (int varID = 0; varID < numVars; ++varID)
           {
             if (processVars[varID])
               {
-                for (int levelID = 0; levelID < varList3[varID].nlevels; ++levelID)
+                for (int levelID = 0; levelID < varList3.vars[varID].nlevels; ++levelID)
                   {
                     cdo_def_record(streamID3, varID, levelID);
                     cdo_write_record(streamID3, interpVars[varID] ? vardata2[varID] : vardata1[varID], levelID,
@@ -405,9 +398,9 @@ INTLEVELX3D = module.get_id("intlevelx3d");
           }
 
         // copy output z coordinate to output stream
-        for (int levelID = 0; levelID < varList3[oz3dvarID].nlevels; ++levelID)
+        for (int levelID = 0; levelID < varList3.vars[oz3dvarID].nlevels; ++levelID)
           {
-            auto offset = varList3[oz3dvarID].gridsize * levelID;
+            auto offset = varList3.vars[oz3dvarID].gridsize * levelID;
             cdo_def_record(streamID3, oz3dvarID, levelID);
             cdo_write_record_f(streamID3, &zlevelsOut[offset], 0);
           }
@@ -416,7 +409,7 @@ INTLEVELX3D = module.get_id("intlevelx3d");
       }
   }
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID1);
     cdo_stream_close(streamID3);
diff --git a/src/Intntime.cc b/src/Intntime.cc
index 69907f3dac5abbfe49504c09b25a3b8aa295c763..faa47842e7c8913e90b3f0290eafcd9d58b9477a 100644
--- a/src/Intntime.cc
+++ b/src/Intntime.cc
@@ -16,12 +16,11 @@
 
 #include "cdo_options.h"
 #include "process_int.h"
-#include "cdo_vlist.h"
 #include "param_conversion.h"
-#include "datetime.h"
 #include "printinfo.h"
+#include "field_functions.h"
 
-void interp_time(double fac1, double fac2, const double *array1, const double *array2, Field &field3, bool withMissval);
+void interp_time(double fac1, double fac2, const Field &field1, const Field &field2, Field &field3, bool withMissval);
 
 class Intntime : public Process
 {
@@ -36,35 +35,22 @@ public:
     .constraints = { 1, 1, NoRestriction },
   };
   inline static RegisterEntry<Intntime> registration = RegisterEntry<Intntime>(module);
+
   int curFirst = 0, curSecond = 1;
 
   CdoStreamID streamID1;
   CdoStreamID streamID2;
 
-  int tsID = 0;
-  int tsIDo = 0;
-
-  int nrecs;
-
   int taxisID1;
   int taxisID2;
 
-  int calendar;
-
-  JulianDate julianDate1;
-
   int numts;
-  VarList varList;
-  Field field3;
-  std::vector<RecordInfo> recList;
-  Varray3D<size_t> numMissVals;
-  Varray3D<double> vardata;
+  VarList varList1;
 
 public:
   void
-  init()
+  init() override
   {
-
     operator_input_arg("number of timesteps between 2 timesteps");
     if (cdo_operator_argc() < 1) cdo_abort("Too few arguments!");
 
@@ -72,33 +58,10 @@ public:
     if (numts < 2) cdo_abort("parameter must be greater than 1!");
 
     streamID1 = cdo_open_read(0);
-
     auto vlistID1 = cdo_stream_inq_vlist(streamID1);
     auto vlistID2 = vlistDuplicate(vlistID1);
 
-    varList_init(varList, vlistID1);
-
-    auto nvars = vlistNvars(vlistID1);
-
-    auto maxrecs = vlistNrecs(vlistID1);
-    recList = std::vector<RecordInfo>(maxrecs);
-
-    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);
-
-    for (int varID = 0; varID < nvars; ++varID)
-      {
-        auto gridsize = varList[varID].gridsize;
-        auto nlevel = varList[varID].nlevels;
-        numMissVals[0][varID].resize(nlevel);
-        numMissVals[1][varID].resize(nlevel);
-        vardata[0][varID].resize(gridsize * nlevel);
-        vardata[1][varID].resize(gridsize * nlevel);
-      }
+    varList1 = VarList(vlistID1);
 
     taxisID1 = vlistInqTaxis(vlistID1);
     taxisID2 = taxisDuplicate(taxisID1);
@@ -108,31 +71,40 @@ public:
     vlistDefTaxis(vlistID2, taxisID2);
 
     streamID2 = cdo_open_write(1);
-
     cdo_def_vlist(streamID2, vlistID2);
+  }
 
-    calendar = taxisInqCalendar(taxisID1);
+  void
+  run() override
+  {
+    Field field3;
+    FieldVector2D varsData[2];
+    field2D_init(varsData[0], varList1, FIELD_VEC | FIELD_NAT);
+    field2D_init(varsData[1], varList1, FIELD_VEC | FIELD_NAT);
+
+    auto maxRecords = varList1.numRecords();
+    std::vector<RecordInfo> recordList(maxRecords);
+
+    auto calendar = taxisInqCalendar(taxisID1);
 
-    nrecs = cdo_stream_inq_timestep(streamID1, tsID++);
-    julianDate1 = julianDate_encode(calendar, taxisInqVdatetime(taxisID1));
+    int tsID = 0;
+    int tsIDo = 0;
+
+    auto nrecs = cdo_stream_inq_timestep(streamID1, tsID++);
+    auto julianDate1 = julianDate_encode(calendar, taxisInqVdatetime(taxisID1));
 
     cdo_taxis_copy_timestep(taxisID2, taxisID1);
     cdo_def_timestep(streamID2, tsIDo++);
     for (int recID = 0; recID < nrecs; ++recID)
       {
-        int varID, levelID;
-        cdo_inq_record(streamID1, &varID, &levelID);
-        auto offset = varList[varID].gridsize * levelID;
-        auto single1 = &vardata[curFirst][varID][offset];
-        cdo_read_record(streamID1, single1, &numMissVals[curFirst][varID][levelID]);
+        auto [varID, levelID] = cdo_inq_record(streamID1);
+        auto &field = varsData[curFirst][varID][levelID];
+        cdo_read_record(streamID1, field);
 
         cdo_def_record(streamID2, varID, levelID);
-        cdo_write_record(streamID2, single1, numMissVals[curFirst][varID][levelID]);
+        cdo_write_record(streamID2, field);
       }
-  }
-  void
-  run()
-  {
+
     while (true)
       {
         nrecs = cdo_stream_inq_timestep(streamID1, tsID++);
@@ -143,14 +115,10 @@ public:
 
         for (int recID = 0; recID < nrecs; ++recID)
           {
-            int varID, levelID;
-            cdo_inq_record(streamID1, &varID, &levelID);
-
-            recList[recID].set(varID, levelID);
-
-            auto offset = varList[varID].gridsize * levelID;
-            auto single2 = &vardata[curSecond][varID][offset];
-            cdo_read_record(streamID1, single2, &numMissVals[curSecond][varID][levelID]);
+            auto [varID, levelID] = cdo_inq_record(streamID1);
+            recordList[recID].set(varID, levelID);
+            auto &field = varsData[curSecond][varID][levelID];
+            cdo_read_record(streamID1, field);
           }
 
         for (int it = 1; it < numts; it++)
@@ -170,16 +138,15 @@ public:
 
             for (int recID = 0; recID < nrecs; ++recID)
               {
-                auto [varID, levelID] = recList[recID].get();
+                auto [varID, levelID] = recordList[recID].get();
 
-                auto offset = varList[varID].gridsize * levelID;
-                auto single1 = &vardata[curFirst][varID][offset];
-                auto single2 = &vardata[curSecond][varID][offset];
+                const auto &field1 = varsData[curFirst][varID][levelID];
+                const auto &field2 = varsData[curSecond][varID][levelID];
 
-                field3.init(varList[varID]);
+                field3.init(varList1.vars[varID]);
 
-                auto withMissval = (numMissVals[curFirst][varID][levelID] || numMissVals[curSecond][varID][levelID]);
-                interp_time(fac1, fac2, single1, single2, field3, withMissval);
+                auto withMissval = (field1.numMissVals || field2.numMissVals);
+                interp_time(fac1, fac2, field1, field2, field3, withMissval);
 
                 cdo_def_record(streamID2, varID, levelID);
                 cdo_write_record(streamID2, field3);
@@ -190,21 +157,19 @@ public:
         cdo_def_timestep(streamID2, tsIDo++);
         for (int recID = 0; recID < nrecs; ++recID)
           {
-            auto [varID, levelID] = recList[recID].get();
-
-            auto offset = varList[varID].gridsize * levelID;
-            auto single2 = &vardata[curSecond][varID][offset];
-
+            auto [varID, levelID] = recordList[recID].get();
+            auto &field = varsData[curSecond][varID][levelID];
             cdo_def_record(streamID2, varID, levelID);
-            cdo_write_record(streamID2, single2, numMissVals[curSecond][varID][levelID]);
+            cdo_write_record(streamID2, field);
           }
 
         julianDate1 = julianDate2;
         std::swap(curFirst, curSecond);
       }
   }
+
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
diff --git a/src/Inttime.cc b/src/Inttime.cc
index 518ed2f9d904ae7d892a603350b3afa628a2ab34..de2d63ec3f500099b615f560b985be5e95c60207 100644
--- a/src/Inttime.cc
+++ b/src/Inttime.cc
@@ -18,14 +18,14 @@
 
 #include "cdo_options.h"
 #include "process_int.h"
-#include "cdo_vlist.h"
 #include "datetime.h"
 #include "printinfo.h"
+#include "field_functions.h"
 
 template <typename T>
 size_t
-interp_time(double fac1, double fac2, size_t gridsize, const double *v1, const double *v2, Varray<T> &v3, bool withMissval,
-            double missval)
+interp_time(double fac1, double fac2, size_t gridsize, const Varray<T> &v1, const Varray<T> &v2, Varray<T> &v3, bool withMissval,
+            T missval)
 {
   size_t numMissVals3 = 0;
 
@@ -33,11 +33,13 @@ interp_time(double fac1, double fac2, size_t gridsize, const double *v1, const d
     {
       for (size_t i = 0; i < gridsize; ++i)
         {
-          if (!dbl_is_equal(v1[i], missval) && !dbl_is_equal(v2[i], missval))
+          auto v1IsMissval = dbl_is_equal(v1[i], missval);
+          auto v2IsMissval = dbl_is_equal(v2[i], missval);
+          if (!v1IsMissval && !v2IsMissval)
             v3[i] = v1[i] * fac1 + v2[i] * fac2;
-          else if (dbl_is_equal(v1[i], missval) && !dbl_is_equal(v2[i], missval) && fac2 >= 0.5)
+          else if (fac2 >= 0.5 && v1IsMissval && !v2IsMissval)
             v3[i] = v2[i];
-          else if (dbl_is_equal(v2[i], missval) && !dbl_is_equal(v1[i], missval) && fac1 >= 0.5)
+          else if (fac1 >= 0.5 && v2IsMissval && !v1IsMissval)
             v3[i] = v1[i];
           else
             {
@@ -58,12 +60,14 @@ interp_time(double fac1, double fac2, size_t gridsize, const double *v1, const d
 }
 
 void
-interp_time(double fac1, double fac2, const double *array1, const double *array2, Field &field3, bool withMissval)
+interp_time(double fac1, double fac2, const Field &field1, const Field &field2, Field &field3, bool withMissval)
 {
   if (field3.memType == MemType::Float)
-    field3.numMissVals = interp_time(fac1, fac2, field3.gridsize, array1, array2, field3.vec_f, withMissval, field3.missval);
+    field3.numMissVals
+        = interp_time(fac1, fac2, field3.gridsize, field1.vec_f, field2.vec_f, field3.vec_f, withMissval, (float) field3.missval);
   else
-    field3.numMissVals = interp_time(fac1, fac2, field3.gridsize, array1, array2, field3.vec_d, withMissval, field3.missval);
+    field3.numMissVals
+        = interp_time(fac1, fac2, field3.gridsize, field1.vec_d, field2.vec_d, field3.vec_d, withMissval, field3.missval);
 }
 
 static void
@@ -108,6 +112,7 @@ public:
     .constraints = { 1, 1, NoRestriction },
   };
   inline static RegisterEntry<Inttime> registration = RegisterEntry<Inttime>(module);
+
   CdiDateTime sDateTime{};
   int incrPeriod = 0, incrUnits = 3600, timeUnits = TUNIT_HOUR;
 
@@ -122,28 +127,14 @@ public:
 
   int curFirst = 0, curSecond = 1;
 
-  int tsID = 0;
-  int tsIDo = 0;
-  int nrecs;
-  int calendar;
-
-  JulianDate julianDate;
-  JulianDate julianDate1;
-
   int64_t ijulinc;
 
-  std::vector<RecordInfo> recList;
-  Field field3;
-
-  Varray3D<size_t> numMissVals;
-  Varray3D<double> vardata;
-  VarList varList;
+  VarList varList1;
 
 public:
   void
-  init()
+  init() override
   {
-
     operator_input_arg("date,time<,increment> (format YYYY-MM-DD,hh:mm:ss)");
     if (cdo_operator_argc() < 2) cdo_abort("Too few arguments!");
 
@@ -155,45 +146,33 @@ public:
     ijulinc = (int64_t) incrPeriod * incrUnits;
 
     streamID1 = cdo_open_read(0);
-
     vlistID1 = cdo_stream_inq_vlist(streamID1);
     vlistID2 = vlistDuplicate(vlistID1);
 
-    varList_init(varList, vlistID1);
+    varList1 = VarList(vlistID1);
 
     if (ijulinc == 0) vlistDefNtsteps(vlistID2, 1);
 
-    auto nvars = vlistNvars(vlistID1);
-
-    auto maxrecs = vlistNrecs(vlistID1);
-    recList = std::vector<RecordInfo>(maxrecs);
-
-    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);
-
-    for (int varID = 0; varID < nvars; ++varID)
-      {
-        auto gridsize = varList[varID].gridsize;
-        auto nlevel = varList[varID].nlevels;
-        numMissVals[0][varID].resize(nlevel);
-        numMissVals[1][varID].resize(nlevel);
-        vardata[0][varID].resize(gridsize * nlevel);
-        vardata[1][varID].resize(gridsize * nlevel);
-      }
-
     taxisID1 = vlistInqTaxis(vlistID1);
     taxisID2 = taxisDuplicate(taxisID1);
     adjust_time_units(taxisID2, timeUnits);
     if (taxisHasBounds(taxisID2)) taxisDeleteBounds(taxisID2);
     vlistDefTaxis(vlistID2, taxisID2);
+  }
+
+  void
+  run() override
+  {
+    Field field3;
+    FieldVector2D varsData[2];
+    field2D_init(varsData[0], varList1, FIELD_VEC | FIELD_NAT);
+    field2D_init(varsData[1], varList1, FIELD_VEC | FIELD_NAT);
 
-    calendar = taxisInqCalendar(taxisID1);
+    auto maxRecords = varList1.numRecords();
+    std::vector<RecordInfo> recordList(maxRecords);
 
-    julianDate = julianDate_encode(calendar, sDateTime);
+    auto calendar = taxisInqCalendar(taxisID1);
+    auto julianDate = julianDate_encode(calendar, sDateTime);
 
     if (Options::cdoVerbose)
       {
@@ -202,16 +181,17 @@ public:
         cdo_print("ijulinc = %lld", ijulinc);
       }
 
-    nrecs = cdo_stream_inq_timestep(streamID1, tsID++);
+    int tsID = 0;
+    int tsIDo = 0;
+
+    auto nrecs = cdo_stream_inq_timestep(streamID1, tsID++);
     auto vDateTime1 = taxisInqVdatetime(taxisID1);
-    julianDate1 = julianDate_encode(calendar, vDateTime1);
+    auto julianDate1 = julianDate_encode(calendar, vDateTime1);
     for (int recID = 0; recID < nrecs; ++recID)
       {
-        int varID, levelID;
-        cdo_inq_record(streamID1, &varID, &levelID);
-        auto offset = varList[varID].gridsize * levelID;
-        auto single1 = &vardata[curFirst][varID][offset];
-        cdo_read_record(streamID1, single1, &numMissVals[curFirst][varID][levelID]);
+        auto [varID, levelID] = cdo_inq_record(streamID1);
+        auto &field = varsData[curFirst][varID][levelID];
+        cdo_read_record(streamID1, field);
       }
 
     if (Options::cdoVerbose)
@@ -225,10 +205,7 @@ public:
         cdo_print("Dataset begins on %s", datetime_to_string(vDateTime1));
         cdo_warning("The start time %s is before the beginning of the dataset!", datetime_to_string(sDateTime));
       }
-  }
-  void
-  run()
-  {
+
     while (julianDate_to_seconds(julianDate1) <= julianDate_to_seconds(julianDate))
       {
         nrecs = cdo_stream_inq_timestep(streamID1, tsID++);
@@ -244,14 +221,10 @@ public:
 
         for (int recID = 0; recID < nrecs; ++recID)
           {
-            int varID, levelID;
-            cdo_inq_record(streamID1, &varID, &levelID);
-
-            recList[recID].set(varID, levelID);
-
-            auto offset = varList[varID].gridsize * levelID;
-            auto single2 = &vardata[curSecond][varID][offset];
-            cdo_read_record(streamID1, single2, &numMissVals[curSecond][varID][levelID]);
+            auto [varID, levelID] = cdo_inq_record(streamID1);
+            recordList[recID].set(varID, levelID);
+            auto &field = varsData[curSecond][varID][levelID];
+            cdo_read_record(streamID1, field);
           }
 
         while (julianDate_to_seconds(julianDate) <= julianDate_to_seconds(julianDate2))
@@ -280,16 +253,15 @@ public:
 
                 for (int recID = 0; recID < nrecs; ++recID)
                   {
-                    auto [varID, levelID] = recList[recID].get();
+                    auto [varID, levelID] = recordList[recID].get();
 
-                    auto offset = varList[varID].gridsize * levelID;
-                    auto single1 = &vardata[curFirst][varID][offset];
-                    auto single2 = &vardata[curSecond][varID][offset];
+                    const auto &field1 = varsData[curFirst][varID][levelID];
+                    const auto &field2 = varsData[curSecond][varID][levelID];
 
-                    field3.init(varList[varID]);
+                    field3.init(varList1.vars[varID]);
 
-                    auto withMissval = (numMissVals[curFirst][varID][levelID] || numMissVals[curSecond][varID][levelID]);
-                    interp_time(fac1, fac2, single1, single2, field3, withMissval);
+                    auto withMissval = (field1.numMissVals || field2.numMissVals);
+                    interp_time(fac1, fac2, field1, field2, field3, withMissval);
 
                     cdo_def_record(streamID2, varID, levelID);
                     cdo_write_record(streamID2, field3);
@@ -304,13 +276,14 @@ public:
         julianDate1 = julianDate2;
         std::swap(curFirst, curSecond);
       }
+
+    if (tsIDo == 0) cdo_warning("Start date/time %s out of range, no time steps interpolated!", datetime_to_string(sDateTime));
   }
+
   void
-  close()
+  close() override
   {
     if (streamID2 != CDO_STREAM_UNDEF) cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    if (tsIDo == 0) cdo_warning("Start date/time %s out of range, no time steps interpolated!", datetime_to_string(sDateTime));
   }
 };
diff --git a/src/Intyear.cc b/src/Intyear.cc
index 33f4cbd303c00d2df34d870574d6ce2e3cdf5555..03d3e23b342d38ea84c09a09a48c6783f928308a 100644
--- a/src/Intyear.cc
+++ b/src/Intyear.cc
@@ -15,7 +15,6 @@
 
 #include "cdo_rlimit.h"
 #include "process_int.h"
-#include "cdo_vlist.h"
 #include "param_conversion.h"
 #include "util_files.h"
 #include "util_string.h"
@@ -30,7 +29,7 @@ intlin_year(double fac1, double fac2, size_t gridsize, const Varray<double> &arr
     {
       for (size_t i = 0; i < gridsize; ++i)
         {
-          if (!dbl_is_equal(array1[i], missval1) && !dbl_is_equal(array2[i], missval2))
+          if (dbl_is_not_equal(array1[i], missval1) && dbl_is_not_equal(array2[i], missval2))
             {
               array3[i] = array1[i] * fac1 + array2[i] * fac2;
             }
@@ -72,7 +71,8 @@ public:
   std::vector<int> iyears;
   int nyears;
 
-  VarList varList1, varList2;
+  VarList varList1;
+  VarList varList2;
 
   std::vector<CdoStreamID> streamIDs;
 
@@ -82,7 +82,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
 
     operator_input_arg("years");
@@ -101,7 +101,9 @@ public:
     auto vlistID2 = cdo_stream_inq_vlist(streamID2);
     auto vlistID3 = vlistDuplicate(vlistID1);
 
-    vlist_compare(vlistID1, vlistID2, CmpVlist::All);
+    varList1 = VarList(vlistID1);
+    varList2 = VarList(vlistID2);
+    varList_compare(varList1, varList2);
 
     auto gridsizemax = vlistGridsizeMax(vlistID1);
 
@@ -125,13 +127,10 @@ public:
         streamIDs[iy] = cdo_open_write(fileName.c_str());
         cdo_def_vlist(streamIDs[iy], vlistID3);
       }
-
-    varList_init(varList1, vlistID1);
-    varList_init(varList2, vlistID2);
   }
 
   void
-  run()
+  run() override
   {
     int tsID = 0;
     while (true)
@@ -167,9 +166,9 @@ public:
             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 gridsize = varList1.vars[varID].gridsize;
+            auto missval1 = varList1.vars[varID].missval;
+            auto missval2 = varList2.vars[varID].missval;
             auto withMissval = (numMissVals1 || numMissVals2);
 
             for (int iy = 0; iy < nyears; iy++)
@@ -189,7 +188,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     for (int iy = 0; iy < nyears; iy++) cdo_stream_close(streamIDs[iy]);
 
diff --git a/src/Invert.cc b/src/Invert.cc
index 5577958dd3b9b3cbb424d4c73a037a4ea42d7c29..6d89c4cb918baeb8639153ec095aff93938d4c92 100644
--- a/src/Invert.cc
+++ b/src/Invert.cc
@@ -44,8 +44,8 @@ invert_lon_data(Varray<T> &v, size_t nlon, size_t nlat)
 static void
 invert_lon_data(Field &field)
 {
-  const auto nlon = gridInqXsize(field.grid);
-  const auto nlat = gridInqYsize(field.grid);
+  auto nlon = gridInqXsize(field.grid);
+  auto nlat = gridInqYsize(field.grid);
 
   if (field.memType == MemType::Float)
     invert_lon_data(field.vec_f, nlon, nlat);
@@ -74,8 +74,8 @@ invert_lat_data(Varray<T> &v, size_t nlon, size_t nlat)
 static void
 invert_lat_data(Field &field)
 {
-  const auto nlon = gridInqXsize(field.grid);
-  const auto nlat = gridInqYsize(field.grid);
+  auto nlon = gridInqXsize(field.grid);
+  auto nlat = gridInqYsize(field.grid);
 
   if (field.memType == MemType::Float)
     invert_lat_data(field.vec_f, nlon, nlat);
@@ -86,13 +86,13 @@ invert_lat_data(Field &field)
 static void
 invert_lon_des(int vlistID)
 {
-  const auto ngrids = vlistNgrids(vlistID);
+  auto ngrids = vlistNumGrids(vlistID);
   for (int index = 0; index < ngrids; ++index)
     {
-      const auto gridID1 = vlistGrid(vlistID, index);
-      const auto gridID2 = gridDuplicate(gridID1);
+      auto gridID1 = vlistGrid(vlistID, index);
+      auto gridID2 = gridDuplicate(gridID1);
 
-      const auto gridtype = gridInqType(gridID1);
+      auto gridtype = gridInqType(gridID1);
 
       if (!(gridtype == GRID_GENERIC || gridtype == GRID_GAUSSIAN || gridtype == GRID_PROJECTION || gridtype == GRID_LONLAT
             || gridtype == GRID_CURVILINEAR))
@@ -100,9 +100,9 @@ invert_lon_des(int vlistID)
 
       if (gridInqXvals(gridID1, nullptr))
         {
-          const auto nlon = gridInqXsize(gridID1);
-          const auto nlat = gridInqYsize(gridID1);
-          const auto size = (gridtype == GRID_CURVILINEAR) ? nlon * nlat : nlon;
+          auto nlon = gridInqXsize(gridID1);
+          auto nlat = gridInqYsize(gridID1);
+          auto size = (gridtype == GRID_CURVILINEAR) ? nlon * nlat : nlon;
 
           Varray<double> coords(size);
 
@@ -129,10 +129,10 @@ invert_lon_des(int vlistID)
 
       if (gridInqXbounds(gridID1, nullptr))
         {
-          const auto nlon = gridInqXsize(gridID1);
-          const auto nlat = gridInqYsize(gridID1);
-          const auto nv = gridInqNvertex(gridID1);
-          const auto size = (gridtype == GRID_CURVILINEAR) ? nv * nlon * nlat : nv * nlon;
+          auto nlon = gridInqXsize(gridID1);
+          auto nlat = gridInqYsize(gridID1);
+          auto nv = gridInqNvertex(gridID1);
+          auto size = (gridtype == GRID_CURVILINEAR) ? nv * nlon * nlat : nv * nlon;
 
           Varray<double> bounds(size);
 
@@ -168,13 +168,13 @@ invert_lon_des(int vlistID)
 static void
 invert_lat_coord(int gridID)
 {
-  const auto gridtype = gridInqType(gridID);
+  auto gridtype = gridInqType(gridID);
 
   if (gridInqYvals(gridID, nullptr))
     {
-      const auto nlon = gridInqXsize(gridID);
-      const auto nlat = gridInqYsize(gridID);
-      const auto size = (gridtype == GRID_CURVILINEAR) ? nlon * nlat : nlat;
+      auto nlon = gridInqXsize(gridID);
+      auto nlat = gridInqYsize(gridID);
+      auto size = (gridtype == GRID_CURVILINEAR) ? nlon * nlat : nlat;
 
       Varray<double> coords(size);
 
@@ -201,10 +201,10 @@ invert_lat_coord(int gridID)
 
   if (gridInqYbounds(gridID, nullptr))
     {
-      const auto nlon = gridInqXsize(gridID);
-      const auto nlat = gridInqYsize(gridID);
-      const auto nv = gridInqNvertex(gridID);
-      const auto size = (gridtype == GRID_CURVILINEAR) ? nv * nlon * nlat : nv * nlat;
+      auto nlon = gridInqXsize(gridID);
+      auto nlat = gridInqYsize(gridID);
+      auto nv = gridInqNvertex(gridID);
+      auto size = (gridtype == GRID_CURVILINEAR) ? nv * nlon * nlat : nv * nlat;
 
       Varray<double> bounds(size);
 
@@ -237,13 +237,13 @@ invert_lat_coord(int gridID)
 static void
 invert_lat_des(int vlistID)
 {
-  const auto ngrids = vlistNgrids(vlistID);
+  auto ngrids = vlistNumGrids(vlistID);
   for (int index = 0; index < ngrids; ++index)
     {
-      const auto gridID1 = vlistGrid(vlistID, index);
-      const auto gridID2 = gridDuplicate(gridID1);
+      auto gridID1 = vlistGrid(vlistID, index);
+      auto gridID2 = gridDuplicate(gridID1);
 
-      const auto gridtype = gridInqType(gridID1);
+      auto gridtype = gridInqType(gridID1);
 
       if (!(gridtype == GRID_GENERIC || gridtype == GRID_GAUSSIAN || gridtype == GRID_PROJECTION || gridtype == GRID_LONLAT
             || gridtype == GRID_CURVILINEAR))
@@ -251,7 +251,7 @@ invert_lat_des(int vlistID)
 
       invert_lat_coord(gridID2);
 
-      const auto projID = gridInqProj(gridID2);
+      auto projID = gridInqProj(gridID2);
       if (projID != CDI_UNDEFID) invert_lat_coord(projID);
 
       vlistChangeGrid(vlistID, gridID1, gridID2);
@@ -296,17 +296,12 @@ public:
   int operfunc2;
 
   VarList varList1;
-  Field field;
 
 public:
   void
-  init()
+  init() override
   {
-
-    // clang-format off
-    // clang-format on
-
-    const auto operatorID = cdo_operator_id();
+    auto operatorID = cdo_operator_id();
     operfunc1 = cdo_operator_f1(operatorID);
     operfunc2 = cdo_operator_f2(operatorID);
 
@@ -314,8 +309,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);
 
     taxisID1 = vlistInqTaxis(vlistID1);
     taxisID2 = taxisDuplicate(taxisID1);
@@ -333,15 +328,18 @@ public:
 
     cdo_def_vlist(streamID2, vlistID2);
 
-    varList_init(varList1, vlistID1);
+    varList1 = VarList(vlistID1);
   }
+
   void
-  run()
+  run() override
   {
+    Field field;
+
     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);
@@ -352,7 +350,7 @@ public:
           {
             int varID, levelID;
             cdo_inq_record(streamID1, &varID, &levelID);
-            field.init(varList1[varID]);
+            field.init(varList1.vars[varID]);
             cdo_read_record(streamID1, field);
 
             cdo_def_record(streamID2, varID, levelID);
@@ -374,7 +372,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
diff --git a/src/Invertlev.cc b/src/Invertlev.cc
index 9389f9dd6dba058584668a6ad16f1a6dad4c2c22..7d8ececee53763dff7079ad4008ba23d5f7763e7 100644
--- a/src/Invertlev.cc
+++ b/src/Invertlev.cc
@@ -14,12 +14,11 @@
 #include <cdi.h>
 
 #include "process_int.h"
-#include "cdo_vlist.h"
 
 static void
 invertLevDes(int vlistID)
 {
-  auto nzaxis = vlistNzaxis(vlistID);
+  auto nzaxis = vlistNumZaxis(vlistID);
   for (int index = 0; index < nzaxis; ++index)
     {
       auto zaxisID1 = vlistZaxis(vlistID, index);
@@ -93,8 +92,6 @@ public:
   Varray2D<double> vardata;
   Varray<double> array;
 
-  int nvars;
-
   bool dataIsUnchanged;
   std::vector<std::vector<size_t>> varnumMissVals;
 
@@ -102,7 +99,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
     dataIsUnchanged = data_is_unchanged();
 
@@ -126,17 +123,16 @@ public:
     auto gridsizemax = vlistGridsizeMax(vlistID1);
     array = Varray<double>(gridsizemax);
 
-    nvars = vlistNvars(vlistID1);
-
-    vardata = Varray2D<double>(nvars);
-    varnumMissVals = Varray2D<size_t>(nvars);
+    varList1 = VarList(vlistID1);
+    auto numVars = varList1.numVars();
 
-    varList_init(varList1, vlistID1);
+    vardata = Varray2D<double>(numVars);
+    varnumMissVals = Varray2D<size_t>(numVars);
 
     auto has3dVar = false;
-    for (int varID = 0; varID < nvars; ++varID)
+    for (int varID = 0; varID < numVars; ++varID)
       {
-        const auto &var = varList1[varID];
+        const auto &var = varList1.vars[varID];
         if (var.nlevels > 1)
           {
             has3dVar = true;
@@ -149,7 +145,7 @@ public:
   }
 
   void
-  run()
+  run() override
   {
     int tsID = 0;
     while (true)
@@ -168,7 +164,7 @@ public:
 
             if (vardata[varID].size())
               {
-                auto offset = varList1[varID].gridsize * levelID;
+                auto offset = varList1.vars[varID].gridsize * levelID;
                 cdo_read_record(streamID1, &vardata[varID][offset], &numMissVals);
                 varnumMissVals[varID][levelID] = numMissVals;
               }
@@ -184,11 +180,11 @@ public:
               }
           }
 
-        for (int varID = 0; varID < nvars; ++varID)
+        for (int varID = 0, numVars = varList1.numVars(); varID < numVars; ++varID)
           {
             if (vardata[varID].size())
               {
-                const auto &var = varList1[varID];
+                const auto &var = varList1.vars[varID];
                 for (int levelID = 0; levelID < var.nlevels; ++levelID)
                   {
                     cdo_def_record(streamID2, varID, levelID);
@@ -206,7 +202,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
diff --git a/src/Lic.cc b/src/Lic.cc
index e8cb8a7e46684434e0ccbdb345c3f49a7fe848ec..c9fd55a6cbd46205d0744a4058d646bcddd03dde 100644
--- a/src/Lic.cc
+++ b/src/Lic.cc
@@ -422,14 +422,15 @@ public:
   using Process::Process;
   inline static CdoModule module = {
     .name = "Lic",
-    .operators = { { "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;
+
+  int numLevels = 0;
   size_t numMissVals;
 
   CdoStreamID streamID1;
@@ -458,12 +459,9 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
-
-    // clang-format off
-  // int LIC  = cdo_operator_add("lic",  0, 0, nullptr);
-    // clang-format on
+    // int LIC  = cdo_operator_add("lic",  0, 0, nullptr);
 
     // int operatorID = cdo_operator_id();
 
@@ -479,19 +477,20 @@ public:
     obasename = cdo_get_stream_name(1);
     streamID1 = cdo_open_read(0);
 
-    const auto vlistID1 = cdo_stream_inq_vlist(streamID1);
+    auto vlistID1 = cdo_stream_inq_vlist(streamID1);
+    VarList varList1(vlistID1);
 
     // int taxisID1 = vlistInqTaxis(vlistID1);
 
     /* find variables */
-    const auto nvars = vlistNvars(vlistID1);
-    if (nvars != 2) cdo_abort("Need 2 input variable, found %d\n", nvars);
+    auto numVars = varList1.numVars();
+    if (numVars != 2) cdo_abort("Need 2 input variable, found %d\n", numVars);
 
-    const auto ngrids = vlistNgrids(vlistID1);
-    if (ngrids != 1) cdo_abort("Need 1 grid, found %d\n", ngrids);
+    auto numGrids = vlistNumGrids(vlistID1);
+    if (numGrids != 1) cdo_abort("Need 1 grid, found %d\n", numGrids);
 
-    gridID = vlistInqVarGrid(vlistID1, varID1);
-    // int zaxisID = vlistInqVarZaxis(vlistID1, varID1);
+    gridID = varList1.vars[varID1].gridID;
+    // int zaxisID = varList1.vars[varID1].zaxisID;
 
     gridsize = vlistGridsizeMax(vlistID1);
     array1 = Varray<double>(gridsize);
@@ -500,14 +499,11 @@ public:
     Varray<double> ivar1, ivar2, ovar1;
     if (varID1 != -1 && varID2 != -1)
       {
-        nlev = zaxisInqSize(vlistInqVarZaxis(vlistID1, varID1));
-
-        gridsize = gridInqSize(gridID);
-        ivar1.resize(nlev * gridsize);
-        ivar2.resize(nlev * gridsize);
-
-        gridsize = gridInqSize(gridID);
-        ovar1.resize(nlev * gridsize);
+        numLevels = varList1.vars[varID1].nlevels;
+        gridsize = varList1.vars[varID1].gridsize;
+        ivar1.resize(numLevels * gridsize);
+        ivar2.resize(numLevels * gridsize);
+        ovar1.resize(numLevels * gridsize);
       }
 
     const auto gridtype = gridInqType(gridID);
@@ -521,13 +517,14 @@ public:
     gridInqYvals(gridID, yvals.data());
     invertlat = (yvals[0] < yvals[ny - 1]);
   }
+
   void
-  run()
+  run() override
   {
     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);
@@ -567,10 +564,10 @@ public:
         tsID++;
       }
   }
+
   void
-  close()
+  close() override
   {
-
     cdo_stream_close(streamID1);
   }
 };
diff --git a/src/Longinfo.cc b/src/Longinfo.cc
index 6951ce7779b2dd6c5252e69908daa19ce8f5c942..2675544491f1c42f69eab0f892375316e23a1e6d 100644
--- a/src/Longinfo.cc
+++ b/src/Longinfo.cc
@@ -16,16 +16,12 @@
 #include <cdi.h>
 
 #include "cdo_options.h"
-#include "cdo_vlist.h"
 #include "process_int.h"
 #include "varray.h"
-#include "datetime.h"
 #include "printinfo.h"
 #include "cdo_zaxis.h"
 #include "field_functions.h"
 
-void cdoPrintAttributes(FILE *fp, int cdiID, int varID, int nblanks);
-
 struct LonginfoStat
 {
   double min = DBL_MAX;
@@ -95,7 +91,7 @@ public:
   using Process::Process;
   inline static CdoModule module = {
     .name = "Longinfo",
-    .operators = { { "linfo"} },
+    .operators = { { "linfo" } },
     .aliases = {},
     .mode = EXPOSED,     // Module mode: 0:intern 1:extern
     .number = CDI_REAL,  // Allowed number type
@@ -109,27 +105,25 @@ public:
   int vlistID;
 
   VarList varList;
-  Field field;
 
 public:
   void
-  init()
+  init() override
   {
-
-    // clang-format off
-    // clang-format on
-
     operator_check_argc(0);
 
     streamID = cdo_open_read(0);
     vlistID = cdo_stream_inq_vlist(streamID);
     taxisID = vlistInqTaxis(vlistID);
 
-    varList_init(varList, vlistID);
+    varList = VarList(vlistID);
   }
+
   void
-  run()
+  run() override
   {
+    Field field;
+
     int tsID = 0;
     while (true)
       {
@@ -147,9 +141,9 @@ public:
 
             int varID, levelID;
             cdo_inq_record(streamID, &varID, &levelID);
-            const auto &var = varList[varID];
+            const auto &var = varList.vars[varID];
 
-            auto dig = (var.datatype == CDI_DATATYPE_FLT64) ? Options::CDO_dbl_digits : Options::CDO_flt_digits;
+            auto dig = (var.dataType == CDI_DATATYPE_FLT64) ? Options::CDO_dbl_digits : Options::CDO_flt_digits;
 
             fprintf(stdout, "\t\tvarIndex: %d\n", varID + 1);
             fprintf(stdout, "\t\tlevelIndex: %d\n", levelID + 1);
@@ -166,7 +160,7 @@ public:
 
             LonginfoStat infostat;
 
-            fprintf(stdout, "\t\tdataType: %s\n", cdo::datatype_to_cstr(var.datatype));
+            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", numMissVals);
@@ -203,7 +197,7 @@ public:
       }
   }
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID);
   }
diff --git a/src/Maggraph.cc b/src/Maggraph.cc
index cdd008ae9bba898715f7263a447e8957e4f82fb5..e9bcd818b6d886cc7bc12f5cc432d4ae3eb9c6fd 100644
--- a/src/Maggraph.cc
+++ b/src/Maggraph.cc
@@ -11,7 +11,6 @@
 
 #include <climits>
 #include <cdi.h>
-#include <cctype>
 
 #include "process_int.h"
 #include <mpim_grid.h>
@@ -21,7 +20,6 @@
 #include "magics_api.h"
 #include "magics_template_parser.h"
 #include "util_string.h"
-#include "cdo_vlist.h"
 #include "printinfo.h"
 
 #define DBG 0
@@ -610,7 +608,8 @@ maggraph(const char *plotfile, const std::string &varname, const std::string &va
   // 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);
+  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");
@@ -779,7 +778,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
 #ifdef HAVE_LIBMAGICS
     nparam = cdo_operator_argc();
@@ -799,7 +798,7 @@ public:
   }
 
   void
-  run()
+  run() override
   {
 #ifdef HAVE_LIBMAGICS
     std::vector<std::vector<double>> datatab(nfiles);
@@ -830,7 +829,7 @@ public:
 
             vlistID0 = vlistDuplicate(vlistID);
           }
-        else { vlist_compare(vlistID0, vlistID, CmpVlist::All); }
+        else { vlist_compare(vlistID0, vlistID, CmpVarList::All); }
 
         int tsID = 0;
         size_t numTsAlloc = 0;
@@ -884,7 +883,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
 #ifdef HAVE_LIBMAGICS
     quit_MAGICS();
diff --git a/src/Magplot.cc b/src/Magplot.cc
index 8e2f4d9de72a90aa1c5198e7c426af11946d34ee..7ea548b788e3793169037c0233f851c38dcace76 100644
--- a/src/Magplot.cc
+++ b/src/Magplot.cc
@@ -1003,7 +1003,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
 
 #ifdef HAVE_LIBMAGICS
@@ -1069,13 +1069,13 @@ public:
 #endif
   }
   void
-  run()
+  run() override
   {
 #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)
@@ -1179,7 +1179,7 @@ public:
 #endif
   }
   void
-  close()
+  close() override
   {
 #ifdef HAVE_LIBMAGICS
 
diff --git a/src/Magvector.cc b/src/Magvector.cc
index dc02af294a8cb9f846a8fac36b527b1888cb2f27..ae546cddeab8a7d21ed41f4396e51ef78a7edb74 100644
--- a/src/Magvector.cc
+++ b/src/Magvector.cc
@@ -308,7 +308,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
 #ifdef HAVE_LIBMAGICS
     nparam = cdo_operator_argc();
@@ -369,7 +369,7 @@ public:
   }
 
   void
-  run()
+  run() override
   {
 #ifdef HAVE_LIBMAGICS
     int tsID = 0;
@@ -469,7 +469,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
 #ifdef HAVE_LIBMAGICS
     cdo_stream_close(streamID);
diff --git a/src/Makefile.am b/src/Makefile.am
index 642e3a94f2b3e770a72fb0fdfd0e25c150c60d04..a88b8f66a87c383f54d36f2a79274f3b0303e2fc 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -17,6 +17,8 @@ libcdo_la_SOURCES =		after_dvtrans.cc          \
 				cdo_cdi_wrapper.cc        \
 				cdo_cdi_wrapper.h         \
 				cdo_cmor.h                \
+				cdo_data.cc               \
+				cdo_data.h                \
 				cdo_default_values.cc     \
 				cdo_default_values.h      \
 				cdo_features.cc           \
@@ -25,8 +27,8 @@ libcdo_la_SOURCES =		after_dvtrans.cc          \
 				cdo_fctrans.h	          \
 				cdo_fft.cc                \
 				cdo_fft.h                 \
-				cdo_fftw3.cc                \
-				cdo_fftw3.h                 \
+				cdo_fftw3.cc              \
+				cdo_fftw3.h               \
 				cdo_fill.cc               \
 				cdo_fill.h                \
 				cdo_getopt.cc             \
@@ -35,8 +37,8 @@ libcdo_la_SOURCES =		after_dvtrans.cc          \
 				cdo_history.h             \
 				cdo_math.cc               \
 				cdo_math.h                \
-				cdo_module.cc               \
-				cdo_module.h                \
+				cdo_module.cc             \
+				cdo_module.h              \
 				cdo_options.cc            \
 				cdo_options.h             \
 				cdo_output.cc             \
@@ -50,6 +52,7 @@ libcdo_la_SOURCES =		after_dvtrans.cc          \
 				cdo_rlimit.h              \
 				cdo_season.cc             \
 				cdo_season.h              \
+				cdo_stepstat.h            \
 				cdo_syntax_error.cc       \
 				cdo_syntax_error.h        \
 				cdo_node_attach_exception.h \
@@ -79,6 +82,7 @@ libcdo_la_SOURCES =		after_dvtrans.cc          \
 				constants.h               \
 				convert_units.cc          \
 				convert_units.h           \
+				cpp_lib.h                 \
 				cthread_debug.cc          \
 				cthread_debug.h           \
 				custom_modules.cc         \
@@ -115,6 +119,8 @@ libcdo_la_SOURCES =		after_dvtrans.cc          \
 				field_zonal.cc            \
 				field_vinterp.cc          \
 				field_vinterp.h           \
+				field_trend.cc            \
+				field_trend.h             \
 				fileStream.cc             \
 				fileStream.h              \
 				fill_1d.cc                \
@@ -179,6 +185,8 @@ libcdo_la_SOURCES =		after_dvtrans.cc          \
 				nanoflann.hpp             \
 				operator_help.cc          \
 				operator_help.h           \
+				oper_args.cc              \
+				oper_args.h               \
 				par_io.cc                 \
 				par_io.h                  \
 				param_conversion.cc       \
@@ -437,7 +445,6 @@ cdo_SOURCES +=                  Adisit.cc            \
 				Setzaxis.cc          \
 				Shiftxy.cc           \
 				Showinfo.cc          \
-				Showattribute.h      \
 				Showattribute.cc     \
 				Sinfo.cc             \
 				Smooth.cc            \
@@ -490,7 +497,6 @@ cdo_SOURCES +=                  Adisit.cc            \
 				WindTrans.cc         \
 				Writegrid.cc         \
 				Writerandom.cc       \
-				XTimstat.cc          \
 				Yeararith.cc         \
 				Yearmonstat.cc       \
 				Ydayarith.cc         \
diff --git a/src/MapReduce.cc b/src/MapReduce.cc
index d04ec39af961b249d10f154cb4f4c9478cc36bd4..594bb7ff5018d46a4f7d93c71a6fd400bb5435b3 100644
--- a/src/MapReduce.cc
+++ b/src/MapReduce.cc
@@ -70,7 +70,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
 
     if (cdo_operator_argc() < 1) cdo_abort("Too few arguments!");
@@ -122,21 +122,20 @@ public:
     auto nvars = vlistNvars(vlistID1);
     vars = std::vector<bool>(nvars, false);
 
-    VarList varList1;
-    varList_init(varList1, vlistID1);
+    VarList varList1(vlistID1);
 
     // use vlist flags for marking the corresponding variables
     vlistClearFlag(vlistID1);
     for (int varID = 0; varID < nvars; ++varID)
       {
-        auto gridID = varList1[varID].gridID;
-        if (inputGridType == gridInqType(gridID) && inputGridSize == gridInqSize(gridID))
+        const auto &var = varList1.vars[varID];
+        if (inputGridType == var.gridType && inputGridSize == var.gridsize)
           {
             vars[varID] = true;
-            auto nlevels = varList1[varID].nlevels;
+            auto nlevels = var.nlevels;
             for (int levID = 0; levID < nlevels; levID++) vlistDefFlag(vlistID1, varID, levID, true);
           }
-        else { cdo_warning("Gridtype or gridsize differ, skipped variable %s!", varList1[varID].name); }
+        else { cdo_warning("Gridtype or gridsize differ, skipped variable %s!", var.name); }
       }
 
     vlistID2 = vlistCreate();
@@ -149,7 +148,7 @@ public:
     vlistDefTaxis(vlistID2, taxisID2);
 
     // use the new selection grid for all output variables
-    auto ngrids = vlistNgrids(vlistID2);
+    auto ngrids = vlistNumGrids(vlistID2);
     for (int index = 0; index < ngrids; ++index) vlistChangeGridIndex(vlistID2, index, outputGridID);
 
     // loop over input fields and mask the data values {{{
@@ -159,8 +158,9 @@ public:
     arrayIn = Varray<double>(inputGridSize);
     arrayOut = Varray<double>(maskSize);
   }
+
   void
-  run()
+  run() override
   {
     int tsID = 0;
     while (true)
@@ -191,10 +191,10 @@ public:
           }
         tsID++;
       }
-    // }}}
   }
+
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
diff --git a/src/Maskbox.cc b/src/Maskbox.cc
index d2570d0443c51a58f3b5dadabdc1fa1c784975c3..d1bf1f47f0fc5d58f5621abdac330e8b38c37e62 100644
--- a/src/Maskbox.cc
+++ b/src/Maskbox.cc
@@ -21,7 +21,6 @@
 #include "process_int.h"
 #include <mpim_grid.h>
 #include "selboxinfo.h"
-#include "util_string.h"
 #include "region.h"
 
 static void
@@ -170,7 +169,7 @@ get_gridID(int vlistID1, bool operIndexBox)
 {
   std::vector<int> gridsFound;
 
-  auto ngrids = vlistNgrids(vlistID1);
+  auto ngrids = vlistNumGrids(vlistID1);
   for (int index = 0; index < ngrids; ++index)
     {
       auto gridID1 = vlistGrid(vlistID1, index);
@@ -201,20 +200,20 @@ get_gridID(int vlistID1, bool operIndexBox)
 }
 
 static std::vector<bool>
-get_processVars(int vlistID1, int gridID)
+get_processVars(const VarList &varList, int gridID)
 {
-  auto nvars = vlistNvars(vlistID1);
+  auto numVars = varList.numVars();
 
-  std::vector<bool> processVars(nvars, false);
+  std::vector<bool> processVars(numVars, false);
 
   int varID;
-  for (varID = 0; varID < nvars; ++varID)
-    if (gridID == vlistInqVarGrid(vlistID1, varID)) processVars[varID] = true;
+  for (varID = 0; varID < numVars; ++varID)
+    if (gridID == varList.vars[varID].gridID) processVars[varID] = true;
 
-  for (varID = 0; varID < nvars; ++varID)
+  for (varID = 0; varID < numVars; ++varID)
     if (processVars[varID]) break;
 
-  if (varID >= nvars) cdo_abort("No processable variable found!");
+  if (varID >= numVars) cdo_abort("No processable variable found!");
 
   return processVars;
 }
@@ -248,11 +247,10 @@ private:
   size_t gridsize;
 
   VarList varList1;
-  Field field;
 
 public:
   void
-  init()
+  init() override
   {
     MASKLONLATBOX = module.get_id("masklonlatbox");
     MASKINDEXBOX = module.get_id("maskindexbox");
@@ -269,13 +267,15 @@ public:
     auto vlistID1 = cdo_stream_inq_vlist(streamID1);
     auto vlistID2 = vlistDuplicate(vlistID1);
 
+    varList1 = VarList(vlistID1);
+
     taxisID1 = vlistInqTaxis(vlistID1);
     taxisID2 = taxisDuplicate(taxisID1);
     vlistDefTaxis(vlistID2, taxisID2);
 
     auto gridID = get_gridID(vlistID1, operIndexBox);
 
-    processVars = get_processVars(vlistID1, gridID);
+    processVars = get_processVars(varList1, gridID);
 
     operator_input_arg(cdo_operator_enter(operatorID));
 
@@ -359,15 +359,15 @@ public:
         */
       }
 
-    varList_init(varList1, vlistID1);
-
     streamID2 = cdo_open_write(1);
     cdo_def_vlist(streamID2, vlistID2);
   }
 
   void
-  run()
+  run() override
   {
+    Field field;
+
     int tsID = 0;
     while (true)
       {
@@ -384,19 +384,19 @@ public:
 
             if (processVars[varID])
               {
-                field.init(varList1[varID]);
+                const auto &var = varList1.vars[varID];
+                field.init(var);
                 cdo_read_record(streamID1, field);
 
-                auto missval = varList1[varID].missval;
                 if (field.memType == MemType::Float)
                   {
                     for (size_t i = 0; i < gridsize; ++i)
-                      if (mask[i]) field.vec_f[i] = missval;
+                      if (mask[i]) field.vec_f[i] = var.missval;
                   }
                 else
                   {
                     for (size_t i = 0; i < gridsize; ++i)
-                      if (mask[i]) field.vec_d[i] = missval;
+                      if (mask[i]) field.vec_d[i] = var.missval;
                   }
 
                 field_num_mv(field);
@@ -410,7 +410,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
diff --git a/src/Mastrfu.cc b/src/Mastrfu.cc
index 5906a494d58259f5ac53b41517dc96abbcb64bc0..62b333ed6055773c5ec0b80ce3d477f6277b45f8 100644
--- a/src/Mastrfu.cc
+++ b/src/Mastrfu.cc
@@ -86,6 +86,7 @@ public:
     .constraints = { 1, 1, NoRestriction },
   };
   inline static RegisterEntry<Mastrfu> registration = RegisterEntry<Mastrfu>(module);
+
   CdoStreamID streamID1;
   CdoStreamID streamID2;
 
@@ -104,33 +105,33 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
-
     streamID1 = cdo_open_read(0);
 
     operator_check_argc(0);
 
     auto vlistID1 = cdo_stream_inq_vlist(streamID1);
+    VarList varList1(vlistID1);
 
-    auto nvars = vlistNvars(vlistID1);
-    if (nvars != 1) cdo_abort("This operator works only with one variable!");
+    auto numVars = varList1.numVars();
+    if (numVars != 1) cdo_abort("This operator works only with one variable!");
 
-    auto code = vlistInqVarCode(vlistID1, 0);
-    if (code > 0 && code != 132) cdo_warning("Unexpected code %d!", code);
+    const auto &var0 = varList1.vars[0];
+    if (var0.code > 0 && var0.code != 132) cdo_warning("Unexpected code %d!", var0.code);
 
-    missval = vlistInqVarMissval(vlistID1, 0);
+    missval = var0.missval;
+    gridID = var0.gridID;
+    zaxisID = var0.zaxisID;
 
-    zaxisID = vlistInqVarZaxis(vlistID1, 0);
-    if (zaxisInqType(zaxisID) != ZAXIS_PRESSURE && zaxisInqType(zaxisID) != ZAXIS_GENERIC)
+    if (var0.zaxisType != ZAXIS_PRESSURE && var0.zaxisType != ZAXIS_GENERIC)
       {
         cdo_warning("Unexpected vertical grid %s!", cdo::inq_key_string(zaxisID, CDI_GLOBAL, CDI_KEY_LONGNAME));
       }
 
-    gridID = vlistInqVarGrid(vlistID1, 0);
-    if (gridInqXsize(gridID) > 1) cdo_abort("Grid must be a zonal mean!");
+    if (gridInqXsize(var0.gridID) > 1) cdo_abort("Grid must be a zonal mean!");
 
-    nlat = gridInqSize(gridID);
+    nlat = gridInqSize(var0.gridID);
     auto nlev = zaxisInqSize(zaxisID);
 
     auto vlistID2 = vlistDuplicate(vlistID1);
@@ -151,8 +152,9 @@ public:
     array1 = Varray2D<double>(nlev, Varray<double>(nlat));
     array2 = Varray2D<double>(nlev, Varray<double>(nlat));
   }
+
   void
-  run()
+  run() override
   {
     int tsID = 0;
     while (true)
@@ -187,8 +189,9 @@ public:
         tsID++;
       }
   }
+
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
diff --git a/src/Math.cc b/src/Math.cc
index 882baf41f48cef355023a935894f778384af4cdd..c39a237c4086ba05c16f6d9a51a0116f8ffdc849 100644
--- a/src/Math.cc
+++ b/src/Math.cc
@@ -38,7 +38,7 @@ check_out_of_range(size_t &numMissVals, const size_t len, double missval, Varray
   if (numMissVals)
     {
       for (size_t i = 0; i < len; ++i)
-        if (!dbl_is_equal(v[i], missval) && (v[i] < rmin || v[i] > rmax))
+        if (dbl_is_not_equal(v[i], missval) && (v[i] < rmin || v[i] > rmax))
           {
             v[i] = missval;
             numMissVals++;
@@ -61,7 +61,7 @@ check_lower_range(size_t &numMissVals, const size_t len, double missval, Varray<
   if (numMissVals)
     {
       for (size_t i = 0; i < len; ++i)
-        if (!dbl_is_equal(v[i], missval) && v[i] < rmin)
+        if (dbl_is_not_equal(v[i], missval) && v[i] < rmin)
           {
             v[i] = missval;
             numMissVals++;
@@ -212,9 +212,8 @@ private:
 
 public:
   void
-  init()
+  init() override
   {
-
     auto operatorID = cdo_operator_id();
     operfunc = (Oper) cdo_operator_f1(operatorID);
 
@@ -232,15 +231,16 @@ public:
     vlistID1 = cdo_stream_inq_vlist(streamID1);
     vlistID2 = vlistDuplicate(vlistID1);
 
+    varList1 = VarList(vlistID1);
+
     if (operfunc == Oper::Re || operfunc == Oper::Im || operfunc == Oper::Abs || operfunc == Oper::Arg)
       {
-        auto nvars = vlistNvars(vlistID2);
-        for (int varID = 0; varID < nvars; ++varID)
+        auto numVars = varList1.numVars();
+        for (int varID = 0; varID < numVars; ++varID)
           {
-            if (vlistInqVarDatatype(vlistID2, varID) == CDI_DATATYPE_CPX32)
-              vlistDefVarDatatype(vlistID2, varID, CDI_DATATYPE_FLT32);
-            if (vlistInqVarDatatype(vlistID2, varID) == CDI_DATATYPE_CPX64)
-              vlistDefVarDatatype(vlistID2, varID, CDI_DATATYPE_FLT64);
+            const auto &var1 = varList1.vars[varID];
+            if (var1.dataType == CDI_DATATYPE_CPX32) vlistDefVarDatatype(vlistID2, varID, CDI_DATATYPE_FLT32);
+            if (var1.dataType == CDI_DATATYPE_CPX64) vlistDefVarDatatype(vlistID2, varID, CDI_DATATYPE_FLT64);
           }
       }
 
@@ -250,12 +250,10 @@ public:
 
     streamID2 = cdo_open_write(1);
     cdo_def_vlist(streamID2, vlistID2);
-
-    varList_init(varList1, vlistID1);
   }
 
   void
-  run()
+  run() override
   {
     auto gridsizemax = vlistGridsizeMax(vlistID1);
     if (vlistNumber(vlistID1) != CDI_REAL) gridsizemax *= 2;
@@ -279,10 +277,11 @@ public:
             size_t numMissVals;
             cdo_read_record(streamID1, &array1[0], &numMissVals);
 
+            const auto &var = varList1.vars[varID];
             auto is_EQ = dbl_is_equal;
-            auto missval1 = varList1[varID].missval;
-            auto n = varList1[varID].gridsize;
-            auto number = varList1[varID].nwpv;
+            auto missval1 = var.missval;
+            auto n = var.gridsize;
+            auto number = var.nwpv;
 
             if (number == CDI_REAL)
               {
@@ -353,7 +352,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
diff --git a/src/Merge.cc b/src/Merge.cc
index f6cefcd9d1411b0003237f7b17c37795973b63f4..52581919292bedd38cca3bc551bb4e1852e17333 100644
--- a/src/Merge.cc
+++ b/src/Merge.cc
@@ -15,6 +15,7 @@
 
 #include "cdo_options.h"
 #include "cdo_rlimit.h"
+#include "cdo_varlist.h"
 #include "process_int.h"
 #include "cdo_zaxis.h"
 #include "util_files.h"
@@ -25,29 +26,23 @@ check_dup_entry(int vlistID1, int vlistID2, const std::string &filename)
 {
   Varray<double> lev1, lev2;
 
-  auto nvars1 = vlistNvars(vlistID1);
-  auto nvars2 = vlistNvars(vlistID2);
+  VarList varList1(vlistID1);
+  VarList varList2(vlistID2);
 
-  VarList varList1, varList2;
-  varList_init(varList1, vlistID1);
-  varList_init(varList2, vlistID2);
-
-  for (int varID1 = 0; varID1 < nvars1; ++varID1)
+  for (const auto &var1 : varList1.vars)
     {
-      const auto &var1 = varList1[varID1];
-      auto gtype1 = gridInqType(var1.gridID);
+      auto gtype1 = var1.gridType;
       auto gsize1 = var1.gridsize;
-      auto ztype1 = zaxisInqType(var1.zaxisID);
+      auto ztype1 = var1.zaxisType;
       size_t nlev1 = var1.nlevels;
       if (nlev1 > lev1.size()) lev1.resize(nlev1);
       cdo_zaxis_inq_levels(var1.zaxisID, lev1.data());
 
-      for (int varID2 = 0; varID2 < nvars2; ++varID2)
+      for (const auto &var2 : varList2.vars)
         {
-          const auto &var2 = varList2[varID2];
-          auto gtype2 = gridInqType(var2.gridID);
+          auto gtype2 = var2.gridType;
           auto gsize2 = var2.gridsize;
-          auto ztype2 = zaxisInqType(var2.zaxisID);
+          auto ztype2 = var2.zaxisType;
           size_t nlev2 = var2.nlevels;
           if (gtype1 == gtype2 && gsize1 == gsize2 && ztype1 == ztype2 && nlev1 == nlev2)
             {
@@ -84,7 +79,7 @@ get_taxis_index(const std::vector<int> &vlistIDs)
 {
   if (vlistNtsteps(vlistIDs[0]) == 0)
     {
-      const int nmerge = vlistIDs.size();
+      int nmerge = vlistIDs.size();
       for (int im = 1; im < nmerge; ++im)
         {
           if (vlistNtsteps(vlistIDs[im]) != 0) return im;
@@ -128,8 +123,8 @@ public:
   inline static RegisterEntry<Merge> registration = RegisterEntry<Merge>(module);
 
   CdoStreamID streamID2;
-  int nmerge;
-  int taxisindex;
+  int numMerge;
+  int taxisIndex;
 
   int taxisID1;
   int taxisID2;
@@ -140,39 +135,37 @@ public:
 
   std::vector<CdoStreamID> streamIDs;
   std::vector<int> vlistIDs;
-  std::vector<int> numRecords;
-  std::vector<int> numTimeSteps;
+  std::vector<int> numRecordsList;
+  std::vector<int> numStepsList;
 
-  VarList varList2;
   Field field;
 
 public:
   void
-  init()
+  init() override
   {
-
     operator_check_argc(0);
 
     dataIsUnchanged = data_is_unchanged();
 
     auto streamCnt = cdo_stream_cnt();
-    nmerge = streamCnt - 1;
+    numMerge = streamCnt - 1;
 
-    cdo::set_numfiles(nmerge + 8);
+    cdo::set_numfiles(numMerge + 8);
 
     std::string ofilename = cdo_get_stream_name(streamCnt - 1);
 
     if (!Options::cdoOverwriteMode && FileUtils::file_exists(ofilename) && !FileUtils::user_file_overwrite(ofilename))
       cdo_abort("Outputfile %s already exists!", ofilename);
 
-    streamIDs = std::vector<CdoStreamID>(nmerge);
-    vlistIDs = std::vector<int>(nmerge);
-    numRecords = std::vector<int>(nmerge);
-    numTimeSteps = std::vector<int>(nmerge);
+    streamIDs = std::vector<CdoStreamID>(numMerge);
+    vlistIDs = std::vector<int>(numMerge);
+    numRecordsList = std::vector<int>(numMerge);
+    numStepsList = std::vector<int>(numMerge);
 
     auto setFileType = (CdoDefault::FileType == CDI_UNDEFID);
     auto baseFiletype = -1;
-    for (int im = 0; im < nmerge; ++im)
+    for (int im = 0; im < numMerge; ++im)
       {
         streamIDs[im] = cdo_open_read(im);
         vlistIDs[im] = cdo_stream_inq_vlist(streamIDs[im]);
@@ -183,14 +176,13 @@ public:
     if (setFileType) CdoDefault::FileType = cdo_inq_filetype(streamIDs[0]);
     // printf("setFileType=%d  filetype=%d\n", setFileType, CdoDefault::FileType);
 
-    taxisindex = get_taxis_index(vlistIDs);
+    taxisIndex = get_taxis_index(vlistIDs);
 
-    taxisID1 = vlistInqTaxis(vlistIDs[taxisindex]);
+    taxisID1 = vlistInqTaxis(vlistIDs[taxisIndex]);
     taxisID2 = taxisDuplicate(taxisID1);
 
-    vlistID2 = vlistCreate();
-    vlistCopy(vlistID2, vlistIDs[0]);
-    for (int im = 1; im < nmerge; ++im)
+    vlistID2 = vlistDuplicate(vlistIDs[0]);
+    for (int im = 1; im < numMerge; ++im)
       {
         std::string commandString = cdo_get_command_from_in_stream(im);
         check_dup_entry(vlistID2, vlistIDs[im], commandString);
@@ -198,28 +190,28 @@ public:
       }
 
     int numConstVars = 0;
-    for (int im = 0; im < nmerge; ++im)
+    for (int im = 0; im < numMerge; ++im)
       {
-        numTimeSteps[im] = vlistNtsteps(vlistIDs[im]);
-        if (numTimeSteps[im] == 0) numTimeSteps[im] = 1;
-        if (numTimeSteps[im] == 1) numConstVars++;
+        numStepsList[im] = vlistNtsteps(vlistIDs[im]);
+        if (numStepsList[im] == 0) numStepsList[im] = 1;
+        if (numStepsList[im] == 1) numConstVars++;
       }
 
-    if (numConstVars > 0 && numConstVars < nmerge)
-      for (int im = 0; im < nmerge; ++im)
+    if (numConstVars > 0 && numConstVars < numMerge)
+      for (int im = 0; im < numMerge; ++im)
         {
-          if (numTimeSteps[im] == 1)
+          if (numStepsList[im] == 1)
             {
               auto vlistID1 = vlistIDs[im];
-              auto nvars = vlistNvars(vlistID1);
-              for (int varID = 0; varID < nvars; ++varID)
+              auto numVars = vlistNvars(vlistID1);
+              for (int varID = 0; varID < numVars; ++varID)
                 vlistDefVarTimetype(vlistID2, vlistMergedVar(vlistID1, varID), TIME_CONSTANT);
             }
         }
 
     if (Options::cdoVerbose)
       {
-        for (int im = 0; im < nmerge; ++im) vlistPrint(vlistIDs[im]);
+        for (int im = 0; im < numMerge; ++im) vlistPrint(vlistIDs[im]);
         vlistPrint(vlistID2);
       }
 
@@ -227,50 +219,51 @@ public:
 
     vlistDefTaxis(vlistID2, taxisID2);
     cdo_def_vlist(streamID2, vlistID2);
-
-    varList_init(varList2, vlistID2);
   }
+
   void
-  run()
+  run() override
   {
+    VarList varList2(vlistID2);
+
     int tsID = 0;
     while (true)
       {
-        for (int im = 0; im < nmerge; ++im)
+        for (int im = 0; im < numMerge; ++im)
           {
             if (vlistIDs[im] == -1) continue;
-            numRecords[im] = cdo_stream_inq_timestep(streamIDs[im], tsID);
+            numRecordsList[im] = cdo_stream_inq_timestep(streamIDs[im], tsID);
           }
 
         {
           int im;
-          for (im = 0; im < nmerge; ++im)
-            if (numRecords[im] != 0) break;
-          if (im == nmerge) break;  // EOF on all input streams
+          for (im = 0; im < numMerge; ++im)
+            if (numRecordsList[im] != 0) break;
+          if (im == numMerge) break;  // EOF on all input streams
         }
 
         if (tsID == 1)
           {
-            for (int im = 0; im < nmerge; ++im)
-              if (numRecords[im] == 0 && numTimeSteps[im] == 1) vlistIDs[im] = -1;
+            for (int im = 0; im < numMerge; ++im)
+              if (numRecordsList[im] == 0 && numStepsList[im] == 1) vlistIDs[im] = -1;
           }
 
-        if (numRecords[taxisindex] == 0)
+        if (numRecordsList[taxisIndex] == 0)
           {
-            for (int im = 1; im < nmerge; ++im)
-              if (vlistIDs[im] != -1 && numRecords[im] != 0)
-                cdo_warning("Input stream %d has %d timestep%s. Stream %d has more timesteps, skipped!", taxisindex + 1, tsID,
+            for (int im = 1; im < numMerge; ++im)
+              if (vlistIDs[im] != -1 && numRecordsList[im] != 0)
+                cdo_warning("Input stream %d has %d timestep%s. Stream %d has more timesteps, skipped!", taxisIndex + 1, tsID,
                             (tsID == 1) ? "" : "s", im + 1);
             break;
           }
         else
           {
             auto lstop = false;
-            for (int im = 1; im < nmerge; ++im)
-              if (vlistIDs[im] != -1 && numRecords[im] == 0)
+            for (int im = 1; im < numMerge; ++im)
+              if (vlistIDs[im] != -1 && numRecordsList[im] == 0)
                 {
                   cdo_warning("Input stream %d has %d timestep%s. Stream %d has more timesteps, skipped!", im + 1, tsID,
-                              (tsID == 1) ? "" : "s", taxisindex + 1);
+                              (tsID == 1) ? "" : "s", taxisIndex + 1);
                   lstop = true;
                   break;
                 }
@@ -280,17 +273,16 @@ public:
         cdo_taxis_copy_timestep(taxisID2, taxisID1);
         cdo_def_timestep(streamID2, tsID);
 
-        for (int im = 0; im < nmerge; ++im)
+        for (int im = 0; im < numMerge; ++im)
           {
             auto streamID1 = streamIDs[im];
             auto vlistID1 = vlistIDs[im];
             if (vlistID1 == -1) continue;
 
-            auto nrecs = numRecords[im];
-            for (int recID = 0; recID < nrecs; ++recID)
+            auto numRecords = numRecordsList[im];
+            for (int recID = 0; recID < numRecords; ++recID)
               {
-                int varID, levelID;
-                cdo_inq_record(streamID1, &varID, &levelID);
+                auto [varID, levelID] = cdo_inq_record(streamID1);
 
                 auto varID2 = vlistMergedVar(vlistID1, varID);
                 auto levelID2 = vlistMergedLevel(vlistID1, varID, levelID);
@@ -302,7 +294,7 @@ public:
                 if (dataIsUnchanged) { cdo_copy_record(streamID2, streamID1); }
                 else
                   {
-                    field.init(varList2[varID2]);
+                    field.init(varList2.vars[varID2]);
                     cdo_read_record(streamID1, field);
                     cdo_write_record(streamID2, field);
                   }
@@ -312,8 +304,9 @@ public:
         tsID++;
       }
   }
+
   void
-  close()
+  close() override
   {
     for (auto &streamID : streamIDs) cdo_stream_close(streamID);
 
diff --git a/src/Mergegrid.cc b/src/Mergegrid.cc
index e287aae92b49edbfa567bd5b3d353c299ef84a28..261da3a4b27b75f9fb375850ece3bb721640cc47 100644
--- a/src/Mergegrid.cc
+++ b/src/Mergegrid.cc
@@ -13,7 +13,6 @@
 #include <cdi.h>
 
 #include "process_int.h"
-#include "cdo_vlist.h"
 #include <mpim_grid.h>
 
 void genGridIndex(int gridID1, int gridID2, std::vector<long> &index);
@@ -31,6 +30,7 @@ public:
     .constraints = { 2, 1, NoRestriction },
   };
   inline static RegisterEntry<Mergegrid> registration = RegisterEntry<Mergegrid>(module);
+
   CdoStreamID streamID1;
   CdoStreamID streamID2;
   CdoStreamID streamID3;
@@ -41,6 +41,9 @@ public:
   int taxisID1;
   int taxisID3;
 
+  VarList varList1;
+  VarList varList2;
+
   Varray<double> array1;
   Varray<double> array2;
 
@@ -51,9 +54,8 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
-
     operator_check_argc(0);
 
     streamID1 = cdo_open_read(0);
@@ -65,16 +67,18 @@ public:
     streamID2 = cdo_open_read(1);
     vlistID2 = cdo_stream_inq_vlist(streamID2);
 
-    vlist_compare(vlistID1, vlistID2, CmpVlist::Name | CmpVlist::NumLevels);
+    varList1 = VarList(vlistID1);
+    varList2 = VarList(vlistID2);
+    varList_compare(varList1, varList2, CmpVarList::Name | CmpVarList::NumLevels);
 
     int ndiffgrids = 0;
-    for (int index = 1; index < vlistNgrids(vlistID1); ++index)
+    for (int index = 1; index < vlistNumGrids(vlistID1); ++index)
       if (vlistGrid(vlistID1, 0) != vlistGrid(vlistID1, index)) ndiffgrids++;
 
     if (ndiffgrids > 0) cdo_abort("Too many different grids in %s!", cdo_get_stream_name(0));
 
     ndiffgrids = 0;
-    for (int index = 1; index < vlistNgrids(vlistID2); ++index)
+    for (int index = 1; index < vlistNumGrids(vlistID2); ++index)
       if (vlistGrid(vlistID2, 0) != vlistGrid(vlistID2, index)) ndiffgrids++;
 
     if (ndiffgrids > 0) cdo_abort("Too many different grids in %s!", cdo_get_stream_name(1));
@@ -96,8 +100,9 @@ public:
     vlistDefTaxis(vlistID3, taxisID3);
     cdo_def_vlist(streamID3, vlistID3);
   }
+
   void
-  run()
+  run() override
   {
     int tsID = 0;
     while (true)
@@ -121,24 +126,23 @@ public:
             size_t numMissVals2;
             cdo_read_record(streamID2, array2.data(), &numMissVals2);
 
-            auto missval2 = vlistInqVarMissval(vlistID2, varID);
-
             cdo_inq_record(streamID1, &varID, &levelID);
             size_t numMissVals1;
             cdo_read_record(streamID1, array1.data(), &numMissVals1);
 
-            auto missval1 = vlistInqVarMissval(vlistID1, varID);
+            auto missval1 = varList1.vars[varID].missval;
+            auto missval2 = varList2.vars[varID].missval;
 
             for (size_t i = 0; i < gridsize2; ++i)
               {
-                if (gindex[i] >= 0 && !DBL_IS_EQUAL(array2[i], missval2)) { array1[gindex[i]] = array2[i]; }
+                if (gindex[i] >= 0 && dbl_is_not_equal(array2[i], missval2)) { array1[gindex[i]] = array2[i]; }
               }
 
             if (numMissVals1)
               {
                 numMissVals1 = 0;
                 for (size_t i = 0; i < gridsize1; ++i)
-                  if (DBL_IS_EQUAL(array1[i], missval1)) numMissVals1++;
+                  if (dbl_is_equal(array1[i], missval1)) numMissVals1++;
               }
 
             cdo_def_record(streamID3, varID, levelID);
@@ -148,8 +152,9 @@ public:
         tsID++;
       }
   }
+
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID3);
     cdo_stream_close(streamID2);
diff --git a/src/Mergetime.cc b/src/Mergetime.cc
index df2163d7475f1b610c9b622182bbf59ffe70e52f..14393de2c4c9ff1f44bdca385deea2810de7ce09 100644
--- a/src/Mergetime.cc
+++ b/src/Mergetime.cc
@@ -16,9 +16,10 @@
 #include "cdo_options.h"
 #include "cdo_rlimit.h"
 #include "process_int.h"
-#include "cdo_vlist.h"
 #include "util_files.h"
 #include "printinfo.h"
+#include "param_conversion.h"
+#include "pmlist.h"
 
 struct StreamInfo
 {
@@ -27,17 +28,18 @@ struct StreamInfo
   int vlistID;
   int taxisID;
   int tsID;
-  int nrecs;
+  int numRecords;
   VarList varList;
+  std::map<int, int> mapOfVarIDs;
 };
 
 bool
 getenv_skip_same_time()
 {
-  const auto envstr = getenv("SKIP_SAME_TIME");
+  auto envstr = getenv("SKIP_SAME_TIME");
   if (envstr)
     {
-      const auto ival = atoi(envstr);
+      auto ival = atoi(envstr);
       if (ival == 1)
         {
           if (Options::cdoVerbose) cdo_print("Set SKIP_SAME_TIME to %d", ival);
@@ -49,29 +51,29 @@ getenv_skip_same_time()
 }
 
 static void
-open_all_files(int nfiles, std::vector<StreamInfo> &streamInfo)
+open_all_files(int numFiles, std::vector<StreamInfo> &streamInfoList)
 {
-  for (int fileID = 0; fileID < nfiles; ++fileID)
+  for (int fileID = 0; fileID < numFiles; ++fileID)
     {
       if (Options::cdoVerbose) cdo_print("process: %s", cdo_get_stream_name(fileID));
 
-      auto &si = streamInfo[fileID];
+      auto &si = streamInfoList[fileID];
       si.streamID = cdo_open_read(fileID);
       si.vlistID = cdo_stream_inq_vlist(si.streamID);
       si.taxisID = vlistInqTaxis(si.vlistID);
-      varList_init(si.varList, si.vlistID);
+      si.varList = VarList(si.vlistID);
     }
 }
 
 static void
-read_first_timestep(int nfiles, std::vector<StreamInfo> &streamInfo)
+read_first_timestep(int numFiles, std::vector<StreamInfo> &streamInfoList)
 {
-  for (int fileID = 0; fileID < nfiles; ++fileID)
+  for (int fileID = 0; fileID < numFiles; ++fileID)
     {
-      auto &si = streamInfo[fileID];
+      auto &si = streamInfoList[fileID];
       si.tsID = 0;
-      si.nrecs = cdo_stream_inq_timestep(si.streamID, si.tsID);
-      if (si.nrecs == 0)
+      si.numRecords = cdo_stream_inq_timestep(si.streamID, si.tsID);
+      if (si.numRecords == 0)
         {
           cdo_stream_close(si.streamID);
           si.streamID = CDO_STREAM_UNDEF;
@@ -80,6 +82,40 @@ read_first_timestep(int nfiles, std::vector<StreamInfo> &streamInfo)
     }
 }
 
+static void
+get_parameter(bool &skipSameTime, MapFlag &mapFlag)
+{
+  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 == "skip_same_time")  skipSameTime = parameter_to_bool(value);
+          else if (key == "names")
+            {
+              if      (value == "union")     mapFlag = MapFlag::Left;
+              else if (value == "intersect") mapFlag = MapFlag::Intersect;
+              else cdo_abort("Invalid value for key >%s< (names=<union/intersect>)", key, value);
+            }
+          else cdo_abort("Invalid parameter key >%s<!", key);
+          // clang-format on
+        }
+    }
+}
+
 class Mergetime : public Process
 {
 public:
@@ -93,79 +129,136 @@ public:
     .constraints = { -1, 1, NoRestriction },
   };
   inline static RegisterEntry<Mergetime> registration = RegisterEntry<Mergetime>(module);
+
   int tsID2 = 0;
+  int vlistID2 = CDI_UNDEFID;
   int taxisID2 = CDI_UNDEFID;
   CdiDateTime lastDateTime{};
   Field field;
 
   CdoStreamID streamID2;
 
-  int nfiles;
-  bool dataIsUnchanged;
-  bool skipSameTime;
-  std::vector<StreamInfo> streamInfo;
+  int numFiles{ 0 };
+  bool dataIsUnchanged{ false };
+  bool skipSameTime{ false };
+  std::vector<StreamInfo> streamInfoList;
+  int vlistFileIDmin{ -1 };
+  int vlistFileIDmax{ -1 };
+
+  MapFlag mapFlag{ MapFlag::Undefined };
 
 public:
   void
-  init()
+  init() override
   {
-
-    operator_check_argc(0);
-
     skipSameTime = getenv_skip_same_time();
 
+    get_parameter(skipSameTime, mapFlag);
+
     dataIsUnchanged = data_is_unchanged();
 
-    nfiles = cdo_stream_cnt() - 1;
-    streamInfo = std::vector<StreamInfo>(nfiles);
+    numFiles = cdo_stream_cnt() - 1;
+    streamInfoList.resize(numFiles);
 
-    cdo::set_numfiles(nfiles + 8);
+    cdo::set_numfiles(numFiles + 8);
 
-    open_all_files(nfiles, streamInfo);
+    open_all_files(numFiles, streamInfoList);
 
     // check that the contents is always the same
-    for (int fileID = 1; fileID < nfiles; ++fileID) vlist_compare(streamInfo[0].vlistID, streamInfo[fileID].vlistID, CmpVlist::All);
+    if (mapFlag == MapFlag::Undefined)
+      {
+        for (int fileID = 1; fileID < numFiles; ++fileID)
+          {
+            auto &si = streamInfoList[fileID];
+            varList_compare(streamInfoList[0].varList, si.varList);
+            for (const auto &var : si.varList.vars) si.mapOfVarIDs[var.ID] = var.ID;
+          }
+      }
+    else
+      {
+        vlistFileIDmin = 0;
+        vlistFileIDmax = 0;
+        for (int fileID = 1; fileID < numFiles; ++fileID)
+          {
+            auto numVars = streamInfoList[fileID].varList.numVars();
+            if (numVars < streamInfoList[vlistFileIDmin].varList.numVars()) vlistFileIDmin = fileID;
+            if (numVars > streamInfoList[vlistFileIDmax].varList.numVars()) vlistFileIDmax = fileID;
+          }
+
+        const auto &varList2 = streamInfoList[(mapFlag == MapFlag::Intersect) ? vlistFileIDmin : vlistFileIDmax].varList;
+        for (int fileID = 0; fileID < numFiles; ++fileID)
+          {
+            auto &si = streamInfoList[fileID];
+            varList_map(si.varList, varList2, mapFlag, si.mapOfVarIDs);
+          }
+      }
 
     // read the first time step
-    read_first_timestep(nfiles, streamInfo);
+    read_first_timestep(numFiles, streamInfoList);
 
-    std::string ofilename = cdo_get_stream_name(nfiles);
+    std::string ofilename = cdo_get_stream_name(numFiles);
     if (!Options::cdoOverwriteMode && FileUtils::file_exists(ofilename) && !FileUtils::user_file_overwrite(ofilename))
       cdo_abort("Outputfile %s already exists!", ofilename);
 
-    streamID2 = cdo_open_write(nfiles);
+    streamID2 = cdo_open_write(numFiles);
   }
 
   void
-  run()
+  fill_missing_fields(StreamInfo &si1, StreamInfo &si2)
   {
+    auto maxVars = si1.varList.numVars();
+    auto numVars = si2.varList.numVars();
+    if (numVars < maxVars)
+      {
+        std::vector<short> missingIDs(maxVars, 0);
+        for (int varID = 0; varID < numVars; ++varID) { missingIDs[si2.mapOfVarIDs[varID]] = 1; }
+        for (int varID = 0; varID < maxVars; ++varID)
+          {
+            if (missingIDs[varID] != 1)
+              {
+                const auto &var1 = si1.varList.vars[varID];
+                field.init(var1);
+                field_fill(field, var1.missval);
+                field.numMissVals = field.gridsize;
+                for (int levelID = 0; levelID < var1.nlevels; levelID++)
+                  {
+                    cdo_def_record(streamID2, varID, levelID);
+                    cdo_write_record(streamID2, field);
+                  }
+              }
+          }
+      }
+  }
 
+  void
+  run() override
+  {
     while (true)
       {
         auto processTimestep = true;
 
         int nextFileID = -1;
         CdiDateTime vDateTime{};
-        for (int fileID = 0; fileID < nfiles; ++fileID)
+        for (int fileID = 0; fileID < numFiles; ++fileID)
           {
-            if (streamInfo[fileID].streamID != CDO_STREAM_UNDEF)
+            if (streamInfoList[fileID].streamID != CDO_STREAM_UNDEF)
               {
-                const auto vdate = cdiDate_get(streamInfo[fileID].vDateTime.date);
-                const auto vtime = cdiTime_get(streamInfo[fileID].vDateTime.time);
+                auto vdate = cdiDate_get(streamInfoList[fileID].vDateTime.date);
+                auto vtime = cdiTime_get(streamInfoList[fileID].vDateTime.time);
                 if (nextFileID == -1 || vdate < cdiDate_get(vDateTime.date)
                     || (vdate == cdiDate_get(vDateTime.date) && vtime < cdiTime_get(vDateTime.time)))
                   {
                     nextFileID = fileID;
-                    vDateTime = streamInfo[fileID].vDateTime;
+                    vDateTime = streamInfoList[fileID].vDateTime;
                   }
               }
           }
 
-        const auto fileID = nextFileID;
+        auto fileID = nextFileID;
         if (Options::cdoVerbose) cdo_print("nextstep = %d  vDateTime = %s", fileID, datetime_to_string(vDateTime));
         if (fileID == -1) break;
 
-        auto &si = streamInfo[fileID];
+        auto &si = streamInfoList[fileID];
 
         if (skipSameTime && cdiDateTime_isEQ(vDateTime, lastDateTime))
           {
@@ -178,12 +271,14 @@ public:
           {
             if (tsID2 == 0)
               {
-                const auto vlistID1 = si.vlistID;
-                const auto vlistID2 = vlistDuplicate(vlistID1);
-                const auto taxisID1 = vlistInqTaxis(vlistID1);
+                auto vlistID1 = (mapFlag == MapFlag::Undefined)
+                                    ? si.vlistID
+                                    : ((mapFlag == MapFlag::Intersect) ? streamInfoList[vlistFileIDmin].vlistID
+                                                                       : streamInfoList[vlistFileIDmax].vlistID);
+                vlistID2 = vlistDuplicate(vlistID1);
+                auto taxisID1 = vlistInqTaxis(si.vlistID);
                 taxisID2 = taxisDuplicate(taxisID1);
                 vlistDefTaxis(vlistID2, taxisID2);
-
                 cdo_def_vlist(streamID2, vlistID2);
               }
 
@@ -192,29 +287,39 @@ public:
             cdo_taxis_copy_timestep(taxisID2, si.taxisID);
             cdo_def_timestep(streamID2, tsID2);
 
-            for (int recID = 0; recID < si.nrecs; ++recID)
+            for (int recID = 0; recID < si.numRecords; ++recID)
               {
-                int varID, levelID;
-                cdo_inq_record(si.streamID, &varID, &levelID);
+                auto [varID, levelID] = cdo_inq_record(si.streamID);
 
-                if (tsID2 > 0 && si.tsID == 0 && si.varList[varID].isConstant) continue;
+                const auto &var = si.varList.vars[varID];
+                if (tsID2 > 0 && si.tsID == 0 && var.isConstant) continue;
 
-                cdo_def_record(streamID2, varID, levelID);
+                auto varID2 = varID;
+                if (mapFlag != MapFlag::Undefined)
+                  {
+                    auto it = si.mapOfVarIDs.find(varID);
+                    if (it == si.mapOfVarIDs.end()) continue;
+                    varID2 = it->second;
+                  }
+
+                cdo_def_record(streamID2, varID2, levelID);
 
                 if (dataIsUnchanged) { cdo_copy_record(streamID2, si.streamID); }
                 else
                   {
-                    field.init(si.varList[varID]);
+                    field.init(var);
                     cdo_read_record(si.streamID, field);
                     cdo_write_record(streamID2, field);
                   }
               }
 
+            if (mapFlag == MapFlag::Left) fill_missing_fields(streamInfoList[vlistFileIDmax], si);
+
             tsID2++;
           }
 
-        si.nrecs = cdo_stream_inq_timestep(si.streamID, ++si.tsID);
-        if (si.nrecs == 0)
+        si.numRecords = cdo_stream_inq_timestep(si.streamID, ++si.tsID);
+        if (si.numRecords == 0)
           {
             cdo_stream_close(si.streamID);
             si.streamID = CDO_STREAM_UNDEF;
@@ -224,8 +329,10 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
+    if (vlistID2 != CDI_UNDEFID) vlistDestroy(vlistID2);
+    if (taxisID2 != CDI_UNDEFID) taxisDestroy(taxisID2);
   }
 };
diff --git a/src/Merstat.cc b/src/Merstat.cc
index 19f38350bed79c78f41fca2dcd89d1d5676035da..09fd6791abbe4f8a3c3c84c22e2d3e63e947ab23 100644
--- a/src/Merstat.cc
+++ b/src/Merstat.cc
@@ -56,7 +56,6 @@ public:
   inline static RegisterEntry<Merstat> registration = RegisterEntry<Merstat>(module);
 
   int gridID1, gridID2 = -1, lastgrid = -1;
-  int index;
 
   CdoStreamID streamID1;
   CdoStreamID streamID2;
@@ -74,9 +73,8 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
-
     auto operatorID = cdo_operator_id();
     operfunc = cdo_operator_f1(operatorID);
 
@@ -97,18 +95,20 @@ public:
 
     if (!lminmax) vlist_unpack(vlistID2);
 
+    varList1 = VarList(vlistID1);
+
     taxisID1 = vlistInqTaxis(vlistID1);
     taxisID2 = taxisDuplicate(taxisID1);
     vlistDefTaxis(vlistID2, taxisID2);
 
-    auto ngrids = vlistNgrids(vlistID1);
-    int ndiffgrids = 0;
-    for (index = 1; index < ngrids; ++index)
-      if (vlistGrid(vlistID1, 0) != vlistGrid(vlistID1, index)) ndiffgrids++;
+    auto numGrids = vlistNumGrids(vlistID1);
+    int numDiffGrids = 0;
+    for (int index = 1; index < numGrids; ++index)
+      if (vlistGrid(vlistID1, 0) != vlistGrid(vlistID1, index)) numDiffGrids++;
 
-    if (ndiffgrids > 0) cdo_abort("Too many different grids!");
+    if (numDiffGrids > 0) cdo_abort("Too many different grids!");
 
-    index = 0;
+    int index = 0;
     gridID1 = vlistGrid(vlistID1, index);
 
     if (gridInqType(gridID1) == GRID_LONLAT || gridInqType(gridID1) == GRID_GAUSSIAN || gridInqType(gridID1) == GRID_GENERIC)
@@ -122,8 +122,8 @@ public:
     streamID2 = cdo_open_write(1);
     cdo_def_vlist(streamID2, vlistID2);
 
-    gridID1 = vlistInqVarGrid(vlistID1, 0);
-    const int nlonmax = gridInqXsize(gridID1);  // max nlon?
+    gridID1 = varList1.vars[0].gridID;
+    int nlonmax = gridInqXsize(gridID1);  // max nlon?
     auto gridsizemax = vlistGridsizeMax(vlistID1);
 
     if (needWeights) field1.weightv.resize(gridsizemax);
@@ -131,12 +131,10 @@ public:
     field2.resize(nlonmax);
     field2.grid = gridID2;
     field2.memType = MemType::Double;
-
-    varList_init(varList1, vlistID1);
   }
 
   void
-  run()
+  run() override
   {
     int tsID = 0;
     while (true)
@@ -151,12 +149,13 @@ public:
           {
             int varID, levelID;
             cdo_inq_record(streamID1, &varID, &levelID);
-            field1.init(varList1[varID]);
+            const auto &var = varList1.vars[varID];
+            field1.init(var);
             cdo_read_record(streamID1, field1);
 
             field2.missval = field1.missval;
 
-            bool wstatus = false;
+            auto wstatus = false;
             if (needWeights && field1.grid != lastgrid)
               {
                 lastgrid = field1.grid;
@@ -164,8 +163,7 @@ public:
               }
 
             if (wstatus != 0 && tsID == 0 && levelID == 0)
-              cdo_warning("Grid cell bounds not available, using constant grid cell area weights for variable %s!",
-                          varList1[varID].name);
+              cdo_warning("Grid cell bounds not available, using constant grid cell area weights for variable %s!", var.name);
 
             (operfunc == FieldFunc_Pctl) ? meridional_pctl(field1, field2, pn) : meridional_function(field1, field2, operfunc);
 
@@ -178,7 +176,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
diff --git a/src/Monarith.cc b/src/Monarith.cc
index c0e73b4b0a236ca025c5c9cafd0112d0932dc6ba..9c04cda0d4d3457bf7d9b4f7950a5ca6349afdbf 100644
--- a/src/Monarith.cc
+++ b/src/Monarith.cc
@@ -36,6 +36,7 @@ public:
     .constraints = { 2, 1, NoRestriction },
   };
   inline static RegisterEntry<Monarith> registration = RegisterEntry<Monarith>(module);
+
   CdoStreamID streamID1;
   CdoStreamID streamID2;
   CdoStreamID streamID3;
@@ -44,17 +45,15 @@ public:
   int taxisID2;
   int taxisID3;
 
-  FieldVector2D vars2;
+  FieldVector2D varsData2;
   VarList varList1;
-  Field field;
 
   int operfunc;
 
 public:
   void
-  init()
+  init() override
   {
-
     auto operatorID = cdo_operator_id();
     operfunc = cdo_operator_f1(operatorID);
 
@@ -69,9 +68,9 @@ public:
 
     vlist_unpack(vlistID3);
 
-    vlist_compare(vlistID1, vlistID2, CmpVlist::All);
-
-    varList_init(varList1, vlistID1);
+    varList1 = VarList(vlistID1);
+    VarList varList2(vlistID2);
+    varList_compare(varList1, varList2);
 
     taxisID1 = vlistInqTaxis(vlistID1);
     taxisID2 = vlistInqTaxis(vlistID2);
@@ -81,12 +80,14 @@ public:
     streamID3 = cdo_open_write(2);
     cdo_def_vlist(streamID3, vlistID3);
 
-    fields_from_vlist(vlistID2, vars2, FIELD_VEC | FIELD_NAT);
+    field2D_init(varsData2, varList2, FIELD_VEC | FIELD_NAT);
   }
 
   void
-  run()
+  run() override
   {
+    Field field;
+
     int yearmon2 = -1;
     int tsID = 0;
     int tsID2 = 0;
@@ -121,7 +122,7 @@ public:
               {
                 int varID, levelID;
                 cdo_inq_record(streamID2, &varID, &levelID);
-                cdo_read_record(streamID2, vars2[varID][levelID]);
+                cdo_read_record(streamID2, varsData2[varID][levelID]);
               }
 
             tsID2++;
@@ -134,10 +135,10 @@ public:
           {
             int varID, levelID;
             cdo_inq_record(streamID1, &varID, &levelID);
-            field.init(varList1[varID]);
+            field.init(varList1.vars[varID]);
             cdo_read_record(streamID1, field);
 
-            field2_function(field, vars2[varID][levelID], operfunc);
+            field2_function(field, varsData2[varID][levelID], operfunc);
 
             cdo_def_record(streamID3, varID, levelID);
             cdo_write_record(streamID3, field);
@@ -148,7 +149,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID3);
     cdo_stream_close(streamID2);
diff --git a/src/Mrotuv.cc b/src/Mrotuv.cc
index e73a8f768478324960fa022ce4dd182778ee2bfe..47e10227007dbe294bb54a2660e9be40b17cbe16 100644
--- a/src/Mrotuv.cc
+++ b/src/Mrotuv.cc
@@ -182,7 +182,7 @@ public:
   using Process::Process;
   inline static CdoModule module = {
     .name = "Mrotuv",
-    .operators = { { "mrotuv"} },
+    .operators = { { "mrotuv" } },
     .aliases = {},
     .mode = EXPOSED,     // Module mode: 0:intern 1:extern
     .number = CDI_REAL,  // Allowed number type
@@ -224,25 +224,26 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
     operator_check_argc(0);
 
     streamID1 = cdo_open_read(0);
 
     auto vlistID1 = cdo_stream_inq_vlist(streamID1);
+    VarList varList1(vlistID1);
 
-    auto nvars = vlistNvars(vlistID1);
-    for (int varid = 0; varid < nvars; varid++)
+    auto numVars = vlistNvars(vlistID1);
+    for (int varid = 0; varid < numVars; varid++)
       {
-        auto code = vlistInqVarCode(vlistID1, varid);
+        auto code = varList1.vars[varid].code;
         if (code == 3 || code == 131) uid = varid;
         if (code == 4 || code == 132) vid = varid;
       }
 
     if (uid == -1 || vid == -1)
       {
-        if (nvars == 2)
+        if (numVars == 2)
           {
             uid = 0;
             vid = 1;
@@ -251,18 +252,19 @@ public:
           cdo_abort("U and V not found in %s", cdo_get_stream_name(0));
       }
 
-    nlevs = zaxisInqSize(vlistInqVarZaxis(vlistID1, uid));
-    if (nlevs != zaxisInqSize(vlistInqVarZaxis(vlistID1, vid))) cdo_abort("U and V have different number of levels!");
+    const auto &varU = varList1.vars[uid];
+    const auto &varV = varList1.vars[vid];
+    if (varU.nlevels != varV.nlevels) cdo_abort("U and V have different number of levels!");
 
-    auto gridID1 = vlistInqVarGrid(vlistID1, uid);
-    auto gridID2 = vlistInqVarGrid(vlistID1, vid);
-    gridsize = gridInqSize(gridID1);
-    if (gridID1 != gridID2) cdo_abort("Input grids differ!");
+    auto gridID1 = varU.gridID;
+    gridsize = varU.gridsize;
+    if (varU.gridID != varV.gridID) cdo_abort("Input grids differ!");
 
-    if (gridInqType(gridID1) != GRID_LONLAT && gridInqType(gridID1) != GRID_GAUSSIAN && gridInqType(gridID1) != GRID_CURVILINEAR)
-      cdo_abort("Grid %s unsupported!", gridNamePtr(gridInqType(gridID1)));
+    auto gridType = varU.gridType;
+    if (gridType != GRID_LONLAT && gridType != GRID_GAUSSIAN && gridType != GRID_CURVILINEAR)
+      cdo_abort("Grid %s unsupported!", gridNamePtr(gridType));
 
-    if (gridInqType(gridID1) != GRID_CURVILINEAR) gridID1 = gridToCurvilinear(gridID1);
+    if (gridType != GRID_CURVILINEAR) gridID1 = gridToCurvilinear(gridID1);
 
     if (gridsize != gridInqSize(gridID1)) cdo_abort("Internal problem: gridsize changed!");
 
@@ -329,12 +331,12 @@ public:
     cdo_def_vlist(streamID2, vlistID2);
     cdo_def_vlist(streamID3, vlistID3);
 
-    missval1 = vlistInqVarMissval(vlistID1, uid);
-    missval2 = vlistInqVarMissval(vlistID1, vid);
+    missval1 = varU.missval;
+    missval2 = varV.missval;
   }
 
   void
-  run()
+  run() override
   {
     ufield_v = Varray<double>(gridsize);
     vfield_v = Varray<double>(gridsize);
@@ -423,7 +425,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID3);
     cdo_stream_close(streamID2);
diff --git a/src/Mrotuvb.cc b/src/Mrotuvb.cc
index 003eefd7c979734011d0b710f9c6899f87257f4d..cfc7aeb26471595aa9d2c1001d5b62288278e7fa 100644
--- a/src/Mrotuvb.cc
+++ b/src/Mrotuvb.cc
@@ -238,6 +238,7 @@ public:
     .constraints = { 2, 1, NoRestriction },
   };
   inline static RegisterEntry<Mrotuvb> registration = RegisterEntry<Mrotuvb>(module);
+
   CdoStreamID streamID1;
   CdoStreamID streamID2;
   CdoStreamID streamID3;
@@ -265,9 +266,8 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
-
     gpint = !(cdo_operator_argc() == 1 && cdo_operator_argv(0) == "noint");
 
     streamID1 = cdo_open_read(0);
@@ -276,26 +276,31 @@ public:
     auto vlistID1 = cdo_stream_inq_vlist(streamID1);
     auto vlistID2 = cdo_stream_inq_vlist(streamID2);
 
-    auto nvars = vlistNvars(vlistID1);
-    if (nvars > 1) cdo_abort("More than one variable found in %s", cdo_get_stream_name(0));
-    nvars = vlistNvars(vlistID2);
-    if (nvars > 1) cdo_abort("More than one variable found in %s", cdo_get_stream_name(1));
+    VarList varList1(vlistID1);
+    VarList varList2(vlistID2);
+
+    const auto &varU = varList1.vars[0];
+    const auto &varV = varList2.vars[0];
 
-    auto gridID1 = vlistGrid(vlistID1, 0);
-    auto gridID2 = vlistGrid(vlistID2, 0);
-    gridsize = gridInqSize(gridID1);
+    if (varList1.numVars() > 1) cdo_abort("More than one variable found in %s", cdo_get_stream_name(0));
+    if (varList2.numVars() > 1) cdo_abort("More than one variable found in %s", cdo_get_stream_name(1));
+
+    auto gridID1 = varU.gridID;
+    auto gridID2 = varV.gridID;
+    gridsize = varU.gridsize;
     if (gpint && gridID1 == gridID2) cdo_abort("Input grids are the same, use parameter >noint< to disable interpolation!");
     if (!gpint && gridID1 != gridID2) cdo_abort("Input grids are not the same!");
-    if (gridsize != gridInqSize(gridID2)) cdo_abort("Grids have different size!");
+    if (gridsize != varV.gridsize) cdo_abort("Grids have different size!");
 
-    if (gridInqType(gridID1) != GRID_LONLAT && gridInqType(gridID1) != GRID_GAUSSIAN && gridInqType(gridID1) != GRID_CURVILINEAR)
-      cdo_abort("Grid %s unsupported!", gridNamePtr(gridInqType(gridID1)));
+    auto gridType = varU.gridType;
+    if (gridType != GRID_LONLAT && gridType != GRID_GAUSSIAN && gridType != GRID_CURVILINEAR)
+      cdo_abort("Grid %s unsupported!", gridNamePtr(gridType));
 
-    if (gridInqType(gridID1) != GRID_CURVILINEAR) gridID1 = gridToCurvilinear(gridID1, NeedCorners::Yes);
+    if (gridType != GRID_CURVILINEAR) gridID1 = gridToCurvilinear(gridID1, NeedCorners::Yes);
 
     if (gridsize != gridInqSize(gridID1)) cdo_abort("Internal problem: gridsize changed!");
 
-    if (gridInqType(gridID2) != GRID_CURVILINEAR) gridID2 = gridToCurvilinear(gridID2, NeedCorners::Yes);
+    if (varV.gridType != GRID_CURVILINEAR) gridID2 = gridToCurvilinear(gridID2, NeedCorners::Yes);
 
     if (gridsize != gridInqSize(gridID2)) cdo_abort("Internal problem: gridsize changed!");
 
@@ -341,14 +346,10 @@ public:
         grid3y[i] *= DEG2RAD;
       }
 
-    auto vlistID3 = vlistCreate();
-    vlistCopy(vlistID3, vlistID1);
+    auto vlistID3 = vlistDuplicate(vlistID1);
     vlistCat(vlistID3, vlistID2);
 
-    auto code1 = vlistInqVarCode(vlistID1, 0);
-    auto code2 = vlistInqVarCode(vlistID2, 0);
-
-    if (code1 == code2) vlistDefVarCode(vlistID3, 1, code1 + 1);
+    if (varU.code == varV.code) vlistDefVarCode(vlistID3, 1, varU.code + 1);
 
     vlistChangeGrid(vlistID3, gridID1, gridID3);
     vlistChangeGrid(vlistID3, gridID2, gridID3);
@@ -362,8 +363,8 @@ public:
     streamID3 = cdo_open_write(2);
     cdo_def_vlist(streamID3, vlistID3);
 
-    missval1 = vlistInqVarMissval(vlistID1, 0);
-    missval2 = vlistInqVarMissval(vlistID2, 0);
+    missval1 = varU.missval;
+    missval2 = varV.missval;
 
     urfield = Varray<double>(gridsize);
     vrfield = Varray<double>(gridsize);
@@ -372,7 +373,7 @@ public:
   }
 
   void
-  run()
+  run() override
   {
     MatrixView<double> ufield(ufield_v.data(), nlat, nlon);
     MatrixView<double> vfield(vfield_v.data(), nlat, nlon);
@@ -488,7 +489,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID3);
     cdo_stream_close(streamID2);
diff --git a/src/NCL_wind.cc b/src/NCL_wind.cc
index 07bd3e05b54aea432dbeb917703268fc07457c4e..bee1a06d26747b692f707ba85bf1bbac05349fb5 100644
--- a/src/NCL_wind.cc
+++ b/src/NCL_wind.cc
@@ -79,10 +79,10 @@ uv2vr_cfd_W(double missval, double *u, double *v, double *lon, double *lat, size
 static int
 find_name(const VarList &varList, const std::string &name)
 {
-  int nvars = varList.size();
-  for (int varID = 0; varID < nvars; ++varID)
+  auto numVars = varList.numVars();
+  for (int varID = 0; varID < numVars; ++varID)
     {
-      if (name == varList[varID].name) return varID;
+      if (name == varList.vars[varID].name) return varID;
     }
 
   return CDI_UNDEFID;
@@ -202,7 +202,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
     UV2DV_CFD = module.get_id("uv2dv_cfd");
     UV2VR_CFD = module.get_id("uv2vr_cfd");
@@ -222,7 +222,7 @@ public:
     else
       cdo_abort("outMode=%d unsupported!", outMode);
 
-    varList_init(varList1, vlistID1);
+    varList1 = VarList(vlistID1);
 
     varIDu = find_name(varList1, uName);
     varIDv = find_name(varList1, vName);
@@ -230,8 +230,8 @@ public:
     if (varIDu == CDI_UNDEFID) cdo_abort("%s not found!", uName);
     if (varIDv == CDI_UNDEFID) cdo_abort("%s not found!", vName);
 
-    const auto &varU = varList1[varIDu];
-    const auto &varV = varList1[varIDv];
+    const auto &varU = varList1.vars[varIDu];
+    const auto &varV = varList1.vars[varIDv];
 
     auto gridIDu = varU.gridID;
     auto gridIDv = varV.gridID;
@@ -255,7 +255,7 @@ public:
 
     if (nlev != zaxisInqSize(varU.zaxisID)) cdo_abort("u and v must have the same number of level!");
 
-    varIDo = vlistDefVar(vlistID2, gridIDu, zaxisIDu, varU.timetype);
+    varIDo = vlistDefVar(vlistID2, gridIDu, zaxisIDu, varU.timeType);
     if (operatorID == UV2DV_CFD)
       {
         cdiDefKeyString(vlistID2, varIDo, CDI_KEY_NAME, "d");
@@ -292,10 +292,10 @@ public:
   }
 
   void
-  run()
+  run() override
   {
-    const auto &varU = varList1[varIDu];
-    const auto &varV = varList1[varIDv];
+    const auto &varU = varList1.vars[varIDu];
+    const auto &varV = varList1.vars[varIDv];
     int tsID = 0;
     while (true)
       {
@@ -338,7 +338,7 @@ public:
         if (numMissValsu != numMissValsv)
           {
             cdo_abort("u and v have different number of missing values!");
-            if (numMissValsu && !dbl_is_equal(varU.missval, varV.missval))
+            if (numMissValsu && dbl_is_not_equal(varU.missval, varV.missval))
               {
                 for (int levelID = 0; levelID < nlev; ++levelID)
                   {
@@ -367,7 +367,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID1);
     cdo_stream_close(streamID2);
diff --git a/src/Ninfo.cc b/src/Ninfo.cc
index df57a367de3c4a05d71b50a2fd5bfde0ae964882..2bcbd403c4bbeb69070d1d629bbacb12ee1eaea8 100644
--- a/src/Ninfo.cc
+++ b/src/Ninfo.cc
@@ -60,14 +60,13 @@ public:
   int ntsteps;
   CdoStreamID streamID;
   int taxisID;
-  int nvars;
   int ngrids;
 
   VarList varList;
 
 public:
   void
-  init()
+  init() override
   {
 
     auto operatorID = cdo_operator_id();
@@ -78,16 +77,17 @@ public:
     streamID = cdo_open_read(0);
     auto vlistID = cdo_stream_inq_vlist(streamID);
 
-    nvars = vlistNvars(vlistID);
     taxisID = vlistInqTaxis(vlistID);
     ntsteps = vlistNtsteps(vlistID);
-    ngrids = vlistNgrids(vlistID);
+    ngrids = vlistNumGrids(vlistID);
 
-    varList_init(varList, vlistID);
+    varList = VarList(vlistID);
   }
+
   void
-  run()
+  run() override
   {
+    auto numVars = varList.numVars();
     switch (operfunc)
       {
       case NYEAR:
@@ -159,12 +159,12 @@ public:
           fprintf(stdout, "%d\n", tsID);
           break;
         }
-      case NPAR: fprintf(stdout, "%d\n", nvars); break;
+      case NPAR: fprintf(stdout, "%d\n", numVars); break;
       case NLEVEL:
-        for (int varID = 0; varID < nvars; ++varID) { fprintf(stdout, "%d\n", varList[varID].nlevels); }
+        for (int varID = 0; varID < numVars; ++varID) { fprintf(stdout, "%d\n", varList.vars[varID].nlevels); }
         break;
       case NGRIDPOINTS:
-        for (int varID = 0; varID < nvars; ++varID) { fprintf(stdout, "%zu\n", varList[varID].gridsize); }
+        for (int varID = 0; varID < numVars; ++varID) { fprintf(stdout, "%zu\n", varList.vars[varID].gridsize); }
         break;
       case NGRIDS: fprintf(stdout, "%d\n", ngrids); break;
       default: cdo_abort("operator not implemented!"); break;
@@ -172,7 +172,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID);
   }
diff --git a/src/Nmldump.cc b/src/Nmldump.cc
index 93d5e8e0dcf6a8dae433a1b0c8e7dddce01176fe..e260498de282e2d9f698b0b1a3b277d0c4900db2 100644
--- a/src/Nmldump.cc
+++ b/src/Nmldump.cc
@@ -84,7 +84,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
 
     NMLDUMP = module.get_id("nmldump");
@@ -95,7 +95,7 @@ public:
     operator_check_argc(0);
   }
   void
-  run()
+  run() override
   {
     pmlist.read_namelist(stdin, "STDIN");
 
@@ -106,7 +106,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
   }
 };
diff --git a/src/Output.cc b/src/Output.cc
index 0a3d924626a97d6736a6a60c166901ea7c29a27f..525a515d36ac376dc535557f2a9de28beacb5c1e 100644
--- a/src/Output.cc
+++ b/src/Output.cc
@@ -18,8 +18,6 @@
 
 #include <cdi.h>
 
-#include <unordered_map>
-
 #include "cdo_options.h"
 #include "process_int.h"
 #include "param_conversion.h"
@@ -76,7 +74,7 @@ outputxyz(size_t gridsize, const Varray<double> &array, double missval, size_t n
   double fmin = 0.0;
   double x, y, z;
   for (size_t i = 0; i < gridsize; ++i)
-    if (!dbl_is_equal(array[i], missval))
+    if (dbl_is_not_equal(array[i], missval))
       {
         if (array[i] < fmin) fmin = array[i];
         fprintf(stdout, "%g\t%g\t%g\t%g\n", lon[i], lat[i], array[i], array[i]);
@@ -308,14 +306,13 @@ public:
     { "year",     kyear,      5 },
     { "month",    kmonth,     2 },
     { "day",      kday,       2 },
-    // clang-format on
   };
+  // clang-format on
 
 public:
   void
-  init()
+  init() override
   {
-
     OUTPUT = module.get_id("output");
     OUTPUTINT = module.get_id("outputint");
     OUTPUTSRV = module.get_id("outputsrv");
@@ -396,18 +393,18 @@ public:
       }
     else { operator_check_argc(0); }
   }
+
   void
-  run()
+  run() override
   {
     for (int indf = 0; indf < cdo_stream_cnt(); indf++)
       {
         auto streamID = cdo_open_read(indf);
         auto vlistID = cdo_stream_inq_vlist(streamID);
 
-        VarList varList;
-        varList_init(varList, vlistID);
+        VarList varList(vlistID);
 
-        auto ngrids = vlistNgrids(vlistID);
+        auto ngrids = vlistNumGrids(vlistID);
         int ndiffgrids = 0;
         for (index = 1; index < ngrids; ++index)
           if (vlistGrid(vlistID, 0) != vlistGrid(vlistID, index)) ndiffgrids++;
@@ -448,11 +445,11 @@ public:
               {
                 int varID, levelID;
                 cdo_inq_record(streamID, &varID, &levelID);
-                const auto &var = varList[varID];
+                const auto &var = varList.vars[varID];
 
                 auto code = var.code;
                 auto gridID = var.gridID;
-                auto dig = (var.datatype == CDI_DATATYPE_FLT64) ? Options::CDO_dbl_digits : Options::CDO_flt_digits;
+                auto dig = (var.dataType == CDI_DATATYPE_FLT64) ? Options::CDO_dbl_digits : Options::CDO_flt_digits;
                 gridsize = var.nwpv * var.gridsize;
                 auto nlon = gridInqXsize(gridID);
                 auto nlat = gridInqYsize(gridID);
@@ -491,7 +488,7 @@ public:
                     cdiTime_decode(vDateTime.time, &hour, &minute, &second, &ms);
                     double xdate = vdate - (vdate / 100) * 100 + (hour * 3600 + minute * 60 + second) / 86400.0;
                     for (size_t i = 0; i < gridsize; ++i)
-                      if (!dbl_is_equal(array[i], missval))
+                      if (dbl_is_not_equal(array[i], missval))
                         fprintf(stdout, "%g\t%g\t%g\t%.*g\n", xdate, grid_center_lat[i], grid_center_lon[i], dig, array[i]);
                   }
                 else if (operatorID == OUTPUTTAB)
@@ -562,8 +559,9 @@ public:
         cdo_stream_close(streamID);
       }
   }
+
   void
-  close()
+  close() override
   {
   }
 };
diff --git a/src/Outputgmt.cc b/src/Outputgmt.cc
index 5447e7f6f2beaf07f64ed40af86a6eba71575dd3..ee222b633c132d5ed3fa6ca2e3fe49ee68e2c58a 100644
--- a/src/Outputgmt.cc
+++ b/src/Outputgmt.cc
@@ -91,8 +91,8 @@ output_zon(double levmin, double levmax, const double *cell_corner_lat)
   auto latmax = cell_corner_lat[0];
   for (int ic = 1; ic < 4; ++ic) latmin = std::min(latmin, cell_corner_lat[ic]);
   for (int ic = 1; ic < 4; ++ic) latmax = std::max(latmax, cell_corner_lat[ic]);
-  const double xlev[4] = { levmin, levmax, levmax, levmin };
-  const double xlat[4] = { latmin, latmin, latmax, latmax };
+  double xlev[4] = { levmin, levmax, levmax, levmin };
+  double xlat[4] = { latmin, latmin, latmax, latmax };
   for (int ic = 0; ic < 4; ++ic) fprintf(stdout, "   %g  %g\n", xlat[ic], xlev[ic]);
   fprintf(stdout, "   %g  %g\n", xlat[0], xlev[0]);
 }
@@ -104,8 +104,8 @@ output_mer(double levmin, double levmax, const double *cell_corner_lon)
   auto lonmax = cell_corner_lon[0];
   for (int ic = 1; ic < 4; ++ic) lonmin = std::min(lonmin, cell_corner_lon[ic]);
   for (int ic = 1; ic < 4; ++ic) lonmax = std::max(lonmax, cell_corner_lon[ic]);
-  const double xlev[4] = { levmin, levmin, levmax, levmax };
-  const double xlon[4] = { lonmin, lonmax, lonmax, lonmin };
+  double xlev[4] = { levmin, levmin, levmax, levmax };
+  double xlon[4] = { lonmin, lonmax, lonmax, lonmin };
   for (int ic = 0; ic < 4; ++ic) fprintf(stdout, "   %g  %g\n", xlon[ic], xlev[ic]);
   fprintf(stdout, "   %g  %g\n", xlon[0], xlev[0]);
 }
@@ -113,7 +113,7 @@ output_mer(double levmin, double levmax, const double *cell_corner_lon)
 static const int *
 get_rgb(double value, double missval, const CPT &cpt)
 {
-  if (!DBL_IS_EQUAL(value, missval))
+  if (dbl_is_not_equal(value, missval))
     {
       int n;
       for (n = 0; n < cpt.ncolors; ++n)
@@ -374,6 +374,7 @@ public:
     .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;
@@ -424,9 +425,8 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
-
     GMTXYZ = module.get_id("gmtxyz");
     OUTPUTCENTER2 = module.get_id("outputcenter2");
     OUTPUTCENTERCPT = module.get_id("outputcentercpt");
@@ -470,11 +470,12 @@ public:
     auto vlistID = cdo_stream_inq_vlist(streamID);
     taxisID = vlistInqTaxis(vlistID);
 
-    varList_init(varList, vlistID);
+    varList = VarList(vlistID);
 
-    gridID = varList[0].gridID;
-    auto zaxisID = varList[0].zaxisID;
-    missval = varList[0].missval;
+    const auto &var0 = varList.vars[0];
+    gridID = var0.gridID;
+    auto zaxisID = var0.zaxisID;
+    missval = var0.missval;
 
     gridID = generate_full_cell_grid(gridID);
 
@@ -563,7 +564,7 @@ public:
     if (needCellCorners)
       {
         if (ncorner == 0) cdo_abort("Number of cell corners undefined!");
-        const size_t nalloc = ncorner * gridsize;
+        size_t nalloc = ncorner * gridsize;
         grid_corner_lat.resize(nalloc);
         grid_corner_lon.resize(nalloc);
 
@@ -606,8 +607,9 @@ public:
     if (operatorID == OUTPUTVECTOR) uf.resize(gridsize);
     if (operatorID == OUTPUTVECTOR) vf.resize(gridsize);
   }
+
   void
-  run()
+  run() override
   {
     int tsID = 0;
     while (true)
@@ -621,6 +623,7 @@ public:
 
         if (tsID == 0 && printHeader)
           {
+            const auto &var0 = varList.vars[0];
             if (operatorID == OUTPUTVRML) printf("#VRML V2.0 utf8\n\n");
 #ifdef VERSION
             fprintf(stdout, "# Generated by CDO version %s\n", VERSION);
@@ -628,10 +631,10 @@ public:
 #endif
             fprintf(stdout, "# Operator = %s\n", cdo_operator_name(operatorID));
             // clang-format off
-          if      (lhov) fprintf(stdout, "# Mode     = hovmoeller\n");
-          else if (lzon) fprintf(stdout, "# Mode     = zonal\n");
-          else if (lmer) fprintf(stdout, "# Mode     = meridional\n");
-          else           fprintf(stdout, "# Mode     = horizonal\n");
+            if      (lhov) fprintf(stdout, "# Mode     = hovmoeller\n");
+            else if (lzon) fprintf(stdout, "# Mode     = zonal\n");
+            else if (lmer) fprintf(stdout, "# Mode     = meridional\n");
+            else           fprintf(stdout, "# Mode     = horizonal\n");
             // clang-format on
 
             if (operatorID == OUTPUTVECTOR) fprintf(stdout, "# Increment = %d\n", ninc);
@@ -639,8 +642,8 @@ public:
             fprintf(stdout, "# Stream = %s\n", cdo_get_stream_name(0));
             fprintf(stdout, "# Date   = %s\n", vdateString.c_str());
             fprintf(stdout, "# Time   = %s\n", vtimeString.c_str());
-            fprintf(stdout, "# Name   = %s\n", varList[0].name.c_str());
-            fprintf(stdout, "# Code   = %d\n", varList[0].code);
+            fprintf(stdout, "# Name   = %s\n", var0.name.c_str());
+            fprintf(stdout, "# Code   = %d\n", var0.code);
           }
 
         varID0 = varID;
@@ -679,26 +682,28 @@ public:
                   {
                     if (grid_mask.size() && grid_mask[i] == 0) continue;
 
+                    auto lon = grid_center_lon[i];
+                    auto lat = grid_center_lat[i];
                     if (operatorID == GMTXYZ)
                       {
                         if (lzon)
-                          fprintf(stdout, " %g  %g  %g\n", grid_center_lat[i], level, array[i]);
+                          fprintf(stdout, " %g  %g  %g\n", lat, level, array[i]);
                         else if (lmer)
-                          fprintf(stdout, " %g  %g  %g\n", grid_center_lon[i], level, array[i]);
+                          fprintf(stdout, " %g  %g  %g\n", lon, level, array[i]);
                         else if (lhov)
-                          fprintf(stdout, " %d  %g  %g\n", tsID + 1, grid_center_lat[i], array[i]);
+                          fprintf(stdout, " %d  %g  %g\n", tsID + 1, lat, array[i]);
                         else
-                          fprintf(stdout, " %g  %g  %g\n", grid_center_lon[i], grid_center_lat[i], array[i]);
+                          fprintf(stdout, " %g  %g  %g\n", lon, lat, array[i]);
                       }
                     else if (operatorID == OUTPUTCENTER2) { fprintf(stdout, " %g  %g  %g\n", plon[i], plat[i], parray[i]); }
                     else
                       {
                         if (lzon)
-                          fprintf(stdout, " %g  %g  %g\n", grid_center_lat[i], level, array[i]);
+                          fprintf(stdout, " %g  %g  %g\n", lat, level, array[i]);
                         else if (lmer)
-                          fprintf(stdout, " %g  %g  %g\n", grid_center_lon[i], level, array[i]);
+                          fprintf(stdout, " %g  %g  %g\n", lon, level, array[i]);
                         else
-                          fprintf(stdout, " %g  %g  %g  %g\n", grid_center_lon[i], grid_center_lat[i], array[i], array[i]);
+                          fprintf(stdout, " %g  %g  %g  %g\n", lon, lat, array[i], array[i]);
                       }
                   }
                 fprintf(stdout, "#\n");
@@ -707,7 +712,7 @@ public:
               {
                 if (gridInqType(gridID) != GRID_CURVILINEAR) cdo_abort("Unsupported grid!");
 
-                const long mlon = nlon - 1;
+                long mlon = nlon - 1;
                 // if ( gridIsCircular(gridID) ) mlon = nlon;
                 for (long j = 0; j < nlat - 1; ++j)
                   for (long i = 0; i < mlon; ++i)
@@ -795,8 +800,9 @@ public:
         tsID++;
       }
   }
+
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID);
   }
diff --git a/src/Pack.cc b/src/Pack.cc
index 59549835cba1f5586905a9f4416a6431322bc35c..a1b066efc230bdd2edc7140f84bc4ca314bfdea8 100644
--- a/src/Pack.cc
+++ b/src/Pack.cc
@@ -17,7 +17,6 @@
 #include <cdi.h>
 
 #include "process_int.h"
-#include "cdo_vlist.h"
 #include "datetime.h"
 #include "cdo_default_values.h"
 #include "field_functions.h"
@@ -169,7 +168,7 @@ print_parameter(const PackParams &params)
 }
 
 static void
-print_pack_params(const std::string& name, double addOffset, double scaleFactor)
+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);
 }
@@ -235,15 +234,14 @@ public:
   PackParams params;
 
   int datatype = CDI_DATATYPE_INT16;
-  int nvars;
 
-  VarList varList;
+  VarList varList1;
 
 private:
   void
   run_method1()
   {
-    FieldVector3D vars;
+    FieldVector3D varsData;
     DateTimeList dtlist;
 
     int tsID = 0;
@@ -253,18 +251,18 @@ private:
         if (nrecs == 0) break;
 
         constexpr size_t NALLOC_INC = 1024;
-        if ((size_t) tsID >= vars.size()) vars.resize(vars.size() + NALLOC_INC);
+        if ((size_t) tsID >= varsData.size()) varsData.resize(varsData.size() + NALLOC_INC);
 
         dtlist.taxis_inq_timestep(taxisID1, tsID);
 
-        fields_from_vlist(vlistID1, vars[tsID]);
+        field2D_init(varsData[tsID], varList1);
 
         for (int recID = 0; recID < nrecs; ++recID)
           {
             int varID, levelID;
             cdo_inq_record(streamID1, &varID, &levelID);
-            auto &field = vars[tsID][varID][levelID];
-            field.init(varList[varID]);
+            auto &field = varsData[tsID][varID][levelID];
+            field.init(varList1.vars[varID]);
             cdo_read_record(streamID1, field);
           }
 
@@ -275,9 +273,10 @@ private:
 
     constexpr double undefValue = 1.0e300;
 
-    for (int varID = 0; varID < nvars; ++varID)
+    auto numVars = varList1.numVars();
+    for (int varID = 0; varID < numVars; ++varID)
       {
-        const auto &var = varList[varID];
+        const auto &var = varList1.vars[varID];
 
         double fmin = undefValue, fmax = -undefValue;
         size_t numMissValspv = 0;
@@ -288,7 +287,7 @@ private:
               {
                 if (t > 0 && var.isConstant) continue;
 
-                auto &field = vars[t][varID][levelID];
+                auto &field = varsData[t][varID][levelID];
                 auto numMissVals = field.numMissVals;
 
                 if (numMissVals) numMissValspv += numMissVals;
@@ -322,7 +321,7 @@ private:
                       {
                         if (t > 0 && var.isConstant) continue;
 
-                        auto &field = vars[t][varID][levelID];
+                        auto &field = varsData[t][varID][levelID];
                         if (field.numMissVals) field_change_missval(field, var.missval, missval2);
                       }
                   }
@@ -350,13 +349,13 @@ private:
         dtlist.taxis_def_timestep(taxisID2, tsID);
         cdo_def_timestep(streamID2, tsID);
 
-        for (int varID = 0; varID < nvars; ++varID)
+        for (int varID = 0; varID < numVars; ++varID)
           {
-            const auto &var = varList[varID];
+            const auto &var = varList1.vars[varID];
             if (tsID > 0 && var.isConstant) continue;
             for (int levelID = 0; levelID < var.nlevels; ++levelID)
               {
-                auto &field = vars[tsID][varID][levelID];
+                auto &field = varsData[tsID][varID][levelID];
                 if (field.hasData())
                   {
                     cdo_def_record(streamID2, varID, levelID);
@@ -372,11 +371,12 @@ private:
   {
     const auto packList = read_params_from_file(params.filename);
 
-    for (int varID = 0; varID < nvars; ++varID)
+    auto numVars = varList1.numVars();
+    for (int varID = 0; varID < numVars; ++varID)
       {
-        const auto &var = varList[varID];
+        const auto &var = varList1.vars[varID];
 
-        for (auto &packEntry: packList)
+        for (auto &packEntry : packList)
           {
             if (var.name == packEntry.name)
               {
@@ -412,7 +412,7 @@ private:
           {
             int varID, levelID;
             cdo_inq_record(streamID1, &varID, &levelID);
-            field.init(varList[varID]);
+            field.init(varList1.vars[varID]);
             cdo_read_record(streamID1, field);
 
             cdo_def_record(streamID2, varID, levelID);
@@ -425,7 +425,7 @@ private:
 
 public:
   void
-  init()
+  init() override
   {
     params = get_parameter();
     if (Options::cdoVerbose) print_parameter(params);
@@ -441,9 +441,7 @@ public:
 
     streamID2 = cdo_open_write(1);
 
-    varList_init(varList, vlistID1);
-
-    nvars = vlistNvars(vlistID1);
+    varList1 = VarList(vlistID1);
 
     if (CdoDefault::DataType != CDI_UNDEFID)
       {
@@ -459,7 +457,7 @@ public:
   }
 
   void
-  run()
+  run() override
   {
     if (params.filename.empty())
       run_method1();
@@ -468,7 +466,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
diff --git a/src/Pardup.cc b/src/Pardup.cc
index 1187cdd2640feebb5478ce4fdabf1e60317575d6..4c4ef21b47cf0b897d3654334822f6c9d193e14a 100644
--- a/src/Pardup.cc
+++ b/src/Pardup.cc
@@ -14,7 +14,6 @@
 
 #include <cdi.h>
 
-#include "cdo_vlist.h"
 #include "process_int.h"
 #include "param_conversion.h"
 
@@ -24,13 +23,14 @@ public:
   using Process::Process;
   inline static CdoModule module = {
     .name = "Pardup",
-    .operators = { { "pardup"}, { "parmul"} },
+    .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;
@@ -38,23 +38,21 @@ public:
   int taxisID1;
   int taxisID2;
   int nmul = 0;
-  int nvars;
+  int numVars;
 
   VarList varList1;
-  std::vector<RecordInfo> recList;
   Varray<double> array;
   Varray2D<double> vardata;
   std::vector<std::vector<size_t>> varnumMissVals;
 
 public:
   void
-  init()
+  init() override
   {
-
     PARDUP = module.get_id("pardup");
     PARMUL = module.get_id("parmul");
 
-    const auto operatorID = cdo_operator_id();
+    auto operatorID = cdo_operator_id();
 
     if (operatorID == PARDUP) { nmul = 2; }
     else if (operatorID == PARMUL)
@@ -67,29 +65,25 @@ 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);
 
     taxisID1 = vlistInqTaxis(vlistID1);
     taxisID2 = taxisDuplicate(taxisID1);
     vlistDefTaxis(vlistID2, taxisID2);
 
-    varList_init(varList1, vlistID1);
-
-    nvars = vlistNvars(vlistID1);
+    varList1 = VarList(vlistID1);
+    numVars = varList1.numVars();
 
-    const auto maxrecs = vlistNrecs(vlistID1);
-    recList = std::vector<RecordInfo>(maxrecs);
-
-    const auto gridsizemax = vlistGridsizeMax(vlistID1);
+    auto gridsizemax = vlistGridsizeMax(vlistID1);
     array = Varray<double>(gridsizemax);
-    vardata = Varray2D<double>(nvars);
-    varnumMissVals = std::vector<std::vector<size_t>>(nvars);
+    vardata = Varray2D<double>(numVars);
+    varnumMissVals = std::vector<std::vector<size_t>>(numVars);
 
-    for (int varID = 0; varID < nvars; ++varID)
+    for (int varID = 0; varID < numVars; ++varID)
       {
-        const auto gridsize = varList1[varID].gridsize;
-        const auto nlevels = varList1[varID].nlevels;
+        auto gridsize = varList1.vars[varID].gridsize;
+        auto nlevels = varList1.vars[varID].nlevels;
         vardata[varID].resize(gridsize * nlevels);
         varnumMissVals[varID].resize(nlevels);
       }
@@ -97,20 +91,24 @@ public:
     for (int i = 1; i < nmul; ++i)
       {
         vlistCat(vlistID2, vlistID1);
-        for (int varID = 0; varID < nvars; ++varID)
-          vlistDefVarParam(vlistID2, varID + nvars * i, cdiEncodeParam(-(varID + nvars * i + 1), 255, 255));
+        for (int varID = 0; varID < numVars; ++varID)
+          vlistDefVarParam(vlistID2, varID + numVars * i, cdiEncodeParam(-(varID + numVars * i + 1), 255, 255));
       }
 
     streamID2 = cdo_open_write(1);
     cdo_def_vlist(streamID2, vlistID2);
   }
+
   void
-  run()
+  run() override
   {
+    auto maxRecords = varList1.numRecords();
+    std::vector<RecordInfo> recordList(maxRecords);
+
     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);
@@ -121,10 +119,10 @@ public:
             int varID, levelID;
             cdo_inq_record(streamID1, &varID, &levelID);
 
-            recList[recID].set(varID, levelID);
+            recordList[recID].set(varID, levelID);
 
-            const auto gridsize = varList1[varID].gridsize;
-            const auto offset = gridsize * levelID;
+            auto gridsize = varList1.vars[varID].gridsize;
+            auto offset = gridsize * levelID;
             auto single = &vardata[varID][offset];
 
             size_t numMissVals;
@@ -135,14 +133,14 @@ public:
         for (int i = 0; i < nmul; ++i)
           for (int recID = 0; recID < nrecs; ++recID)
             {
-              auto [varID, levelID] = recList[recID].get();
+              auto [varID, levelID] = recordList[recID].get();
 
-              const auto varID2 = varID + i * nvars;
+              auto varID2 = varID + i * numVars;
 
-              const auto gridsize = varList1[varID].gridsize;
-              const auto offset = gridsize * levelID;
+              auto gridsize = varList1.vars[varID].gridsize;
+              auto offset = gridsize * levelID;
               auto single = &vardata[varID][offset];
-              const auto numMissVals = varnumMissVals[varID][levelID];
+              auto numMissVals = varnumMissVals[varID][levelID];
 
               array_copy(gridsize, single, array.data());
               cdo_def_record(streamID2, varID2, levelID);
@@ -154,7 +152,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
diff --git a/src/Pinfo.cc b/src/Pinfo.cc
index 5904d90218ed2b890e96f8e182c074d5f8b2bae8..e5743bebeb40fc427e706f1cc61dae6c6f527b76 100644
--- a/src/Pinfo.cc
+++ b/src/Pinfo.cc
@@ -22,13 +22,14 @@ public:
   using Process::Process;
   inline static CdoModule module = {
     .name = "Pinfo",
-    .operators = { { "pinfo"}, { "pinfov"} },
+    .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;
@@ -46,9 +47,8 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
-
     PINFO = module.get_id("pinfo");
     PINFOV = module.get_id("pinfov");
 
@@ -70,12 +70,13 @@ public:
     streamID2 = cdo_open_write(1);
     cdo_def_vlist(streamID2, vlistID2);
 
-    varList_init(varList1, vlistID1);
+    varList1 = VarList(vlistID1);
 
     gridsizemax = vlistGridsizeMax(vlistID1);
   }
+
   void
-  run()
+  run() override
   {
     Varray<double> array1(gridsizemax), array2(gridsizemax);
 
@@ -110,7 +111,7 @@ public:
             size_t numMissVals;
             cdo_read_record(streamID1, array1.data(), &numMissVals);
 
-            const auto &var = varList1[varID];
+            const auto &var = varList1.vars[varID];
             indg += 1;
             auto gridsize = var.gridsize;
 
@@ -164,8 +165,9 @@ public:
         tsID++;
       }
   }
+
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID1);
     cdo_stream_close(streamID2);
diff --git a/src/Pressure.cc b/src/Pressure.cc
index cd0e10ab0dc073fcddbb0c2c6eade560a9ec1dd7..522e83b1f71477c1c8972f45fdad6f426ccd208c 100644
--- a/src/Pressure.cc
+++ b/src/Pressure.cc
@@ -67,7 +67,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
     PRESSURE_FULL = module.get_id("pressure");
     PRESSURE_HALF = module.get_id("pressure_half");
@@ -80,10 +80,9 @@ public:
     streamID1 = cdo_open_read(0);
     auto vlistID1 = cdo_stream_inq_vlist(streamID1);
 
-    VarList varList1;
-    varList_init(varList1, vlistID1);
-    // varListSetUniqueMemtype(varList1);
-    // auto memType = varList1[0].memType;
+    VarList varList1(vlistID1);
+    // varList_setUniqueMemtype(varList1);
+    // auto memType = varList1.vars[0].memType;
 
     gridsize = vlist_check_gridsize(vlistID1);
 
@@ -132,22 +131,22 @@ public:
         zaxisDefVct(zaxisID_PL, 2 * numHalfLevels, vct.data());
       }
 
-    varIDs = search_varIDs(varList1, vlistID1, numFullLevels);
+    varIDs = varList_search_varIDs(varList1, numFullLevels);
 
     if (Options::cdoVerbose)
       {
         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.vars[varIDs.psID].name);
+        if (-1 != varIDs.lnpsID) cdo_print("  LOG(%s) -> %s", var_stdname(surface_air_pressure), varList1.vars[varIDs.lnpsID].name);
         // clang-format on
       }
 
     pvarID = varIDs.lnpsID;
     if (zaxisID_ML != -1 && varIDs.lnpsID != -1)
       {
-        auto gridID = varList1[varIDs.lnpsID].gridID;
-        if (gridInqType(gridID) == GRID_SPECTRAL)
+        auto gridType = varList1.vars[varIDs.lnpsID].gridType;
+        if (gridType == GRID_SPECTRAL)
           {
             varIDs.lnpsID = -1;
             cdo_warning("Spectral LOG(%s) not supported - using %s!", var_stdname(surface_air_pressure),
@@ -161,7 +160,7 @@ public:
         if (varIDs.psID == -1) cdo_abort("%s not found!", var_stdname(surface_air_pressure));
       }
 
-    auto gridID = varList1[pvarID].gridID;
+    auto gridID = varList1.vars[pvarID].gridID;
     if (gridInqType(gridID) == GRID_SPECTRAL)
       cdo_abort("%s on spectral representation not supported!", var_stdname(surface_air_pressure));
 
@@ -185,7 +184,7 @@ public:
   }
 
   void
-  run()
+  run() override
   {
     int tsID = 0;
     while (true)
@@ -257,7 +256,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
 
     cdo_stream_close(streamID2);
diff --git a/src/Query.cc b/src/Query.cc
index d69af6d45a8859f5549f5e4455e4b0a22979cdb9..8cc61152c0bce453141c9eb7111604886b32631a 100644
--- a/src/Query.cc
+++ b/src/Query.cc
@@ -20,7 +20,7 @@ public:
   using Process::Process;
   inline static CdoModule module = {
     .name = "Query",
-    .operators = { { "query", 0, 0, "queryentries"} },
+    .operators = { { "query", 0, 0, "queryentries" } },
     .aliases = {},
     .mode = EXPOSED,     // Module mode: 0:intern 1:extern
     .number = CDI_REAL,  // Allowed number type
@@ -34,12 +34,11 @@ public:
   int taxisID1;
   int taxisID2;
 
-  Field field;
   VarList varList1;
 
 public:
   void
-  init()
+  init() override
   {
     // auto dataIsUnchanged = data_is_unchanged();
 
@@ -98,12 +97,14 @@ public:
     streamID2 = cdo_open_write(1);
     cdo_def_vlist(streamID2, vlistID2);
 
-    varList_init(varList1, vlistID1);
+    varList1 = VarList(vlistID1);
   }
 
   void
-  run()
+  run() override
   {
+    Field field;
+
     int tsID = 0;
     while (true)
       {
@@ -126,7 +127,7 @@ public:
             else
             */
             {
-              field.init(varList1[varID]);
+              field.init(varList1.vars[varID]);
               if (field.memType == MemType::Float)
                 streamReadRecordF(streamID1, field.vec_f.data(), &field.numMissVals);
               else
@@ -140,7 +141,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     streamClose(streamID1);
     cdo_stream_close(streamID2);
diff --git a/src/Recttocomplex.cc b/src/Recttocomplex.cc
index 5773837799e005a865d124f6cd78193949b53581..ce278e0cff04cd547b28b482e72014c7323e3b9d 100644
--- a/src/Recttocomplex.cc
+++ b/src/Recttocomplex.cc
@@ -15,13 +15,14 @@ public:
   using Process::Process;
   inline static CdoModule module = {
     .name = "Recttocomplex",
-    .operators = { { "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;
@@ -39,9 +40,8 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
-
     operator_check_argc(0);
 
     streamID1 = cdo_open_read(0);
@@ -72,11 +72,11 @@ public:
     array2 = Varray<double>(gridsizemax);
     array3 = Varray<double>(2 * gridsizemax);
 
-    varList_init(varList1, vlistID1);
+    varList1 = VarList(vlistID1);
   }
 
   void
-  run()
+  run() override
   {
     int tsID = 0;
     while (true)
@@ -101,7 +101,7 @@ public:
             cdo_read_record(streamID1, array1.data(), &numMissVals);
             cdo_read_record(streamID2, array2.data(), &numMissVals);
 
-            auto gridsize = varList1[varID].gridsize;
+            auto gridsize = varList1.vars[varID].gridsize;
             for (size_t i = 0; i < gridsize; ++i)
               {
                 array3[2 * i] = array1[i];
@@ -114,8 +114,9 @@ public:
         tsID++;
       }
   }
+
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID3);
     cdo_stream_close(streamID2);
diff --git a/src/Regres.cc b/src/Regres.cc
index 0cabd833811c7d029bb8d30c49c0129ea2a9f02a..d979160ce7b2bcf55ffa089a75d62679188b06ff 100644
--- a/src/Regres.cc
+++ b/src/Regres.cc
@@ -15,8 +15,8 @@
 
 #include "arithmetic.h"
 #include "process_int.h"
-#include "cdo_vlist.h"
 #include "cdo_options.h"
+#include "field_trend.h"
 #include "datetime.h"
 #include "pmlist.h"
 #include "param_conversion.h"
@@ -74,19 +74,16 @@ public:
   int taxisID2;
 
   bool tstepIsEqual;
-  int maxrecs;
 
-  VarList varList;
+  VarList varList1;
   Field field1, field2;
-  static const size_t nwork = 5;
-  FieldVector2D work[nwork];
-  std::vector<RecordInfo> recList;
+  static const size_t numWork = 5;
 
   int calendar;
 
 public:
   void
-  init()
+  init() override
   {
     tstepIsEqual = true;
     regresGetParameter(tstepIsEqual);
@@ -107,24 +104,25 @@ public:
     streamID3 = cdo_open_write(1);
     cdo_def_vlist(streamID3, vlistID2);
 
-    varList_init(varList, vlistID1);
-
-    maxrecs = vlistNrecs(vlistID1);
-    recList = std::vector<RecordInfo>(maxrecs);
+    varList1 = VarList(vlistID1);
 
     auto gridsizemax = vlistGridsizeMax(vlistID1);
 
     field1.resize(gridsizemax);
     field2.resize(gridsizemax);
 
-    for (auto &w : work) fields_from_vlist(vlistID1, w, FIELD_VEC, 0);
-
     calendar = taxisInqCalendar(taxisID1);
   }
 
   void
-  run()
+  run() override
   {
+    FieldVector3D work(numWork);
+    for (auto &w : work) field2D_init(w, varList1, FIELD_VEC, 0);
+
+    auto maxRecords = varList1.numRecords();
+    std::vector<RecordInfo> recordList(maxRecords);
+
     CheckTimeIncr checkTimeIncr;
     JulianDate julianDate0;
     double deltat1 = 0;
@@ -145,29 +143,12 @@ public:
             int varID, levelID;
             cdo_inq_record(streamID1, &varID, &levelID);
 
-            recList[recID].set(varID, levelID);
+            recordList[recID].set(varID, levelID);
 
             size_t numMissVals;
             cdo_read_record(streamID1, field1.vec_d.data(), &numMissVals);
 
-            auto gridsize = varList[varID].gridsize;
-            auto missval = varList[varID].missval;
-
-            auto &sumj = work[0][varID][levelID].vec_d;
-            auto &sumjj = work[1][varID][levelID].vec_d;
-            auto &sumjx = work[2][varID][levelID].vec_d;
-            auto &sumx = work[3][varID][levelID].vec_d;
-            auto &zn = work[4][varID][levelID].vec_d;
-
-            for (size_t i = 0; i < gridsize; ++i)
-              if (!DBL_IS_EQUAL(field1.vec_d[i], missval))
-                {
-                  sumj[i] += zj;
-                  sumjj[i] += zj * zj;
-                  sumjx[i] += zj * field1.vec_d[i];
-                  sumx[i] += field1.vec_d[i];
-                  zn[i]++;
-                }
+            calc_trend_sum(work, field1, zj, varID, levelID);
           }
 
         tsID++;
@@ -177,34 +158,17 @@ public:
     // cdo_def_timestep(streamID2, 0);
     cdo_def_timestep(streamID3, 0);
 
-    for (int recID = 0; recID < maxrecs; ++recID)
+    for (int recID = 0; recID < maxRecords; ++recID)
       {
-        auto [varID, levelID] = recList[recID].get();
-
-        auto gridsize = varList[varID].gridsize;
-        auto missval = varList[varID].missval;
-        auto missval1 = missval;
-        auto missval2 = missval;
-        field1.size = gridsize;
-        field1.missval = missval;
-        field2.size = gridsize;
-        field2.missval = missval;
-
-        const auto &sumj = work[0][varID][levelID].vec_d;
-        const auto &sumjj = work[1][varID][levelID].vec_d;
-        const auto &sumjx = work[2][varID][levelID].vec_d;
-        const auto &sumx = work[3][varID][levelID].vec_d;
-        const auto &zn = work[4][varID][levelID].vec_d;
-
-        auto is_EQ = dbl_is_equal;
-        for (size_t i = 0; i < gridsize; ++i)
-          {
-            auto temp1 = SUBM(sumjx[i], DIVM(MULM(sumj[i], sumx[i]), zn[i]));
-            auto temp2 = SUBM(sumjj[i], DIVM(MULM(sumj[i], sumj[i]), zn[i]));
+        auto [varID, levelID] = recordList[recID].get();
 
-            field2.vec_d[i] = DIVM(temp1, temp2);
-            field1.vec_d[i] = SUBM(DIVM(sumx[i], zn[i]), MULM(DIVM(sumj[i], zn[i]), field2.vec_d[i]));
-          }
+        const auto &var = varList1.vars[varID];
+        field1.size = var.gridsize;
+        field1.missval = var.missval;
+        field2.size = var.gridsize;
+        field2.missval = var.missval;
+
+        calc_trend_param(work, field1, field2, varID, levelID);
 
         cdo_def_record(streamID3, varID, levelID);
         cdo_write_record(streamID3, field2.vec_d.data(), field_num_miss(field2));
@@ -212,7 +176,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID3);
     cdo_stream_close(streamID1);
diff --git a/src/Remapeta.cc b/src/Remapeta.cc
index 0e9993616e9b2a6b9e0be99a60eed031601149d7..36bdc403c8cb2ee9506eb47dc0be31ceb6810be1 100644
--- a/src/Remapeta.cc
+++ b/src/Remapeta.cc
@@ -20,12 +20,10 @@
 #include "hetaeta.h"
 #include "vertical_interp.h"
 #include "stdnametable.h"
-#include "util_string.h"
 #include "const.h"
 #include "cdo_options.h"
 #include "cdo_zaxis.h"
 #include "cdi_lockedIO.h"
-#include <memory>
 
 template <typename T>
 static void
@@ -185,7 +183,7 @@ public:
   };
   inline static auto registration = RegisterEntry<Remapeta>(module);
 
-  int REMAPETA, REMAPETA_S, REMAPETA_Z;
+  int REMAPETA_S, REMAPETA_Z;
   double cconst = 1.0E-6;
   size_t nfis2gp = 0;
   int nvars3D = 0;
@@ -258,7 +256,7 @@ public:
     if (ltq)
       {
         int varID = varIDs.tempID;
-        for (int levelID = 0; levelID < varList2[varID].nlevels; ++levelID)
+        for (int levelID = 0; levelID < varList2.vars[varID].nlevels; ++levelID)
           {
             auto offset = gridsize * levelID;
             auto single2 = &t2[offset];
@@ -277,7 +275,7 @@ public:
           }
 
         varID = varIDs.humID;
-        for (int levelID = 0; levelID < varList2[varID].nlevels; ++levelID)
+        for (int levelID = 0; levelID < varList2.vars[varID].nlevels; ++levelID)
           {
             auto offset = gridsize * levelID;
             auto single2 = &q2[offset];
@@ -305,7 +303,7 @@ public:
       {
         int varID = varids[iv];
 
-        auto nlevels = varList2[varID].nlevels;
+        auto nlevels = varList2.vars[varID].nlevels;
 
         if (operatorID == REMAPETA_S)
           {
@@ -354,7 +352,43 @@ public:
 
 public:
   void
-  init()
+  read_fis(const std::string &fileName)
+  {
+    auto streamID = stream_open_read_locked(fileName.c_str());
+    auto vlistID = streamInqVlist(streamID);
+    VarList varList(vlistID);
+
+    int varID, levelID;
+    streamInqRecord(streamID, &varID, &levelID);
+
+    const auto var = varList.vars[0];
+
+    nfis2gp = var.gridsize;
+    fis2.resize(nfis2gp);
+
+    size_t numMissVals;
+    streamReadRecord(streamID, fis2.data(), &numMissVals);
+
+    if (numMissVals)
+      {
+        imiss.resize(nfis2gp);
+        for (size_t i = 0; i < nfis2gp; ++i) imiss[i] = dbl_is_equal(fis2[i], var.missval);
+
+        numMissValsout = numMissVals;
+      }
+
+    // check range of surface_geopotential
+    auto mm = array_min_max_mask(nfis2gp, fis2.data(), imiss);
+    if (mm.min < MIN_FIS || mm.max > MAX_FIS)
+      cdo_warning("%s out of range (min=%g max=%g)!", var_stdname(surface_geopotential), mm.min, mm.max);
+
+    if (mm.min < -1.e10 || mm.max > 1.e10) cdo_abort("%s out of range!", var_stdname(surface_geopotential));
+
+    streamClose(streamID);
+  }
+
+  void
+  init() override
   {
     memType = Options::CDO_Memtype;
 
@@ -390,38 +424,7 @@ public:
     if (cdo_operator_argc() == 2)
       {
         lfis2 = true;
-
-        const char *fname = cdo_operator_argv(1).c_str();
-        auto streamID = stream_open_read_locked(fname);
-        auto vlistID1 = streamInqVlist(streamID);
-
-        int varID, levelID;
-        streamInqRecord(streamID, &varID, &levelID);
-        auto gridID = vlistInqVarGrid(vlistID1, varID);
-        nfis2gp = gridInqSize(gridID);
-
-        fis2.resize(nfis2gp);
-
-        size_t numMissVals;
-        streamReadRecord(streamID, fis2.data(), &numMissVals);
-
-        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);
-
-            numMissValsout = numMissVals;
-          }
-
-        // check range of surface_geopotential
-        auto mm = array_min_max_mask(nfis2gp, fis2.data(), imiss);
-        if (mm.min < MIN_FIS || mm.max > MAX_FIS)
-          cdo_warning("%s out of range (min=%g max=%g)!", var_stdname(surface_geopotential), mm.min, mm.max);
-
-        if (mm.min < -1.e10 || mm.max > 1.e10) cdo_abort("%s out of range!", var_stdname(surface_geopotential));
-
-        streamClose(streamID);
+        read_fis(cdo_operator_argv(1));
       }
 
     auto vlistID1 = cdo_stream_inq_vlist(streamID1);
@@ -433,7 +436,7 @@ public:
     taxisID2 = taxisDuplicate(taxisID1);
     vlistDefTaxis(vlistID2, taxisID2);
 
-    varList_init(varList1, vlistID1);
+    varList1 = VarList(vlistID1);
 
     auto gridID0 = vlistGrid(vlistID1, 0);
     if (gridInqType(gridID0) == GRID_SPECTRAL) cdo_abort("Spectral data unsupported!");
@@ -459,7 +462,7 @@ public:
 
     vlist_change_hybrid_zaxis(vlistID1, vlistID2, zaxisID_ML, zaxisID2);
 
-    auto nzaxis = vlistNzaxis(vlistID1);
+    auto nzaxis = vlistNumZaxis(vlistID1);
     for (int i = 0; i < nzaxis; ++i)
       {
         auto zaxisID = vlistZaxis(vlistID1, i);
@@ -475,38 +478,35 @@ public:
     streamID2 = cdo_open_write(1);
     cdo_def_vlist(streamID2, vlistID2);
 
-    varList_init(varList2, vlistID2);
+    varList2 = VarList(vlistID2);
 
     if (zaxisID_ML == -1) cdo_warning("No 3D variable with hybrid sigma pressure coordinate found!");
 
-    auto nvars = vlistNvars(vlistID1);
+    auto numVars = varList1.numVars();
 
-    varIDs = search_varIDs(varList1, vlistID1, numFullLevels1);
+    varIDs = varList_search_varIDs(varList1, numFullLevels1);
 
     if (Options::cdoVerbose)
       {
         cdo_print("Found:");
         // clang-format off
-      if (-1 != varIDs.tempID)    cdo_print("  %s", var_stdname(air_temperature));
-      if (-1 != varIDs.psID)      cdo_print("  %s", var_stdname(surface_air_pressure));
-      if (-1 != varIDs.lnpsID)    cdo_print("  LOG(%s)", var_stdname(surface_air_pressure));
-      if (-1 != varIDs.sgeopotID) cdo_print("  %s", var_stdname(surface_geopotential));
-      if (-1 != varIDs.humID)     cdo_print("  %s", var_stdname(specific_humidity));
+        if (-1 != varIDs.tempID)    cdo_print("  %s", var_stdname(air_temperature));
+        if (-1 != varIDs.psID)      cdo_print("  %s", var_stdname(surface_air_pressure));
+        if (-1 != varIDs.lnpsID)    cdo_print("  LOG(%s)", var_stdname(surface_air_pressure));
+        if (-1 != varIDs.sgeopotID) cdo_print("  %s", var_stdname(surface_geopotential));
+        if (-1 != varIDs.humID)     cdo_print("  %s", var_stdname(specific_humidity));
         // clang-format on
       }
 
-    for (int varID = 0; varID < nvars; ++varID)
+    for (int varID = 0; varID < numVars; ++varID)
       {
-        auto gridID = varList1[varID].gridID;
-        auto zaxisID = varList1[varID].zaxisID;
-        auto nlevels = varList1[varID].nlevels;
+        const auto &var = varList1.vars[varID];
 
-        if (gridInqType(gridID) == GRID_SPECTRAL && zaxisInqType(zaxisID) == ZAXIS_HYBRID)
-          cdo_abort("Spectral data on model level unsupported!");
+        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!");
 
-        if (zaxisInqType(zaxisID) == ZAXIS_HYBRID && zaxisID_ML != -1 && nlevels == numFullLevels1)
+        if (var.zaxisType == ZAXIS_HYBRID && zaxisID_ML != -1 && var.nlevels == numFullLevels1)
           {
             if (!(varID == varIDs.tempID || varID == varIDs.humID)) varids[nvars3D++] = varID;
           }
@@ -569,8 +569,8 @@ public:
 
     if (nvars3D)
       {
-        (memType == MemType::Float) ? resize(f_vars1, nvars) : resize(d_vars1, nvars);
-        (memType == MemType::Float) ? resize(f_vars2, nvars) : resize(d_vars2, nvars);
+        (memType == MemType::Float) ? resize(f_vars1, numVars) : resize(d_vars1, numVars);
+        (memType == MemType::Float) ? resize(f_vars2, numVars) : resize(d_vars2, numVars);
 
         if (memType == MemType::Float)
           {
@@ -611,7 +611,7 @@ public:
   }
 
   void
-  run()
+  run() override
   {
     int tsID = 0;
     while (true)
@@ -630,8 +630,7 @@ public:
 
             if (zaxisID_ML != -1)
               {
-                auto zaxisID = varList1[varID].zaxisID;
-                auto nlevels = varList1[varID].nlevels;
+                const auto &var = varList1.vars[varID];
                 auto offset = gridsize * levelID;
 
                 if (varID == varIDs.sgeopotID)
@@ -654,8 +653,8 @@ public:
                 else if (ltq && varID == varIDs.humID)
                   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)
+                // else if ( var.zaxisID == zaxisID_ML )
+                else if (var.zaxisType == ZAXIS_HYBRID && var.nlevels == numFullLevels1)
                   {
                     int i;
                     for (i = 0; i < nvars3D; ++i)
@@ -696,7 +695,8 @@ public:
         if (ltq)
           {
             int varID = varIDs.tempID;
-            for (int levelID = 0; levelID < varList1[varID].nlevels; ++levelID)
+            const auto &var1 = varList1.vars[varID];
+            for (int levelID = 0; levelID < var1.nlevels; ++levelID)
               {
                 auto offset = gridsize * levelID;
                 auto mm = [&]() {
@@ -709,7 +709,7 @@ public:
               }
 
             varID = varIDs.humID;
-            for (int levelID = 0; levelID < varList1[varID].nlevels; ++levelID)
+            for (int levelID = 0; levelID < var1.nlevels; ++levelID)
               {
                 auto offset = gridsize * levelID;
 
@@ -768,7 +768,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
diff --git a/src/Remapgrid.cc b/src/Remapgrid.cc
index 9e77955afb495c126cf0158de300953d59275006..3e26e9ff6f1a53348f947241dded6ea9faea69f0 100644
--- a/src/Remapgrid.cc
+++ b/src/Remapgrid.cc
@@ -30,7 +30,6 @@
 #include <mpim_grid.h>
 #include "griddes.h"
 #include "cdo_options.h"
-#include "util_string.h"
 #include "util_wildcards.h"
 
 static int
@@ -130,7 +129,7 @@ remap_normalize_field(NormOpt normOpt, size_t gridsize, Varray<T> &array, T miss
     {
       for (size_t i = 0; i < gridsize; ++i)
         {
-          if (!dbl_is_equal(array[i], missval))
+          if (dbl_is_not_equal(array[i], missval))
             {
               auto gridError = tgtGrid.cell_frac[i] * tgtGrid.cell_area[i];
               array[i] = (std::fabs(gridError) > 0.0) ? (array[i] / gridError) : missval;
@@ -141,7 +140,7 @@ remap_normalize_field(NormOpt normOpt, size_t gridsize, Varray<T> &array, T miss
     {
       for (size_t i = 0; i < gridsize; ++i)
         {
-          if (!dbl_is_equal(array[i], missval))
+          if (dbl_is_not_equal(array[i], missval))
             {
               array[i] = (std::fabs(tgtGrid.cell_frac[i]) > 0.0) ? (array[i] / tgtGrid.cell_frac[i]) : missval;
             }
@@ -293,6 +292,7 @@ public:
     .constraints = { 1, 1, NoRestriction },
   };
   inline static RegisterEntry<Remapgrid> registration = RegisterEntry<Remapgrid>(module);
+
   RemapSwitches remapSwitches;
   bool remap_genweights = Options::REMAP_genweights;
   int numRemaps = 0;
@@ -326,13 +326,12 @@ public:
 
   NormOpt normOpt;
 
-  Field field1, field2;
   VarList varList1;
   VarList varList2;
 
 public:
   void
-  init()
+  init() override
   {
     auto operatorID = cdo_operator_id();
     operfunc = cdo_operator_f1(operatorID);
@@ -383,14 +382,14 @@ public:
     taxisID2 = taxisDuplicate(taxisID1);
     vlistDefTaxis(vlistID2, taxisID2);
 
-    varList_init(varList1, vlistID1);
+    varList1 = VarList(vlistID1);
 
     remapGrids = remap_set_grids(vlistID1, varList1);
 
     auto numRemapGrids = ranges::count_if(remapGrids, [](auto flag) { return (flag == true); });
     if (numRemapGrids == 0) cdo_abort("No remappable grid found!");
 
-    auto numGrids = vlistNgrids(vlistID1);
+    auto numGrids = vlistNumGrids(vlistID1);
     for (int index = 0; index < numGrids; ++index)
       if (remapGrids[index]) vlistChangeGridIndex(vlistID2, index, gridID2);
 
@@ -432,7 +431,7 @@ public:
     mapType = remapSwitches.mapType;
     remapOrder = remapSwitches.remapOrder;
 
-    varList_init(varList2, vlistID2);
+    varList2 = VarList(vlistID2);
 
     // if (!remap_genweights && (mapType == RemapMethod::CONSERV_SCRIP || mapType == RemapMethod::CONSERV)) remap_genweights = true;
     if (!remap_genweights && mapType == RemapMethod::CONSERV_SCRIP) remap_genweights = true;
@@ -460,7 +459,7 @@ public:
       {
         // remap() gives rounding errors on target arrays with single precision floats
         auto numVars = vlistNvars(vlistID2);
-        for (int varID = 0; varID < numVars; ++varID) varList2[varID].memType = MemType::Double;
+        for (int varID = 0; varID < numVars; ++varID) varList2.vars[varID].memType = MemType::Double;
       }
 
     streamID2 = cdo_open_write(1);
@@ -468,8 +467,9 @@ public:
   }
 
   void
-  run()
+  run() override
   {
+    Field field1, field2;
     Varray<short> imask;
 
     int tsID = 0;
@@ -486,12 +486,12 @@ public:
           {
             int varID, levelID;
             cdo_inq_record(streamID1, &varID, &levelID);
-            auto &var = varList1[varID];
+            auto &var = varList1.vars[varID];
             field1.init(var);
             cdo_read_record(streamID1, field1);
             auto numMissVals1 = useMask ? field1.numMissVals : 0;
 
-            field2.init(varList2[varID]);
+            field2.init(varList2.vars[varID]);
 
             auto gridIndex = vlistGridIndex(vlistID1, var.gridID);
 
@@ -585,12 +585,14 @@ public:
                         ranges::fill(remap.srcGrid.cell_frac, 0.0);
                         ranges::fill(remap.tgtGrid.cell_area, 0.0);
                       }
-                    ranges::fill(remap.tgtGrid.cell_frac, 0.0);
+                    // ranges::fill(remap.tgtGrid.cell_frac, 0.0); failed with pgc++ 23.9.0
+                    for (auto &cf : remap.tgtGrid.cell_frac) cf = 0.0;
 
                     // initialize some remapping variables
                     remap_vars_init(mapType, remapOrder, remap.vars);
 
-                    remap_print_info(operfunc, remap_genweights, remap.srcGrid, remap.tgtGrid, numMissVals1, remapSwitches.numNeighbors);
+                    remap_print_info(operfunc, remap_genweights, remap.srcGrid, remap.tgtGrid, numMissVals1,
+                                     remapSwitches.numNeighbors);
 
                     if (remap_genweights)
                       {
@@ -679,11 +681,9 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
-    gridDestroy(gridID2);
   }
 };
diff --git a/src/Remapstat.cc b/src/Remapstat.cc
index a2cfb4df5ea08d4b94b39a4062c33fe2fbb2e112..4dfe12bc156abc8ac88967fa68ff38f47090577e 100644
--- a/src/Remapstat.cc
+++ b/src/Remapstat.cc
@@ -17,7 +17,6 @@
 #include "griddes.h"
 #include "grid_point_search.h"
 #include <mpim_grid.h>
-#include "gridreference.h"
 #include "cimdOmp.h"
 #include "verifygrid.h"
 #include "field_functions.h"
@@ -579,8 +578,8 @@ gen_mapdata(int gridID1, int gridID2)
 
 template <typename T>
 static T
-remap_kernel(int operfunc, const Varray<size_t> &indices, size_t &numMissVals2, Field &field, Varray<T> &fieldvec, const Varray<T> &vec1,
-             T missval)
+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();
@@ -676,23 +675,23 @@ public:
     .constraints = { 1, 1, NoRestriction },
   };
   inline static RegisterEntry<Remapstat> registration = RegisterEntry<Remapstat>(module);
+
   CdoStreamID streamID1;
   CdoStreamID streamID2;
 
   int taxisID1;
   int taxisID2;
 
-  Field field1, field2;
-  VarList varList1, varList2;
+  VarList varList1;
+  VarList varList2;
 
   Varray2D<size_t> mapdata;
   int operfunc;
 
 public:
   void
-  init()
+  init() override
   {
-
     auto operatorID = cdo_operator_id();
     operfunc = cdo_operator_f1(operatorID);
 
@@ -724,7 +723,7 @@ public:
     taxisID2 = taxisDuplicate(taxisID1);
     vlistDefTaxis(vlistID2, taxisID2);
 
-    auto ngrids = vlistNgrids(vlistID1);
+    auto ngrids = vlistNumGrids(vlistID1);
     auto gridID1 = vlistGrid(vlistID1, 0);
     for (int index = 0; index < ngrids; ++index)
       {
@@ -745,13 +744,15 @@ public:
 
     mapdata = gen_mapdata(gridID1, gridID2);
 
-    varList_init(varList1, vlistID1);
-    varList_init(varList2, vlistID2);
+    varList1 = VarList(vlistID1);
+    varList2 = VarList(vlistID2);
   }
 
   void
-  run()
+  run() override
   {
+    Field field1, field2;
+
     int tsID = 0;
     while (true)
       {
@@ -765,10 +766,10 @@ public:
           {
             int varID, levelID;
             cdo_inq_record(streamID1, &varID, &levelID);
-            field1.init(varList1[varID]);
+            field1.init(varList1.vars[varID]);
             cdo_read_record(streamID1, field1);
 
-            field2.init(varList2[varID]);
+            field2.init(varList2.vars[varID]);
 
             remap_field(mapdata, field1, field2, operfunc);
 
@@ -780,7 +781,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
diff --git a/src/Remapweights.cc b/src/Remapweights.cc
index 9c73da285d9e7f67f1be0f4596a8e217533ec087..9659d413d5166237cfd6e7de83012d034d2b3e16 100644
--- a/src/Remapweights.cc
+++ b/src/Remapweights.cc
@@ -87,9 +87,9 @@ public:
                    { "gennn", GENNN, 0, RemapnnHelp },
                    { "gendis", GENDIS, 0, RemapdisHelp },
                    { "gencon", GENCON, 0, RemapconHelp },
-                   { "genycon2test", GENYCON2, 0, nullptr},
-                   { "genscon", GENSCON, 0, nullptr},
-                   { "genscon2", GENSCON2, 0, nullptr},
+                   { "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
@@ -132,7 +132,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
 
     auto operatorID = cdo_operator_id();
@@ -171,7 +171,7 @@ public:
 
     vlistID1 = cdo_stream_inq_vlist(streamID1);
 
-    varList_init(varList1, vlistID1);
+    varList1 = VarList(vlistID1);
 
     remapGrids = remap_set_grids(vlistID1, varList1);
 
@@ -207,7 +207,7 @@ public:
   }
 
   void
-  run()
+  run() override
   {
     std::thread writeWorker;
     Varray<short> imask;
@@ -220,7 +220,7 @@ public:
       {
         int varID, levelID;
         cdo_inq_record(streamID1, &varID, &levelID);
-        auto &var = varList1[varID];
+        auto &var = varList1.vars[varID];
         field1.init(var);
         cdo_read_record(streamID1, field1);
         auto numMissVals1 = useMask ? field1.numMissVals : 0;
@@ -342,7 +342,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID1);
   }
diff --git a/src/Replace.cc b/src/Replace.cc
index abf0032548f7944f0886c51b35d181dac5cb3fbd..7d55eda19cb46b89120ccabbeb2cbfb2e3110cf9 100644
--- a/src/Replace.cc
+++ b/src/Replace.cc
@@ -15,7 +15,6 @@
 
 #include "cdo_options.h"
 #include "process_int.h"
-#include "cdo_vlist.h"
 #include "cdo_zaxis.h"
 
 class Replace : public Process
@@ -31,6 +30,7 @@ public:
     .constraints = { 2, 1, NoRestriction },
   };
   inline static RegisterEntry<Replace> registration = RegisterEntry<Replace>(module);
+
   static const int MaxVars = 1024;
   int nchvars = 0;
   int idx;
@@ -50,14 +50,14 @@ public:
   std::vector<std::vector<size_t>> varnumMissVals2;
 
   Varray<double> array;
-  VarList varList1, varList2;
+  VarList varList1;
+  VarList varList2;
   Varray2D<double> vardata2;
 
 public:
   void
-  init()
+  init() override
   {
-
     operator_check_argc(0);
 
     streamID1 = cdo_open_read(0);
@@ -70,42 +70,38 @@ public:
 
     auto vlistID2 = cdo_stream_inq_vlist(streamID2);
 
-    varList_init(varList1, vlistID1);
-    varList_init(varList2, vlistID2);
+    varList1 = VarList(vlistID1);
+    varList2 = VarList(vlistID2);
 
     // compare all variables in vlistID2
 
-    auto nvars1 = vlistNvars(vlistID1);
-    auto nvars2 = vlistNvars(vlistID2);
+    auto numVars1 = varList1.numVars();
+    auto numVars2 = varList2.numVars();
 
-    for (int varID2 = 0; varID2 < nvars2; ++varID2)
+    for (int varID2 = 0; varID2 < numVars2; ++varID2)
       {
+        const auto &var2 = varList2.vars[varID2];
         int varID1;
-        for (varID1 = 0; varID1 < nvars1; ++varID1)
+        for (varID1 = 0; varID1 < numVars1; ++varID1)
           {
-            if (varList1[varID1].name == varList2[varID2].name) break;
+            if (varList1.vars[varID1].name == var2.name) break;
           }
 
-        if (varID1 < nvars1)
+        if (varID1 < numVars1)
           {
-            auto gridsize1 = varList1[varID1].gridsize;
-            auto nlevel1 = varList1[varID1].nlevels;
-
-            auto gridsize2 = varList2[varID2].gridsize;
-            auto nlevel2 = varList2[varID2].nlevels;
-
-            if (gridsize1 != gridsize2) cdo_abort("Variables have different gridsize!");
+            const auto &var1 = varList1.vars[varID1];
 
-            if (nlevel1 < nlevel2) cdo_abort("Variables have different number of levels!");
+            if (var1.gridsize != var2.gridsize) cdo_abort("Variables have different gridsize!");
+            if (var1.nlevels < var2.nlevels) cdo_abort("Variables have different number of levels!");
 
-            if (Options::cdoVerbose) cdo_print("Variable %s replaced.", varList1[varID1].name);
+            if (Options::cdoVerbose) cdo_print("Variable %s replaced.", var1.name);
 
             vars1[nchvars] = varID1;
             vars2[nchvars] = varID2;
             nchvars++;
             if (nchvars > MaxVars) cdo_abort("Internal problem - too many variables!");
           }
-        else { cdo_warning("Variable %s not found!", varList2[varID2].name); }
+        else { cdo_warning("Variable %s not found!", var2.name); }
       }
 
     if (nchvars)
@@ -115,37 +111,36 @@ public:
         varlevel.resize(nchvars);
         for (idx = 0; idx < nchvars; idx++)
           {
-            auto varID1 = vars1[idx];
-            auto varID2 = vars2[idx];
-            auto nlevel1 = varList1[varID1].nlevels;
-            auto nlevel2 = varList2[varID2].nlevels;
-            auto gridsize = varList2[varID2].gridsize;
-            vardata2[idx].resize(nlevel2 * gridsize);
-            varnumMissVals2[idx].resize(nlevel2);
-            varlevel[idx].resize(nlevel1);
+            const auto &var1 = varList1.vars[vars1[idx]];
+            const auto &var2 = varList2.vars[vars2[idx]];
+
+            auto gridsize = var2.gridsize;
+            vardata2[idx].resize(var2.nlevels * gridsize);
+            varnumMissVals2[idx].resize(var2.nlevels);
+            varlevel[idx].resize(var1.nlevels);
             /*
-            for ( levelID = 0; levelID < nlevel1; levelID++ )
+            for ( levelID = 0; levelID < var1.nlevels; levelID++ )
               varlevel[idx][levelID] = levelID;
             */
-            if (nlevel2 <= nlevel1)
+            if (var2.nlevels <= var1.nlevels)
               {
-                Varray<double> level1(nlevel1), level2(nlevel2);
-                cdo_zaxis_inq_levels(vlistInqVarZaxis(vlistID1, varID1), level1.data());
-                cdo_zaxis_inq_levels(vlistInqVarZaxis(vlistID2, varID2), level2.data());
+                Varray<double> level1(var1.nlevels), level2(var2.nlevels);
+                cdo_zaxis_inq_levels(var1.zaxisID, level1.data());
+                cdo_zaxis_inq_levels(var2.zaxisID, level2.data());
 
-                for (int levelID = 0; levelID < nlevel1; ++levelID) varlevel[idx][levelID] = -1;
+                for (int levelID = 0; levelID < var1.nlevels; ++levelID) varlevel[idx][levelID] = -1;
 
-                for (int l2 = 0; l2 < nlevel2; ++l2)
+                for (int l2 = 0; l2 < var2.nlevels; ++l2)
                   {
                     int l1;
-                    for (l1 = 0; l1 < nlevel1; ++l1)
-                      if (IS_EQUAL(level2[l2], level1[l1]))
+                    for (l1 = 0; l1 < var1.nlevels; ++l1)
+                      if (is_equal(level2[l2], level1[l1]))
                         {
                           varlevel[idx][l1] = l2;
                           break;
                         }
 
-                    if (l1 == nlevel1) cdo_warning("Variable %s on level %g not found!", varList2[varID2].name, level2[l2]);
+                    if (l1 == var1.nlevels) cdo_warning("Variable %s on level %g not found!", var2.name, level2[l2]);
                   }
               }
           }
@@ -163,8 +158,9 @@ public:
 
     nts2 = vlistNtsteps(vlistID2);
   }
+
   void
-  run()
+  run() override
   {
     int tsID = 0;
     while (true)
@@ -187,7 +183,7 @@ public:
                 for (idx = 0; idx < nchvars; idx++)
                   if (vars2[idx] == varID)
                     {
-                      auto offset = varList2[varID].gridsize * levelID;
+                      auto offset = varList2.vars[varID].gridsize * levelID;
                       cdo_read_record(streamID2, &vardata2[idx][offset], &numMissVals);
                       varnumMissVals2[idx][levelID] = numMissVals;
                       break;
@@ -210,7 +206,7 @@ public:
                   auto levelID2 = varlevel[idx][levelID];
                   if (levelID2 != -1)
                     {
-                      auto offset = varList1[varID].gridsize * levelID2;
+                      auto offset = varList1.vars[varID].gridsize * levelID2;
                       parray = &vardata2[idx][offset];
                       numMissVals = varnumMissVals2[idx][levelID2];
                       break;
@@ -226,8 +222,9 @@ public:
         tsID++;
       }
   }
+
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID3);
     cdo_stream_close(streamID2);
diff --git a/src/Replacevalues.cc b/src/Replacevalues.cc
index b87bceea987c78980fed473dafb40bf80eab34cd..b34bdeddbe8e3fd24b2bf38c9c60e8cef07ffff3 100644
--- a/src/Replacevalues.cc
+++ b/src/Replacevalues.cc
@@ -33,6 +33,7 @@ public:
     .constraints = { 1, 1, NoRestriction },
   };
   inline static RegisterEntry<Replacevalues> registration = RegisterEntry<Replacevalues>(module);
+
   int SETVALS, SETRTOC, SETRTOC2;
   int nvals = 0;
   std::vector<double> fltarr;
@@ -52,9 +53,8 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
-
     SETVALS = module.get_id("setvals");
     SETRTOC = module.get_id("setrtoc");
     SETRTOC2 = module.get_id("setrtoc2");
@@ -89,8 +89,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);
 
     taxisID1 = vlistInqTaxis(vlistID1);
     taxisID2 = taxisDuplicate(taxisID1);
@@ -100,18 +100,18 @@ public:
 
     cdo_def_vlist(streamID2, vlistID2);
 
-    const auto gridsizemax = vlistGridsizeMax(vlistID1);
+    auto gridsizemax = vlistGridsizeMax(vlistID1);
     array = Varray<double>(gridsizemax);
-    varList_init(varList1, vlistID1);
+    varList1 = VarList(vlistID1);
   }
 
   void
-  run()
+  run() override
   {
     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);
@@ -123,18 +123,19 @@ public:
             cdo_inq_record(streamID1, &varID, &levelID);
             size_t numMissVals;
             cdo_read_record(streamID1, array.data(), &numMissVals);
+            const auto &var = varList1.vars[varID];
 
-            const auto gridsize = varList1[varID].gridsize;
-            const auto missval = varList1[varID].missval;
+            auto gridsize = var.gridsize;
+            auto missval = var.missval;
 
             if (operatorID == SETVALS)
               {
                 for (size_t i = 0; i < gridsize; ++i)
-                  if (!DBL_IS_EQUAL(array[i], missval))
+                  if (dbl_is_not_equal(array[i], missval))
                     {
                       for (int j = 0; j < nvals; ++j)
                         {
-                          if (DBL_IS_EQUAL(array[i], fltarr[j * 2]))
+                          if (dbl_is_equal(array[i], fltarr[j * 2]))
                             {
                               array[i] = fltarr[j * 2 + 1];
                               break;
@@ -145,7 +146,7 @@ public:
             else if (operatorID == SETRTOC)
               {
                 for (size_t i = 0; i < gridsize; ++i)
-                  if (!DBL_IS_EQUAL(array[i], missval))
+                  if (dbl_is_not_equal(array[i], missval))
                     {
                       if (array[i] >= rmin && array[i] <= rmax) array[i] = newval;
                     }
@@ -153,7 +154,7 @@ public:
             else if (operatorID == SETRTOC2)
               {
                 for (size_t i = 0; i < gridsize; ++i)
-                  if (!DBL_IS_EQUAL(array[i], missval)) { array[i] = (array[i] >= rmin && array[i] <= rmax) ? newval : newval2; }
+                  if (dbl_is_not_equal(array[i], missval)) { array[i] = (array[i] >= rmin && array[i] <= rmax) ? newval : newval2; }
               }
 
             cdo_def_record(streamID2, varID, levelID);
@@ -163,8 +164,9 @@ public:
         tsID++;
       }
   }
+
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
diff --git a/src/Rhopot.cc b/src/Rhopot.cc
index 9431edadfa6b4d91538d2906108a5d18ec119221..0a2c9da57004a942fdfde77d217e88729999a8ba 100644
--- a/src/Rhopot.cc
+++ b/src/Rhopot.cc
@@ -167,7 +167,7 @@ public:
   int taxisID1;
   int taxisID2;
 
-  int nlevel;
+  int numLevels;
   int gridsize;
 
   FieldVector to;
@@ -178,29 +178,30 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
     if (cdo_operator_argc() == 1) pin = parameter_to_double(cdo_operator_argv(0));
 
     streamID1 = cdo_open_read(0);
     auto vlistID1 = cdo_stream_inq_vlist(streamID1);
 
-    auto nvars = vlistNvars(vlistID1);
+    VarList varList1(vlistID1);
+    auto numVars = varList1.numVars();
 
-    for (int varID = 0; varID < nvars; ++varID)
+    for (int varID = 0; varID < numVars; ++varID)
       {
-        auto code = vlistInqVarCode(vlistID1, varID);
+        const auto &var = varList1.vars[varID];
+        auto code = var.code;
         if (code <= 0)
           {
-            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));
 
             // 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;
+            if      (var.name == "to") code = 20;
+            else if (var.name == "sao") code = 5;
+            else if (var.name == "tho") code = 2;
+            else if (var.name == "s") code = 5;
+            else if (var.name == "t") code = 2;
             else if (stdname == "sea_water_salinity") code = 5;
             else if (stdname == "sea_water_potential_temperature") code = 2;
             // clang-format on
@@ -225,45 +226,43 @@ public:
     auto gridID = vlistGrid(vlistID1, 0);
     gridsize = vlist_check_gridsize(vlistID1);
 
-    zaxisID = vlistInqVarZaxis(vlistID1, saoID);
-    auto nlevel1 = zaxisInqSize(zaxisID);
-    zaxisID = vlistInqVarZaxis(vlistID1, toID);
-    auto nlevel2 = zaxisInqSize(zaxisID);
+    const auto varSAO = varList1.vars[saoID];
+    const auto varTO = varList1.vars[toID];
+    zaxisID = varSAO.zaxisID;
 
-    if (nlevel1 != nlevel2) cdo_abort("temperature and salinity have different number of levels!");
-    nlevel = nlevel1;
+    if (varSAO.nlevels != varTO.nlevels) cdo_abort("temperature and salinity have different number of levels!");
+    numLevels = varSAO.nlevels;
 
-    pressure = Varray<double>(nlevel);
+    pressure = Varray<double>(numLevels);
     cdo_zaxis_inq_levels(zaxisID, pressure.data());
 
     if (pin >= 0)
-      for (int i = 0; i < nlevel; ++i) pressure[i] = pin;
+      for (int i = 0; i < numLevels; ++i) pressure[i] = pin;
     else
-      for (int i = 0; i < nlevel; ++i) pressure[i] /= 10;
+      for (int i = 0; i < numLevels; ++i) pressure[i] /= 10;
 
     if (Options::cdoVerbose)
       {
         cdo_print("Level Pressure");
-        for (int i = 0; i < nlevel; ++i) cdo_print("%5d  %g", i + 1, pressure[i]);
+        for (int i = 0; i < numLevels; ++i) cdo_print("%5d  %g", i + 1, pressure[i]);
       }
 
-    to = FieldVector(nlevel);
-    sao = FieldVector(nlevel);
-    rho = FieldVector(nlevel);
+    to = FieldVector(numLevels);
+    sao = FieldVector(numLevels);
+    rho = FieldVector(numLevels);
 
-    for (int levelID = 0; levelID < nlevel; ++levelID)
+    for (int levelID = 0; levelID < numLevels; ++levelID)
       {
         to[levelID].resize(gridsize);
         sao[levelID].resize(gridsize);
         rho[levelID].resize(gridsize);
-        to[levelID].missval = vlistInqVarMissval(vlistID1, toID);
-        sao[levelID].missval = vlistInqVarMissval(vlistID1, saoID);
+        to[levelID].missval = varTO.missval;
+        sao[levelID].missval = varSAO.missval;
         rho[levelID].missval = to[levelID].missval;
       }
 
     int datatype = CDI_DATATYPE_FLT32;
-    if (vlistInqVarDatatype(vlistID1, toID) == CDI_DATATYPE_FLT64 && vlistInqVarDatatype(vlistID1, saoID) == CDI_DATATYPE_FLT64)
-      datatype = CDI_DATATYPE_FLT64;
+    if (varTO.dataType == CDI_DATATYPE_FLT64 && varSAO.dataType == CDI_DATATYPE_FLT64) datatype = CDI_DATATYPE_FLT64;
 
     vlistID2 = vlistCreate();
     vlistDefNtsteps(vlistID2, vlistNtsteps(vlistID1));
@@ -286,7 +285,7 @@ public:
   }
 
   void
-  run()
+  run() override
   {
     int tsID = 0;
     while (true)
@@ -305,9 +304,9 @@ public:
             if (varID == saoID) cdo_read_record(streamID1, sao[levelID].vec_d.data(), &sao[levelID].numMissVals);
           }
 
-        calc_rhopot(gridsize, nlevel, pressure, to, sao, rho);
+        calc_rhopot(gridsize, numLevels, pressure, to, sao, rho);
 
-        for (int levelID = 0; levelID < nlevel; ++levelID)
+        for (int levelID = 0; levelID < numLevels; ++levelID)
           {
             cdo_def_record(streamID2, 0, levelID);
             cdo_write_record(streamID2, rho[levelID].vec_d.data(), field_num_miss(rho[levelID]));
@@ -318,7 +317,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
diff --git a/src/Rotuv.cc b/src/Rotuv.cc
index 6ebcb1468e4fece4190828a22a45355e20c5d309..b86c7c178e1d47e3cdaecc129775b2cd029e5e49 100644
--- a/src/Rotuv.cc
+++ b/src/Rotuv.cc
@@ -77,18 +77,17 @@ public:
   int taxisID2;
 
   int nch;
-  int nvars;
+  int numVars;
 
   bool lvar;
 
   VarList varList1;
-  Varray3D<double> vardata;
-  std::vector<RecordInfo> recList;
+  Varray3D<double> varsData;
   std::vector<std::vector<size_t>> varnumMissVals;
 
 public:
   void
-  init()
+  init() override
   {
 
     operator_input_arg("pairs of u and v in the rotated system");
@@ -120,35 +119,31 @@ public:
     auto vlistID1 = cdo_stream_inq_vlist(streamID1);
     auto vlistID2 = vlistDuplicate(vlistID1);
 
-    varList_init(varList1, vlistID1);
+    varList1 = VarList(vlistID1);
 
-    nvars = vlistNvars(vlistID1);
+    numVars = varList1.numVars();
 
-    auto maxrecs = vlistNrecs(vlistID1);
-
-    recList = std::vector<RecordInfo>(maxrecs);
-
-    varnumMissVals = std::vector<std::vector<size_t>>(nvars);
-    vardata = Varray3D<double>(nvars);
+    varnumMissVals = std::vector<std::vector<size_t>>(numVars);
+    varsData = Varray3D<double>(numVars);
 
     bool lfound[MAXARG];
     for (int i = 0; i < nch; ++i) lfound[i] = false;
 
     if (lvar)
       {
-        for (int varID = 0; varID < nvars; ++varID)
+        for (int varID = 0; varID < numVars; ++varID)
           {
             for (int i = 0; i < nch; ++i)
-              if (varList1[varID].name == chvars[i]) lfound[i] = true;
+              if (varList1.vars[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 (int varID = 0; varID < nvars; ++varID)
+        for (int varID = 0; varID < numVars; ++varID)
           {
-            auto code = varList1[varID].code;
+            auto code = varList1.vars[varID].code;
             for (int i = 0; i < nch; ++i)
               if (code == chcodes[i]) lfound[i] = true;
           }
@@ -156,17 +151,17 @@ public:
           if (!lfound[i]) cdo_abort("Code %d not found!", chcodes[i]);
       }
 
-    for (int varID = 0; varID < nvars; ++varID)
+    for (int varID = 0; varID < numVars; ++varID)
       {
-        auto gridID = varList1[varID].gridID;
+        auto gridID = varList1.vars[varID].gridID;
         if (!(gridInqType(gridID) == GRID_PROJECTION && gridInqProjType(gridID) == CDI_PROJ_RLL))
           cdo_abort("Only rotated lon/lat grids supported!");
 
         auto gridsize = gridInqSize(gridID);
-        auto nlevels = varList1[varID].nlevels;
+        auto nlevels = varList1.vars[varID].nlevels;
         varnumMissVals[varID].resize(nlevels);
-        vardata[varID].resize(nlevels);
-        for (int levelID = 0; levelID < nlevels; ++levelID) vardata[varID][levelID].resize(gridsize);
+        varsData[varID].resize(nlevels);
+        for (int levelID = 0; levelID < nlevels; ++levelID) varsData[varID][levelID].resize(gridsize);
       }
 
     taxisID1 = vlistInqTaxis(vlistID1);
@@ -178,8 +173,11 @@ public:
   }
 
   void
-  run()
+  run() override
   {
+    auto maxRecords = varList1.numRecords();
+    std::vector<RecordInfo> recordList(maxRecords);
+
     int tsID = 0;
     while (true)
       {
@@ -194,71 +192,71 @@ public:
             int varID, levelID;
             cdo_inq_record(streamID1, &varID, &levelID);
 
-            recList[recID].set(varID, levelID);
+            recordList[recID].set(varID, levelID);
 
-            cdo_read_record(streamID1, vardata[varID][levelID].data(), &varnumMissVals[varID][levelID]);
+            cdo_read_record(streamID1, varsData[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)
           {
             int varID;
-            for (varID = 0; varID < nvars; ++varID)
+            for (varID = 0; varID < numVars; ++varID)
               {
                 if (lvar)
                   {
-                    if (varList1[varID].name == chvars[i]) break;
+                    if (varList1.vars[varID].name == chvars[i]) break;
                   }
                 else
                   {
-                    if (varList1[varID].code == chcodes[i]) break;
+                    if (varList1.vars[varID].code == chcodes[i]) break;
                   }
               }
 
-            if (varID == nvars) cdo_abort("u-wind not found!");
+            if (varID == numVars) cdo_abort("u-wind not found!");
 
             auto usvarID = varID;
 
-            for (varID = 0; varID < nvars; ++varID)
+            for (varID = 0; varID < numVars; ++varID)
               {
                 if (lvar)
                   {
-                    if (varList1[varID].name == chvars[i + 1]) break;
+                    if (varList1.vars[varID].name == chvars[i + 1]) break;
                   }
                 else
                   {
-                    if (varList1[varID].code == chcodes[i + 1]) break;
+                    if (varList1.vars[varID].code == chcodes[i + 1]) break;
                   }
               }
 
-            if (varID == nvars) cdo_abort("v-wind not found!");
+            if (varID == numVars) cdo_abort("v-wind not found!");
 
             auto vsvarID = varID;
+            const auto &usVar = varList1.vars[usvarID];
+            const auto &vsVar = varList1.vars[vsvarID];
 
             if (Options::cdoVerbose)
               {
                 if (lvar)
-                  cdo_print("Using var %s [%s](u) and var %s [%s](v)", varList1[usvarID].name, chvars[i], varList1[vsvarID].name,
-                            chvars[i + 1]);
+                  cdo_print("Using var %s [%s](u) and var %s [%s](v)", usVar.name, chvars[i], vsVar.name, chvars[i + 1]);
                 else
-                  cdo_print("Using code %d [%d](u) and code %d [%d](v)", varList1[usvarID].code, chcodes[i], varList1[vsvarID].code,
-                            chcodes[i + 1]);
+                  cdo_print("Using code %d [%d](u) and code %d [%d](v)", usVar.code, chcodes[i], vsVar.code, chcodes[i + 1]);
               }
 
-            auto gridID = varList1[varID].gridID;
-            auto nlevels1 = varList1[usvarID].nlevels;
-            auto nlevels2 = varList1[vsvarID].nlevels;
+            auto gridID = usVar.gridID;
+            auto nlevels1 = usVar.nlevels;
+            auto nlevels2 = vsVar.nlevels;
             if (nlevels1 != nlevels2) cdo_abort("u-wind and v-wind have different number of levels!");
 
             for (int levelID = 0; levelID < nlevels1; ++levelID)
-              rot_uv_back(gridID, vardata[usvarID][levelID], vardata[vsvarID][levelID]);
+              rot_uv_back(gridID, varsData[usvarID][levelID], varsData[vsvarID][levelID]);
           }
 
         for (int recID = 0; recID < nrecs; ++recID)
           {
-            auto [varID, levelID] = recList[recID].get();
+            auto [varID, levelID] = recordList[recID].get();
             cdo_def_record(streamID2, varID, levelID);
-            cdo_write_record(streamID2, vardata[varID][levelID].data(), varnumMissVals[varID][levelID]);
+            cdo_write_record(streamID2, varsData[varID][levelID].data(), varnumMissVals[varID][levelID]);
           }
 
         tsID++;
@@ -266,7 +264,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
diff --git a/src/Runpctl.cc b/src/Runpctl.cc
index c9e8815047992cf4ce1a965dfa2361a317814f9f..699da21186b3c1ab7036716d9080791e6e7439f2 100644
--- a/src/Runpctl.cc
+++ b/src/Runpctl.cc
@@ -43,7 +43,7 @@ runpctl(double pn, int ndates, size_t gridsize, Varray<T> &v2, T missval, const
           for (int inp = 0; inp < ndates; ++inp)
             {
               auto val = vars1[inp][varID][levelID].vec_f[i];
-              if (!dbl_is_equal(val, missval)) array[j++] = val;
+              if (dbl_is_not_equal(val, missval)) array[j++] = val;
             }
         }
       else
@@ -51,7 +51,7 @@ runpctl(double pn, int ndates, size_t gridsize, Varray<T> &v2, T missval, const
           for (int inp = 0; inp < ndates; ++inp)
             {
               auto val = vars1[inp][varID][levelID].vec_d[i];
-              if (!dbl_is_equal(val, missval)) array[j++] = val;
+              if (dbl_is_not_equal(val, missval)) array[j++] = val;
             }
         }
 
@@ -89,6 +89,7 @@ public:
     .constraints = { 1, 1, NoRestriction },
   };
   inline static RegisterEntry<Runpctl> registration = RegisterEntry<Runpctl>(module);
+
   CdoStreamID streamID1;
   CdoStreamID streamID2;
   int vlistID1;
@@ -96,19 +97,18 @@ public:
   VarList varList1;
   int taxisID1;
   int taxisID2;
-  FieldVector3D vars1;
+  FieldVector3D varsData1;
 
   DateTimeList dtlist;
   double pn;
   int ndates;
-  int nvars;
-  int maxrecs;
-  std::vector<RecordInfo> recList;
+  int maxRecords;
+  std::vector<RecordInfo> recordList;
   int tsID;
 
 public:
   void
-  init()
+  init() override
   {
     constexpr auto timestatDate{ TimeStat::MEAN };
 
@@ -132,21 +132,16 @@ public:
     streamID2 = cdo_open_write(1);
     cdo_def_vlist(streamID2, vlistID2);
 
-    nvars = vlistNvars(vlistID1);
+    varList1 = VarList(vlistID1);
 
-    maxrecs = vlistNrecs(vlistID1);
-    recList = std::vector<RecordInfo>(maxrecs);
+    maxRecords = varList1.numRecords();
+    recordList = std::vector<RecordInfo>(maxRecords);
 
     dtlist.set_stat(timestatDate);
     dtlist.set_calendar(taxisInqCalendar(taxisID1));
 
-    Varray<float> array_f(ndates);
-    Varray<double> array_d(ndates);
-    vars1 = FieldVector3D(ndates + 1);
-
-    for (int its = 0; its < ndates; its++) fields_from_vlist(vlistID1, vars1[its]);
-
-    varList_init(varList1, vlistID1);
+    varsData1 = FieldVector3D(ndates + 1);
+    for (int its = 0; its < ndates; its++) field2D_init(varsData1[its], varList1);
   }
 
   void
@@ -154,21 +149,20 @@ public:
   {
     dtlist.stat_taxis_def_timestep(taxisID2, ndates);
     cdo_def_timestep(streamID2, otsID);
-    for (int recID = 0; recID < maxrecs; ++recID)
+    for (int recID = 0; recID < maxRecords; ++recID)
       {
-        auto [varID, levelID] = recList[recID].get();
-        if (otsID && varList1[varID].isConstant) continue;
+        auto [varID, levelID] = recordList[recID].get();
+        if (otsID && varList1.vars[varID].isConstant) continue;
 
         cdo_def_record(streamID2, varID, levelID);
-        auto &field1 = vars1[0][varID][levelID];
+        auto &field1 = varsData1[0][varID][levelID];
         cdo_write_record(streamID2, field1);
       }
   }
 
   void
-  run()
+  run() override
   {
-
     for (tsID = 0; tsID < ndates; ++tsID)
       {
         auto nrecs = cdo_stream_inq_timestep(streamID1, tsID);
@@ -181,25 +175,26 @@ public:
             int varID, levelID;
             cdo_inq_record(streamID1, &varID, &levelID);
 
-            if (tsID == 0) recList[recID].set(varID, levelID);
+            if (tsID == 0) recordList[recID].set(varID, levelID);
 
-            auto &field = vars1[tsID][varID][levelID];
-            field.init(varList1[varID]);
+            auto &field = varsData1[tsID][varID][levelID];
+            field.init(varList1.vars[varID]);
             cdo_read_record(streamID1, field);
           }
       }
     int otsID = 0;
     while (true)
       {
-        for (int varID = 0; varID < nvars; ++varID)
+        auto numVars = varList1.numVars();
+        for (int varID = 0; varID < numVars; ++varID)
           {
-            if (varList1[varID].isConstant) continue;
+            if (varList1.vars[varID].isConstant) continue;
 
-            auto nlevels = varList1[varID].nlevels;
+            auto nlevels = varList1.vars[varID].nlevels;
             for (int levelID = 0; levelID < nlevels; ++levelID)
               {
-                auto &field1 = vars1[0][varID][levelID];
-                runpctl(pn, ndates, field1, vars1, varID, levelID);
+                auto &field1 = varsData1[0][varID][levelID];
+                runpctl(pn, ndates, field1, varsData1, varID, levelID);
               }
           }
 
@@ -208,8 +203,8 @@ public:
 
         dtlist.shift();
 
-        vars1[ndates] = vars1[0];
-        for (int inp = 0; inp < ndates; ++inp) vars1[inp] = vars1[inp + 1];
+        varsData1[ndates] = varsData1[0];
+        for (int inp = 0; inp < ndates; ++inp) varsData1[inp] = varsData1[inp + 1];
 
         auto nrecs = cdo_stream_inq_timestep(streamID1, tsID);
         if (nrecs == 0) break;
@@ -220,7 +215,7 @@ public:
           {
             int varID, levelID;
             cdo_inq_record(streamID1, &varID, &levelID);
-            auto &fieldN = vars1[ndates - 1][varID][levelID];
+            auto &fieldN = varsData1[ndates - 1][varID][levelID];
             cdo_read_record(streamID1, fieldN);
           }
 
@@ -229,7 +224,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
diff --git a/src/Runstat.cc b/src/Runstat.cc
index 88e3126985ca315b15cb3cfb2d8ba3430bde3a9d..286a36d805408758829bda546a587336375dd26d 100644
--- a/src/Runstat.cc
+++ b/src/Runstat.cc
@@ -22,6 +22,7 @@
 
 #include <cdi.h>
 
+#include "cdo_stepstat.h"
 #include "process_int.h"
 #include "param_conversion.h"
 #include "datetime.h"
@@ -49,37 +50,32 @@ public:
     .constraints = { 1, 1, NoRestriction },
   };
   inline static RegisterEntry<Runstat> registration = RegisterEntry<Runstat>(module);
+
+private:
   TimeStat timestatDate{ TimeStat::MEAN };
   bool runstat_nomiss = false;
 
   CdoStreamID streamID1;
   CdoStreamID streamID2;
-
   int taxisID1;
   int taxisID2;
 
   DateTimeList dtlist;
-  VarList varList;
-  std::vector<RecordInfo> recList;
-  FieldVector3D vars1;
-  FieldVector3D vars2;
-  FieldVector3D samp1;
+
+  VarList varList1;
+  int maxRecords;
+  std::vector<RecordInfo> recordList;
 
   int ndates;
-  bool lrange;
-  bool lvarstd;
-  bool lvars2;
-  bool lmean;
-  bool lstd;
-  int operfunc;
-  int maxrecs;
-  int divisor;
+
+  bool varsData2needed;
+
+  cdo::StepStat3D stepStat;
 
 public:
   void
-  init()
+  init() override
   {
-
     auto envstr = getenv("RUNSTAT_NOMISS");
     if (envstr)
       {
@@ -89,15 +85,11 @@ public:
       }
 
     auto operatorID = cdo_operator_id();
-    operfunc = cdo_operator_f1(operatorID);  // used in omp loop
+    auto operfunc = cdo_operator_f1(operatorID);  // used in omp loop
+
+    stepStat.init(operfunc);
 
-    auto lminmax = (operfunc == FieldFunc_Min || operfunc == FieldFunc_Max);
-    lrange = (operfunc == FieldFunc_Range);
-    lmean = (operfunc == FieldFunc_Mean || operfunc == FieldFunc_Avg);
-    lstd = (operfunc == FieldFunc_Std || operfunc == FieldFunc_Std1);
-    lvarstd = (lstd || operfunc == FieldFunc_Var || operfunc == FieldFunc_Var1);
-    lvars2 = (lvarstd || lrange);
-    divisor = (operfunc == FieldFunc_Std1 || operfunc == FieldFunc_Var1);
+    varsData2needed = (stepStat.lvarstd || stepStat.lrange);
 
     operator_input_arg("number of timesteps");
     operator_check_argc(1);
@@ -108,7 +100,7 @@ public:
     auto vlistID1 = cdo_stream_inq_vlist(streamID1);
     auto vlistID2 = vlistDuplicate(vlistID1);
 
-    if (!lminmax) vlist_unpack(vlistID2);
+    if (!stepStat.lminmax) vlist_unpack(vlistID2);
 
     taxisID1 = vlistInqTaxis(vlistID1);
     taxisID2 = taxisDuplicate(taxisID1);
@@ -125,36 +117,29 @@ public:
     streamID2 = cdo_open_write(1);
     cdo_def_vlist(streamID2, vlistID2);
 
-    maxrecs = vlistNrecs(vlistID1);
+    varList1 = VarList(vlistID1);
 
-    recList = std::vector<RecordInfo>(maxrecs);
+    maxRecords = varList1.numRecords();
+    recordList = std::vector<RecordInfo>(maxRecords);
 
     dtlist.set_stat(timestatDate);
     dtlist.set_calendar(taxisInqCalendar(taxisID1));
 
-    varList_init(varList, vlistID1);
-
-    int VARS_MEMTYPE = 0;
-    if ((operfunc == FieldFunc_Min) || (operfunc == FieldFunc_Max)) VARS_MEMTYPE = FIELD_NAT;
-
-    vars1 = FieldVector3D(ndates + 1);
-    if (!runstat_nomiss) samp1.resize(ndates + 1);
-    if (lvars2) vars2.resize(ndates + 1);
+    stepStat.set_dimlen0(ndates + 1);
 
+    int VARS_MEMTYPE = stepStat.lminmax ? FIELD_NAT : 0;
     for (int its = 0; its < ndates; its++)
       {
-        if (!runstat_nomiss) fields_from_vlist(vlistID1, samp1[its], FIELD_VEC);
-        fields_from_vlist(vlistID1, vars1[its], FIELD_VEC | VARS_MEMTYPE);
-        if (lvars2) fields_from_vlist(vlistID1, vars2[its], FIELD_VEC);
+        field2D_init(stepStat.samp(its), varList1, !runstat_nomiss ? FIELD_VEC : 0);
+        field2D_init(stepStat.var1(its), varList1, FIELD_VEC | VARS_MEMTYPE);
+        field2D_init(stepStat.var2(its), varList1, varsData2needed ? FIELD_VEC : 0);
       }
   }
 
   void
-  run()
+  run() override
   {
     std::vector<short> imask;
-    auto field2_stdvar_func = lstd ? field2_std : field2_var;
-    auto fieldc_stdvar_func = lstd ? fieldc_std : fieldc_var;
 
     int tsID = 0;
     int otsID = 0;
@@ -180,63 +165,62 @@ public:
             int varID, levelID;
             cdo_inq_record(streamID1, &varID, &levelID);
 
-            if (tsID == 0) recList[recID].set(varID, levelID);
+            if (tsID == 0) recordList[recID].set(varID, levelID);
 
-            auto &rvars1 = vars1[numSteps][varID][levelID];
+            auto &rsamp = stepStat.samp(numSteps, varID, levelID);
+            auto &rvar1 = stepStat.var1(numSteps, varID, levelID);
+            auto &rvar2 = stepStat.var2(numSteps, varID, levelID);
 
-            auto fieldsize = rvars1.size;  // used in omp loop
+            auto fieldsize = rvar1.size;  // used in omp loop
 
-            cdo_read_record(streamID1, rvars1);
-            if (lrange)
-              {
-                vars2[numSteps][varID][levelID].numMissVals = rvars1.numMissVals;
-                vars2[numSteps][varID][levelID].vec_d = rvars1.vec_d;
-              }
+            cdo_read_record(streamID1, rvar1);
+
+            if (runstat_nomiss && rvar1.numMissVals) 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 (stepStat.lrange) field_copy(rvar1, rvar2);
 
             if (!runstat_nomiss)
               {
                 imask.resize(fieldsize);
-                auto missval = rvars1.missval;
+                auto missval = rvar1.missval;
 
-                if (rvars1.memType == MemType::Float)
-                  for (size_t i = 0; i < fieldsize; ++i) imask[i] = !dbl_is_equal(rvars1.vec_f[i], missval);
+                if (rvar1.memType == MemType::Float)
+                  for (size_t i = 0; i < fieldsize; ++i) imask[i] = dbl_is_not_equal(rvar1.vec_f[i], (float) missval);
                 else
-                  for (size_t i = 0; i < fieldsize; ++i) imask[i] = !dbl_is_equal(rvars1.vec_d[i], missval);
+                  for (size_t i = 0; i < fieldsize; ++i) imask[i] = dbl_is_not_equal(rvar1.vec_d[i], missval);
 
-                for (size_t i = 0; i < fieldsize; ++i) samp1[numSteps][varID][levelID].vec_d[i] = (double) imask[i];
+                for (size_t i = 0; i < fieldsize; ++i) rsamp.vec_d[i] = (double) imask[i];
 
 #ifdef _OPENMP
 #pragma omp parallel for default(shared)
 #endif
                 for (int inp = 0; inp < numSteps; ++inp)
                   {
-                    auto &samp = samp1[inp][varID][levelID].vec_d;
+                    auto &samp = stepStat.samp(inp, varID, levelID).vec_d;
                     for (size_t i = 0; i < fieldsize; ++i)
                       if (imask[i]) samp[i]++;
                   }
               }
 
-            if (lvarstd)
+            if (stepStat.lvarstd)
               {
-                field2_moq(vars2[numSteps][varID][levelID], vars1[numSteps][varID][levelID]);
+                field2_moq(stepStat.var2(numSteps, varID, levelID), stepStat.var1(numSteps, varID, levelID));
 #ifdef _OPENMP
 #pragma omp parallel for default(shared)
 #endif
                 for (int inp = 0; inp < numSteps; ++inp)
                   {
-                    field2_sumsumq(vars1[inp][varID][levelID], vars2[inp][varID][levelID], rvars1);
+                    field2_sumsumq(stepStat.var1(inp, varID, levelID), stepStat.var2(inp, varID, levelID), rvar1);
                   }
               }
-            else if (lrange)
+            else if (stepStat.lrange)
               {
 #ifdef _OPENMP
 #pragma omp parallel for default(shared)
 #endif
                 for (int inp = 0; inp < numSteps; ++inp)
                   {
-                    field2_maxmin(vars1[inp][varID][levelID], vars2[inp][varID][levelID], rvars1);
+                    field2_maxmin(stepStat.var1(inp, varID, levelID), stepStat.var2(inp, varID, levelID), rvar1);
                   }
               }
             else
@@ -244,7 +228,10 @@ public:
 #ifdef _OPENMP
 #pragma omp parallel for default(shared)
 #endif
-                for (int inp = 0; inp < numSteps; ++inp) { field2_function(vars1[inp][varID][levelID], rvars1, operfunc); }
+                for (int inp = 0; inp < numSteps; ++inp)
+                  {
+                    field2_function(stepStat.var1(inp, varID, levelID), rvar1, stepStat.operfunc);
+                  }
               }
           }
 
@@ -252,65 +239,40 @@ public:
 
         if (tsID < ndates) goto FILL_FIRST_NDATES;
 
-        for (int recID = 0; recID < maxrecs; ++recID)
-          {
-            auto [varID, levelID] = recList[recID].get();
-            if (varList[varID].isConstant) continue;
-
-            const auto &rsamp1 = samp1[0][varID][levelID];
-            auto &rvars1 = vars1[0][varID][levelID];
-            auto numSets = ndates;
-
-            if (lmean)
-              {
-                if (!runstat_nomiss)
-                  field2_div(rvars1, rsamp1);
-                else
-                  fieldc_div(rvars1, (double) numSets);
-              }
-            else if (lvarstd)
-              {
-                if (!runstat_nomiss)
-                  field2_stdvar_func(rvars1, vars2[0][varID][levelID], rsamp1, divisor);
-                else
-                  fieldc_stdvar_func(rvars1, vars2[0][varID][levelID], numSets, divisor);
-              }
-            else if (lrange) { field2_sub(rvars1, vars2[0][varID][levelID]); }
-          }
+        auto numSets = ndates;
+        cdo::records_process_3D(0, recordList, varList1, stepStat, numSets);
 
         dtlist.stat_taxis_def_timestep(taxisID2, ndates);
         cdo_def_timestep(streamID2, otsID);
 
-        for (int recID = 0; recID < maxrecs; ++recID)
+        for (int recID = 0; recID < maxRecords; ++recID)
           {
-            auto [varID, levelID] = recList[recID].get();
-            if (otsID && varList[varID].isConstant) continue;
-
-            auto &rvars1 = vars1[0][varID][levelID];
+            auto [varID, levelID] = recordList[recID].get();
+            if (otsID && varList1.vars[varID].isConstant) continue;
 
             cdo_def_record(streamID2, varID, levelID);
-            cdo_write_record(streamID2, rvars1);
+            cdo_write_record(streamID2, stepStat.var1(0, varID, levelID));
           }
 
         otsID++;
 
         dtlist.shift();
 
-        vars1[ndates] = vars1[0];
-        if (!runstat_nomiss) samp1[ndates] = samp1[0];
-        if (lvars2) vars2[ndates] = vars2[0];
+        stepStat.var1(ndates) = stepStat.var1(0);
+        if (!runstat_nomiss) stepStat.samp(ndates) = stepStat.samp(0);
+        if (varsData2needed) stepStat.var2(ndates) = stepStat.var2(0);
 
         for (int inp = 0; inp < ndates; ++inp)
           {
-            vars1[inp] = vars1[inp + 1];
-            if (!runstat_nomiss) samp1[inp] = samp1[inp + 1];
-            if (lvars2) vars2[inp] = vars2[inp + 1];
+            stepStat.var1(inp) = stepStat.var1(inp + 1);
+            if (!runstat_nomiss) stepStat.samp(inp) = stepStat.samp(inp + 1);
+            if (varsData2needed) stepStat.var2(inp) = stepStat.var2(inp + 1);
           }
       }
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
diff --git a/src/Samplegrid.cc b/src/Samplegrid.cc
index feb78ea1f0b1d5a512df394685f4c40d33e12441..1c5f994ddef9416a09c2c0aad78d22884d2b85bb 100644
--- a/src/Samplegrid.cc
+++ b/src/Samplegrid.cc
@@ -87,6 +87,8 @@ public:
   int vlistID1;
   int vlistID2;
 
+  VarList varList1;
+
   int taxisID1;
   int taxisID2;
 
@@ -102,7 +104,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
     SAMPLEGRID = module.get_id("samplegrid");
     SUBGRID = module.get_id("subgrid");
@@ -138,15 +140,17 @@ public:
     vlistID1 = cdo_stream_inq_vlist(streamID1);
     vlistID2 = vlistDuplicate(vlistID1);
 
+    varList1 = VarList(vlistID1);
+
     taxisID1 = vlistInqTaxis(vlistID1);
     taxisID2 = taxisDuplicate(taxisID1);
     vlistDefTaxis(vlistID2, taxisID2);
 
-    const auto nvars = vlistNvars(vlistID1);
+    auto numVars = varList1.numVars();
 
-    vars = std::vector<bool>(nvars, false);
+    vars = std::vector<bool>(numVars, false);
 
-    ngrids = vlistNgrids(vlistID1);
+    ngrids = vlistNumGrids(vlistID1);
 
     Debug(cdoDebugExt, "ngrids=%d", ngrids);
 
@@ -175,8 +179,8 @@ public:
 
         vlistChangeGridIndex(vlistID2, index, gridIDsampled);
 
-        for (int varID = 0; varID < nvars; ++varID)
-          if (gridSrcID == vlistInqVarGrid(vlistID1, varID)) vars[varID] = true;
+        for (const auto &var : varList1.vars)
+          if (gridSrcID == var.gridID) vars[var.ID] = true;
       }
 
     Debug(cdoDebugExt, [&]() {
@@ -199,7 +203,7 @@ public:
   }
 
   void
-  run()
+  run() override
   {
     int tsID = 0;
     while (true)
@@ -223,7 +227,7 @@ public:
 
             if (vars[varID])
               {
-                auto gridSrcID = vlistInqVarGrid(vlistID1, varID);
+                auto gridSrcID = varList1.vars[varID].gridID;
 
                 int index;
                 for (index = 0; index < ngrids; ++index)
@@ -259,7 +263,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
diff --git a/src/Samplegridicon.cc b/src/Samplegridicon.cc
index 90cbaa5e0b026ffc2d2455a09487b73b6806679f..cc0d1e38649a1e6b2d0b18158ec4b431ee099562 100644
--- a/src/Samplegridicon.cc
+++ b/src/Samplegridicon.cc
@@ -20,9 +20,8 @@ constexpr int MAX_CHILDS = 9;
 struct CellIndex
 {
   long ncells;
-  Varray<long> neighbor;  // neighbor cell index
-  Varray<long> parent;    // parent cell index
-  Varray<long> child;     // child cell index
+  Varray<long> parent;  // parent cell index
+  Varray<long> child;   // child cell index
   std::string filename;
 };
 
@@ -37,7 +36,7 @@ read_cellindex(const std::string &filename, CellIndex &cellindex)
 {
   auto streamID = stream_open_read_locked(filename.c_str());
   auto vlistID = streamInqVlist(streamID);
-  auto ngrids = vlistNgrids(vlistID);
+  auto ngrids = vlistNumGrids(vlistID);
   int gridID = -1;
   for (int index = 0; index < ngrids; ++index)
     {
@@ -120,7 +119,7 @@ read_grid(const std::string &filename)
 {
   auto streamID = stream_open_read_locked(filename.c_str());
   auto vlistID = streamInqVlist(streamID);
-  auto ngrids = vlistNgrids(vlistID);
+  auto ngrids = vlistNumGrids(vlistID);
   int gridID = -1;
   for (int index = 0; index < ngrids; ++index)
     {
@@ -234,7 +233,7 @@ read_coordinates(const std::string &filename, long n, Varray<double> &lon, Varra
 {
   auto streamID = streamOpenRead(filename.c_str());
   auto vlistID = streamInqVlist(streamID);
-  auto ngrids = vlistNgrids(vlistID);
+  auto ngrids = vlistNumGrids(vlistID);
   int gridID = -1;
   for (int index = 0; index < ngrids; ++index)
     {
@@ -260,7 +259,7 @@ read_coordinates(const std::string &filename, long n, Varray<double> &lon, Varra
 {
   auto streamID = streamOpenRead(filename.c_str());
   auto vlistID = streamInqVlist(streamID);
-  auto ngrids = vlistNgrids(vlistID);
+  auto ngrids = vlistNumGrids(vlistID);
   int gridID = -1;
   for (int index = 0; index < ngrids; ++index)
     {
@@ -465,7 +464,7 @@ public:
   using Process::Process;
   inline static CdoModule module = {
     .name = "Samplegridicon",
-    .operators = { { "samplegridicon", 0, 0, "samplegrids"} },
+    .operators = { { "samplegridicon", 0, 0, "samplegrids" } },
     .aliases = {},
     .mode = EXPOSED,     // Module mode: 0:intern 1:extern
     .number = CDI_REAL,  // Allowed number type
@@ -496,7 +495,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
     nsamplegrids = cdo_operator_argc();
     if (nsamplegrids < 2) cdo_abort("Parameter missing!");
@@ -533,7 +532,7 @@ public:
     vlistDefTaxis(vlistID2, taxisID2);
     vlistDefTaxis(vlistID3, taxisID3);
 
-    auto ngrids = vlistNgrids(vlistID1);
+    auto ngrids = vlistNumGrids(vlistID1);
     for (int index = 0; index < ngrids; ++index)
       {
         auto gridID = vlistGrid(vlistID1, index);
@@ -559,7 +558,7 @@ public:
   }
 
   void
-  run()
+  run() override
   {
     int tsID = 0;
     while (true)
@@ -597,13 +596,11 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID3);
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
-
     vlistDestroy(vlistID2);
-    gridDestroy(gridID2);
   }
 };
diff --git a/src/Seascount.cc b/src/Seascount.cc
index a3efafe858ebd64a882ce62438bd052fdfee7244..331b201436dba4f58132442cf67ab28087e509b5 100644
--- a/src/Seascount.cc
+++ b/src/Seascount.cc
@@ -27,13 +27,14 @@ public:
   using Process::Process;
   inline static CdoModule module = {
     .name = "Seascount",
-    .operators = { { "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;
@@ -44,18 +45,13 @@ public:
   int taxisID1;
   int taxisID2;
 
-  int maxrecs;
-
   VarList varList1;
-  Field field;
-  std::vector<RecordInfo> recList;
-  FieldVector2D vars1;
+  FieldVector2D varsData1;
 
 public:
   void
-  init()
+  init() override
   {
-
     operator_check_argc(0);
 
     streamID1 = cdo_open_read(0);
@@ -70,16 +66,18 @@ public:
     streamID2 = cdo_open_write(1);
     cdo_def_vlist(streamID2, vlistID2);
 
-    maxrecs = vlistNrecs(vlistID1);
-    recList = std::vector<RecordInfo>(maxrecs);
-
-    varList_init(varList1, vlistID1);
+    varList1 = VarList(vlistID1);
 
-    fields_from_vlist(vlistID1, vars1, FIELD_VEC);
+    field2D_init(varsData1, varList1, FIELD_VEC);
   }
+
   void
-  run()
+  run() override
   {
+    auto maxRecords = varList1.numRecords();
+    std::vector<RecordInfo> recordList(maxRecords);
+    Field field;
+
     auto seasonStart = get_season_start();
 
     int tsID = 0;
@@ -122,22 +120,22 @@ public:
               {
                 int varID, levelID;
                 cdo_inq_record(streamID1, &varID, &levelID);
-                const auto &var = varList1[varID];
+                const auto &var = varList1.vars[varID];
 
-                if (tsID == 0) recList[recID].set(varID, levelID);
+                if (tsID == 0) recordList[recID].set(varID, levelID);
 
-                auto fieldsize = vars1[varID][levelID].size;
+                auto fieldsize = varsData1[varID][levelID].size;
 
                 if (numSets == 0)
                   {
-                    for (size_t i = 0; i < fieldsize; ++i) vars1[varID][levelID].vec_d[i] = vars1[varID][levelID].missval;
-                    vars1[varID][levelID].numMissVals = fieldsize;
+                    for (size_t i = 0; i < fieldsize; ++i) varsData1[varID][levelID].vec_d[i] = varsData1[varID][levelID].missval;
+                    varsData1[varID][levelID].numMissVals = fieldsize;
                   }
 
                 field.init(var);
                 cdo_read_record(streamID1, field);
 
-                field2_count(vars1[varID][levelID], field);
+                field2_count(varsData1[varID][levelID], field);
               }
 
             vDateTime0 = vDateTime;
@@ -150,21 +148,22 @@ public:
         taxisDefVdatetime(taxisID2, vDateTime0);
         cdo_def_timestep(streamID2, otsID);
 
-        for (int recID = 0; recID < maxrecs; ++recID)
+        for (int recID = 0; recID < maxRecords; ++recID)
           {
-            auto [varID, levelID] = recList[recID].get();
-            if (otsID && varList1[varID].isConstant) continue;
+            auto [varID, levelID] = recordList[recID].get();
+            if (otsID && varList1.vars[varID].isConstant) continue;
 
             cdo_def_record(streamID2, varID, levelID);
-            cdo_write_record(streamID2, vars1[varID][levelID].vec_d.data(), vars1[varID][levelID].numMissVals);
+            cdo_write_record(streamID2, varsData1[varID][levelID].vec_d.data(), varsData1[varID][levelID].numMissVals);
           }
 
         if (nrecs == 0) break;
         otsID++;
       }
   }
+
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
diff --git a/src/Seasmonstat.cc b/src/Seasmonstat.cc
index 4cac345819833f6a9c452e939704005566eac76c..a52f14c1ba170ea27ed65a54d43940c80a07427e 100644
--- a/src/Seasmonstat.cc
+++ b/src/Seasmonstat.cc
@@ -28,13 +28,14 @@ public:
   using Process::Process;
   inline static CdoModule module = {
     .name = "Seasmonstat",
-    .operators = { { "seasmonmean", FieldFunc_Mean, 0, nullptr}, { "seasmonavg", FieldFunc_Avg, 0, nullptr} },
+    .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{};
@@ -54,24 +55,15 @@ public:
   int taxisID1;
   int taxisID2;
 
-  VarList varList;
-  Field field;
-  FieldVector2D samp1, vars1;
-
-  std::vector<RecordInfo> recList;
+  VarList varList1;
+  FieldVector2D samp1, varsData1;
 
   int operfunc;
-  int nvars;
-  int maxrecs;
 
 public:
   void
-  init()
+  init() override
   {
-
-    // clang-format off
-    // clang-format on
-
     auto operatorID = cdo_operator_id();
     operfunc = cdo_operator_f1(operatorID);
 
@@ -95,24 +87,23 @@ public:
     streamID2 = cdo_open_write(1);
     cdo_def_vlist(streamID2, vlistID2);
 
-    nvars = vlistNvars(vlistID1);
-
-    maxrecs = vlistNrecs(vlistID1);
-    recList = std::vector<RecordInfo>(maxrecs);
-
     calendar = taxisInqCalendar(taxisID1);
     dtlist.set_stat(timestatDate);
     dtlist.set_calendar(calendar);
 
-    varList_init(varList, vlistID1);
+    varList1 = VarList(vlistID1);
 
-    fields_from_vlist(vlistID1, samp1);
-    fields_from_vlist(vlistID1, vars1, FIELD_VEC);
+    field2D_init(samp1, varList1);
+    field2D_init(varsData1, varList1, FIELD_VEC);
   }
 
   void
-  run()
+  run() override
   {
+    auto maxRecords = varList1.numRecords();
+    std::vector<RecordInfo> recordList(maxRecords);
+    Field field;
+
     auto seasonStart = get_season_start();
     auto seasonNames = get_season_name();
 
@@ -168,12 +159,12 @@ public:
               {
                 int varID, levelID;
                 cdo_inq_record(streamID1, &varID, &levelID);
-                const auto &var = varList[varID];
+                const auto &var = varList1.vars[varID];
 
-                if (tsID == 0) recList[recID].set(varID, levelID);
+                if (tsID == 0) recordList[recID].set(varID, levelID);
 
                 auto &rsamp1 = samp1[varID][levelID];
-                auto &rvars1 = vars1[varID][levelID];
+                auto &rvars1 = varsData1[varID][levelID];
 
                 auto fieldsize = rvars1.size;
 
@@ -215,14 +206,15 @@ public:
 
         if (nrecs == 0 && numSets == 0) break;
 
-        for (int varID = 0; varID < nvars; ++varID)
+        auto numVars = varList1.numVars();
+        for (int varID = 0; varID < numVars; ++varID)
           {
-            const auto &var = varList[varID];
+            const auto &var = varList1.vars[varID];
             if (var.isConstant) continue;
             for (int levelID = 0; levelID < var.nlevels; ++levelID)
               {
                 const auto &rsamp1 = samp1[varID][levelID];
-                auto &rvars1 = vars1[varID][levelID];
+                auto &rvars1 = varsData1[varID][levelID];
                 if (!rsamp1.empty())
                   field2_div(rvars1, rsamp1);
                 else
@@ -241,12 +233,12 @@ public:
           cdo_warning("Season %3d (%s) has only %d input time step%s!", otsID + 1, date_to_string(vDateTime0.date), numSets,
                       numSets == 1 ? "" : "s");
 
-        for (int recID = 0; recID < maxrecs; ++recID)
+        for (int recID = 0; recID < maxRecords; ++recID)
           {
-            auto [varID, levelID] = recList[recID].get();
-            if (otsID && varList[varID].isConstant) continue;
+            auto [varID, levelID] = recordList[recID].get();
+            if (otsID && varList1.vars[varID].isConstant) continue;
 
-            auto &rvars1 = vars1[varID][levelID];
+            auto &rvars1 = varsData1[varID][levelID];
             cdo_def_record(streamID2, varID, levelID);
             cdo_write_record(streamID2, rvars1);
           }
@@ -257,7 +249,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
diff --git a/src/Seaspctl.cc b/src/Seaspctl.cc
index b0230087f7124fbae786ac279865c91a92261106..2a58ac7e46bb6fb4aa85cea10a552f010b496faa 100644
--- a/src/Seaspctl.cc
+++ b/src/Seaspctl.cc
@@ -37,6 +37,7 @@ public:
     .constraints = { 3, 1, NoRestriction },
   };
   inline static RegisterEntry<Seaspctl> registration = RegisterEntry<Seaspctl>(module);
+
   TimeStat timestatDate{ TimeStat::MEAN };
   int seas0 = 0;
   int oldmon = 0;
@@ -51,21 +52,16 @@ public:
   int taxisID3;
   int taxisID4;
 
-  Field field1, field2;
   DateTimeList dtlist;
   VarList varList1;
-  int maxrecs;
   double pn;
 
-  std::vector<RecordInfo> recList;
   HistogramSet hset;
-  FieldVector constFields;
 
 public:
   void
-  init()
+  init() override
   {
-
     operator_input_arg("percentile number");
     pn = parameter_to_double(cdo_operator_argv(0));
 
@@ -80,8 +76,12 @@ public:
 
     vlist_unpack(vlistID4);
 
-    vlist_compare(vlistID1, vlistID2, CmpVlist::All);
-    vlist_compare(vlistID1, vlistID3, CmpVlist::All);
+    varList1 = VarList(vlistID1);
+    VarList varList2(vlistID2);
+    VarList varList3(vlistID3);
+
+    varList_compare(varList1, varList2);
+    varList_compare(varList1, varList3);
 
     taxisID1 = vlistInqTaxis(vlistID1);
     taxisID2 = vlistInqTaxis(vlistID2);
@@ -95,30 +95,26 @@ public:
     streamID4 = cdo_open_write(3);
     cdo_def_vlist(streamID4, vlistID4);
 
-    auto ntsteps = vlistNtsteps(vlistID1);
-    auto nvars = vlistNvars(vlistID1);
-
-    maxrecs = vlistNrecs(vlistID1);
-    recList = std::vector<RecordInfo>(maxrecs);
-    constFields = FieldVector(maxrecs);
+    auto numVars = varList1.numVars();
+    auto numSteps = varList1.numSteps();
 
     dtlist.set_stat(timestatDate);
     dtlist.set_calendar(taxisInqCalendar(taxisID1));
 
-    varList_init(varList1, vlistID1);
-
-    hset = HistogramSet(nvars, ntsteps);
+    hset = HistogramSet(numVars, numSteps);
 
-    for (int varID = 0; varID < nvars; ++varID)
-      {
-        const auto &var = varList1[varID];
-        hset.createVarLevels(varID, var.nlevels, var.gridsize);
-      }
+    for (const auto &var : varList1.vars) hset.createVarLevels(var.ID, var.nlevels, var.gridsize);
   }
 
   void
-  run()
+  run() override
   {
+    auto maxRecords = varList1.numRecords();
+    std::vector<RecordInfo> recordList(maxRecords);
+    FieldVector constFields(maxRecords);
+
+    Field field1, field2;
+
     auto seasonStart = get_season_start();
 
     int tsID = 0;
@@ -140,7 +136,7 @@ public:
           {
             int varID, levelID;
             cdo_inq_record(streamID2, &varID, &levelID);
-            const auto &var = varList1[varID];
+            const auto &var = varList1.vars[varID];
             field1.init(var);
             cdo_read_record(streamID2, field1);
 
@@ -184,9 +180,9 @@ public:
               {
                 int varID, levelID;
                 cdo_inq_record(streamID1, &varID, &levelID);
-                const auto &var = varList1[varID];
+                const auto &var = varList1.vars[varID];
 
-                if (tsID == 0) recList[recID].set(varID, levelID);
+                if (tsID == 0) recordList[recID].set(varID, levelID);
 
                 if (tsID == 0 && var.isConstant)
                   {
@@ -211,17 +207,17 @@ public:
         dtlist.stat_taxis_def_timestep(taxisID4, numSets);
         cdo_def_timestep(streamID4, otsID);
 
-        for (int recID = 0; recID < maxrecs; ++recID)
+        for (int recID = 0; recID < maxRecords; ++recID)
           {
-            auto [varID, levelID] = recList[recID].get();
-            if (otsID && varList1[varID].isConstant) continue;
+            auto [varID, levelID] = recordList[recID].get();
+            const auto &var = varList1.vars[varID];
+            if (otsID && var.isConstant) continue;
 
             cdo_def_record(streamID4, varID, levelID);
 
-            if (varList1[varID].isConstant) { cdo_write_record(streamID4, constFields[recID]); }
+            if (var.isConstant) { cdo_write_record(streamID4, constFields[recID]); }
             else
               {
-                const auto &var = varList1[varID];
                 field1.init(var);
                 hset.getVarLevelPercentiles(field1, varID, levelID, pn);
                 cdo_write_record(streamID4, field1);
@@ -234,7 +230,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID4);
     cdo_stream_close(streamID3);
diff --git a/src/Seasstat.cc b/src/Seasstat.cc
index 5a3293cf8a2b619f0a75e30a0d68542271fee5dc..fff66bde2ad3e7a765dd85f539e0ce2041189fa6 100644
--- a/src/Seasstat.cc
+++ b/src/Seasstat.cc
@@ -23,10 +23,12 @@
 #include <cdi.h>
 
 #include "cdo_options.h"
+#include "cdo_stepstat.h"
 #include "process_int.h"
 #include "datetime.h"
 #include "printinfo.h"
 #include "cdo_season.h"
+#include "progress.h"
 #include "field_functions.h"
 
 class Seasstat : public Process
@@ -35,6 +37,7 @@ public:
   using Process::Process;
   inline static CdoModule module = {
     .name = "Seasstat",
+    // clang-format off
     .operators = { { "seasrange", FieldFunc_Range, 0, SeasstatHelp },
                    { "seasmin", FieldFunc_Min, 0, SeasstatHelp },
                    { "seasmax", FieldFunc_Max, 0, SeasstatHelp },
@@ -45,20 +48,20 @@ public:
                    { "seasstd1", FieldFunc_Std1, 0, SeasstatHelp },
                    { "seasvar", FieldFunc_Var, 0, SeasstatHelp },
                    { "seasvar1", FieldFunc_Var1, 0, SeasstatHelp } },
+    // 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<Seasstat> registration = RegisterEntry<Seasstat>(module);
+
+private:
   CdoStreamID streamID1;
   CdoStreamID streamID2;
-
   int taxisID1;
   int taxisID2;
 
-  int operfunc;
-
   TimeStat timestatDate{ TimeStat::MEAN };
   CdiDateTime vDateTime0{};
   CdiDateTime vDateTime1{};
@@ -67,45 +70,33 @@ public:
   int oldmon = 0;
   int nseason = 0;
 
-  int maxrecs;
+  cdo::StepStat2D stepStat;
 
-  Field field;
-  DateTimeList dtlist;
-  VarList varList;
-  FieldVector2D samp1, vars1, vars2;
-  std::vector<RecordInfo> recList;
+  int maxRecords;
+  std::vector<RecordInfo> recordList;
 
-  bool lrange;
-  bool lmean;
-  bool lstd;
-  bool lvarstd;
-  bool lvars2;
-  int divisor;
+  DateTimeList dtlist;
+  VarList varList1;
 
 public:
   void
-  init()
+  init() override
   {
-
     auto operatorID = cdo_operator_id();
-    operfunc = cdo_operator_f1(operatorID);
+    auto operfunc = cdo_operator_f1(operatorID);
 
     operator_check_argc(0);
 
-    auto lminmax = (operfunc == FieldFunc_Min || operfunc == FieldFunc_Max);
-    lrange = (operfunc == FieldFunc_Range);
-    lmean = (operfunc == FieldFunc_Mean || operfunc == FieldFunc_Avg);
-    lstd = (operfunc == FieldFunc_Std || operfunc == FieldFunc_Std1);
-    lvarstd = (lstd || operfunc == FieldFunc_Var || operfunc == FieldFunc_Var1);
-    lvars2 = (lvarstd || lrange);
-    divisor = (operfunc == FieldFunc_Std1 || operfunc == FieldFunc_Var1);
+    stepStat.init(operfunc);
 
     streamID1 = cdo_open_read(0);
 
     auto vlistID1 = cdo_stream_inq_vlist(streamID1);
     auto vlistID2 = vlistDuplicate(vlistID1);
 
-    if (!lminmax) vlist_unpack(vlistID2);
+    varList1 = VarList(vlistID1);
+
+    if (!stepStat.lminmax) vlist_unpack(vlistID2);
 
     taxisID1 = vlistInqTaxis(vlistID1);
     taxisID2 = taxisDuplicate(taxisID1);
@@ -116,42 +107,41 @@ public:
     streamID2 = cdo_open_write(1);
     cdo_def_vlist(streamID2, vlistID2);
 
-    maxrecs = vlistNrecs(vlistID1);
-    recList = std::vector<RecordInfo>(maxrecs);
+    maxRecords = varList1.numRecords();
+    recordList = std::vector<RecordInfo>(maxRecords);
+
     dtlist.set_stat(timestatDate);
     dtlist.set_calendar(taxisInqCalendar(taxisID1));
 
-    varList_init(varList, vlistID1);
-
-    int VARS_MEMTYPE = 0;
-    if ((operfunc == FieldFunc_Min) || (operfunc == FieldFunc_Max)) VARS_MEMTYPE = FIELD_NAT;
-
-    fields_from_vlist(vlistID1, samp1);
-    fields_from_vlist(vlistID1, vars1, FIELD_VEC | VARS_MEMTYPE);
-    if (lvars2) fields_from_vlist(vlistID1, vars2, FIELD_VEC);
+    int VARS_MEMTYPE = stepStat.lminmax ? FIELD_NAT : 0;
+    stepStat.alloc(varList1, VARS_MEMTYPE);
   }
 
   void
-  run()
+  run() override
   {
+    Field field;
+
     auto seasonStart = get_season_start();
     auto seasonNames = get_season_name();
 
-    auto field2_stdvar_func = lstd ? field2_std : field2_var;
-    auto fieldc_stdvar_func = lstd ? fieldc_std : fieldc_var;
+    auto numSteps = varList1.numSteps();
+    cdo::Progress progress;
 
     int tsID = 0;
     int otsID = 0;
     while (true)
       {
-        int nrecs = 0;
-        long numSets = 0;
+        int numSets = 0;
         bool newseas = false;
+        int nrecs = 0;
         while (true)
           {
             nrecs = cdo_stream_inq_timestep(streamID1, tsID);
             if (nrecs == 0) break;
 
+            if (numSteps > 1) progress.update((tsID + 1.0) / numSteps);
+
             dtlist.taxis_inq_timestep(taxisID1, numSets);
             auto vDateTime = dtlist.get_vDateTime(numSets);
 
@@ -181,58 +171,13 @@ public:
 
             for (int recID = 0; recID < nrecs; ++recID)
               {
-                int varID, levelID;
-                cdo_inq_record(streamID1, &varID, &levelID);
-                const auto &var = varList[varID];
-
-                if (tsID == 0) recList[recID].set(varID, levelID);
-
-                auto &rsamp1 = samp1[varID][levelID];
-                auto &rvars1 = vars1[varID][levelID];
-
-                if (numSets == 0)
-                  {
-                    cdo_read_record(streamID1, rvars1);
-                    if (lrange)
-                      {
-                        vars2[varID][levelID].numMissVals = rvars1.numMissVals;
-                        vars2[varID][levelID].vec_d = rvars1.vec_d;
-                      }
-
-                    if (rvars1.numMissVals || !rsamp1.empty())
-                      {
-                        if (rsamp1.empty()) rsamp1.resize(rvars1.size);
-                        field2_vinit(rsamp1, rvars1);
-                      }
-                  }
-                else
-                  {
-                    field.init(var);
-                    cdo_read_record(streamID1, field);
-
-                    if (field.numMissVals || !rsamp1.empty())
-                      {
-                        if (rsamp1.empty()) rsamp1.resize(rvars1.size, numSets);
-                        field2_vincr(rsamp1, field);
-                      }
-
-                    // clang-format off
-                  if      (lvarstd) field2_sumsumq(rvars1, vars2[varID][levelID], field);
-                  else if (lrange)  field2_maxmin(rvars1, vars2[varID][levelID], field);
-                  else              field2_function(rvars1, field, operfunc);
-                    // clang-format on
-                  }
+                auto [varID, levelID] = cdo_inq_record(streamID1);
+                if (tsID == 0) recordList[recID].set(varID, levelID);
+                field.init(varList1.vars[varID]);
+                cdo_read_record(streamID1, field);
+                stepStat.add_field(field, varID, levelID, numSets);
               }
 
-            if (numSets == 0 && lvarstd)
-              for (int recID = 0; recID < maxrecs; ++recID)
-                {
-                  auto [varID, levelID] = recList[recID].get();
-                  if (varList[varID].isConstant) continue;
-
-                  field2_moq(vars2[varID][levelID], vars1[varID][levelID]);
-                }
-
             vDateTime1 = vDateTime;
             numSets++;
             tsID++;
@@ -240,52 +185,18 @@ public:
 
         if (nrecs == 0 && numSets == 0) break;
 
-        for (int recID = 0; recID < maxrecs; ++recID)
-          {
-            auto [varID, levelID] = recList[recID].get();
-            if (varList[varID].isConstant) continue;
-
-            const auto &rsamp1 = samp1[varID][levelID];
-            auto &rvars1 = vars1[varID][levelID];
-
-            if (lmean)
-              {
-                if (!rsamp1.empty())
-                  field2_div(rvars1, rsamp1);
-                else
-                  fieldc_div(rvars1, (double) numSets);
-              }
-            else if (lvarstd)
-              {
-                if (!rsamp1.empty())
-                  field2_stdvar_func(rvars1, vars2[varID][levelID], rsamp1, divisor);
-                else
-                  fieldc_stdvar_func(rvars1, vars2[varID][levelID], numSets, divisor);
-              }
-            else if (lrange) { field2_sub(rvars1, vars2[varID][levelID]); }
-          }
+        cdo::records_process(recordList, varList1, stepStat, numSets);
 
         if (Options::cdoVerbose)
           cdo_print("season: %3d %3s  start: %s  end: %s ntimesteps: %ld", nseason, seasonNames[seas0],
                     datetime_to_string(vDateTime0), datetime_to_string(vDateTime1), numSets);
 
-        dtlist.stat_taxis_def_timestep(taxisID2, numSets);
-        cdo_def_timestep(streamID2, otsID);
-
         if (numSets < 3)
           cdo_warning("Season %3d (%s) has only %d input time step%s!", otsID + 1, date_to_string(vDateTime0.date), numSets,
                       (numSets == 1) ? "" : "s");
 
-        for (int recID = 0; recID < maxrecs; ++recID)
-          {
-            auto [varID, levelID] = recList[recID].get();
-            if (otsID && varList[varID].isConstant) continue;
-
-            auto &rvars1 = vars1[varID][levelID];
-
-            cdo_def_record(streamID2, varID, levelID);
-            cdo_write_record(streamID2, rvars1);
-          }
+        dtlist.stat_taxis_def_timestep(taxisID2, numSets);
+        cdo::write_out_stream(streamID2, recordList, varList1, stepStat, otsID);
 
         if (nrecs == 0) break;
         otsID++;
@@ -293,7 +204,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
diff --git a/src/Selbox.cc b/src/Selbox.cc
index 5a1f8c9cce0e81b1ca009453d899844d9fc837a8..b5af8d0aebc46e9dab6762aa212db1def0e0b876 100644
--- a/src/Selbox.cc
+++ b/src/Selbox.cc
@@ -18,9 +18,7 @@
 #include "grid_define.h"
 #include "cdo_options.h"
 #include "process_int.h"
-#include "cdo_vlist.h"
 #include "param_conversion.h"
-#include "gridreference.h"
 #include "selboxinfo.h"
 
 static void
@@ -968,7 +966,7 @@ get_selboxInfo(int vlistID1, int vlistID2, bool operIndexBox)
 {
   std::vector<SelboxInfo> selboxInfo;
 
-  auto ngrids = vlistNgrids(vlistID1);
+  auto ngrids = vlistNumGrids(vlistID1);
   for (int index = 0; index < ngrids; ++index)
     {
       auto gridID1 = vlistGrid(vlistID1, index);
@@ -1013,23 +1011,23 @@ get_selboxInfo(int vlistID1, int vlistID2, bool operIndexBox)
 }
 
 static std::vector<bool>
-get_processVars(int vlistID1, const std::vector<SelboxInfo> &selboxInfo)
+get_processVars(const VarList &varList, const std::vector<SelboxInfo> &selboxInfo)
 {
-  auto nvars = vlistNvars(vlistID1);
+  auto numVars = varList.numVars();
 
-  std::vector<bool> processVars(nvars, false);
+  std::vector<bool> processVars(numVars, false);
 
   int varID;
   for (const auto &sb : selboxInfo)
     {
-      for (varID = 0; varID < nvars; ++varID)
-        if (sb.gridID1 == vlistInqVarGrid(vlistID1, varID)) processVars[varID] = true;
+      for (varID = 0; varID < numVars; ++varID)
+        if (sb.gridID1 == varList.vars[varID].gridID) processVars[varID] = true;
     }
 
-  for (varID = 0; varID < nvars; ++varID)
+  for (varID = 0; varID < numVars; ++varID)
     if (processVars[varID]) break;
 
-  if (varID >= nvars) cdo_abort("No processable variable found!");
+  if (varID >= numVars) cdo_abort("No processable variable found!");
 
   return processVars;
 }
@@ -1068,9 +1066,8 @@ public:
   int taxisID1;
   int taxisID2;
 
-  Field field1, field2;
-
-  VarList varList1, varList2;
+  VarList varList1;
+  VarList varList2;
 
   int vlistID2;
   int operatorID;
@@ -1080,7 +1077,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
     SELLONLATBOX = module.get_id("sellonlatbox");
     SELINDEXBOX = module.get_id("selindexbox");
@@ -1100,18 +1097,20 @@ public:
 
     selboxInfo = get_selboxInfo(vlistID1, vlistID2, (operatorID == SELINDEXBOX));
 
-    processVars = get_processVars(vlistID1, selboxInfo);
+    varList1 = VarList(vlistID1);
+    varList2 = VarList(vlistID2);
 
-    varList_init(varList1, vlistID1);
-    varList_init(varList2, vlistID2);
+    processVars = get_processVars(varList1, selboxInfo);
 
     streamID2 = cdo_open_write(1);
     cdo_def_vlist(streamID2, vlistID2);
   }
 
   void
-  run()
+  run() override
   {
+    Field field1, field2;
+
     int tsID = 0;
     while (true)
       {
@@ -1125,16 +1124,16 @@ public:
           {
             int varID, levelID;
             cdo_inq_record(streamID1, &varID, &levelID);
-            field1.init(varList1[varID]);
+            field1.init(varList1.vars[varID]);
             cdo_read_record(streamID1, field1);
 
             cdo_def_record(streamID2, varID, levelID);
 
             if (processVars[varID])
               {
-                field2.init(varList2[varID]);
+                field2.init(varList2.vars[varID]);
 
-                const auto &sb = selboxInfo[get_grid_index(varList1[varID].gridID, selboxInfo)];
+                const auto &sb = selboxInfo[get_grid_index(varList1.vars[varID].gridID, selboxInfo)];
 
                 if (operatorID == SELLONLATBOX && (sb.gridtype == GRID_UNSTRUCTURED || is_healpix_grid(sb.gridID1)))
                   window_cell(field1, field2, sb.cellidx);
@@ -1153,7 +1152,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
diff --git a/src/Select.cc b/src/Select.cc
index 2d7d88cc48636267c15db44fc1e7976c316b9ec6..5499768a3ed72913da15ee34853ba1f6def24223 100644
--- a/src/Select.cc
+++ b/src/Select.cc
@@ -15,7 +15,6 @@
 
 #include "cdo_options.h"
 #include "process_int.h"
-#include "cdo_vlist.h"
 #include "cdo_zaxis.h"
 #include "datetime.h"
 #include "sellist.h"
@@ -42,22 +41,21 @@ dom_to_string(CdiDate date)
 }
 
 static void
-write_const_vars(CdoStreamID streamID2, const VarList &varList2, int nvars, Varray2D<double> &vardata2)
+write_const_vars(CdoStreamID streamID2, const VarList &varList, Varray2D<double> &varsData2)
 {
-  for (int varID2 = 0; varID2 < nvars; ++varID2)
+  for (const auto &var : varList.vars)
     {
-      if (vardata2[varID2].size())
+      if (varsData2[var.ID].size())
         {
-          const auto &var = varList2[varID2];
           for (int levelID2 = 0; levelID2 < var.nlevels; ++levelID2)
             {
-              auto pdata = &vardata2[varID2][var.gridsize * levelID2];
+              auto pdata = &varsData2[var.ID][var.gridsize * levelID2];
               auto numMissVals = array_num_mv(var.gridsize, pdata, var.missval);
-              cdo_def_record(streamID2, varID2, levelID2);
+              cdo_def_record(streamID2, var.ID, levelID2);
               cdo_write_record(streamID2, pdata, numMissVals);
             }
-          vardata2[varID2].clear();
-          vardata2[varID2].shrink_to_fit();
+          varsData2[var.ID].clear();
+          varsData2[var.ID].shrink_to_fit();
         }
     }
 }
@@ -97,11 +95,10 @@ eval_timestepmask(const char *maskfile, KVList &kvlist)
 static bool
 has_selected_params(const VarList &varList, int vlistID)
 {
-  for (int varID = 0, numVars = varList.size(); varID < numVars; ++varID)
+  for (auto &var : varList.vars)
     {
-      const auto &var = varList[varID];
       for (int levelID = 0; levelID < var.nlevels; ++levelID)
-        if (vlistInqFlag(vlistID, varID, levelID) == true) return true;
+        if (vlistInqFlag(vlistID, var.ID, levelID) == true) return true;
     }
 
   return false;
@@ -110,10 +107,9 @@ has_selected_params(const VarList &varList, int vlistID)
 static bool
 has_const_vars(const VarList &varList, const std::vector<bool> &processVars)
 {
-  for (int varID = 0, numVars = varList.size(); varID < numVars; ++varID)
+  for (auto &var : varList.vars)
     {
-      const auto &var = varList[varID];
-      if (processVars[varID] && var.isConstant) return true;
+      if (processVars[var.ID] && var.isConstant) return true;
     }
 
   return false;
@@ -153,7 +149,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
     SELECT = module.get_id("select");
     DELETE = module.get_id("delete");
@@ -182,9 +178,9 @@ public:
   }
 
   void
-  run()
+  run() override
   {
-    int nvars2 = 0;
+    int numVars2 = 0;
     bool hasConstVars = true;
     char paramstr[32];
     char gname[CDI_MAX_NAME];
@@ -197,7 +193,7 @@ public:
     int numStepsOut = 0;
     bool doTimeSel = false;
     std::vector<bool> processVars;
-    Varray2D<double> vardata2;
+    Varray2D<double> varsData2;
 
     SelectInfo selInfo(kvlist);
 
@@ -242,14 +238,14 @@ public:
 
     DateTimeList dtlist;
 
-    if (!Options::cdoVerbose && nfiles > 1) progress::init();
+    cdo::Progress progress;
 
     int tsmax = -1;
 
     timestep = 0;
     for (int indf = 0; indf < nfiles; ++indf)
       {
-        if (!Options::cdoVerbose && nfiles > 1) progress::update(0, 1, (indf + 1.) / nfiles);
+        if (nfiles > 1) progress.update((indf + 1.) / nfiles);
         if (Options::cdoVerbose) cdo_print("Process file: %s", cdo_get_stream_name(indf));
 
         auto streamID1 = cdo_open_read(indf);
@@ -257,8 +253,8 @@ public:
         auto vlistID1 = cdo_stream_inq_vlist(streamID1);
         auto taxisID1 = vlistInqTaxis(vlistID1);
 
-        VarList varList1, varList2;
-        varList_init(varList1, vlistID1);
+        VarList varList1(vlistID1);
+        VarList varList2;
 
         auto copyConstVars = false;
 
@@ -269,15 +265,15 @@ public:
             // vlistID0 = vlistDuplicate(vlistID1);
 
             vlistClearFlag(vlistID1);
-            int nvars = vlistNvars(vlistID1);
-            processVars.resize(nvars);
+            int numVars = varList1.numVars();
+            processVars.resize(numVars);
 
             if (operatorID == DELETE)
               {
                 xresult = false;
-                for (int varID = 0; varID < nvars; ++varID)
+                for (int varID = 0; varID < numVars; ++varID)
                   {
-                    const auto &var = varList1[varID];
+                    const auto &var = varList1.vars[varID];
                     for (int levelID = 0; levelID < var.nlevels; ++levelID) vlistDefFlag(vlistID1, varID, levelID, true);
                   }
               }
@@ -293,9 +289,9 @@ public:
                         || SELINFO_NVAL(timestep_of_year) || SELINFO_NVAL(timestep) || SELINFO_NVAL(year) || SELINFO_NVAL(month)
                         || SELINFO_NVAL(day) || SELINFO_NVAL(hour) || SELINFO_NVAL(minute) || SELINFO_NVAL(dom);
 
-            for (int varID = 0; varID < nvars; ++varID)
+            for (int varID = 0; varID < numVars; ++varID)
               {
-                const auto &var = varList1[varID];
+                const auto &var = varList1.vars[varID];
 
                 code = var.code;
 
@@ -330,7 +326,7 @@ public:
 
                 if (SELINFO_NVAL(steptype) && !found_stype)
                   {
-                    steptype = cdo::get_steptype_name(var.tsteptype);
+                    steptype = cdo::get_steptype_name(var.stepType);
                     found_stype = SELINFO_CHECK(steptype);
                   }
 
@@ -365,11 +361,11 @@ public:
                   }
               }
 
-            for (int varID = 0; varID < nvars; ++varID)
+            for (int varID = 0; varID < numVars; ++varID)
               {
                 if (processVars[varID])
                   {
-                    const auto &var = varList1[varID];
+                    const auto &var = varList1.vars[varID];
                     if (zaxisInqType(var.zaxisID) == ZAXIS_HYBRID)
                       {
                         auto psvarid = varList_get_psvarid(varList1, var.zaxisID);
@@ -378,11 +374,11 @@ public:
                   }
               }
 
-            for (int varID = 0; varID < nvars; ++varID)
+            for (int varID = 0; varID < numVars; ++varID)
               {
                 if (processVars[varID])
                   {
-                    const auto &var = varList1[varID];
+                    const auto &var = varList1.vars[varID];
                     for (int levelID = 0; levelID < var.nlevels; ++levelID)
                       {
                         levidx = levelID + 1;
@@ -436,10 +432,10 @@ public:
                   {
                     copyConstVars = true;
 
-                    for (int varID = 0; varID < nvars; ++varID)
+                    for (int varID = 0; varID < numVars; ++varID)
                       {
                         processVars[varID] = true;
-                        const auto &var = varList1[varID];
+                        const auto &var = varList1.vars[varID];
                         for (int levelID = 0; levelID < var.nlevels; ++levelID) vlistDefFlag(vlistID1, varID, levelID, true);
                       }
                   }
@@ -449,9 +445,9 @@ public:
             // if (Options::cdoVerbose) vlistPrint(vlistID1);
 
             vlistID0 = vlistDuplicate(vlistID1);
-            for (int varID = 0; varID < nvars; ++varID)
+            for (int varID = 0; varID < numVars; ++varID)
               {
-                const auto &var = varList1[varID];
+                const auto &var = varList1.vars[varID];
                 for (int levelID = 0; levelID < var.nlevels; ++levelID)
                   vlistDefFlag(vlistID0, varID, levelID, vlistInqFlag(vlistID1, varID, levelID));
               }
@@ -461,17 +457,16 @@ public:
             vlistID2 = vlistCreate();
             cdo_vlist_copy_flag(vlistID2, vlistID0);
 
-            varList_init(varList2, vlistID2);
+            varList2 = VarList(vlistID2);
 
             // if (Options::cdoVerbose) vlistPrint(vlistID2);
 
             taxisID2 = taxisDuplicate(taxisID1);
-
             auto ntsteps = vlistNtsteps(vlistID1);
 
-            nvars2 = vlistNvars(vlistID2);
+            numVars2 = varList2.numVars();
 
-            if (ntsteps == 1 && nfiles == 1 && varList_numVaryingVars(varList2) == 0) ntsteps = 0;
+            if (ntsteps == 1 && nfiles == 1 && varList2.numVaryingVars() == 0) ntsteps = 0;
 
             numStepsOut = (nfiles == 1 && doTimeSel == false) ? ntsteps : -1;
             if (operatorID == SELECT && SELINFO_NVAL(timestep) > 0) numStepsOut = SELINFO_NVAL(timestep);
@@ -479,7 +474,7 @@ public:
             if (numStepsOut == 0 && nfiles > 1)
               {
                 hasConstVars = false;
-                for (int varID = 0; varID < nvars2; ++varID) vlistDefVarTimetype(vlistID2, varID, TIME_VARYING);
+                for (int varID = 0; varID < numVars2; ++varID) vlistDefVarTimetype(vlistID2, varID, TIME_VARYING);
               }
 
             auto nsel = SELINFO_NVAL(timestep);
@@ -523,15 +518,15 @@ 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 { varList_compare(VarList(vlistID0), varList1); }
 
-        if (nvars2 == 0)
+        if (numVars2 == 0)
           {
             cdo_warning("No variable selected!");
             return;
           }
 
-        if (copyConstVars) vardata2.resize(nvars2);
+        if (copyConstVars) varsData2.resize(numVars2);
 
         auto stopReading = false;
         int tsID1 = 0;
@@ -644,18 +639,17 @@ public:
 
                 for (int recID = 0; recID < nrecs; ++recID)
                   {
-                    int varID, levelID;
-                    cdo_inq_record(streamID1, &varID, &levelID);
+                    auto [varID, levelID] = cdo_inq_record(streamID1);
                     if (vlistInqFlag(vlistID0, varID, levelID) == true)
                       {
-                        const auto &var = varList1[varID];
+                        const auto &var = varList1.vars[varID];
 
                         if (hasConstVars && tsID2 > 0 && tsID1 == 0)
                           if (var.isConstant) continue;
 
                         auto varID2 = vlistFindVar(vlistID2, varID);
                         auto levelID2 = vlistFindLevel(vlistID2, varID, levelID);
-                        if (copyConstVars && tsID2 == 0) write_const_vars(streamID2, varList2, varID2, vardata2);
+                        if (copyConstVars && tsID2 == 0) write_const_vars(streamID2, varList2, varsData2);
 
                         cdo_def_record(streamID2, varID2, levelID2);
                         if (dataIsUnchanged) { cdo_copy_record(streamID2, streamID1); }
@@ -668,7 +662,7 @@ public:
                       }
                   }
 
-                if (copyConstVars && tsID2 == 0) write_const_vars(streamID2, varList2, nvars2, vardata2);
+                if (copyConstVars && tsID2 == 0) write_const_vars(streamID2, varList2, varsData2);
 
                 tsID2++;
               }
@@ -681,13 +675,13 @@ public:
                     if (vlistInqFlag(vlistID0, varID, levelID) == true)
                       {
                         auto varID2 = vlistFindVar(vlistID2, varID);
-                        const auto &var = varList2[varID2];
+                        const auto &var = varList2.vars[varID2];
                         if (var.isConstant)
                           {
                             auto levelID2 = vlistFindLevel(vlistID2, varID, levelID);
-                            if (levelID == 0) vardata2[varID2].resize(var.gridsize * var.nlevels);
+                            if (levelID == 0) varsData2[varID2].resize(var.gridsize * var.nlevels);
                             size_t numMissVals;
-                            cdo_read_record(streamID1, &vardata2[varID2][var.gridsize * levelID2], &numMissVals);
+                            cdo_read_record(streamID1, &varsData2[varID2][var.gridsize * levelID2], &numMissVals);
                           }
                       }
                   }
@@ -701,8 +695,6 @@ public:
         if (stopReading) break;
       }
 
-    if (!Options::cdoVerbose && nfiles > 1) progress::update(0, 1, 1);
-
     SELINFO_CHECK_FLAG(timestep_of_year);
     SELINFO_CHECK_FLAG(timestep);
     SELINFO_CHECK_FLAG(year);
@@ -718,7 +710,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     if (streamID2 != CDO_STREAM_UNDEF) cdo_stream_close(streamID2);
 
diff --git a/src/Selgridcell.cc b/src/Selgridcell.cc
index 7480c4ac591bdb616a610bdbb214bfd1b765defb..1648055a3a4b6ed8f6f00a0fd77060da89a66d4c 100644
--- a/src/Selgridcell.cc
+++ b/src/Selgridcell.cc
@@ -127,15 +127,13 @@ public:
   VarList varList1;
   VarList varList2;
 
-  Field field1, field2;
-
   std::vector<long> cellidx;
   std::vector<sindex_t> sindex;
   std::vector<bool> processVars;
 
 public:
   void
-  init()
+  init() override
   {
     int nind = 0;
     std::vector<int> indarr;
@@ -216,12 +214,12 @@ public:
     taxisID2 = taxisDuplicate(taxisID1);
     vlistDefTaxis(vlistID2, taxisID2);
 
-    varList_init(varList1, vlistID1);
+    varList1 = VarList(vlistID1);
 
     auto nvars = vlistNvars(vlistID1);
     processVars = std::vector<bool>(nvars, false);
 
-    auto ngrids = vlistNgrids(vlistID1);
+    auto ngrids = vlistNumGrids(vlistID1);
     sindex = std::vector<sindex_t>(ngrids);
 
     ncells = nind;
@@ -270,7 +268,7 @@ public:
         vlistChangeGridIndex(vlistID2, index, gridID2);
 
         for (int varID = 0; varID < nvars; ++varID)
-          if (gridID1 == varList1[varID].gridID) processVars[varID] = true;
+          if (gridID1 == varList1.vars[varID].gridID) processVars[varID] = true;
       }
 
     {
@@ -281,15 +279,17 @@ public:
       if (varID >= nvars) cdo_abort("No variables selected!");
     }
 
-    varList_init(varList2, vlistID2);
+    varList2 = VarList(vlistID2);
 
     streamID2 = cdo_open_write(1);
     cdo_def_vlist(streamID2, vlistID2);
   }
 
   void
-  run()
+  run() override
   {
+    Field field1, field2;
+
     int tsID = 0;
     while (true)
       {
@@ -303,14 +303,14 @@ public:
           {
             int varID, levelID;
             cdo_inq_record(streamID1, &varID, &levelID);
-            field1.init(varList1[varID]);
+            field1.init(varList1.vars[varID]);
             cdo_read_record(streamID1, field1);
 
             cdo_def_record(streamID2, varID, levelID);
 
             if (processVars[varID])
               {
-                field2.init(varList2[varID]);
+                field2.init(varList1.vars[varID]);
                 select_index(field1, field2, ncells, cellidx);
 
                 if (field1.numMissVals) field2.numMissVals = field_num_mv(field2);
@@ -325,7 +325,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
diff --git a/src/Selmulti.cc b/src/Selmulti.cc
index 92dee562348987d49fd6ece4ffcb1507bee9042d..8cbd51e7b485a357e93f49d8f4ef4e6098e3f4f4 100644
--- a/src/Selmulti.cc
+++ b/src/Selmulti.cc
@@ -177,7 +177,7 @@ public:
   int vlistID2;
 
   int operatorID;
-  int nvars;
+  int numVars;
   size_t gridsizemax;
 
   bool lcopy = false;
@@ -187,7 +187,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
     SELMULTI = module.get_id("selmulti");
     DELMULTI = module.get_id("delmulti");
@@ -230,17 +230,17 @@ public:
 
     vlistID1 = cdo_stream_inq_vlist(streamID1);
 
-    varList_init(varList1, vlistID1);
+    varList1 = VarList(vlistID1);
 
     vlistClearFlag(vlistID1);
-    nvars = vlistNvars(vlistID1);
+    numVars = varList1.numVars();
 
-    Debug(cdoDebugExt, " Total number of variables: %d", nvars);
+    Debug(cdoDebugExt, " Total number of variables: %d", numVars);
 
-    for (int varID = 0; varID < nvars; ++varID)
+    for (int varID = 0; varID < numVars; ++varID)
       {
-        const auto &var = varList1[varID];
-        const auto ltype = zaxis_to_ltype(var.zaxisID);
+        const auto &var = varList1.vars[varID];
+        auto ltype = zaxis_to_ltype(var.zaxisID);
 
         for (int levelID = 0; levelID < var.nlevels; ++levelID)
           {
@@ -262,9 +262,9 @@ public:
                 // (int)level, nvars, varID);
                 // Note: When the list is Empty then function
                 // checkListContainsInt() also returns true !
-                const auto selcode = checkListContainsInt(var.code, tuplerec->codeLST, tuplerec->ncodes);
-                const auto selltype = checkListContainsInt(ltype, tuplerec->levelTypeLST, tuplerec->nlevelTypes);
-                const auto sellevel = checkListContainsInt((int) level, tuplerec->levelLST, tuplerec->nlevels);
+                auto selcode = checkListContainsInt(var.code, tuplerec->codeLST, tuplerec->ncodes);
+                auto selltype = checkListContainsInt(ltype, tuplerec->levelTypeLST, tuplerec->nlevelTypes);
+                auto sellevel = checkListContainsInt((int) level, tuplerec->levelLST, tuplerec->nlevels);
                 if (selcode && selltype && sellevel)
                   {
                     if (operatorID == SELMULTI)
@@ -370,11 +370,11 @@ public:
     vlistDefNtsteps(vlistID2, vlistNtsteps(vlistID1));
 
     {
-      nvars = vlistNvars(vlistID2);
+      numVars = vlistNvars(vlistID2);
       int varID;
-      for (varID = 0; varID < nvars; ++varID)
+      for (varID = 0; varID < numVars; ++varID)
         if (vlistInqVarTimetype(vlistID2, varID) != TIME_CONSTANT) break;
-      if (varID == nvars) vlistDefNtsteps(vlistID2, 0);
+      if (varID == numVars) vlistDefNtsteps(vlistID2, 0);
     }
 
     taxisID1 = vlistInqTaxis(vlistID1);
@@ -388,13 +388,14 @@ public:
     if (vlistNumber(vlistID1) != CDI_REAL) gridsizemax *= 2;
     array = Varray<double>(gridsizemax);
   }
+
   void
-  run()
+  run() override
   {
     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);
@@ -404,24 +405,24 @@ public:
           {
             int varID, levelID;
             cdo_inq_record(streamID1, &varID, &levelID);
-            const auto &var = varList1[varID];
+            const auto &var = varList1.vars[varID];
 
             if (vlistInqFlag(vlistID1, varID, levelID) == true)
               {
                 int simpleMath = 0;  // 1:  simple array arithmetics ( *,+), 0: do nothing
                 double scale = 1.0;
                 double offset = 0.0;
-                const auto code = var.code;
-                const auto level = zaxisInqLevel(var.zaxisID, levelID);
-                const auto ltype = zaxis_to_ltype(var.zaxisID);
+                auto code = var.code;
+                auto level = zaxisInqLevel(var.zaxisID, levelID);
+                auto ltype = zaxis_to_ltype(var.zaxisID);
                 for (int ii = 0; ii < NUMTUPLES; ++ii)
                   {
                     TUPLEREC *tuplerec = getSelTuple(ii);
                     // Note: When the list is Empty then function
                     // checkListContainsInt() also returns true !
-                    const auto selcode = checkListContainsInt(code, tuplerec->codeLST, tuplerec->ncodes);
-                    const auto selltype = checkListContainsInt(ltype, tuplerec->levelTypeLST, tuplerec->nlevelTypes);
-                    const auto sellevel = checkListContainsInt((int) level, tuplerec->levelLST, tuplerec->nlevels);
+                    auto selcode = checkListContainsInt(code, tuplerec->codeLST, tuplerec->ncodes);
+                    auto selltype = checkListContainsInt(ltype, tuplerec->levelTypeLST, tuplerec->nlevelTypes);
+                    auto sellevel = checkListContainsInt((int) level, tuplerec->levelLST, tuplerec->nlevels);
                     lcopy = true;
                     if (selcode && selltype && sellevel)
                       {
@@ -430,8 +431,8 @@ public:
                             if (cdoDebugExt)
                               cdo_print(" Processing: (code %d, ltype %d, level %d);  nvars=%d, varID=%d => (selcode %d, selltype "
                                         "%d, sellevel %d) => change (%d,%d,%d)",
-                                        code, ltype, (int) level, nvars, varID, selcode, selltype, sellevel, tuplerec->changedCode,
-                                        tuplerec->changedLevelType, tuplerec->changedLevel);
+                                        code, ltype, (int) level, numVars, varID, selcode, selltype, sellevel,
+                                        tuplerec->changedCode, tuplerec->changedLevelType, tuplerec->changedLevel);
                             if ((tuplerec->changedCode == -1) && (tuplerec->changedLevelType == -1)
                                 && (tuplerec->changedLevel == -1))
                               cdo_print(" WARNING: Cannot CHANGE identification!");
@@ -450,7 +451,7 @@ public:
                             if (cdoDebugExt)
                               cdo_print(" Processing: (code %d, ltype %d, level %d);  nvars=%d, varID=%d => (selcode %d, "
                                         "selltype %d, sellevel %d)",
-                                        code, ltype, (int) level, nvars, varID, selcode, selltype, sellevel);
+                                        code, ltype, (int) level, numVars, varID, selcode, selltype, sellevel);
                           }
                         simpleMath = tuplerec->simpleMath;  // 1:  simple array arithmetics ( *,+),
                                                             // 0: do nothing
@@ -464,8 +465,8 @@ public:
                       }
                   }  // end of for (ii=0; ii<NUMTUPLES ..
 
-                const auto varID2 = vlistFindVar(vlistID2, varID);
-                const auto levelID2 = vlistFindLevel(vlistID2, varID, levelID);
+                auto varID2 = vlistFindVar(vlistID2, varID);
+                auto levelID2 = vlistFindLevel(vlistID2, varID, levelID);
 
                 // tijdelijk PATCH M.K.
                 if ((varID2 == -1) || (levelID2 == -1))
@@ -518,8 +519,9 @@ public:
         tsID++;
       }  // end while
   }
+
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID1);
     cdo_stream_close(streamID2);
diff --git a/src/Seloperator.cc b/src/Seloperator.cc
index a0f5e3cbf5efc514f151797343a4f24af6e9a77b..e3556bb4db2d616716d7003781a947bed9121873 100644
--- a/src/Seloperator.cc
+++ b/src/Seloperator.cc
@@ -18,7 +18,7 @@ public:
   using Process::Process;
   inline static CdoModule module = {
     .name = "Seloperator",
-    .operators = { { "seloperator"} },
+    .operators = { { "seloperator" } },
     .aliases = {},
     .mode = EXPOSED,     // Module mode: 0:intern 1:extern
     .number = CDI_REAL,  // Allowed number type
@@ -35,16 +35,13 @@ public:
   int taxisID1;
   int taxisID2;
 
-  int varID;
-
-  Field field;
   VarList varList1;
 
   bool dataIsUnchanged;
 
 public:
   void
-  init()
+  init() override
   {
     bool selfound = false;
 
@@ -61,7 +58,7 @@ public:
     vlistID1 = cdo_stream_inq_vlist(streamID1);
 
     auto nvars = vlistNvars(vlistID1);
-    for (varID = 0; varID < nvars; ++varID)
+    for (int varID = 0; varID < nvars; ++varID)
       {
         auto code = vlistInqVarCode(vlistID1, varID);
         auto zaxisID = vlistInqVarZaxis(vlistID1, varID);
@@ -96,14 +93,15 @@ public:
     streamID2 = cdo_open_write(1);
     cdo_def_vlist(streamID2, vlistID2);
 
-    varList_init(varList1, vlistID1);
+    varList1 = VarList(vlistID1);
   }
 
   void
-  run()
+  run() override
   {
+    Field field;
+
     int tsID = 0;
-    int levelID;
     while (true)
       {
         auto nrecs = cdo_stream_inq_timestep(streamID1, tsID);
@@ -114,7 +112,7 @@ public:
 
         for (int recID = 0; recID < nrecs; ++recID)
           {
-            cdo_inq_record(streamID1, &varID, &levelID);
+            auto [varID, levelID] = cdo_inq_record(streamID1);
             if (vlistInqFlag(vlistID1, varID, levelID) == true)
               {
                 auto varID2 = vlistFindVar(vlistID2, varID);
@@ -124,7 +122,7 @@ public:
                 if (dataIsUnchanged) { cdo_copy_record(streamID2, streamID1); }
                 else
                   {
-                    field.init(varList1[varID]);
+                    field.init(varList1.vars[varID]);
                     cdo_read_record(streamID1, field);
                     cdo_write_record(streamID2, field);
                   }
@@ -136,7 +134,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID1);
     cdo_stream_close(streamID2);
diff --git a/src/Selrec.cc b/src/Selrec.cc
index 4db1886229bcc372b1e21118cfd5938f232b4419..f8ee37081c2f0c225c544b0de89bb478ed9cf2ee 100644
--- a/src/Selrec.cc
+++ b/src/Selrec.cc
@@ -41,7 +41,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
 
     operator_input_arg("records");
@@ -62,8 +62,8 @@ public:
         || filetype == CDI_FILETYPE_NC5 || filetype == CDI_FILETYPE_NCZARR)
       cdo_abort("This operator does not work on NetCDF data!");
 
-    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);
@@ -74,13 +74,13 @@ public:
   }
 
   void
-  run()
+  run() override
   {
     int recordID = 0;
     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);
@@ -108,7 +108,7 @@ public:
       }
   }
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
diff --git a/src/Selregion.cc b/src/Selregion.cc
index 0f7f077304537fd192d3d3fa6ab6428a82f91975..b432ee0c640935f0fdda6eec3cc34a7af5b41858 100644
--- a/src/Selregion.cc
+++ b/src/Selregion.cc
@@ -249,16 +249,16 @@ public:
   int taxisID1;
   int taxisID2;
 
-  int ngrids;
+  int numGrids;
 
-  Field field1, field2;
-  VarList varList1, varList2;
+  VarList varList1;
+  VarList varList2;
   std::vector<bool> varIDs;
   std::vector<RegionInfo> regions;
 
 public:
   void
-  init()
+  init() override
   {
     SELREGION = module.get_id("selregion");
     SELCIRCLE = module.get_id("selcircle");
@@ -272,15 +272,17 @@ public:
     auto vlistID1 = cdo_stream_inq_vlist(streamID1);
     auto vlistID2 = vlistDuplicate(vlistID1);
 
+    varList1 = VarList(vlistID1);
+
     taxisID1 = vlistInqTaxis(vlistID1);
     taxisID2 = taxisDuplicate(taxisID1);
     vlistDefTaxis(vlistID2, taxisID2);
 
-    auto nvars = vlistNvars(vlistID1);
-    varIDs = std::vector<bool>(nvars, false);
+    auto numVars = varList1.numVars();
+    varIDs = std::vector<bool>(numVars, false);
 
-    ngrids = vlistNgrids(vlistID1);
-    regions = std::vector<RegionInfo>(ngrids);
+    numGrids = vlistNumGrids(vlistID1);
+    regions = std::vector<RegionInfo>(numGrids);
 
     int numFiles = 0;
     CirclePoint cpoint;
@@ -306,7 +308,7 @@ public:
         cpoint.lat *= DEG2RAD;
       }
 
-    for (int index = 0; index < ngrids; ++index)
+    for (int index = 0; index < numGrids; ++index)
       {
         auto &region = regions[index];
         auto gridID1 = vlistGrid(vlistID1, index);
@@ -334,23 +336,24 @@ public:
 
                 vlistChangeGridIndex(vlistID2, index, gridID2);
 
-                for (int varID = 0; varID < nvars; ++varID)
-                  if (gridID1 == vlistInqVarGrid(vlistID1, varID)) varIDs[varID] = true;
+                for (const auto &var : varList1.vars)
+                  if (gridID1 == var.gridID) varIDs[var.ID] = true;
               }
           }
         else { cdo_abort("Unsupported grid type: %s", gridNamePtr(gridtype)); }
       }
 
-    varList_init(varList1, vlistID1);
-    varList_init(varList2, vlistID2);
+    varList2 = VarList(vlistID2);
 
     streamID2 = cdo_open_write(1);
     cdo_def_vlist(streamID2, vlistID2);
   }
 
   void
-  run()
+  run() override
   {
+    Field field1, field2;
+
     int tsID = 0;
     while (true)
       {
@@ -364,7 +367,7 @@ public:
           {
             int varID, levelID;
             cdo_inq_record(streamID1, &varID, &levelID);
-            const auto &var = varList1[varID];
+            const auto &var = varList1.vars[varID];
             field1.init(var);
             cdo_read_record(streamID1, field1);
 
@@ -374,11 +377,11 @@ public:
               {
                 auto gridID1 = var.gridID;
                 int index;
-                for (index = 0; index < ngrids; ++index)
+                for (index = 0; index < numGrids; ++index)
                   if (gridID1 == regions[index].gridID1) break;
-                if (index == ngrids) cdo_abort("Internal problem, grid not found!");
+                if (index == numGrids) cdo_abort("Internal problem, grid not found!");
 
-                field2.init(varList2[varID]);
+                field2.init(varList2.vars[varID]);
                 window_cell(field1, field2, regions[index].cellidx);
 
                 if (field1.numMissVals) field_num_mv(field2);
@@ -393,7 +396,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
diff --git a/src/Selsurface.cc b/src/Selsurface.cc
index 40eea4883f9d1c7ceb61358635bb72cdc25ebabf..ac3b0cad42a0e640772deef49dd52dd691d8491f 100644
--- a/src/Selsurface.cc
+++ b/src/Selsurface.cc
@@ -89,7 +89,7 @@ layer_value_min_kernel(int nlevels, size_t gridsize, T missval, const Varray<con
       for (int k = 0; k < nlevels; ++k)
         {
           auto val = data3D[k][i];
-          if (!dbl_is_equal(val, missval))
+          if (dbl_is_not_equal(val, missval))
             {
               data2D[i] = val;
               break;
@@ -134,7 +134,7 @@ layer_value_max_kernel(int nlevels, size_t gridsize, T missval, const Varray<con
       for (int k = nlevels - 1; k >= 0; --k)
         {
           auto val = data3D[k][i];
-          if (!dbl_is_equal(val, missval))
+          if (dbl_is_not_equal(val, missval))
             {
               data2D[i] = val;
               break;
@@ -171,9 +171,11 @@ public:
   using Process::Process;
   inline static CdoModule module = {
     .name = "Selsurface",
+    // clang-format off
     .operators = { { "isosurface", SelsurfaceHelp },
                    { "bottomvalue", SelsurfaceHelp },
                    { "topvalue", SelsurfaceHelp } },
+    // clang-format on
     .aliases = {},
     .mode = EXPOSED,     // Module mode: 0:intern 1:extern
     .number = CDI_REAL,  // Allowed number type
@@ -200,7 +202,7 @@ public:
 
   VarList varList1;
   Field field2;
-  FieldVector2D vars1;
+  FieldVector2D varsData1;
 
   int nlevels;
   Varray<double> levels;
@@ -210,14 +212,11 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
-
-    // clang-format off
-ISOSURFACE = module.get_id("isosurface");
-BOTTOMVALUE = module.get_id("bottomvalue");
-TOPVALUE = module.get_id("topvalue");
-    // clang-format on
+    ISOSURFACE = module.get_id("isosurface");
+    BOTTOMVALUE = module.get_id("bottomvalue");
+    TOPVALUE = module.get_id("topvalue");
 
     operatorID = cdo_operator_id();
 
@@ -239,7 +238,7 @@ TOPVALUE = module.get_id("topvalue");
     taxisID2 = taxisDuplicate(taxisID1);
     vlistDefTaxis(vlistID2, taxisID2);
 
-    auto nzaxis = vlistNzaxis(vlistID1);
+    auto nzaxis = vlistNumZaxis(vlistID1);
     for (int i = 0; i < nzaxis; ++i)
       {
         auto zaxisID = vlistZaxis(vlistID1, i);
@@ -267,32 +266,31 @@ TOPVALUE = module.get_id("topvalue");
     streamID2 = cdo_open_write(1);
     cdo_def_vlist(streamID2, vlistID2);
 
-    varList_init(varList1, vlistID1);
+    varList1 = VarList(vlistID1);
 
     nvars = vlistNvars(vlistID1);
     isVar3D = std::vector<bool>(nvars);
     foundVar = std::vector<bool>(nvars);
 
-    fields_from_vlist(vlistID1, vars1);
+    field2D_init(varsData1, varList1);
 
     for (int varID = 0; varID < nvars; ++varID)
       {
-        const auto &var = varList1[varID];
+        const auto &var = varList1.vars[varID];
         isVar3D[varID] = (var.zaxisID == zaxisID1);
       }
   }
 
   void
-  run()
+  run() override
   {
-
     auto bottom_value_func = isReverse ? layer_value_max : layer_value_min;
     auto top_value_func = isReverse ? layer_value_min : layer_value_max;
     if (isPositive && positive_is_down(zaxisID1)) std::swap(bottom_value_func, top_value_func);
+
     int tsID = 0;
     while (true)
       {
-
         auto nrecs = cdo_stream_inq_timestep(streamID1, tsID);
         if (nrecs == 0) break;
 
@@ -305,8 +303,8 @@ TOPVALUE = module.get_id("topvalue");
           {
             int varID, levelID;
             cdo_inq_record(streamID1, &varID, &levelID);
-            const auto &var = varList1[varID];
-            auto &field1 = vars1[varID][levelID];
+            const auto &var = varList1.vars[varID];
+            auto &field1 = varsData1[varID][levelID];
             field1.init(var);
             cdo_read_record(streamID1, field1);
             foundVar[varID] = true;
@@ -316,14 +314,14 @@ TOPVALUE = module.get_id("topvalue");
           {
             if (foundVar[varID])
               {
-                const auto &var = varList1[varID];
+                const auto &var = varList1.vars[varID];
                 if (isVar3D[varID])
                   {
                     field2.init(var);
                     // clang-format off
-                  if      (operatorID == ISOSURFACE)  isosurface(isoval, nlevels, levels, vars1[varID], field2);
-                  else if (operatorID == BOTTOMVALUE) bottom_value_func(nlevels, vars1[varID], field2);
-                  else if (operatorID == TOPVALUE)    top_value_func(nlevels, vars1[varID], field2);
+                    if      (operatorID == ISOSURFACE)  isosurface(isoval, nlevels, levels, varsData1[varID], field2);
+                    else if (operatorID == BOTTOMVALUE) bottom_value_func(nlevels, varsData1[varID], field2);
+                    else if (operatorID == TOPVALUE)    top_value_func(nlevels, varsData1[varID], field2);
                     // clang-format on
 
                     cdo_def_record(streamID2, varID, 0);
@@ -334,7 +332,7 @@ TOPVALUE = module.get_id("topvalue");
                     for (int levelID = 0; levelID < var.nlevels; ++levelID)
                       {
                         cdo_def_record(streamID2, varID, levelID);
-                        cdo_write_record(streamID2, vars1[varID][levelID]);
+                        cdo_write_record(streamID2, varsData1[varID][levelID]);
                       }
                   }
               }
@@ -343,10 +341,10 @@ TOPVALUE = module.get_id("topvalue");
         tsID++;
       }
   }
+
   void
-  close()
+  close() override
   {
-
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
 
diff --git a/src/Seltime.cc b/src/Seltime.cc
index 4e5a687ee4a4ca0251eff095ededc06859820960..e273d3bd29e158f0386f0718daff64a8fa19bc72 100644
--- a/src/Seltime.cc
+++ b/src/Seltime.cc
@@ -18,13 +18,11 @@
       Seltime    seldate         Select dates
       Seltime    selsmon         Select single month
 */
-#include <cassert>
 #include <cdi.h>
 
 #include "cdo_options.h"
 #include "process_int.h"
 #include "param_conversion.h"
-#include "util_string.h"
 #include "field_functions.h"
 
 static std::vector<int>
@@ -224,13 +222,12 @@ public:
   std::vector<double> fltarr;
 
   VarList varList1;
-  Field field;
-  FieldVector3D vars;
   std::vector<bool> selfound;
+  FieldVector3D varsData;
 
 public:
   void
-  init()
+  init() override
   {
     dataIsUnchanged = data_is_unchanged();
 
@@ -337,7 +334,7 @@ public:
     vlistID1 = cdo_stream_inq_vlist(streamID1);
     vlistID2 = vlistDuplicate(vlistID1);
 
-    varList_init(varList1, vlistID1);
+    varList1 = VarList(vlistID1);
 
     // add support for negative timestep values
     if (operatorID == SELTIMESTEP)
@@ -399,8 +396,8 @@ public:
     if (operatorID != SELDATE)
       for (int i = 0; i < numSel; ++i) iselmax = std::max(iselmax, intarr[i]);
 
-    numVars = varList1.size();
-    haveConstVars = (varList_numConstVars(varList1) > 0);
+    numVars = varList1.numVars();
+    haveConstVars = (varList1.numConstVars() > 0);
 
     lnts1 = (operatorID == SELSMON) && (nts1 > 0);
 
@@ -409,18 +406,18 @@ public:
         if (lnts1) { vDateTimes.resize(nts1); }
         else { nts1 = 1; }
 
-        vars.resize(nts1);
+        varsData.resize(nts1);
 
         for (int tsID = 0; tsID < nts1; ++tsID)
           {
-            fields_from_vlist(vlistID1, vars[tsID]);
+            field2D_init(varsData[tsID], varList1);
 
             for (varID = 0; varID < numVars; ++varID)
               {
-                const auto &var = varList1[varID];
+                const auto &var = varList1.vars[varID];
                 if (lnts1 || var.isConstant)
                   {
-                    for (levelID = 0; levelID < var.nlevels; ++levelID) vars[tsID][varID][levelID].resize(var.gridsize);
+                    for (levelID = 0; levelID < var.nlevels; ++levelID) varsData[tsID][varID][levelID].resize(var.gridsize);
                   }
               }
           }
@@ -431,8 +428,10 @@ public:
   }
 
   void
-  run()
+  run() override
   {
+    Field field;
+
     int indexNext = 0;
     int tsID = 0;
     int tsID2 = 0;
@@ -548,13 +547,13 @@ public:
 
                     for (varID = 0; varID < numVars; ++varID)
                       {
-                        const auto &var = varList1[varID];
+                        const auto &var = varList1.vars[varID];
                         if (var.isConstant && tsID2 > 1) continue;
                         for (levelID = 0; levelID < var.nlevels; ++levelID)
                           {
                             cdo_def_record(streamID2, varID, levelID);
-                            auto single = vars[it][varID][levelID].vec_d.data();
-                            auto numMissVals = vars[it][varID][levelID].numMissVals;
+                            auto single = varsData[it][varID][levelID].vec_d.data();
+                            auto numMissVals = varsData[it][varID][levelID].numMissVals;
                             cdo_write_record(streamID2, single, numMissVals);
                           }
                       }
@@ -578,14 +577,14 @@ public:
                 nts = nts1 - 1;
                 for (varID = 0; varID < numVars; ++varID)
                   {
-                    const auto &var = varList1[varID];
+                    const auto &var = varList1.vars[varID];
                     if (var.isConstant)
                       {
                         for (levelID = 0; levelID < var.nlevels; ++levelID)
                           {
                             cdo_def_record(streamID2, varID, levelID);
-                            auto single = vars[nts][varID][levelID].vec_d.data();
-                            auto numMissVals = vars[nts][varID][levelID].numMissVals;
+                            auto single = varsData[nts][varID][levelID].vec_d.data();
+                            auto numMissVals = varsData[nts][varID][levelID].numMissVals;
                             cdo_write_record(streamID2, single, numMissVals);
                           }
                       }
@@ -599,7 +598,7 @@ public:
                 if (dataIsUnchanged) { cdo_copy_record(streamID2, streamID1); }
                 else
                   {
-                    const auto &var = varList1[varID];
+                    const auto &var = varList1.vars[varID];
                     field.init(var);
                     cdo_read_record(streamID1, field);
                     cdo_write_record(streamID2, field);
@@ -625,12 +624,12 @@ public:
                           vDateTimes[it] = vDateTimes[it + 1];
                           for (varID = 0; varID < numVars; ++varID)
                             {
-                              const auto &var = varList1[varID];
+                              const auto &var = varList1.vars[varID];
                               if (var.isConstant) continue;
                               for (levelID = 0; levelID < var.nlevels; ++levelID)
                                 {
-                                  vars[it][varID][levelID].vec_d = vars[it + 1][varID][levelID].vec_d;
-                                  vars[it][varID][levelID].numMissVals = vars[it + 1][varID][levelID].numMissVals;
+                                  varsData[it][varID][levelID].vec_d = varsData[it + 1][varID][levelID].vec_d;
+                                  varsData[it][varID][levelID].numMissVals = varsData[it + 1][varID][levelID].numMissVals;
                                 }
                             }
                         }
@@ -643,11 +642,11 @@ public:
                 for (int recID = 0; recID < nrecs; ++recID)
                   {
                     cdo_inq_record(streamID1, &varID, &levelID);
-                    const auto &var = varList1[varID];
+                    const auto &var = varList1.vars[varID];
                     if (lnts1 || var.isConstant)
                       {
-                        auto single = vars[nts][varID][levelID].vec_d.data();
-                        cdo_read_record(streamID1, single, &vars[nts][varID][levelID].numMissVals);
+                        auto single = varsData[nts][varID][levelID].vec_d.data();
+                        cdo_read_record(streamID1, single, &varsData[nts][varID][levelID].numMissVals);
                       }
                   }
               }
@@ -709,7 +708,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     vlistDestroy(vlistID2);
   }
diff --git a/src/Selvar.cc b/src/Selvar.cc
index f52d0ed9c7a9bd34acdbac730073efe13369c778..ce3609e66b0411bc1b56cb86efad37fd47b9f36b 100644
--- a/src/Selvar.cc
+++ b/src/Selvar.cc
@@ -27,7 +27,6 @@
 
 #include "cdo_options.h"
 #include "process_int.h"
-#include "cdo_vlist.h"
 #include "cdo_zaxis.h"
 #include "util_wildcards.h"
 #include "cdi_lockedIO.h"
@@ -72,14 +71,13 @@ public:
   int taxisID1;
   int taxisID2;
 
-  Field field;
   VarList varList1;
 
   bool dataIsUnchanged;
 
 public:
   void
-  init()
+  init() override
   {
     int nsel = 0;
     char paramstr[32];
@@ -156,7 +154,7 @@ public:
     auto nvars = vlistNvars(vlistID1);
     std::vector<bool> vars(nvars);
 
-    varList_init(varList1, vlistID1);
+    varList1 = VarList(vlistID1);
 
     if (operatorID == SELGRID && !args_are_numeric && nsel == 1 && argnames[0].starts_with("var="))
       {
@@ -166,7 +164,7 @@ public:
 
         for (int varID = 0; varID < nvars; ++varID)
           {
-            const auto &var = varList1[varID];
+            const auto &var = varList1.vars[varID];
             if (var.name == gridvarname)
               {
                 gridnum = 1 + vlistGridIndex(vlistID1, var.gridID);
@@ -184,7 +182,7 @@ public:
       {
         vars[varID] = doDelete;
 
-        const auto &var = varList1[varID];
+        const auto &var = varList1.vars[varID];
         auto stdname = cdo::inq_key_string(vlistID1, varID, CDI_KEY_STDNAME);
         auto tabnum = tableInqNum(vlistInqVarTable(vlistID1, varID));
         auto grididx = vlistGridIndex(vlistID1, var.gridID);
@@ -256,7 +254,7 @@ public:
       {
         if (vars[varID])
           {
-            const auto &var = varList1[varID];
+            const auto &var = varList1.vars[varID];
             if (zaxisInqType(var.zaxisID) == ZAXIS_HYBRID)
               {
                 auto psvarid = varList_get_psvarid(varList1, var.zaxisID);
@@ -326,8 +324,10 @@ public:
   }
 
   void
-  run()
+  run() override
   {
+    Field field;
+
     int tsID = 0;
     while (true)
       {
@@ -350,7 +350,7 @@ public:
                 if (dataIsUnchanged) { cdo_copy_record(streamID2, streamID1); }
                 else
                   {
-                    const auto &var = varList1[varID];
+                    const auto &var = varList1.vars[varID];
                     field.init(var);
                     cdo_read_record(streamID1, field);
                     cdo_write_record(streamID2, field);
@@ -363,7 +363,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
diff --git a/src/Selyearidx.cc b/src/Selyearidx.cc
index 52e477a13a572c65a043d2eae5136485e578a99f..fe0d51e8b3f618977953875fba989bcda26b8ada 100644
--- a/src/Selyearidx.cc
+++ b/src/Selyearidx.cc
@@ -14,7 +14,6 @@
 #include <cdi.h>
 
 #include "process_int.h"
-#include "cdo_vlist.h"
 #include "field_functions.h"
 
 class Selyearidx : public Process
@@ -23,47 +22,52 @@ public:
   using Process::Process;
   inline static CdoModule module = {
     .name = "Selyearidx",
-    .operators = { { "selyearidx", SelyearidxHelp }, { "seltimeidx", 1, 0, SelyearidxHelp } },
+    // clang-format off
+    .operators = { { "selyearidx", 0, 0, SelyearidxHelp },
+                   { "seltimeidx", 1, 0, SeltimeidxHelp } },
+    // clang-format on
     .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;
+  int vlistID1;
+  int vlistID2;
 
   int taxisID1;
   int taxisID2;
   int taxisID3;
 
-  bool ltime;
-  int maxrecs;
+  bool allSteps;
   size_t gridsizemax;
 
-  FieldVector2D vars1, vars2;
-  std::vector<RecordInfo> recList;
-  VarList varList1, varList2;
+  VarList varList1;
+  VarList varList2;
   Varray<double> array;
 
 public:
   void
-  init()
+  init() override
   {
-
-    ltime = cdo_operator_f1(cdo_operator_id());
+    allSteps = cdo_operator_f1(cdo_operator_id());
 
     streamID1 = cdo_open_read(0);
-    auto vlistID1 = cdo_stream_inq_vlist(streamID1);
+    vlistID1 = cdo_stream_inq_vlist(streamID1);
 
     taxisID1 = vlistInqTaxis(vlistID1);
 
     streamID2 = cdo_open_read(1);
-    auto vlistID2 = cdo_stream_inq_vlist(streamID2);
+    vlistID2 = cdo_stream_inq_vlist(streamID2);
     taxisID2 = vlistInqTaxis(vlistID2);
 
-    vlist_compare(vlistID1, vlistID2, CmpVlist::All);
+    varList1 = VarList(vlistID1);
+    varList2 = VarList(vlistID2);
+    varList_compare(varList1, varList2);
 
     auto vlistID3 = vlistDuplicate(vlistID2);
     taxisID3 = taxisDuplicate(taxisID1);
@@ -72,35 +76,32 @@ public:
     streamID3 = cdo_open_write(2);
     cdo_def_vlist(streamID3, vlistID3);
 
-    varList_init(varList1, vlistID1);
-    varList_init(varList2, vlistID2);
-
-    maxrecs = vlistNrecs(vlistID1);
-
-    recList = std::vector<RecordInfo>(maxrecs);
-
     gridsizemax = vlistGridsizeMax(vlistID1);
 
     array = Varray<double>(gridsizemax);
+  }
 
-    fields_from_vlist(vlistID1, vars1, FIELD_VEC);
-    fields_from_vlist(vlistID2, vars2, FIELD_VEC);
+  void
+  run() override
+  {
+    auto maxRecords = varList1.numRecords();
+    std::vector<RecordInfo> recordList(maxRecords);
+
+    FieldVector2D varsData1, varsData2;
+    field2D_init(varsData1, varList1, FIELD_VEC);
+    field2D_init(varsData2, varList2, FIELD_VEC);
 
-    const int nvars = vlistNvars(vlistID1);
-    for (int varID = 0; varID < nvars; ++varID)
+    auto numVars = varList1.numVars();
+    for (int varID = 0; varID < numVars; ++varID)
       {
-        const auto &var = varList1[varID];
-        auto missval = varList2[varID].missval;
+        const auto &var = varList1.vars[varID];
+        auto missval = varList2.vars[varID].missval;
         for (int levelID = 0; levelID < var.nlevels; ++levelID)
           {
-            for (size_t i = 0; i < var.gridsize; ++i) vars2[varID][levelID].vec_d[i] = missval;
+            for (size_t i = 0; i < var.gridsize; ++i) varsData2[varID][levelID].vec_d[i] = missval;
           }
       }
-  }
 
-  void
-  run()
-  {
     int tsID = 0;
     int tsID2 = 0;
     int tsID3 = 0;
@@ -111,17 +112,16 @@ public:
 
         auto year1 = taxisInqVdatetime(taxisID1).date.year;
 
-        auto lexactdate = (gridsizemax == 1 && nrecs == 1);
+        auto useExactDate = (gridsizemax == 1 && nrecs == 1);
 
         for (int recID = 0; recID < nrecs; ++recID)
           {
             int varID, levelID;
             cdo_inq_record(streamID1, &varID, &levelID);
-            size_t numMissVals;
-            cdo_read_record(streamID1, vars1[varID][levelID].vec_d.data(), &numMissVals);
-            vars1[varID][levelID].numMissVals = numMissVals;
+            auto &var1 = varsData1[varID][levelID];
+            cdo_read_record(streamID1, var1.vec_d.data(), &var1.numMissVals);
 
-            if (tsID == 0) recList[recID].set(varID, levelID);
+            if (tsID == 0) recordList[recID].set(varID, levelID);
           }
 
         int nrecs2;
@@ -130,7 +130,7 @@ public:
           {
             auto year = taxisInqVdatetime(taxisID2).date.year;
 
-            if (ltime == false && year1 != year) break;
+            if (allSteps == false && year1 != year) break;
 
             for (int recID = 0; recID < nrecs2; ++recID)
               {
@@ -139,12 +139,14 @@ public:
                 size_t numMissVals;
                 cdo_read_record(streamID2, array.data(), &numMissVals);
 
-                const auto &var = varList2[varID];
+                const auto &var = varList2.vars[varID];
+                const auto &var1 = varsData1[varID][levelID];
+                auto &var2 = varsData2[varID][levelID];
                 for (size_t i = 0; i < var.gridsize; ++i)
-                  if (numSets == (int) std::lround(vars1[varID][levelID].vec_d[i]))
+                  if (numSets == (int) std::lround(var1.vec_d[i]))
                     {
-                      if (lexactdate) cdo_taxis_copy_timestep(taxisID3, taxisID2);
-                      vars2[varID][levelID].vec_d[i] = array[i];
+                      if (useExactDate) cdo_taxis_copy_timestep(taxisID3, taxisID2);
+                      var2.vec_d[i] = array[i];
                     }
               }
 
@@ -154,15 +156,15 @@ public:
 
         if (numSets)
           {
-            if (!lexactdate) cdo_taxis_copy_timestep(taxisID3, taxisID1);
+            if (!useExactDate) cdo_taxis_copy_timestep(taxisID3, taxisID1);
             cdo_def_timestep(streamID3, tsID3);
 
-            for (int recID = 0; recID < maxrecs; ++recID)
+            for (int recID = 0; recID < maxRecords; ++recID)
               {
-                auto [varID, levelID] = recList[recID].get();
-                if (tsID && varList1[varID].isConstant) continue;
+                auto [varID, levelID] = recordList[recID].get();
+                if (tsID && varList1.vars[varID].isConstant) continue;
 
-                auto &var2 = vars2[varID][levelID];
+                auto &var2 = varsData2[varID][levelID];
                 cdo_def_record(streamID3, varID, levelID);
                 cdo_write_record(streamID3, var2.vec_d.data(), field_num_miss(var2));
               }
@@ -183,7 +185,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID3);
     cdo_stream_close(streamID2);
diff --git a/src/Set.cc b/src/Set.cc
index 86845c5aa8e23dd939b61ebdaca614ac9fba008a..2052bc48687b06dd4369721b7d14f8fedb4af11d 100644
--- a/src/Set.cc
+++ b/src/Set.cc
@@ -25,7 +25,7 @@
 static void
 set_level(int vlistID2, double newlevel)
 {
-  auto nzaxis = vlistNzaxis(vlistID2);
+  auto nzaxis = vlistNumZaxis(vlistID2);
   for (int index = 0; index < nzaxis; ++index)
     {
       auto zaxisID1 = vlistZaxis(vlistID2, index);
@@ -42,7 +42,7 @@ set_level(int vlistID2, double newlevel)
 static void
 set_ltype(int vlistID2, double newval)
 {
-  auto nzaxis = vlistNzaxis(vlistID2);
+  auto nzaxis = vlistNumZaxis(vlistID2);
   for (int index = 0; index < nzaxis; ++index)
     {
       auto zaxisID1 = vlistZaxis(vlistID2, index);
@@ -88,12 +88,11 @@ public:
   int taxisID1;
   int taxisID2;
 
-  Field field;
   VarList varList1;
 
 public:
   void
-  init()
+  init() override
   {
     SETCODE = module.get_id("setcode");
     SETPARAM = module.get_id("setparam");
@@ -125,10 +124,12 @@ public:
     taxisID2 = taxisDuplicate(taxisID1);
     vlistDefTaxis(vlistID2, taxisID2);
 
+    varList1 = VarList(vlistID1);
+
     if (operatorID == SETCODE)
       {
-        auto nvars = vlistNvars(vlistID2);
-        for (int varID = 0; varID < nvars; ++varID) vlistDefVarCode(vlistID2, varID, newval);
+        auto numVars = varList1.numVars();
+        for (int varID = 0; varID < numVars; ++varID) vlistDefVarCode(vlistID2, varID, newval);
       }
     else if (operatorID == SETPARAM) { vlistDefVarParam(vlistID2, 0, newparam); }
     else if (operatorID == SETNAME) { cdiDefKeyString(vlistID2, 0, CDI_KEY_NAME, newname); }
@@ -136,21 +137,22 @@ public:
     else if (operatorID == SETTABNUM)
       {
         auto tableID = tableDef(-1, tabnum, nullptr);
-        auto nvars = vlistNvars(vlistID2);
-        for (int varID = 0; varID < nvars; ++varID) vlistDefVarTable(vlistID2, varID, tableID);
+        auto numVars = varList1.numVars();
+        for (int varID = 0; varID < numVars; ++varID) vlistDefVarTable(vlistID2, varID, tableID);
       }
     else if (operatorID == SETLEVEL) { set_level(vlistID2, newlevel); }
     else if (operatorID == SETLTYPE) { set_ltype(vlistID2, newval); }
     else if (operatorID == SETMAXSTEPS) { vlistDefNtsteps(vlistID2, maxSteps); }
 
-    varList_init(varList1, vlistID1);
-
     streamID2 = cdo_open_write(1);
     cdo_def_vlist(streamID2, vlistID2);
   }
+
   void
-  run()
+  run() override
   {
+    Field field;
+
     int tsID = 0;
     while (true)
       {
@@ -166,7 +168,7 @@ public:
             cdo_inq_record(streamID1, &varID, &levelID);
             cdo_def_record(streamID2, varID, levelID);
 
-            field.init(varList1[varID]);
+            field.init(varList1.vars[varID]);
             cdo_read_record(streamID1, field);
             cdo_write_record(streamID2, field);
           }
@@ -174,8 +176,9 @@ public:
         tsID++;
       }
   }
+
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
diff --git a/src/Setattribute.cc b/src/Setattribute.cc
index afbea5dd8357a01a3ff947b14189e2a8569e5e18..bea0a20fc94571dd2f31e8568d84d6124fca0b24 100644
--- a/src/Setattribute.cc
+++ b/src/Setattribute.cc
@@ -68,12 +68,12 @@ find_variables(const std::string &varName, int vlistID, std::vector<std::string>
 
   constexpr int Undefined = -99;
   cdiID = Undefined;
-  auto nvars = vlistNvars(vlistID);
-  // auto ngrids = vlistNgrids(vlistID);
-  auto nzaxis = vlistNzaxis(vlistID);
+  auto numVars = vlistNvars(vlistID);
+  // auto ngrids = vlistNumGrids(vlistID);
+  auto nzaxis = vlistNumZaxis(vlistID);
   if (varName.size())
     {
-      for (int idx = 0; idx < nvars; idx++)
+      for (int idx = 0; idx < numVars; idx++)
         {
           auto name = cdo::inq_var_name(vlistID, idx);
           if (wildcardmatch(varName, name) == 0)
@@ -185,28 +185,23 @@ find_attribute(int cdiID, int varID, const std::string &attrName, int &dtype)
 
   if (varID != CDI_GLOBAL && attrValues.empty())
     {
+      VarList varList(cdiID);
+      const auto &var = varList.vars[varID];
       auto stdname = cdo::inq_key_string(cdiID, varID, CDI_KEY_STDNAME);
-      auto longname = cdo::inq_var_longname(cdiID, varID);
-      auto units = cdo::inq_var_units(cdiID, varID);
-
-      auto paramstr = param_to_string(vlistInqVarParam(cdiID, varID));
-
-      auto code = vlistInqVarCode(cdiID, varID);
       auto table = vlistInqVarTable(cdiID, varID);
-      auto missval = vlistInqVarMissval(cdiID, varID);
 
       double addoffset = 0.0, scalefactor = 1.0;
       cdiInqKeyFloat(cdiID, varID, CDI_KEY_ADDOFFSET, &addoffset);
       cdiInqKeyFloat(cdiID, varID, CDI_KEY_SCALEFACTOR, &scalefactor);
 
       // clang-format off
-      if      (attrName == "long_name")     attrValues.push_back(longname);
+      if      (attrName == "long_name")     attrValues.push_back(var.longname);
       else if (attrName == "standard_name") attrValues.push_back(stdname);
-      else if (attrName == "units")         attrValues.push_back(units);
-      else if (attrName == "param")         attrValues.push_back(paramstr);
-      else if (attrName == "code")          attrValues.push_back(std::to_string(code));
+      else if (attrName == "units")         attrValues.push_back(var.units);
+      else if (attrName == "param")         attrValues.push_back(param_to_string(var.param));
+      else if (attrName == "code")          attrValues.push_back(std::to_string(var.code));
       else if (attrName == "table")         attrValues.push_back(std::to_string(table));
-      else if (attrName == "missing_value") attrValues.push_back(std::to_string(missval));
+      else if (attrName == "missing_value") attrValues.push_back(std::to_string(var.missval));
       else if (attrName == "add_offset")    attrValues.push_back(std::to_string(addoffset));
       else if (attrName == "scale_factor")  attrValues.push_back(std::to_string(scalefactor));
       // clang-format off
@@ -291,6 +286,8 @@ set_attribute(int cdiID, int varID, const std::string &attName, int dtype, const
         {
           if (i > 0 && value[i - 1] == '\\' && value[i] == 'n')
             outvalue[outlen - 1] = '\n';
+          else if (i > 0 && value[i - 1] == '\\' && value[i] == '"')
+            outvalue[outlen - 1] = '\"';
           else
             outvalue[outlen++] = value[i];
         }
@@ -367,14 +364,13 @@ public:
   int taxisID1;
   int taxisID2;
 
-  Field field;
   bool dataIsUnchanged;
 
   VarList varList1;
 
 public:
   void
-  init()
+  init() override
   {
     dataIsUnchanged = data_is_unchanged();
 
@@ -382,8 +378,8 @@ public:
 
     operator_input_arg(cdo_operator_enter(operatorID));
 
-    auto natts = cdo_operator_argc();
-    if (natts == 0) cdo_abort("Parameter missing!");
+    auto numAtts = cdo_operator_argc();
+    if (numAtts == 0) cdo_abort("Parameter missing!");
 
     PMList pmlist;
     KVList kvlist;
@@ -392,7 +388,7 @@ public:
     if (Options::cdoVerbose) kvlist.print();
 
     auto pkvlist = &kvlist;
-    if (natts == 1)
+    if (numAtts == 1)
       {
         KeyValues &kv = kvlist.front();
         if (kv.key == "FILE")
@@ -413,6 +409,8 @@ public:
     auto vlistID1 = cdo_stream_inq_vlist(streamID1);
     auto vlistID2 = vlistDuplicate(vlistID1);
 
+    varList1 = VarList(vlistID1);
+
     set_attributes(*pkvlist, vlistID2);
 
     taxisID1 = vlistInqTaxis(vlistID1);
@@ -421,13 +419,13 @@ public:
 
     streamID2 = cdo_open_write(1);
     cdo_def_vlist(streamID2, vlistID2);
-
-    varList_init(varList1, vlistID1);
   }
 
   void
-  run()
+  run() override
   {
+    Field field;
+
     int tsID = 0;
     while (true)
       {
@@ -446,7 +444,7 @@ public:
             if (dataIsUnchanged) { cdo_copy_record(streamID2, streamID1); }
             else
               {
-                field.init(varList1[varID]);
+                field.init(varList1.vars[varID]);
                 cdo_read_record(streamID1, field);
                 cdo_write_record(streamID2, field);
               }
@@ -457,7 +455,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID1);
     cdo_stream_close(streamID2);
diff --git a/src/Setbox.cc b/src/Setbox.cc
index 5ef49deac3d1440e0a822606e9633de2a32f497d..2a95b33aacadfc4f2d021621fde379540e44e005 100644
--- a/src/Setbox.cc
+++ b/src/Setbox.cc
@@ -44,7 +44,7 @@ get_gridID(int vlistID1, bool operIndexBox)
 {
   std::vector<int> gridsFound;
 
-  auto ngrids = vlistNgrids(vlistID1);
+  auto ngrids = vlistNumGrids(vlistID1);
   for (int index = 0; index < ngrids; ++index)
     {
       auto gridID1 = vlistGrid(vlistID1, index);
@@ -74,20 +74,20 @@ get_gridID(int vlistID1, bool operIndexBox)
 }
 
 static std::vector<bool>
-get_processVars(int vlistID1, int gridID)
+get_processVars(const VarList &varList, int gridID)
 {
-  auto nvars = vlistNvars(vlistID1);
+  auto numVars = varList.numVars();
 
-  std::vector<bool> processVars(nvars, false);
+  std::vector<bool> processVars(numVars, false);
 
   int varID;
-  for (varID = 0; varID < nvars; ++varID)
-    if (gridID == vlistInqVarGrid(vlistID1, varID)) processVars[varID] = true;
+  for (varID = 0; varID < numVars; ++varID)
+    if (gridID == varList.vars[varID].gridID) processVars[varID] = true;
 
-  for (varID = 0; varID < nvars; ++varID)
+  for (varID = 0; varID < numVars; ++varID)
     if (processVars[varID]) break;
 
-  if (varID >= nvars) cdo_abort("No processable variable found!");
+  if (varID >= numVars) cdo_abort("No processable variable found!");
 
   return processVars;
 }
@@ -98,8 +98,9 @@ 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 } },
+    .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
@@ -115,6 +116,7 @@ public:
   int taxisID2;
 
   int vlistID1;
+  VarList varList1;
 
   int gridID;
 
@@ -127,7 +129,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
     SETCLONLATBOX = module.get_id("setclonlatbox");
     SETCINDEXBOX = module.get_id("setcindexbox");
@@ -144,9 +146,11 @@ public:
     streamID1 = cdo_open_read(0);
     vlistID1 = cdo_stream_inq_vlist(streamID1);
 
+    varList1 = VarList(vlistID1);
+
     gridID = get_gridID(vlistID1, operIndexBox);
 
-    processVars = get_processVars(vlistID1, gridID);
+    processVars = get_processVars(varList1, gridID);
 
     operator_input_arg(cdo_operator_enter(operatorID));
 
@@ -167,7 +171,7 @@ public:
   }
 
   void
-  run()
+  run() override
   {
     int tsID = 0;
     while (true)
@@ -190,7 +194,7 @@ public:
 
                 setcbox(constant, array.data(), gridID, selboxInfo);
 
-                auto missval = vlistInqVarMissval(vlistID1, varID);
+                auto missval = varList1.vars[varID].missval;
                 numMissVals = varray_num_mv(gridsize, array, missval);
                 cdo_def_record(streamID2, varID, levelID);
                 cdo_write_record(streamID2, array.data(), numMissVals);
@@ -202,7 +206,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
diff --git a/src/Setgrid.cc b/src/Setgrid.cc
index 6a1fb6a8624540cf379bdaebbdf37c5184792205..e3cc212ae44941d2b86b26e155a4aa83d7e83f1e 100644
--- a/src/Setgrid.cc
+++ b/src/Setgrid.cc
@@ -30,7 +30,7 @@ change_grid(int vlistID1, int vlistID2, int gridID2)
 {
   auto gridsize2 = gridInqSize(gridID2);
   int found = 0;
-  auto ngrids = vlistNgrids(vlistID1);
+  auto ngrids = vlistNumGrids(vlistID1);
   for (int index = 0; index < ngrids; ++index)
     {
       if (gridsize2 == gridInqSize(vlistGrid(vlistID1, index)))
@@ -49,7 +49,7 @@ grid_set_type(int vlistID1, int vlistID2, int gridtype, const std::string &gridn
   auto needCorners = withBounds ? NeedCorners::Yes : NeedCorners::No;
   int gridID2;
   auto lrgrid = false;
-  auto ngrids = vlistNgrids(vlistID1);
+  auto ngrids = vlistNumGrids(vlistID1);
   for (int index = 0; index < ngrids; ++index)
     {
       auto gridID1 = vlistGrid(vlistID1, index);
@@ -141,7 +141,7 @@ grid_set_cellarea(int vlistID1, int vlistID2, Varray<double> &gridcellArea)
 {
   auto areasize = gridcellArea.size();
   int numGridsChanges = 0;
-  auto ngrids = vlistNgrids(vlistID1);
+  auto ngrids = vlistNumGrids(vlistID1);
   for (int index = 0; index < ngrids; ++index)
     {
       auto gridID1 = vlistGrid(vlistID1, index);
@@ -161,7 +161,7 @@ static void
 grid_set_mask(int vlistID1, int vlistID2, Varray<double> &gridmask)
 {
   auto masksize = gridmask.size();
-  auto ngrids = vlistNgrids(vlistID1);
+  auto ngrids = vlistNumGrids(vlistID1);
   for (int index = 0; index < ngrids; ++index)
     {
       auto gridID1 = vlistGrid(vlistID1, index);
@@ -183,7 +183,7 @@ grid_set_mask(int vlistID1, int vlistID2, Varray<double> &gridmask)
 static void
 grid_unset_mask(int vlistID1, int vlistID2)
 {
-  auto ngrids = vlistNgrids(vlistID1);
+  auto ngrids = vlistNumGrids(vlistID1);
   for (int index = 0; index < ngrids; ++index)
     {
       auto gridID1 = vlistGrid(vlistID1, index);
@@ -196,14 +196,14 @@ grid_unset_mask(int vlistID1, int vlistID2)
 static void
 grid_set_proj_params(int vlistID1, int vlistID2, const std::string &projparams)
 {
-  auto ngrids = vlistNgrids(vlistID1);
+  auto ngrids = vlistNumGrids(vlistID1);
   for (int index = 0; index < ngrids; ++index)
     {
       auto gridID1 = vlistGrid(vlistID1, index);
       if (gridInqType(gridID1) == GRID_PROJECTION)
         {
           auto gridID2 = gridDuplicate(gridID1);
-          cdiDefAttTxt(gridID2, CDI_GLOBAL, "proj4_params", (int) projparams.size(), projparams.c_str());
+          cdiDefAttTxt(gridID2, CDI_GLOBAL, "proj_params", (int) projparams.size(), projparams.c_str());
           vlistChangeGridIndex(vlistID2, index, gridID2);
         }
     }
@@ -230,8 +230,7 @@ grid_read_cellarea(const std::string &areafile, Varray<double> &gridcellArea)
   auto streamID = stream_open_read_locked(filename.c_str());
   auto vlistID = streamInqVlist(streamID);
 
-  VarList varList;
-  varList_init(varList, vlistID);
+  VarList varList(vlistID);
 
   int svarID = 0;
   if (searchName)
@@ -240,7 +239,7 @@ grid_read_cellarea(const std::string &areafile, Varray<double> &gridcellArea)
       int varID;
       for (varID = 0; varID < nvars; ++varID)
         {
-          if (varList[varID].name == varname)
+          if (varList.vars[varID].name == varname)
             {
               svarID = varID;
               break;
@@ -256,8 +255,7 @@ grid_read_cellarea(const std::string &areafile, Varray<double> &gridcellArea)
       streamInqRecord(streamID, &varID, &levelID);
       if (varID == svarID)
         {
-          auto areasize = gridInqSize(varList[varID].gridID);
-          gridcellArea.resize(areasize);
+          gridcellArea.resize(varList.vars[varID].gridsize);
 
           size_t numMissVals;
           streamReadRecord(streamID, gridcellArea.data(), &numMissVals);
@@ -274,6 +272,7 @@ public:
   using Process::Process;
   inline static CdoModule module = {
     .name = "Setgrid",
+    // clang-format off
     .operators = { { "setgrid", 0, 0, "grid description file or name", SetgridHelp },
                    { "setgridtype", 0, 0, "gridtype", SetgridHelp },
                    { "setgridarea", 0, 0, "filename with areaweights", SetgridHelp },
@@ -283,6 +282,7 @@ public:
                    { "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 } },
+    // clang-format on
     .aliases = {},
     .mode = EXPOSED,     // Module mode: 0:intern 1:extern
     .number = CDI_BOTH,  // Allowed number type
@@ -302,12 +302,13 @@ public:
 
   std::vector<int> grid2_vgpm;
   Varray<double> gridmask, gridcellArea;
-  VarList varList1, varList2;
+  VarList varList1;
+  VarList varList2;
   Field field;
 
 public:
   void
-  init()
+  init() override
   {
     int number = 0, position = 0;
     std::string griduri;
@@ -358,13 +359,13 @@ public:
         auto maskfile = cdo_operator_argv(0).c_str();
         auto streamID = stream_open_read_locked(maskfile);
         auto vlistID = streamInqVlist(streamID);
+        VarList varList(vlistID);
 
         (void) streamInqTimestep(streamID, 0);
         int varID, levelID;
         streamInqRecord(streamID, &varID, &levelID);
-
-        auto missval = vlistInqVarMissval(vlistID, varID);
-        auto gridID = vlistInqVarGrid(vlistID, varID);
+        auto missval = varList.vars[varID].missval;
+        auto gridID = varList.vars[varID].gridID;
         auto masksize = gridInqSize(gridID);
         gridmask.resize(masksize);
 
@@ -426,8 +427,8 @@ public:
           }
         else if (operatorID == USEGRIDNUMBER)
           {
-            if (number < 1 || number > vlistNgrids(vlistID1))
-              cdo_abort("Invalid grid number: %d (max = %d)!", number, vlistNgrids(vlistID1));
+            if (number < 1 || number > vlistNumGrids(vlistID1))
+              cdo_abort("Invalid grid number: %d (max = %d)!", number, vlistNumGrids(vlistID1));
 
             gridID2x = vlistGrid(vlistID1, number - 1);
           }
@@ -474,18 +475,17 @@ public:
     cdo_def_vlist(streamID2, vlistID2);
     // vlistPrint(vlistID2);
 
-    varList_init(varList1, vlistID1);
-    varList_init(varList2, vlistID2);
+    varList1 = VarList(vlistID1);
+    varList2 = VarList(vlistID2);
 
-    auto nvars = vlistNvars(vlistID1);
     if (lregular || lregularnn)
-      for (int varID = 0; varID < nvars; ++varID) varList1[varID].memType = MemType::Double;
+      for (auto &var : varList1.vars) var.memType = MemType::Double;
     if (lregular || lregularnn)
-      for (int varID = 0; varID < nvars; ++varID) varList2[varID].memType = MemType::Double;
+      for (auto &var : varList2.vars) var.memType = MemType::Double;
   }
 
   void
-  run()
+  run() override
   {
     int tsID = 0;
     while (true)
@@ -502,23 +502,25 @@ public:
             cdo_inq_record(streamID1, &varID, &levelID);
             cdo_def_record(streamID2, varID, levelID);
 
-            field.init((lregular || lregularnn) ? varList2[varID] : varList1[varID]);
+            const auto &var1 = varList1.vars[varID];
+            const auto &var2 = varList2.vars[varID];
+            field.init((lregular || lregularnn) ? var2 : var1);
             cdo_read_record(streamID1, field);
             auto numMissVals = field.numMissVals;
 
-            auto gridID1 = varList1[varID].gridID;
+            auto gridID1 = var1.gridID;
             if (lregular || lregularnn)
               {
                 if (gridInqType(gridID1) == GRID_GAUSSIAN_REDUCED)
                   {
-                    auto missval = varList1[varID].missval;
+                    auto missval = var1.missval;
                     int lnearst = lregularnn ? 1 : 0;
-                    field2regular(gridID1, varList2[varID].gridID, missval, field.vec_d.data(), numMissVals, lnearst);
+                    field2regular(gridID1, var2.gridID, missval, field.vec_d.data(), numMissVals, lnearst);
                   }
               }
             else if (gridInqType(gridID1) == GRID_GME)
               {
-                auto gridsize = varList1[varID].gridsize;
+                auto gridsize = var1.gridsize;
                 size_t j = 0;
                 if (field.memType == MemType::Float)
                   {
@@ -539,7 +541,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
diff --git a/src/Setgridcell.cc b/src/Setgridcell.cc
index 5e8fa8c8495c40bf01dd72d631cf802db121780e..51296cf2e8ab9205f411751f534610bd09610652 100644
--- a/src/Setgridcell.cc
+++ b/src/Setgridcell.cc
@@ -149,7 +149,7 @@ public:
   int taxisID2;
 
   int numVars;
-  VarList varList;
+  VarList varList1;
   Varray<bool> selectVars;
 
   std::vector<std::string> varnames;
@@ -160,9 +160,8 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
-
     operator_input_arg(cdo_operator_enter(0));
 
     const auto nparam = cdo_operator_argc();
@@ -188,16 +187,16 @@ 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);
 
     taxisID1 = vlistInqTaxis(vlistID1);
     taxisID2 = taxisDuplicate(taxisID1);
     vlistDefTaxis(vlistID2, taxisID2);
 
-    varList_init(varList, vlistID1);
+    varList1 = VarList(vlistID1);
 
-    numVars = varList.size();
+    numVars = varList1.numVars();
     selectVars = Varray<bool>(numVars, !varnames.size());
     if (varnames.size())
       {
@@ -206,7 +205,7 @@ public:
             auto varFound = false;
             for (int varID = 0; varID < numVars; ++varID)
               {
-                if (varname == varList[varID].name)
+                if (varname == varList1.vars[varID].name)
                   {
                     selectVars[varID] = true;
                     varFound = true;
@@ -222,12 +221,12 @@ public:
   }
 
   void
-  run()
+  run() override
   {
     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);
@@ -237,7 +236,7 @@ public:
           {
             int varID, levelID;
             cdo_inq_record(streamID1, &varID, &levelID);
-            field.init(varList[varID]);
+            field.init(varList1.vars[varID]);
             cdo_read_record(streamID1, field);
 
             if (selectVars[varID])
@@ -257,8 +256,9 @@ public:
         tsID++;
       }
   }
+
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
diff --git a/src/Sethalo.cc b/src/Sethalo.cc
index 3957d0118fa6621d9f4caac14ab6296d94b105bb..6168afa23fec98e4a1d605ce3296a80e06a5d5a4 100644
--- a/src/Sethalo.cc
+++ b/src/Sethalo.cc
@@ -478,7 +478,7 @@ get_gridID(int vlistID1)
 {
   int gridID1 = -1;
 
-  auto ngrids = vlistNgrids(vlistID1);
+  auto ngrids = vlistNumGrids(vlistID1);
   int ndiffgrids = 0;
   int index;
   for (index = 1; index < ngrids; ++index)
@@ -526,7 +526,7 @@ public:
 
   int gridID1 = -1;
 
-  VarList varList;
+  VarList varList1;
 
   std::vector<bool> vars;
   Varray<double> array1;
@@ -534,7 +534,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
     SETHALO = module.get_id("sethalo");
 
@@ -553,7 +553,7 @@ public:
     taxisID2 = taxisDuplicate(taxisID1);
     vlistDefTaxis(vlistID2, taxisID2);
 
-    auto ngrids = vlistNgrids(vlistID1);
+    auto ngrids = vlistNumGrids(vlistID1);
     for (int index = 0; index < ngrids; ++index)
       {
         if (gridID1 == vlistGrid(vlistID1, index))
@@ -563,11 +563,11 @@ public:
           }
       }
 
-    varList_init(varList, vlistID1);
+    varList1 = VarList(vlistID1);
 
     auto nvars = vlistNvars(vlistID1);
     vars = std::vector<bool>(nvars);
-    for (int varID = 0; varID < nvars; ++varID) vars[varID] = (gridID1 == varList[varID].gridID);
+    for (int varID = 0; varID < nvars; ++varID) vars[varID] = (gridID1 == varList1.vars[varID].gridID);
 
     streamID2 = cdo_open_write(1);
     cdo_def_vlist(streamID2, vlistID2);
@@ -577,7 +577,7 @@ public:
   }
 
   void
-  run()
+  run() override
   {
     int tsID = 0;
     while (true)
@@ -599,7 +599,7 @@ public:
                 cdo_read_record(streamID1, array1.data(), &numMissVals);
 
                 auto recalcNumMiss = false;
-                auto missval = varList[varID].missval;
+                auto missval = varList1.vars[varID].missval;
                 if (operatorID == SETHALO)
                   recalcNumMiss = regular_halo(array1, gridID1, array2, halo, missval);
                 else
@@ -617,7 +617,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
diff --git a/src/Setmiss.cc b/src/Setmiss.cc
index aade5cc3617b0acaf5a0d24e48c87c3b4511fed2..6610549ae36f20843d5ac61c13890e7e02708cf5 100644
--- a/src/Setmiss.cc
+++ b/src/Setmiss.cc
@@ -20,6 +20,7 @@
 
 #include "process_int.h"
 #include "param_conversion.h"
+#include "progress.h"
 
 template <typename T>
 static size_t
@@ -132,7 +133,7 @@ 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 numMissVals = varray_num_mv(gridsize, array, missval);
+  auto numMissVals = varray_num_mv(gridsize, array, missval);
 
   return numMissVals;
 }
@@ -163,6 +164,7 @@ public:
     .constraints = { 1, 1, NoRestriction },
   };
   inline static RegisterEntry<Setmiss> registration = RegisterEntry<Setmiss>(module);
+
   int SETMISSVAL, SETCTOMISS, SETMISSTOC, SETRTOMISS, SETVRANGE;
 
   CdoStreamID streamID1;
@@ -170,7 +172,7 @@ public:
   int taxisID1;
   int taxisID2;
 
-  VarList varList;
+  VarList varList1;
   Field field;
   int operatorID;
 
@@ -181,16 +183,13 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
-
-    // clang-format off
-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
+    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");
 
     operatorID = cdo_operator_id();
 
@@ -213,18 +212,18 @@ SETVRANGE = module.get_id("setvrange");
 
     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);
     vlistDefTaxis(vlistID2, taxisID2);
 
-    varList_init(varList, vlistID1);
+    varList1 = VarList(vlistID1);
 
     if (operatorID == SETMISSVAL)
       {
-        const auto nvars = vlistNvars(vlistID2);
+        auto nvars = vlistNvars(vlistID2);
         for (int varID = 0; varID < nvars; ++varID) vlistDefVarMissval(vlistID2, varID, new_missval);
       }
     else if (operatorID == SETMISSTOC)
@@ -232,7 +231,7 @@ SETVRANGE = module.get_id("setvrange");
         auto nvars = vlistNvars(vlistID2);
         for (int varID = 0; varID < nvars; ++varID)
           {
-            const auto &var = varList[varID];
+            const auto &var = varList1.vars[varID];
             if (DBL_IS_EQUAL(rconst, var.missval))
               {
                 cdo_warning("Missing value and constant have the same value!");
@@ -253,32 +252,37 @@ SETVRANGE = module.get_id("setvrange");
     streamID2 = cdo_open_write(1);
     cdo_def_vlist(streamID2, vlistID2);
   }
+
   void
-  run()
+  run() override
   {
+    auto numSteps = varList1.numSteps();
+    cdo::Progress progress;
+
     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;
 
+        if (numSteps > 1) progress.update((tsID + 1.0) / numSteps);
+
         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);
-            const auto &var = varList[varID];
+            auto [varID, levelID] = cdo_inq_record(streamID1);
+            const auto &var = varList1.vars[varID];
             field.init(var);
             cdo_read_record(streamID1, field);
 
             // clang-format off
-          if      (operatorID == SETMISSVAL) set_missval(field, new_missval);
-          else if (operatorID == SETCTOMISS) set_const_to_miss(field, rconst);
-          else if (operatorID == SETMISSTOC) set_miss_to_const(field, rconst);
-          else if (operatorID == SETRTOMISS) set_range_to_miss(field, rmin, rmax);
-          else if (operatorID == SETVRANGE)  set_valid_range(field, rmin, rmax);
+            if      (operatorID == SETMISSVAL) set_missval(field, new_missval);
+            else if (operatorID == SETCTOMISS) set_const_to_miss(field, rconst);
+            else if (operatorID == SETMISSTOC) set_miss_to_const(field, rconst);
+            else if (operatorID == SETRTOMISS) set_range_to_miss(field, rmin, rmax);
+            else if (operatorID == SETVRANGE)  set_valid_range(field, rmin, rmax);
             // clang-format on
 
             cdo_def_record(streamID2, varID, levelID);
@@ -288,8 +292,9 @@ SETVRANGE = module.get_id("setvrange");
         tsID++;
       }
   }
+
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
diff --git a/src/Setpartab.cc b/src/Setpartab.cc
index bf7420feb4b36dc759cb8b0c6c902954a6022e4e..17be9377fc0cf4caec68c6022b805d1bb651932a 100644
--- a/src/Setpartab.cc
+++ b/src/Setpartab.cc
@@ -33,7 +33,7 @@ enum pt_mode_t
 };
 
 static void
-apply_parameterList(pt_mode_t ptmode, PMList &pmlist, int nvars, int vlistID2, std::vector<CmorVar> &vars)
+apply_parameterList(pt_mode_t ptmode, PMList &pmlist, int numVars, int vlistID2, std::vector<CmorVar> &vars)
 {
   const std::vector<std::string> hentry = { "Header" };
   const std::vector<std::string> ventry = { "variable_entry", "parameter" };
@@ -41,6 +41,8 @@ apply_parameterList(pt_mode_t ptmode, PMList &pmlist, int nvars, int vlistID2, s
   char paramstr[32];
   int codenum = 0;
 
+  VarList varList2(vlistID2);
+
   // search for global missing value
   auto hasMissvals = false;
   double missval = 0.0;
@@ -59,18 +61,18 @@ apply_parameterList(pt_mode_t ptmode, PMList &pmlist, int nvars, int vlistID2, s
   }
 
   int numVarsFound = 0;
-  for (int varID = 0; varID < nvars; ++varID)
+  for (int varID = 0; varID < numVars; ++varID)
     {
       auto &var = vars[varID];
       var.name = cdo::inq_var_name(vlistID2, varID);
 
+      const auto &var2 = varList2.vars[varID];
       if (hasMissvals)
         {
-          auto missval_old = vlistInqVarMissval(vlistID2, varID);
-          if (!dbl_is_equal(missval, missval_old))
+          if (dbl_is_not_equal(missval, var2.missval))
             {
               var.changemissval = true;
-              var.missval_old = missval_old;
+              var.missvalOld = var2.missval;
               vlistDefVarMissval(vlistID2, varID, missval);
             }
         }
@@ -78,7 +80,7 @@ apply_parameterList(pt_mode_t ptmode, PMList &pmlist, int nvars, int vlistID2, s
       const KVList *kvlist = nullptr;
       if (ptmode == CODE_NUMBER)
         {
-          codenum = vlistInqVarCode(vlistID2, varID);
+          codenum = var2.code;
           std::snprintf(valstr, sizeof(valstr), "%d", codenum);
           kvlist = pmlist.searchKVListVentry("code", valstr, ventry);
           if (kvlist)
@@ -86,7 +88,7 @@ apply_parameterList(pt_mode_t ptmode, PMList &pmlist, int nvars, int vlistID2, s
               auto tableID = vlistInqVarTable(vlistID2, varID);
               auto tabnum = tableInqNum(tableID);
               int levtype = 0;
-              cdiInqKeyInt(vlistInqVarZaxis(vlistID2, varID), CDI_GLOBAL, CDI_KEY_TYPEOFFIRSTFIXEDSURFACE, &levtype);
+              cdiInqKeyInt(var2.zaxisID, CDI_GLOBAL, CDI_KEY_TYPEOFFIRSTFIXEDSURFACE, &levtype);
               auto table = tabnum;
               auto ltype = levtype;
               {
@@ -102,14 +104,14 @@ apply_parameterList(pt_mode_t ptmode, PMList &pmlist, int nvars, int vlistID2, s
         }
       else if (ptmode == PARAMETER_ID)
         {
-          auto param = vlistInqVarParam(vlistID2, varID);
+          auto param = var2.param;
           cdiParamToString(param, paramstr, sizeof(paramstr));
           std::snprintf(valstr, sizeof(valstr), "%s", paramstr);
           kvlist = pmlist.searchKVListVentry("param", valstr, ventry);
           if (kvlist)
             {
               int levtype = 0;
-              cdiInqKeyInt(vlistInqVarZaxis(vlistID2, varID), CDI_GLOBAL, CDI_KEY_TYPEOFFIRSTFIXEDSURFACE, &levtype);
+              cdiInqKeyInt(var2.zaxisID, CDI_GLOBAL, CDI_KEY_TYPEOFFIRSTFIXEDSURFACE, &levtype);
               auto kv = kvlist->search("ltype");
               auto ltype = (kv && kv->nvalues == 1) ? parameter_to_int(kv->values[0]) : levtype;
               if (levtype != ltype) kvlist = nullptr;
@@ -121,7 +123,7 @@ apply_parameterList(pt_mode_t ptmode, PMList &pmlist, int nvars, int vlistID2, s
         {
           numVarsFound++;
           int pnum, ptab, pdum;
-          cdiDecodeParam(vlistInqVarParam(vlistID2, varID), &pnum, &ptab, &pdum);
+          cdiDecodeParam(var2.param, &pnum, &ptab, &pdum);
           auto hasValidMin = false, hasValidMax = false;
 
           for (const auto &kv : *kvlist)
@@ -183,14 +185,14 @@ public:
   int vlistID2;
 
   std::vector<CmorVar> vars;
-  int nvars;
+  int numVars;
 
   Varray<double> array;
   VarList varList2;
 
 public:
   void
-  init()
+  init() override
   {
     SETCODETAB = module.get_id("setcodetab");
     SETPARTABC = module.get_id("setpartabc");
@@ -254,17 +256,17 @@ public:
     vlistID2 = vlistDuplicate(vlistID1);
     // vlistPrint(vlistID2);
 
-    nvars = vlistNvars(vlistID2);
-    vars = std::vector<CmorVar>(nvars);
+    numVars = vlistNvars(vlistID2);
+    vars = std::vector<CmorVar>(numVars);
 
     if (convertData)
-      for (int varID = 0; varID < nvars; ++varID) vars[varID].convert = true;
+      for (int varID = 0; varID < numVars; ++varID) vars[varID].convert = true;
 
     if (tableformat == 0)
       {
-        // for (int varID = 0; varID < nvars; ++varID) vlistDefVarTable(vlistID2, varID, tableID);
+        // for (int varID = 0; varID < numVars; ++varID) vlistDefVarTable(vlistID2, varID, tableID);
         char name[CDI_MAX_NAME], longname[CDI_MAX_NAME], units[CDI_MAX_NAME];
-        for (int varID = 0; varID < nvars; ++varID)
+        for (int varID = 0; varID < numVars; ++varID)
           {
             auto param = vlistInqVarParam(vlistID2, varID);
             int pdis, pcat, pnum;
@@ -299,10 +301,10 @@ public:
           pmlist.read_namelist(fp, filename);
           std::fclose(fp);
 
-          apply_parameterList(ptmode, pmlist, nvars, vlistID2, vars);
+          apply_parameterList(ptmode, pmlist, numVars, vlistID2, vars);
         }
 
-        for (int varID = 0; varID < nvars; ++varID)
+        for (int varID = 0; varID < numVars; ++varID)
           if (vars[varID].remove)
             {
               deleteVars = true;
@@ -314,7 +316,7 @@ public:
             vlistClearFlag(vlistID1);
             vlistClearFlag(vlistID2);
 
-            for (int varID = 0; varID < nvars; ++varID)
+            for (int varID = 0; varID < numVars; ++varID)
               {
                 auto zaxisID = vlistInqVarZaxis(vlistID2, varID);
                 auto nlevs = zaxisInqSize(zaxisID);
@@ -339,7 +341,7 @@ public:
             if (vlistNvars(vlistID2) == 0) cdo_abort("No variable selected!");
           }
 
-        for (int varID = 0; varID < nvars; ++varID)
+        for (int varID = 0; varID < numVars; ++varID)
           {
             auto &var = vars[varID];
             if (!var.convert) var.changeunits = false;
@@ -361,11 +363,11 @@ public:
     if (vlistNumber(vlistID1) != CDI_REAL) gridsizemax *= 2;
     array = Varray<double>(gridsizemax);
 
-    varList_init(varList2, vlistID2);
+    varList2 = VarList(vlistID2);
   }
 
   void
-  run()
+  run() override
   {
     int tsID = 0;
     while (true)
@@ -376,7 +378,7 @@ public:
         cdo_taxis_copy_timestep(taxisID2, taxisID1);
         cdo_def_timestep(streamID2, tsID);
 
-        cmor_check_init(nvars, vars);
+        cmor_check_init(numVars, vars);
 
         for (int recID = 0; recID < nrecs; ++recID)
           {
@@ -403,14 +405,15 @@ public:
             size_t numMissVals;
             cdo_read_record(streamID1, array.data(), &numMissVals);
 
-            auto missval = varList2[varID2].missval;
-            auto gridsize = varList2[varID2].nwpv * varList2[varID2].gridsize;
+            const auto &var2 = varList2.vars[varID2];
+            auto missval = var2.missval;
+            auto gridsize = var2.nwpv * var2.gridsize;
 
             if (numMissVals && var.changemissval)
               {
                 for (size_t i = 0; i < gridsize; ++i)
                   {
-                    if (dbl_is_equal(array[i], var.missval_old)) array[i] = missval;
+                    if (dbl_is_equal(array[i], var.missvalOld)) array[i] = missval;
                   }
               }
 
@@ -418,7 +421,7 @@ public:
               {
                 for (size_t i = 0; i < gridsize; ++i)
                   {
-                    if (!dbl_is_equal(array[i], missval)) array[i] *= var.factor;
+                    if (dbl_is_not_equal(array[i], missval)) array[i] *= var.factor;
                   }
               }
 
@@ -428,7 +431,7 @@ public:
                 int nerr = 0;
                 for (size_t i = 0; i < gridsize; ++i)
                   {
-                    if (!dbl_is_equal(array[i], missval))
+                    if (dbl_is_not_equal(array[i], missval))
                       {
                         array[i] = cv_convert_double((const cv_converter *) var.ut_converter, array[i]);
                         if (ut_get_status() != UT_SUCCESS) nerr++;
@@ -448,20 +451,21 @@ public:
             cmor_check_prep(var, gridsize, missval, array.data());
           }
 
-        cmor_check_eval(vlistID2, nvars, vars);
+        cmor_check_eval(vlistID2, numVars, vars);
 
         tsID++;
       }
   }
+
   void
-  close()
+  close() override
   {
 
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
 
 #ifdef HAVE_UDUNITS2
-    for (int varID = 0; varID < nvars; ++varID)
+    for (int varID = 0; varID < numVars; ++varID)
       if (vars[varID].changeunits) cdo_convert_free(vars[varID].ut_converter);
 
     cdo_convert_destroy();
diff --git a/src/Setrcaname.cc b/src/Setrcaname.cc
index 3f3ad613ef5b7eade9c7af74bf617449050bb140..713c9a5301d301decc3e087a4fbc624d7ec2c4ed 100644
--- a/src/Setrcaname.cc
+++ b/src/Setrcaname.cc
@@ -102,7 +102,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
     dataIsUnchanged = data_is_unchanged();
 
@@ -126,16 +126,16 @@ public:
 
     cdo_def_vlist(streamID2, vlistID2);
 
-    varList_init(varList1, vlistID1);
+    varList1 = VarList(vlistID1);
   }
 
   void
-  run()
+  run() override
   {
     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);
@@ -150,7 +150,7 @@ public:
             if (dataIsUnchanged) { cdo_copy_record(streamID2, streamID1); }
             else
               {
-                field.init(varList1[varID]);
+                field.init(varList1.vars[varID]);
                 cdo_read_record(streamID1, field);
                 cdo_write_record(streamID2, field);
               }
@@ -161,7 +161,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID1);
     cdo_stream_close(streamID2);
diff --git a/src/Settime.cc b/src/Settime.cc
index 3ce9d159f784964272ed375cce83df82532c443f..a4ca67e0fb2cc51fe23b9240c021480bc1c15170 100644
--- a/src/Settime.cc
+++ b/src/Settime.cc
@@ -185,6 +185,7 @@ public:
     .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;
@@ -218,22 +219,19 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
-
-    // clang-format off
-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
+    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");
 
     operatorID = cdo_operator_id();
     // nargs = cdo_operator_f2(operatorID);
@@ -293,9 +291,9 @@ SHIFTTIME = module.get_id("shifttime");
     ntsteps = vlistNtsteps(vlistID1);
     auto nvars = vlistNvars(vlistID1);
 
-    varList_init(varList1, vlistID1);
+    varList1 = VarList(vlistID1);
 
-    if (ntsteps == 1 && varList_numVaryingVars(varList1) == 0) ntsteps = 0;
+    if (ntsteps == 1 && varList1.numVaryingVars() == 0) ntsteps = 0;
 
     if (ntsteps == 0)
       {
@@ -372,7 +370,7 @@ SHIFTTIME = module.get_id("shifttime");
   }
 
   void
-  run()
+  run() override
   {
     int tsID = 0;
     while (true)
@@ -481,7 +479,7 @@ SHIFTTIME = module.get_id("shifttime");
             cdo_inq_record(streamID1, &varID, &levelID);
             cdo_def_record(streamID2, varID, levelID);
 
-            field.init(varList1[varID]);
+            field.init(varList1.vars[varID]);
             cdo_read_record(streamID1, field);
             cdo_write_record(streamID2, field);
           }
@@ -490,7 +488,7 @@ SHIFTTIME = module.get_id("shifttime");
       }
   }
   void
-  close()
+  close() override
   {
 
     cdo_stream_close(streamID2);
diff --git a/src/Setzaxis.cc b/src/Setzaxis.cc
index 76241a1b7d109ba416a30b55dfaa28c0b29ca52d..d4595b3634983d9f630b564f4f05d711f1c28656 100644
--- a/src/Setzaxis.cc
+++ b/src/Setzaxis.cc
@@ -73,7 +73,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
     SETZAXIS = module.get_id("setzaxis");
     GENLEVELBOUNDS = module.get_id("genlevelbounds");
@@ -117,12 +117,12 @@ public:
   }
 
   void
-  run()
+  run() override
   {
     if (operatorID == SETZAXIS)
       {
         int found = 0;
-        nzaxis = vlistNzaxis(vlistID1);
+        nzaxis = vlistNumZaxis(vlistID1);
         for (index = 0; index < nzaxis; ++index)
           {
             zaxisID1 = vlistZaxis(vlistID1, index);
@@ -137,7 +137,7 @@ public:
       }
     else if (operatorID == GENLEVELBOUNDS)
       {
-        nzaxis = vlistNzaxis(vlistID1);
+        nzaxis = vlistNumZaxis(vlistID1);
         for (index = 0; index < nzaxis; ++index)
           {
             zaxisID1 = vlistZaxis(vlistID1, index);
@@ -208,7 +208,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID1);
     cdo_stream_close(streamID2);
diff --git a/src/Shiftxy.cc b/src/Shiftxy.cc
index ba8c87516da30e6d24568229c7cfc495b612b46e..c6408e4a11addf576b4b3a25fc9de07c6c8147eb 100644
--- a/src/Shiftxy.cc
+++ b/src/Shiftxy.cc
@@ -166,6 +166,9 @@ public:
   int vlistID1;
   int vlistID2;
 
+  VarList varList1;
+  VarList varList2;
+
   Varray<double> array1;
   Varray<double> array2;
 
@@ -178,7 +181,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
     SHIFTX = module.get_id("shiftx");
     SHIFTY = module.get_id("shifty");
@@ -204,14 +207,17 @@ public:
     vlistID1 = cdo_stream_inq_vlist(streamID1);
     vlistID2 = vlistDuplicate(vlistID1);
 
+    varList1 = VarList(vlistID1);
+    varList2 = VarList(vlistID2);
+
     taxisID1 = vlistInqTaxis(vlistID1);
     taxisID2 = taxisDuplicate(taxisID1);
     vlistDefTaxis(vlistID2, taxisID2);
 
-    auto nvars = vlistNvars(vlistID1);
-    vars = std::vector<bool>(nvars, false);
+    auto numVars = varList1.numVars();
+    vars = std::vector<bool>(numVars, false);
 
-    auto ngrids = vlistNgrids(vlistID1);
+    auto ngrids = vlistNumGrids(vlistID1);
     for (int index = 0; index < ngrids; ++index)
       {
         auto gridID1 = vlistGrid(vlistID1, index);
@@ -232,8 +238,8 @@ public:
                 vlistChangeGridIndex(vlistID2, index, gridID2);
               }
 
-            for (int varID = 0; varID < nvars; ++varID)
-              if (gridID1 == vlistInqVarGrid(vlistID1, varID)) vars[varID] = true;
+            for (const auto &var : varList1.vars)
+              if (gridID1 == var.gridID) vars[var.ID] = true;
           }
         else if (gridtype == GRID_GENERIC && gridInqXsize(gridID1) <= 1 && gridInqYsize(gridID1) <= 1) {}
         else { cdo_abort("Unsupported grid type: %s", gridNamePtr(gridtype)); }
@@ -241,9 +247,9 @@ public:
 
     {
       int varID;
-      for (varID = 0; varID < nvars; ++varID)
+      for (varID = 0; varID < numVars; ++varID)
         if (vars[varID]) break;
-      if (varID >= nvars) cdo_warning("No variables selected!");
+      if (varID >= numVars) cdo_warning("No variables selected!");
     }
 
     streamID2 = cdo_open_write(1);
@@ -255,7 +261,7 @@ public:
   }
 
   void
-  run()
+  run() override
   {
     int tsID = 0;
     while (true)
@@ -277,9 +283,9 @@ public:
 
             if (vars[varID])
               {
-                auto gridID1 = vlistInqVarGrid(vlistID1, varID);
-                auto gridsize = gridInqSize(gridID1);
-                auto missval = vlistInqVarMissval(vlistID2, varID);
+                auto gridID1 = varList1.vars[varID].gridID;
+                auto gridsize = varList1.vars[varID].gridsize;
+                auto missval = varList2.vars[varID].missval;
 
                 auto nx = gridInqXsize(gridID1);
                 auto ny = gridInqYsize(gridID1);
@@ -299,7 +305,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
diff --git a/src/Showattribute.cc b/src/Showattribute.cc
index 095ef685878e48ad6afaf83a49f6ab27d22de06b..a9f61510962198355755b69b947f5cac5d8e823c 100644
--- a/src/Showattribute.cc
+++ b/src/Showattribute.cc
@@ -13,161 +13,196 @@
 #include "util_string.h"
 #include "cdi_uuid.h"
 
-void
-print_attributes(const VarList &varList, int vlistID, int varOrGlobal, int natts, char *argument)
+static void
+printf_attname(const char *&varName, const char *attname)
 {
-  if (varOrGlobal != CDI_GLOBAL)
-    {
-      const auto &var = varList[varOrGlobal];
-      auto stdname = cdo::inq_key_string(vlistID, varOrGlobal, CDI_KEY_STDNAME);
-
-      double addoffset = 0.0, scalefactor = 1.0;
-      auto haveAddoffset = (cdiInqKeyFloat(vlistID, varOrGlobal, CDI_KEY_ADDOFFSET, &addoffset) == CDI_NOERR);
-      auto haveScalefactor = (cdiInqKeyFloat(vlistID, varOrGlobal, CDI_KEY_SCALEFACTOR, &scalefactor) == CDI_NOERR);
+  if (varName)
+    fprintf(stdout, "%s@%s = ", varName, attname);
+  else
+    fprintf(stdout, "%s = ", attname);
+}
 
-      if (argument)
+static void
+print_attr_txt(const char *varName, int vlistID, int varOrGlobal, const char *attname, int attlen)
+{
+  printf_attname(varName, attname);
+  std::vector<char> atttxt(attlen + 1);
+  cdiInqAttTxt(vlistID, varOrGlobal, attname, attlen, atttxt.data());
+  atttxt[attlen] = 0;
+  fprintf(stdout, "\"");
+  for (int i = 0; i < attlen; ++i)
+    {
+      if (atttxt[i] == '\n')
         {
-          if (stdname.size() && wildcardmatch(argument, "standard_name") == 0)
-            fprintf(stdout, "   standard_name = \"%s\"\n", stdname.c_str());
-          if (var.longname.size() && wildcardmatch(argument, "long_name") == 0)
-            fprintf(stdout, "   long_name = \"%s\"\n", var.longname.c_str());
-          if (var.units.size() && wildcardmatch(argument, "units") == 0) fprintf(stdout, "   units = \"%s\"\n", var.units.c_str());
-          if (wildcardmatch(argument, "missing_value") == 0) fprintf(stdout, "   missing_value = %g\n", var.missval);
-          if (haveAddoffset && wildcardmatch(argument, "add_offset") == 0) fprintf(stdout, "   add_offset = %g\n", addoffset);
-          if (haveScalefactor && wildcardmatch(argument, "scale_factor") == 0)
-            fprintf(stdout, "   scale_factor = %g\n", scalefactor);
+          printf("\\n");
+          /*
+          if (atttxt[i + 1] != 0)
+            {
+              printf("\"\n");
+              printf("             \"");
+            }
+            */
         }
+      else if (atttxt[i] == '"') { printf("\\\""); }
       else
-        {
-          if (stdname.size()) fprintf(stdout, "   standard_name = \"%s\"\n", stdname.c_str());
-          if (var.longname.size()) fprintf(stdout, "   long_name = \"%s\"\n", var.longname.c_str());
-          if (var.units.size()) fprintf(stdout, "   units = \"%s\"\n", var.units.c_str());
-          fprintf(stdout, "   missing_value = %g\n", var.missval);
-          if (haveAddoffset) fprintf(stdout, "   add_offset = %g\n", addoffset);
-          if (haveScalefactor) fprintf(stdout, "   scale_factor = %g\n", scalefactor);
-        }
+        printf("%c", atttxt[i]);
     }
+  printf("\"\n");
+}
 
-  for (int ia = 0; ia < natts; ia++)
+void
+print_attr_int(const char *varName, int vlistID, int varOrGlobal, const char *attname, int attlen)
+{
+  printf_attname(varName, attname);
+  std::vector<int> attint(attlen);
+  cdiInqAttInt(vlistID, varOrGlobal, attname, attlen, attint.data());
+  for (int i = 0; i < attlen; ++i)
     {
-      char attname[CDI_MAX_NAME];
-      int atttype, attlen;
-      cdiInqAtt(vlistID, varOrGlobal, ia, attname, &atttype, &attlen);
-
-      if (argument && wildcardmatch(argument, attname) != 0) continue;
+      if (i) printf(", ");
+      printf("%d", attint[i]);
+    }
+  printf("\n");
+}
 
-      if (atttype == CDI_DATATYPE_TXT)
-        {
-          std::vector<char> atttxt(attlen + 1);
-          cdiInqAttTxt(vlistID, varOrGlobal, attname, attlen, atttxt.data());
-          atttxt[attlen] = 0;
-          fprintf(stdout, "   %s = \"", attname);
-          for (int i = 0; i < attlen; ++i)
-            {
-              if (atttxt[i] == '\n')
-                {
-                  printf("\\n");
-                  if (atttxt[i + 1] != 0)
-                    {
-                      printf("\"\n");
-                      printf("             \"");
-                    }
-                }
-              else
-                printf("%c", atttxt[i]);
-            }
-          printf("\"\n");
-        }
-      else if (atttype == CDI_DATATYPE_INT32)
-        {
-          std::vector<int> attint(attlen);
-          cdiInqAttInt(vlistID, varOrGlobal, attname, attlen, attint.data());
-          fprintf(stdout, "   %s = ", attname);
-          for (int i = 0; i < attlen; ++i)
-            {
-              if (i) printf(", ");
-              printf("%d", attint[i]);
-            }
-          printf("\n");
-        }
-      else if (atttype == CDI_DATATYPE_FLT32 || atttype == CDI_DATATYPE_FLT64)
-        {
-          char fltstr[128];
-          std::vector<double> attflt(attlen);
-          cdiInqAttFlt(vlistID, varOrGlobal, attname, attlen, attflt.data());
-          fprintf(stdout, "   %s = ", attname);
-          for (int i = 0; i < attlen; ++i)
-            {
-              if (i) printf(", ");
-              if (atttype == CDI_DATATYPE_FLT32)
-                printf("%sf", double_to_att_str(Options::CDO_flt_digits, fltstr, sizeof(fltstr), attflt[i]));
-              else
-                printf("%s", double_to_att_str(Options::CDO_dbl_digits, fltstr, sizeof(fltstr), attflt[i]));
-            }
-          printf("\n");
-        }
-      else { cdo_warning("Unsupported type %i name %s", atttype, attname); }
+static void
+print_attr_flt(const char *varName, int vlistID, int varOrGlobal, const char *attname, int attlen, int atttype)
+{
+  printf_attname(varName, attname);
+  char fltstr[128];
+  std::vector<double> attflt(attlen);
+  cdiInqAttFlt(vlistID, varOrGlobal, attname, attlen, attflt.data());
+  for (int i = 0; i < attlen; ++i)
+    {
+      if (i) printf(", ");
+      if (atttype == CDI_DATATYPE_FLT32)
+        printf("%sf", double_to_att_str(Options::CDO_flt_digits, fltstr, sizeof(fltstr), attflt[i]));
+      else
+        printf("%s", double_to_att_str(Options::CDO_dbl_digits, fltstr, sizeof(fltstr), attflt[i]));
     }
+  printf("\n");
+}
 
-  if (varOrGlobal == CDI_GLOBAL)
+static void
+print_attr_special_global(int vlistID, const char *argument)
+{
+  auto gridID = vlistInqVarGrid(vlistID, 0);
+  if (gridInqType(gridID) == GRID_UNSTRUCTURED)
     {
-      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))
           {
-            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);
-              }
+            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))
           {
-            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))
               {
-                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);
-                  }
+                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))
           {
-            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))
               {
-                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);
-                  }
+                char uuidStr[uuidNumHexChars + 1] = { 0 };
+                if (cdiUUID2Str(uuid, uuidStr) == uuidNumHexChars) fprintf(stdout, "%s = \"%s\"\n", attname, uuidStr);
               }
           }
-        }
+      }
     }
 }
 
-void
-check_varname_and_print(const VarList &varList, int vlistID, int nvars, char *checkvarname, char *attname)
+static void
+print_attr_special(const char *varName, const CdoVars &cdoVars, int vlistID, int varOrGlobal, const char *argument)
+{
+  const auto &var = cdoVars[varOrGlobal];
+  auto stdname = cdo::inq_key_string(vlistID, varOrGlobal, CDI_KEY_STDNAME);
+
+  double addoffset = 0.0, scalefactor = 1.0;
+  auto haveAddoffset = (cdiInqKeyFloat(vlistID, varOrGlobal, CDI_KEY_ADDOFFSET, &addoffset) == CDI_NOERR);
+  auto haveScalefactor = (cdiInqKeyFloat(vlistID, varOrGlobal, CDI_KEY_SCALEFACTOR, &scalefactor) == CDI_NOERR);
+
+  if (argument)
+    {
+      if (stdname.size() && wildcardmatch(argument, "standard_name") == 0)
+        fprintf(stdout, "%s@standard_name = \"%s\"\n", varName, stdname.c_str());
+      if (var.longname.size() && wildcardmatch(argument, "long_name") == 0)
+        fprintf(stdout, "%s@long_name = \"%s\"\n", varName, var.longname.c_str());
+      if (var.units.size() && wildcardmatch(argument, "units") == 0)
+        fprintf(stdout, "%s@units = \"%s\"\n", varName, var.units.c_str());
+      if (wildcardmatch(argument, "missing_value") == 0) fprintf(stdout, "%s@missing_value = %g\n", varName, var.missval);
+      if (haveAddoffset && wildcardmatch(argument, "add_offset") == 0) fprintf(stdout, "%s@add_offset = %g\n", varName, addoffset);
+      if (haveScalefactor && wildcardmatch(argument, "scale_factor") == 0)
+        fprintf(stdout, "%s@scale_factor = %g\n", varName, scalefactor);
+    }
+  else
+    {
+      if (stdname.size()) fprintf(stdout, "%s@standard_name = \"%s\"\n", varName, stdname.c_str());
+      if (var.longname.size()) fprintf(stdout, "%s@long_name = \"%s\"\n", varName, var.longname.c_str());
+      if (var.units.size()) fprintf(stdout, "%s@units = \"%s\"\n", varName, var.units.c_str());
+      fprintf(stdout, "%s@missing_value = %g\n", varName, var.missval);
+      if (haveAddoffset) fprintf(stdout, "%s@add_offset = %g\n", varName, addoffset);
+      if (haveScalefactor) fprintf(stdout, "%s@scale_factor = %g\n", varName, scalefactor);
+    }
+}
+
+static void
+print_attributes(const char *varName, const CdoVars &cdoVars, int vlistID, int varOrGlobal, int natts, const char *argument)
+{
+  if (varOrGlobal != CDI_GLOBAL) print_attr_special(varName, cdoVars, vlistID, varOrGlobal, argument);
+
+  for (int ia = 0; ia < natts; ia++)
+    {
+      char attname[CDI_MAX_NAME];
+      int atttype, attlen;
+      cdiInqAtt(vlistID, varOrGlobal, ia, attname, &atttype, &attlen);
+
+      if (argument && wildcardmatch(argument, attname) != 0) continue;
+
+      if (atttype == CDI_DATATYPE_TXT)
+        print_attr_txt(varName, vlistID, varOrGlobal, attname, attlen);
+      else if (atttype == CDI_DATATYPE_INT32)
+        print_attr_int(varName, vlistID, varOrGlobal, attname, attlen);
+      else if (atttype == CDI_DATATYPE_FLT32 || atttype == CDI_DATATYPE_FLT64)
+        print_attr_flt(varName, vlistID, varOrGlobal, attname, attlen, atttype);
+      else
+        cdo_warning("Unsupported type %i name %s", atttype, attname);
+    }
+
+  if (varOrGlobal == CDI_GLOBAL) print_attr_special_global(vlistID, argument);
+}
+
+static void
+check_varname_and_print(const VarList &varList, int vlistID, char *checkvarname, char *attname)
 {
   auto lfound = false;
-  for (int varID = 0; varID < nvars; ++varID)
+  auto numVars = varList.numVars();
+  for (int varID = 0; varID < numVars; ++varID)
     {
-      const auto &var = varList[varID];
+      const auto &var = varList.vars[varID];
       if (!checkvarname || (wildcardmatch(checkvarname, var.name) == 0))
         {
           lfound = true;
-          fprintf(stdout, "%s:\n", var.name.c_str());
+          // fprintf(stdout, "%s:\n", var.name.c_str());
+          fprintf(stdout, "\n");
           int natts;
           cdiInqNatts(vlistID, varID, &natts);
-          print_attributes(varList, vlistID, varID, natts, attname);
+          print_attributes(checkvarname, varList.vars, vlistID, varID, natts, attname);
           if (!checkvarname) break;
         }
     }
@@ -195,14 +230,13 @@ public:
 
   CdoStreamID streamID;
   int operatorID;
-  int nvars;
   int vlistID;
 
   VarList varList;
 
 public:
   void
-  init()
+  init() override
   {
     SHOWATTRIBUTE = module.get_id("showattribute");
     SHOWATTSVAR = module.get_id("showattsvar");
@@ -212,35 +246,37 @@ public:
     streamID = cdo_open_read(0);
     vlistID = cdo_stream_inq_vlist(streamID);
 
-    nvars = vlistNvars(vlistID);
-
-    varList_init(varList, vlistID);
+    varList = VarList(vlistID);
   }
 
   void
-  run()
+  run() override
   {
+    auto numVars = varList.numVars();
+
     auto nargs = cdo_operator_argc();
     if (nargs == 0)
       {
         if (operatorID == SHOWATTSVAR)
-          check_varname_and_print(varList, vlistID, nvars, nullptr, nullptr);
+          check_varname_and_print(varList, vlistID, nullptr, nullptr);
         else
           {
-            for (int varID = 0; varID < nvars; ++varID)
+            for (int varID = 0; varID < numVars; ++varID)
               {
-                const auto &var = varList[varID];
-                fprintf(stdout, "%s:\n", var.name.c_str());
+                const auto &var = varList.vars[varID];
+                // fprintf(stdout, "%s:\n", var.name.c_str());
+                fprintf(stdout, "\n");
 
                 int nattsvar;
                 cdiInqNatts(vlistID, varID, &nattsvar);
-                print_attributes(varList, vlistID, varID, nattsvar, nullptr);
+                print_attributes(var.name.c_str(), varList.vars, vlistID, varID, nattsvar, nullptr);
               }
 
             int natts;
             cdiInqNatts(vlistID, CDI_GLOBAL, &natts);
-            if (natts) fprintf(stdout, "Global:\n");
-            print_attributes(varList, vlistID, CDI_GLOBAL, natts, nullptr);
+            // if (natts) fprintf(stdout, "Global:\n");
+            fprintf(stdout, "\n");
+            print_attributes(nullptr, varList.vars, vlistID, CDI_GLOBAL, natts, nullptr);
           }
       }
     else
@@ -259,11 +295,12 @@ public:
                   {
                     int natts;
                     cdiInqNatts(vlistID, CDI_GLOBAL, &natts);
-                    if (natts) fprintf(stdout, "Global:\n");
-                    print_attributes(varList, vlistID, CDI_GLOBAL, natts, input);
+                    // if (natts) fprintf(stdout, "Global:\n");
+                    fprintf(stdout, "\n");
+                    print_attributes(nullptr, varList.vars, vlistID, CDI_GLOBAL, natts, input);
                   }
                 else if (operatorID == SHOWATTSVAR)
-                  check_varname_and_print(varList, vlistID, nvars, input, nullptr);
+                  check_varname_and_print(varList, vlistID, input, nullptr);
               }
             else
               {
@@ -274,17 +311,17 @@ public:
                     *result = 0;
                     char *varname = buffer;
                     if (*varname == 0) cdo_abort("Variable name not specified!");
-                    check_varname_and_print(varList, vlistID, nvars, varname, input);
+                    check_varname_and_print(varList, vlistID, varname, input);
                   }
                 else if (operatorID == SHOWATTSVAR)
-                  check_varname_and_print(varList, vlistID, nvars, input, nullptr);
+                  check_varname_and_print(varList, vlistID, input, nullptr);
               }
           }
       }
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID);
   }
diff --git a/src/Showattribute.h b/src/Showattribute.h
deleted file mode 100644
index a145817eab74be9aace67dd8654e3bc8cd8b13f2..0000000000000000000000000000000000000000
--- a/src/Showattribute.h
+++ /dev/null
@@ -1,8 +0,0 @@
-#ifndef SHOWATTRIBUTE_H
-#define SHOWATTRIBUTE_H
-
-#include "cdo_varlist.h"
-
-void print_attributes(const VarList &varList, int vlistID, int varOrGlobal, int natts, char *argument);
-
-#endif
diff --git a/src/Showinfo.cc b/src/Showinfo.cc
index 6ee2f28c852fe31d790e0a9127e4f6c3cd520b09..6a69b0ca5c41666ab383b72a2e3fe5d64c38a831 100644
--- a/src/Showinfo.cc
+++ b/src/Showinfo.cc
@@ -24,7 +24,6 @@
 #include <cdi.h>
 
 #include "process_int.h"
-#include "Showattribute.h"
 #include "printinfo.h"
 #include "cdo_zaxis.h"
 
@@ -201,11 +200,11 @@ show_code(const VarList &varList)
   constexpr int maxOut = 25;
   int nout = 0;
 
-  int nvars = varList.size();
-  for (int varID = 0; varID < nvars; ++varID)
+  int numVars = varList.numVars();
+  for (int varID = 0; varID < numVars; ++varID)
     {
       nout++;
-      const auto &var = varList[varID];
+      const auto &var = varList.vars[varID];
       fprintf(stdout, " %d", var.code);
       print_newline(nout, maxOut);
     }
@@ -213,18 +212,14 @@ show_code(const VarList &varList)
 }
 
 static void
-show_grid(int vlistID)
+show_grid(const VarList &varList)
 {
-  auto nvars = vlistNvars(vlistID);
-
   fprintf(stdout, "# param nr | grid nr | z-axis nr:   /* Use in combination with operatores: griddes and zaxisdes */\n");
-  for (int varID = 0; varID < nvars; ++varID)
+  auto vlistID = varList.vlistID;
+  for (const auto &var : varList.vars)
     {
-      auto gridID = vlistInqVarGrid(vlistID, varID);
-      auto zaxisID = vlistInqVarZaxis(vlistID, varID);
-
-      fprintf(stdout, "      %3d     %3d      %3d\n", vlistInqVarCode(vlistID, varID), vlistGridIndex(vlistID, gridID) + 1,
-              vlistZaxisIndex(vlistID, zaxisID) + 1);
+      fprintf(stdout, "      %3d     %3d      %3d\n", var.code, vlistGridIndex(vlistID, var.gridID) + 1,
+              vlistZaxisIndex(vlistID, var.zaxisID) + 1);
     }
 }
 
@@ -234,11 +229,9 @@ show_unit(const VarList &varList)
   constexpr int maxOut = 10;
   int nout = 0;
 
-  int nvars = varList.size();
-  for (int varID = 0; varID < nvars; ++varID)
+  for (const auto &var : varList.vars)
     {
       nout++;
-      const auto &var = varList[varID];
       if (var.units.size()) fprintf(stdout, " %s", var.units.c_str());
       print_newline(nout, maxOut);
     }
@@ -252,11 +245,11 @@ show_param(const VarList &varList)
   int nout = 0;
 
   char paramstr[32];
-  int nvars = varList.size();
-  for (int varID = 0; varID < nvars; ++varID)
+  int numVars = varList.numVars();
+  for (int varID = 0; varID < numVars; ++varID)
     {
       nout++;
-      const auto &var = varList[varID];
+      const auto &var = varList.vars[varID];
       cdiParamToString(var.param, paramstr, sizeof(paramstr));
 
       fprintf(stdout, " %s", paramstr);
@@ -271,11 +264,11 @@ show_name(const VarList &varList)
   constexpr int maxOut = 10;
   int nout = 0;
 
-  int nvars = varList.size();
-  for (int varID = 0; varID < nvars; ++varID)
+  int numVars = varList.numVars();
+  for (int varID = 0; varID < numVars; ++varID)
     {
       nout++;
-      const auto &var = varList[varID];
+      const auto &var = varList.vars[varID];
       fprintf(stdout, " %s", var.name.c_str());
       print_newline(nout, maxOut);
     }
@@ -285,12 +278,12 @@ show_name(const VarList &varList)
 static void
 show_stdname(int vlistID)
 {
-  auto nvars = vlistNvars(vlistID);
+  auto numVars = vlistNvars(vlistID);
 
   constexpr int maxOut = 1;
   int nout = 0;
 
-  for (int varID = 0; varID < nvars; ++varID)
+  for (int varID = 0; varID < numVars; ++varID)
     {
       nout++;
       auto stdname = cdo::inq_key_string(vlistID, varID, CDI_KEY_STDNAME);
@@ -303,10 +296,10 @@ show_stdname(int vlistID)
 static void
 show_level(const VarList &varList)
 {
-  int nvars = varList.size();
-  for (int varID = 0; varID < nvars; ++varID)
+  int numVars = varList.numVars();
+  for (int varID = 0; varID < numVars; ++varID)
     {
-      const auto &var = varList[varID];
+      const auto &var = varList.vars[varID];
       for (int levelID = 0; levelID < var.nlevels; ++levelID) fprintf(stdout, " %.9g", cdo_zaxis_inq_level(var.zaxisID, levelID));
       fprintf(stdout, "\n");
     }
@@ -315,7 +308,7 @@ show_level(const VarList &varList)
 static void
 show_ltype(int vlistID)
 {
-  auto nzaxis = vlistNzaxis(vlistID);
+  auto nzaxis = vlistNumZaxis(vlistID);
   for (int index = 0; index < nzaxis; ++index)
     {
       auto zaxisID = vlistZaxis(vlistID, index);
@@ -326,21 +319,6 @@ show_ltype(int vlistID)
   fprintf(stdout, "\n");
 }
 
-static void
-show_atts(int vlistID, const VarList &varList)
-{
-  auto nvars = vlistNvars(vlistID);
-
-  for (int varID = 0; varID < nvars; ++varID)
-    {
-      const auto &var = varList[varID];
-      fprintf(stdout, "%s:\n", var.name.c_str());
-
-      int nattsvar;
-      cdiInqNatts(vlistID, varID, &nattsvar);
-      print_attributes(varList, vlistID, varID, nattsvar, nullptr);
-    }
-}
 class Showinfo : public Process
 {
 public:
@@ -369,6 +347,7 @@ public:
     .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;
@@ -378,64 +357,55 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
+    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");
+
+    operatorID = cdo_operator_id();
+
+    operator_check_argc(0);
+
+    streamID = cdo_open_read(0);
+    vlistID = cdo_stream_inq_vlist(streamID);
+    varList = VarList(vlistID);
+  }
 
+  void
+  run() override
+  {
     // clang-format off
-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);
-
-   streamID = cdo_open_read(0);
-  vlistID = cdo_stream_inq_vlist(streamID);
-  varList_init(varList, vlistID);}
-  void run(){
-
-  if      (operatorID == SHOWYEAR)      show_year(streamID);
-  else if (operatorID == SHOWMON)       show_mon(streamID);
-  else if (operatorID == SHOWDATE)      show_date(streamID);
-  else if (operatorID == SHOWTIME)      show_time(streamID);
-  else if (operatorID == SHOWTIMESTAMP) show_timestamp(streamID);
-  else if (operatorID == SHOWCODE)      show_code(varList);
-  else if (operatorID == SHOWGRID)      show_grid(vlistID);
-  else if (operatorID == SHOWUNIT)      show_unit(varList);
-  else if (operatorID == SHOWPARAM)     show_param(varList);
-  else if (operatorID == SHOWNAME)      show_name(varList);
-  else if (operatorID == SHOWSTDNAME)   show_stdname(vlistID);
-  else if (operatorID == SHOWLEVEL)     show_level(varList);
-  else if (operatorID == SHOWLTYPE)     show_ltype(vlistID);
-  else if (operatorID == SHOWFORMAT)
-    {
-      print_filetype(streamID, vlistID);
-    }
-  else if (operatorID == SHOWATTS || operatorID == SHOWATTSGLOB)
-    {
-      if (operatorID == SHOWATTS) show_atts(vlistID, varList);
-
-      fprintf(stdout, "Global:\n");
-      int natts;
-      cdiInqNatts(vlistID, CDI_GLOBAL, &natts);
-      print_attributes(varList, vlistID, CDI_GLOBAL, natts, nullptr);
-    }}
-  // clang-format on
+    if      (operatorID == SHOWYEAR)      show_year(streamID);
+    else if (operatorID == SHOWMON)       show_mon(streamID);
+    else if (operatorID == SHOWDATE)      show_date(streamID);
+    else if (operatorID == SHOWTIME)      show_time(streamID);
+    else if (operatorID == SHOWTIMESTAMP) show_timestamp(streamID);
+    else if (operatorID == SHOWCODE)      show_code(varList);
+    else if (operatorID == SHOWGRID)      show_grid(varList);
+    else if (operatorID == SHOWUNIT)      show_unit(varList);
+    else if (operatorID == SHOWPARAM)     show_param(varList);
+    else if (operatorID == SHOWNAME)      show_name(varList);
+    else if (operatorID == SHOWSTDNAME)   show_stdname(vlistID);
+    else if (operatorID == SHOWLEVEL)     show_level(varList);
+    else if (operatorID == SHOWLTYPE)     show_ltype(vlistID);
+    else if (operatorID == SHOWFORMAT)    print_filetype(streamID, vlistID);
+    // clang-format on
+  }
+
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID);
   }
diff --git a/src/Sinfo.cc b/src/Sinfo.cc
index 3906a1d7f380615a4d6d6ee20e57516d02ccbda7..855cef1ccfc7f6c48399cdaabdf79fc2dec3d6b4 100644
--- a/src/Sinfo.cc
+++ b/src/Sinfo.cc
@@ -19,7 +19,6 @@
 #include "printinfo.h"
 #include "mpmo_color.h"
 #include "process_int.h"
-#include "compare.h"
 #include "util_string.h"
 #include "datetime.h"
 #include "cdo_default_values.h"
@@ -108,20 +107,17 @@ limit_string_length(char *string, size_t maxlen)
 }
 
 static void
-print_vars_info(int operfunc, bool ensembleInfo, int vlistID, bool xsInfo)
+print_vars_info(int operfunc, bool ensembleInfo, const VarList &varList, int vlistID, bool xsInfo)
 {
   char tmpname[CDI_MAX_NAME];
   char paramstr[32];
 
-  VarList varList;
-  varList_init(varList, vlistID);
-
-  auto nvars = vlistNvars(vlistID);
+  auto numVars = varList.numVars();
   auto nsubtypes = vlistNsubtypes(vlistID);
 
-  for (int varID = 0; varID < nvars; ++varID)
+  for (int varID = 0; varID < numVars; ++varID)
     {
-      const auto &var = varList[varID];
+      const auto &var = varList.vars[varID];
 
       auto tabnum = tableInqNum(vlistInqVarTable(vlistID, varID));
 
@@ -147,7 +143,7 @@ print_vars_info(int operfunc, bool ensembleInfo, int vlistID, bool xsInfo)
       fprintf(stdout, "%c ", var.isConstant ? 'c' : 'v');
 
       // tsteptype
-      fprintf(stdout, "%-8s ", cdo::get_steptype_name(var.tsteptype));
+      fprintf(stdout, "%-8s ", cdo::get_steptype_name(var.stepType));
 
       // ensemble information
       if (ensembleInfo)
@@ -184,15 +180,15 @@ print_vars_info(int operfunc, bool ensembleInfo, int vlistID, bool xsInfo)
 
       // datatype
       set_text_color(stdout, BLUE);
-      fprintf(stdout, " %-3s", cdo::datatype_to_cstr(var.datatype));
-
-      // memType
-      if (xsInfo) fprintf(stdout, "   %-3s", memtype_to_cstr(var.memType));
+      fprintf(stdout, " %-3s", cdo::datatype_to_cstr(var.dataType));
 
       auto compType = vlistInqVarCompType(vlistID, varID);
       auto isCompressed = (compType != CDI_COMPRESS_NONE);
       fprintf(stdout, "%c ", isCompressed ? (int) comptype_to_name(compType)[0] : ' ');
 
+      // memType
+      if (xsInfo) fprintf(stdout, "   %-3s", memtype_to_cstr(var.memType));
+
       reset_text_color(stdout);
 
       fprintf(stdout, ": ");
@@ -402,9 +398,8 @@ private:
 
 public:
   void
-  init()
+  init() override
   {
-
     auto operatorID = cdo_operator_id();
 
     operfunc = cdo_operator_f1(operatorID);
@@ -415,12 +410,15 @@ public:
   }
 
   void
-  run()
+  run() override
   {
     for (int indf = 0; indf < cdo_stream_cnt(); indf++)
       {
         auto streamID = cdo_open_read(indf);
         auto vlistID = cdo_stream_inq_vlist(streamID);
+
+        VarList varList(vlistID);
+
         auto nsubtypes = vlistNsubtypes(vlistID);
 
         set_text_color(stdout, BRIGHT);
@@ -442,7 +440,7 @@ public:
         reset_text_color(stdout);
         fprintf(stdout, "\n");
 
-        int numVariables = vlistNvars(vlistID);
+        auto numVars = varList.numVars();
         int numRecordsConst = 0;
         int numRecordsVar = 0;
         int numValues = 0;
@@ -451,11 +449,9 @@ public:
         size_t outputSize = 0;
         if (Options::test && xsInfo)
           {
-            VarList varList;
-            varList_init(varList, vlistID);
-            for (int varID = 0; varID < numVariables; ++varID)
+            for (int varID = 0; varID < numVars; ++varID)
               {
-                const auto &var = varList[varID];
+                const auto &var = varList.vars[varID];
 
                 if (var.isConstant)
                   numRecordsConst += var.nlevels;
@@ -468,15 +464,15 @@ public:
                 // auto numBytes = (var.memType == MemType::Double) ? 8 : 4;
                 // memorySize += size * numBytes;
 
-                auto numBits = get_num_input_bits(var.datatype);
+                auto numBits = get_num_input_bits(var.dataType);
                 inputSize += (size * numBits) / 8;
 
-                numBits = get_num_output_bits(var.datatype);
+                numBits = get_num_output_bits(var.dataType);
                 outputSize += (size * numBits) / 8;
               }
           }
 
-        print_vars_info(operfunc, ensembleInfo, vlistID, xsInfo);
+        print_vars_info(operfunc, ensembleInfo, varList, vlistID, xsInfo);
 
         set_text_color(stdout, BRIGHT);
         fprintf(stdout, "   Grid coordinates");
@@ -530,7 +526,7 @@ public:
 
             int numFields = numRecordsVar * numTimesteps + numRecordsConst;
             fprintf(stdout, "%33s : %d\n", "number of fields", numFields);
-            fprintf(stdout, "%33s : %d\n", "number of variables", numVariables);
+            fprintf(stdout, "%33s : %d\n", "number of variables", numVars);
             if (numTimesteps) fprintf(stdout, "%33s : %d\n", "number of timesteps", numTimesteps);
             fprintf(stdout, "%33s : %d\n", "number of values", numTimesteps * numValues);
             // fprintf(stdout, "%33s : %s\n", "required memory", num_values_to_byte_cstr(memorySize));
@@ -543,7 +539,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
   }
 };
diff --git a/src/Smooth.cc b/src/Smooth.cc
index d7550a961d9654008a2c2a664cb72480fb9b5e98..5c4959fb042f63e1206480ed3de5a60bf6f079f5 100644
--- a/src/Smooth.cc
+++ b/src/Smooth.cc
@@ -56,7 +56,7 @@ smooth(int gridID, T missval, const Varray<T> &array1, Varray<T> &array2, const
   if (numNeighbors > gridsize) numNeighbors = gridsize;
 
   Varray<uint8_t> mask(gridsize);
-  for (size_t i = 0; i < gridsize; ++i) mask[i] = !dbl_is_equal(array1[i], missval);
+  for (size_t i = 0; i < gridsize; ++i) mask[i] = dbl_is_not_equal(array1[i], missval);
 
   gridID = generate_full_point_grid(gridID);
   if (!gridHasCoordinates(gridID)) cdo_abort("Cell center coordinates missing!");
@@ -86,7 +86,7 @@ smooth(int gridID, T missval, const Varray<T> &array1, Varray<T> &array2, const
 
   if (Options::cdoVerbose) cdo_print("Point search created: %.2f seconds (%zu points)", timer.elapsed(), gridsize);
 
-  if (Options::cdoVerbose) progress::init();
+  cdo::Progress progress;
 
   timer.reset();
 
@@ -101,7 +101,7 @@ smooth(int gridID, T missval, const Varray<T> &array1, Varray<T> &array2, const
       auto ompthID = cdo_omp_get_thread_num();
 
       atomicCount++;
-      if (Options::cdoVerbose && cdo_omp_get_thread_num() == 0) progress::update(0, 1, (double) atomicCount / gridsize);
+      if (cdo_omp_get_thread_num() == 0) progress.update((double) atomicCount / gridsize);
 
       grid_search_point_smooth(gps, xvals[i], yvals[i], knnWeights[ompthID]);
 
@@ -115,8 +115,6 @@ smooth(int gridID, T missval, const Varray<T> &array1, Varray<T> &array2, const
       if (nadds == 0) atomicNumMiss++;
     }
 
-  progress::update(0, 1, 1);
-
   size_t numMissValsx = atomicNumMiss;
   size_t numPoints = atomicSum;
 
@@ -163,7 +161,7 @@ smooth9(int gridID, T missval, const Varray<T> &array1, Varray<T> &array2)
 
   std::vector<uint8_t> mask(gridsize);
 
-  for (size_t i = 0; i < gridsize; ++i) mask[i] = !dbl_is_equal(missval, array1[i]);
+  for (size_t i = 0; i < gridsize; ++i) mask[i] = dbl_is_not_equal(missval, array1[i]);
 
   size_t numMissVals = 0;
   for (size_t i = 0; i < nlat; ++i)
@@ -350,7 +348,7 @@ public:
   inline static RegisterEntry<Smooth> registration = RegisterEntry<Smooth>(module);
 
   int SMOOTH, SMOOTH9;
-  int nvars;
+  int numVars;
   VarList varList1;
   Field field1, field2;
   std::vector<bool> varIDs;
@@ -370,7 +368,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
     SMOOTH = module.get_id("smooth");
     SMOOTH9 = module.get_id("smooth9");
@@ -391,14 +389,14 @@ public:
     taxisID2 = taxisDuplicate(taxisID1);
     vlistDefTaxis(vlistID2, taxisID2);
 
-    varList_init(varList1, vlistID1);
+    varList1 = VarList(vlistID1);
 
-    nvars = vlistNvars(vlistID1);
-    varIDs = std::vector<bool>(nvars, false);
+    numVars = vlistNvars(vlistID1);
+    varIDs = std::vector<bool>(numVars, false);
 
-    for (int varID = 0; varID < nvars; ++varID)
+    for (int varID = 0; varID < numVars; ++varID)
       {
-        const auto &var = varList1[varID];
+        const auto &var = varList1.vars[varID];
         auto gridID = var.gridID;
         auto gridtype = gridInqType(gridID);
         if (gridtype == GRID_GAUSSIAN || gridtype == GRID_LONLAT || gridtype == GRID_CURVILINEAR || gridtype == GRID_PROJECTION
@@ -421,7 +419,7 @@ public:
   }
 
   void
-  run()
+  run() override
   {
     cdo_def_vlist(streamID2, vlistID2);
 
@@ -438,7 +436,7 @@ public:
           {
             int varID, levelID;
             cdo_inq_record(streamID1, &varID, &levelID);
-            const auto &var = varList1[varID];
+            const auto &var = varList1.vars[varID];
             field1.init(var);
             field2.init(var);
             cdo_read_record(streamID1, field1);
@@ -465,7 +463,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
diff --git a/src/Sort.cc b/src/Sort.cc
index 5c9085a342d68d1a67878b31baa5a2eb288e0254..1ae5d98c99eebf312c8741a2a4ef380342722350 100644
--- a/src/Sort.cc
+++ b/src/Sort.cc
@@ -14,7 +14,6 @@
 #include <cdi.h>
 
 #include "cdo_options.h"
-#include "cdo_vlist.h"
 #include "process_int.h"
 #include "param_conversion.h"
 #include "cdo_zaxis.h"
@@ -61,10 +60,7 @@ public:
   using Process::Process;
   inline static CdoModule module = {
     .name = "Sort",
-    .operators = { { "sortcode"},
-                   { "sortparam"},
-                   { "sortname"},
-                   { "sortlevel"} },
+    .operators = { { "sortcode" }, { "sortparam" }, { "sortname" }, { "sortlevel" } },
     .aliases = { { "sortvar", "sortname" } },
     .mode = EXPOSED,     // Module mode: 0:intern 1:extern
     .number = CDI_REAL,  // Allowed number type
@@ -88,7 +84,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
     SORTCODE = module.get_id("sortcode");
     SORTPARAM = module.get_id("sortparam");
@@ -125,14 +121,14 @@ public:
     streamID2 = cdo_open_write(1);
     cdo_def_vlist(streamID2, vlistID2);
 
-    varList_init(varList1, vlistID1);
+    varList1 = VarList(vlistID1);
 
     nvars = vlistNvars(vlistID1);
 
     varsInfo = std::vector<VarInfo>(nvars);
     for (int varID = 0; varID < nvars; ++varID)
       {
-        const auto &var = varList1[varID];
+        const auto &var = varList1.vars[varID];
         varsInfo[varID].nlevels = var.nlevels;
         varsInfo[varID].levInfo.resize(var.nlevels);
       }
@@ -140,13 +136,13 @@ public:
     vardata = Varray2D<double>(nvars);
     for (int varID = 0; varID < nvars; ++varID)
       {
-        const auto &var = varList1[varID];
+        const auto &var = varList1.vars[varID];
         vardata[varID].resize(var.gridsize * var.nlevels);
       }
   }
 
   void
-  run()
+  run() override
   {
     int tsID = 0;
     while (true)
@@ -161,7 +157,7 @@ public:
           {
             int varID, levelID;
             cdo_inq_record(streamID1, &varID, &levelID);
-            const auto &var = varList1[varID];
+            const auto &var = varList1.vars[varID];
 
             if (tsID == 0)
               {
@@ -225,7 +221,7 @@ public:
         for (int vindex = 0; vindex < nvars; vindex++)
           {
             const auto &varInfo = varsInfo[vindex];
-            const auto &var = varList1[varInfo.varID];
+            const auto &var = varList1.vars[varInfo.varID];
             for (int lindex = 0; lindex < var.nlevels; ++lindex)
               {
                 auto levelID = varInfo.levInfo[lindex].levelID;
@@ -247,7 +243,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID1);
     cdo_stream_close(streamID2);
diff --git a/src/Sorttimestamp.cc b/src/Sorttimestamp.cc
index 52937e2c17183728225186e4dfa601b2f14da4d1..abb3f3442f8e571d3b855a51867cf171b5ffec29 100644
--- a/src/Sorttimestamp.cc
+++ b/src/Sorttimestamp.cc
@@ -12,11 +12,9 @@
 */
 
 #include <cdi.h>
-#include "julian_date.h"
 
 #include "cdo_options.h"
 #include "process_int.h"
-#include "cdo_vlist.h"
 #include "printinfo.h"
 #include "field_functions.h"
 
@@ -34,7 +32,7 @@ public:
   using Process::Process;
   inline static CdoModule module = {
     .name = "Sorttimestamp",
-    .operators = { { "sorttimestamp"}, { "sorttaxis"} },
+    .operators = { { "sorttimestamp" }, { "sorttaxis" } },
     .aliases = {},
     .mode = EXPOSED,     // Module mode: 0:intern 1:extern
     .number = CDI_REAL,  // Allowed number type
@@ -46,7 +44,7 @@ public:
   int lasttsID = -1;
   int nalloc = 0;
   int vlistID2 = -1, taxisID2 = -1;
-  int nvars = 0;
+  int numVars = 0;
 
   CdoStreamID streamID1;
   CdoStreamID streamID2;
@@ -56,7 +54,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
     skipSameTime = getenv_skip_same_time();
 
@@ -71,9 +69,9 @@ public:
   }
 
   void
-  run()
+  run() override
   {
-    FieldVector3D vars;
+    FieldVector3D varsData;
     std::vector<CdiDateTime> vDateTimes;
     auto nfiles = cdo_stream_cnt() - 1;
 
@@ -84,8 +82,7 @@ public:
         auto vlistID1 = cdo_stream_inq_vlist(streamID1);
         auto taxisID1 = vlistInqTaxis(vlistID1);
 
-        VarList varList1;
-        varList_init(varList1, vlistID1);
+        VarList varList1(vlistID1);
 
         if (fileID == 0)
           {
@@ -97,9 +94,9 @@ public:
                 taxisDeleteBounds(taxisID2);
               }
           }
-        else { vlist_compare(vlistID2, vlistID1, CmpVlist::All); }
+        else { vlist_compare(vlistID2, vlistID1, CmpVarList::All); }
 
-        nvars = vlistNvars(vlistID1);
+        numVars = varList1.numVars();
 
         int tsID = 0;
         while (true)
@@ -112,18 +109,18 @@ public:
                 constexpr int NALLOC_INC = 1024;
                 nalloc += NALLOC_INC;
                 vDateTimes.resize(nalloc);
-                vars.resize(nalloc);
+                varsData.resize(nalloc);
               }
 
             vDateTimes[xtsID] = taxisInqVdatetime(taxisID1);
 
-            fields_from_vlist(vlistID1, vars[xtsID]);
+            field2D_init(varsData[xtsID], varList1);
 
             for (int recID = 0; recID < nrecs; ++recID)
               {
                 cdo_inq_record(streamID1, &varID, &levelID);
-                auto &field = vars[xtsID][varID][levelID];
-                field.init(varList1[varID]);
+                auto &field = varsData[xtsID][varID][levelID];
+                field.init(varList1.vars[varID]);
                 cdo_read_record(streamID1, field);
               }
 
@@ -143,7 +140,7 @@ public:
       {
         auto julday = date_to_julday(calendar, cdiDate_get(vDateTimes[tsID].date));
         auto secofday = time_to_sec(cdiTime_get(vDateTimes[tsID].time));
-        const double jdatetime = julday + secofday / 86400.0;
+        double jdatetime = julday + secofday / 86400.0;
         timeinfo[tsID].index = tsID;
         timeinfo[tsID].datetime = jdatetime;
       }
@@ -173,8 +170,8 @@ public:
               {
                 auto lskip = false;
                 auto xtsID2 = timeinfo[lasttsID].index;
-                const auto &field1 = vars[xtsID][0][0];
-                const auto &field2 = vars[xtsID2][0][0];
+                const auto &field1 = varsData[xtsID][0][0];
+                const auto &field2 = varsData[xtsID2][0][0];
                 if (field1.memType == MemType::Float)
                   {
                     if (field1.vec_f == field2.vec_f) lskip = true;
@@ -199,12 +196,13 @@ public:
         taxisDefVdatetime(taxisID2, vDateTimes[xtsID]);
         cdo_def_timestep(streamID2, tsID2++);
 
-        for (varID = 0; varID < nvars; ++varID)
+        VarList varList2(vlistID2);
+
+        for (varID = 0; varID < numVars; ++varID)
           {
-            auto nlevels = zaxisInqSize(vlistInqVarZaxis(vlistID2, varID));
-            for (levelID = 0; levelID < nlevels; ++levelID)
+            for (levelID = 0; levelID < varList2.vars[varID].nlevels; ++levelID)
               {
-                auto &field = vars[xtsID][varID][levelID];
+                auto &field = varsData[xtsID][varID][levelID];
                 if (field.hasData())
                   {
                     cdo_def_record(streamID2, varID, levelID);
@@ -216,7 +214,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
   }
diff --git a/src/Specinfo.cc b/src/Specinfo.cc
index e6c17c0ad72b4734683479eb9bba9d283496efbd..a1783d411c61258bc4c0daba8eb32f401e262a5d 100644
--- a/src/Specinfo.cc
+++ b/src/Specinfo.cc
@@ -478,7 +478,7 @@ private:
 
 public:
   void
-  init()
+  init() override
   {
 
     operator_input_arg("Txx, TLxx, NLON=xx, NLAT=xx, NIxx or ICONRyyLxx");
@@ -495,7 +495,7 @@ public:
   }
 
   void
-  run()
+  run() override
   {
     if (argument.substr(0, 2) == "TL") { TL_TC(grid_specs2, ntr_to_nlat_linear); }
     else if (argument.substr(0, 2) == "TC") { TL_TC(grid_specs3, ntr_to_nlat_cubic); }
@@ -539,7 +539,7 @@ public:
               grid_specs3.nlevel, grid_specs3.ngp_icon);
   }
   void
-  close()
+  close() override
   {
   }
 };
diff --git a/src/Spectral.cc b/src/Spectral.cc
index 3b56d1dc09e2455198967b45ba0f9d5fd718e79d..f95a322c1c75a17da53745d2f102cbc7268bddc9 100644
--- a/src/Spectral.cc
+++ b/src/Spectral.cc
@@ -130,6 +130,7 @@ public:
   int taxisID2;
 
   int vlistID1;
+  VarList varList1;
 
   std::vector<bool> processVars;
   Varray<double> array1;
@@ -141,19 +142,16 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
-
     dataIsUnchanged = data_is_unchanged();
 
-    // clang-format off
-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
+    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");
 
     operatorID = cdo_operator_id();
 
@@ -195,6 +193,8 @@ SPCUT = module.get_id("spcut");
     vlistID1 = cdo_stream_inq_vlist(streamID1);
     auto vlistID2 = vlistDuplicate(vlistID1);
 
+    varList1 = VarList(vlistID1);
+
     taxisID1 = vlistInqTaxis(vlistID1);
     taxisID2 = taxisDuplicate(taxisID1);
     vlistDefTaxis(vlistID2, taxisID2);
@@ -261,9 +261,9 @@ SPCUT = module.get_id("spcut");
         gridID2 = gridIDsp;
       }
 
-    auto nvars = vlistNvars(vlistID2);
-    processVars = std::vector<bool>(nvars);
-    for (int varID = 0; varID < nvars; ++varID) processVars[varID] = (gridID1 == vlistInqVarGrid(vlistID1, varID));
+    auto numVars = varList1.numVars();
+    processVars = std::vector<bool>(numVars);
+    for (const auto &var : varList1.vars) processVars[var.ID] = (gridID1 == var.gridID);
 
     if (gridID1 != -1) vlistChangeGrid(vlistID2, gridID1, gridID2);
 
@@ -276,7 +276,7 @@ SPCUT = module.get_id("spcut");
   }
 
   void
-  run()
+  run() override
   {
     int tsID = 0;
     while (true)
@@ -298,7 +298,7 @@ SPCUT = module.get_id("spcut");
                 cdo_read_record(streamID1, array1.data(), &numMissVals);
                 if (numMissVals) cdo_abort("Missing values unsupported for spectral data!");
 
-                gridID1 = vlistInqVarGrid(vlistID1, varID);
+                gridID1 = varList1.vars[varID].gridID;
                 // clang-format off
                 if      (lgp2sp) grid2spec(spTrans, gridID1, array1, gridID2, array2);
                 else if (lsp2gp) spec2grid(spTrans, gridID1, array1, gridID2, array2);
@@ -327,7 +327,7 @@ SPCUT = module.get_id("spcut");
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
diff --git a/src/Spectrum.cc b/src/Spectrum.cc
index 61a8352900b3b500f9e32ff9e18b95a1d918f54b..f8fee276d44e15bc395f260c52ca6c0e98977d0b 100644
--- a/src/Spectrum.cc
+++ b/src/Spectrum.cc
@@ -14,7 +14,6 @@
 #include <cdi.h>
 
 #include "process_int.h"
-#include "cdo_vlist.h"
 #include "param_conversion.h"
 #include "cdo_fft.h"
 #include "field_functions.h"
@@ -133,7 +132,7 @@ public:
   using Process::Process;
   inline static CdoModule module = {
     .name = "Spectrum",
-    .operators = { { "spectrum"} },
+    .operators = { { "spectrum" } },
     .aliases = {},
     .mode = EXPOSED,     // Module mode: 0:intern 1:extern
     .number = CDI_REAL,  // Allowed number type
@@ -145,7 +144,6 @@ public:
   int nalloc = 0;
   size_t numMissVals;
   int freq;
-  int nvars;
 
   CdoStreamID streamID1;
   CdoStreamID streamID2;
@@ -154,19 +152,18 @@ public:
   int taxisID2;
   int vlistID1;
 
-  VarList varList;
-  FieldVector3D vars;
+  VarList varList1;
+  FieldVector3D varsData;
   std::vector<CdiDateTime> vDateTimes;
 
 public:
   void
-  init()
+  init() override
   {
-
     streamID1 = cdo_open_read(0);
 
     vlistID1 = cdo_stream_inq_vlist(streamID1);
-    const auto vlistID2 = vlistDuplicate(vlistID1);
+    auto vlistID2 = vlistDuplicate(vlistID1);
 
     taxisID1 = vlistInqTaxis(vlistID1);
     taxisID2 = cdo_taxis_create(TAXIS_ABSOLUTE);
@@ -175,18 +172,18 @@ public:
     streamID2 = cdo_open_write(1);
     cdo_def_vlist(streamID2, vlistID2);
 
-    varList_init(varList, vlistID1);
-
-    nvars = vlistNvars(vlistID1);
+    varList1 = VarList(vlistID1);
   }
 
   void
-  run()
+  run() override
   {
+    auto numVars = varList1.numVars();
+
     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;
 
         if (tsID >= nalloc)
@@ -194,20 +191,20 @@ public:
             constexpr int NALLOC_INC = 1024;
             nalloc += NALLOC_INC;
             vDateTimes.resize(nalloc);
-            vars.resize(nalloc);
+            varsData.resize(nalloc);
           }
 
         vDateTimes[tsID] = taxisInqVdatetime(taxisID1);
 
-        fields_from_vlist(vlistID1, vars[tsID]);
+        field2D_init(varsData[tsID], varList1);
 
         for (int recID = 0; recID < nrecs; ++recID)
           {
             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(), &numMissVals);
-            vars[tsID][varID][levelID].numMissVals = numMissVals;
+            const auto &var = varList1.vars[varID];
+            varsData[tsID][varID][levelID].resize(var.gridsize);
+            cdo_read_record(streamID1, varsData[tsID][varID][levelID].vec_d.data(), &numMissVals);
+            varsData[tsID][varID][levelID].numMissVals = numMissVals;
 
             if (numMissVals) cdo_abort("Missing values are not allowed!");
           }
@@ -215,7 +212,7 @@ public:
         tsID++;
       }
 
-    const int nts = tsID;
+    int nts = tsID;
 
     operator_input_arg("detrend type, length of segments, number of segments, window type\n\n"
                        "       detrend type: 0 - data should be used unchanged\n"
@@ -229,10 +226,10 @@ public:
 
     operator_check_argc(4);
 
-    const auto detrend = parameter_to_int(cdo_operator_argv(0));
-    const auto seg_l = parameter_to_int(cdo_operator_argv(1));
-    const auto seg_n = parameter_to_int(cdo_operator_argv(2));
-    const auto which_window = parameter_to_int(cdo_operator_argv(3));
+    auto detrend = parameter_to_int(cdo_operator_argv(0));
+    auto seg_l = parameter_to_int(cdo_operator_argv(1));
+    auto seg_n = parameter_to_int(cdo_operator_argv(2));
+    auto which_window = parameter_to_int(cdo_operator_argv(3));
 
     if (detrend < 0 || detrend > 3) cdo_abort("Illegal value for detrend (=%d)!", detrend);
 
@@ -241,10 +238,10 @@ public:
     if (seg_n <= 0 || seg_n > nts - seg_l + 1)
       cdo_abort("Number of segments must be positiv and not greater than %d!", nts - seg_l + 1);
 
-    const int nfreq = seg_l / 2 + 1;
+    int nfreq = seg_l / 2 + 1;
 
     FieldVector3D vars2(nfreq);
-    for (freq = 0; freq < nfreq; freq++) fields_from_vlist(vlistID1, vars2[freq], FIELD_VEC);
+    for (freq = 0; freq < nfreq; freq++) field2D_init(vars2[freq], varList1, FIELD_VEC);
 
     Varray<double> array1(nts);
     Varray<double> array2(nfreq);
@@ -276,14 +273,14 @@ public:
     double wssum = 0;
     for (k = 0; k < seg_l; ++k) wssum += window[k] * window[k];
 
-    for (varID = 0; varID < nvars; ++varID)
+    for (varID = 0; varID < numVars; ++varID)
       {
-        const auto &var = varList[varID];
+        const auto &var = varList1.vars[varID];
         for (levelID = 0; levelID < var.nlevels; ++levelID)
           {
             for (size_t i = 0; i < var.gridsize; ++i)
               {
-                for (tsID = 0; tsID < nts; ++tsID) array1[tsID] = vars[tsID][varID][levelID].vec_d[i];
+                for (tsID = 0; tsID < nts; ++tsID) array1[tsID] = varsData[tsID][varID][levelID].vec_d[i];
 
                 for (freq = 0; freq < nfreq; freq++) array2[freq] = 0;
 
@@ -299,9 +296,9 @@ public:
         taxisDefVdatetime(taxisID2, vDateTimes[0]);
         cdo_def_timestep(streamID2, tsID);
 
-        for (varID = 0; varID < nvars; ++varID)
+        for (varID = 0; varID < numVars; ++varID)
           {
-            const auto &var = varList[varID];
+            const auto &var = varList1.vars[varID];
             for (levelID = 0; levelID < var.nlevels; ++levelID)
               {
                 if (!vars2[tsID][varID][levelID].empty())
@@ -315,7 +312,7 @@ public:
       }
   }
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
diff --git a/src/Split.cc b/src/Split.cc
index 808b239883fd42235b83f07a347e57de39d38467..6eee7369b4c4e927e810b4738dfb72257fe6e1a1 100644
--- a/src/Split.cc
+++ b/src/Split.cc
@@ -36,19 +36,19 @@ gen_filename(std::string &fileName, bool swapObase, const std::string &obase, co
 }
 
 static int
-split_code(bool swapObase, const std::string &fileSuffix, std::string &fileName, int vlistID1, const VarList &varList1,
+split_code(bool swapObase, const std::string &fileSuffix, std::string &fileName, int vlistID1, const VarList &varList,
            std::vector<CdoStreamID> &streamIDs, std::vector<int> &vlistIDs)
 {
-  auto nvars = vlistNvars(vlistID1);
-  std::vector<int> codes(nvars);
+  auto numVars = varList.numVars();
+  std::vector<int> codes(numVars);
 
   int nsplit = 0;
-  for (int varID = 0; varID < nvars; ++varID)
+  for (int varID = 0; varID < numVars; ++varID)
     {
-      const auto &var = varList1[varID];
+      const auto &var = varList.vars[varID];
       int index;
       for (index = 0; index < varID; ++index)
-        if (var.code == varList1[index].code) break;
+        if (var.code == varList.vars[index].code) break;
 
       if (index == varID) codes[nsplit++] = var.code;
     }
@@ -59,9 +59,9 @@ split_code(bool swapObase, const std::string &fileSuffix, std::string &fileName,
   for (int index = 0; index < nsplit; ++index)
     {
       vlistClearFlag(vlistID1);
-      for (int varID = 0; varID < nvars; ++varID)
+      for (int varID = 0; varID < numVars; ++varID)
         {
-          const auto &var = varList1[varID];
+          const auto &var = varList.vars[varID];
           if (codes[index] == var.code)
             {
               for (int levelID = 0; levelID < var.nlevels; ++levelID)
@@ -87,20 +87,20 @@ split_code(bool swapObase, const std::string &fileSuffix, std::string &fileName,
 }
 
 static int
-split_param(bool swapObase, const std::string &fileSuffix, std::string &fileName, int vlistID1, const VarList &varList1,
+split_param(bool swapObase, const std::string &fileSuffix, std::string &fileName, int vlistID1, const VarList &varList,
             std::vector<CdoStreamID> &streamIDs, std::vector<int> &vlistIDs)
 {
   char paramstr[32];
-  auto nvars = vlistNvars(vlistID1);
-  std::vector<int> params(nvars);
+  auto numVars = varList.numVars();
+  std::vector<int> params(numVars);
 
   int nsplit = 0;
-  for (int varID = 0; varID < nvars; ++varID)
+  for (int varID = 0; varID < numVars; ++varID)
     {
-      const auto &var = varList1[varID];
+      const auto &var = varList.vars[varID];
       int index;
       for (index = 0; index < varID; ++index)
-        if (var.param == varList1[index].param) break;
+        if (var.param == varList.vars[index].param) break;
 
       if (index == varID) params[nsplit++] = var.param;
     }
@@ -111,9 +111,9 @@ split_param(bool swapObase, const std::string &fileSuffix, std::string &fileName
   for (int index = 0; index < nsplit; ++index)
     {
       vlistClearFlag(vlistID1);
-      for (int varID = 0; varID < nvars; ++varID)
+      for (int varID = 0; varID < numVars; ++varID)
         {
-          const auto &var = varList1[varID];
+          const auto &var = varList.vars[varID];
           if (params[index] == var.param)
             {
               for (int levelID = 0; levelID < var.nlevels; ++levelID)
@@ -140,11 +140,11 @@ split_param(bool swapObase, const std::string &fileSuffix, std::string &fileName
 }
 
 static int
-split_name(bool swapObase, const std::string &fileSuffix, std::string &fileName, int vlistID1, const VarList &varList1,
+split_name(bool swapObase, const std::string &fileSuffix, std::string &fileName, int vlistID1, const VarList &varList,
            std::vector<CdoStreamID> &streamIDs, std::vector<int> &vlistIDs)
 {
-  auto nvars = vlistNvars(vlistID1);
-  auto nsplit = nvars;
+  auto numVars = varList.numVars();
+  auto nsplit = numVars;
 
   vlistIDs.resize(nsplit);
   streamIDs.resize(nsplit);
@@ -153,7 +153,7 @@ split_name(bool swapObase, const std::string &fileSuffix, std::string &fileName,
     {
       vlistClearFlag(vlistID1);
       int varID = index;
-      const auto &var = varList1[varID];
+      const auto &var = varList.vars[varID];
       for (int levelID = 0; levelID < var.nlevels; ++levelID)
         {
           vlistDefIndex(vlistID1, varID, levelID, index);
@@ -174,11 +174,11 @@ split_name(bool swapObase, const std::string &fileSuffix, std::string &fileName,
 }
 
 static int
-split_level(bool swapObase, const std::string &fileSuffix, std::string &fileName, int vlistID1, const VarList &varList1,
+split_level(bool swapObase, const std::string &fileSuffix, std::string &fileName, int vlistID1, const VarList &varList,
             std::vector<CdoStreamID> &streamIDs, std::vector<int> &vlistIDs)
 {
-  auto nvars = vlistNvars(vlistID1);
-  auto nzaxis = vlistNzaxis(vlistID1);
+  auto numVars = varList.numVars();
+  auto nzaxis = vlistNumZaxis(vlistID1);
   Varray<double> ftmp(999, 0.0);
 
   int nsplit = 0;
@@ -204,9 +204,9 @@ split_level(bool swapObase, const std::string &fileSuffix, std::string &fileName
   for (int index = 0; index < nsplit; ++index)
     {
       vlistClearFlag(vlistID1);
-      for (int varID = 0; varID < nvars; ++varID)
+      for (int varID = 0; varID < numVars; ++varID)
         {
-          const auto &var = varList1[varID];
+          const auto &var = varList.vars[varID];
           for (int levelID = 0; levelID < var.nlevels; ++levelID)
             {
               auto level = cdo_zaxis_inq_level(var.zaxisID, levelID);
@@ -232,11 +232,11 @@ split_level(bool swapObase, const std::string &fileSuffix, std::string &fileName
 }
 
 static int
-split_grid(bool swapObase, const std::string &fileSuffix, std::string &fileName, int vlistID1, const VarList &varList1,
+split_grid(bool swapObase, const std::string &fileSuffix, std::string &fileName, int vlistID1, const VarList &varList,
            std::vector<CdoStreamID> &streamIDs, std::vector<int> &vlistIDs)
 {
-  auto nsplit = vlistNgrids(vlistID1);
-  auto nvars = vlistNvars(vlistID1);
+  auto nsplit = vlistNumGrids(vlistID1);
+  auto numVars = varList.numVars();
 
   vlistIDs.resize(nsplit);
   streamIDs.resize(nsplit);
@@ -246,9 +246,9 @@ split_grid(bool swapObase, const std::string &fileSuffix, std::string &fileName,
   for (int index = 0; index < nsplit; ++index)
     {
       vlistClearFlag(vlistID1);
-      for (int varID = 0; varID < nvars; ++varID)
+      for (int varID = 0; varID < numVars; ++varID)
         {
-          const auto &var = varList1[varID];
+          const auto &var = varList.vars[varID];
           if (gridIDs[index] == var.gridID)
             {
               for (int levelID = 0; levelID < var.nlevels; ++levelID)
@@ -273,11 +273,11 @@ split_grid(bool swapObase, const std::string &fileSuffix, std::string &fileName,
 }
 
 static int
-split_zaxis(bool swapObase, const std::string &fileSuffix, std::string &fileName, int vlistID1, const VarList &varList1,
+split_zaxis(bool swapObase, const std::string &fileSuffix, std::string &fileName, int vlistID1, const VarList &varList,
             std::vector<CdoStreamID> &streamIDs, std::vector<int> &vlistIDs)
 {
-  auto nsplit = vlistNzaxis(vlistID1);
-  auto nvars = vlistNvars(vlistID1);
+  auto nsplit = vlistNumZaxis(vlistID1);
+  auto numVars = varList.numVars();
 
   vlistIDs.resize(nsplit);
   streamIDs.resize(nsplit);
@@ -287,9 +287,9 @@ split_zaxis(bool swapObase, const std::string &fileSuffix, std::string &fileName
   for (int index = 0; index < nsplit; ++index)
     {
       vlistClearFlag(vlistID1);
-      for (int varID = 0; varID < nvars; ++varID)
+      for (int varID = 0; varID < numVars; ++varID)
         {
-          const auto &var = varList1[varID];
+          const auto &var = varList.vars[varID];
           if (zaxisIDs[index] == var.zaxisID)
             {
               for (int levelID = 0; levelID < var.nlevels; ++levelID)
@@ -314,14 +314,14 @@ split_zaxis(bool swapObase, const std::string &fileSuffix, std::string &fileName
 }
 
 static int
-split_tabnum(bool swapObase, const std::string &fileSuffix, std::string &fileName, int vlistID1, const VarList &varList1,
+split_tabnum(bool swapObase, const std::string &fileSuffix, std::string &fileName, int vlistID1, const VarList &varList,
              std::vector<CdoStreamID> &streamIDs, std::vector<int> &vlistIDs)
 {
-  auto nvars = vlistNvars(vlistID1);
-  std::vector<int> tabnums(nvars);
+  auto numVars = varList.numVars();
+  std::vector<int> tabnums(numVars);
 
   int nsplit = 0;
-  for (int varID = 0; varID < nvars; ++varID)
+  for (int varID = 0; varID < numVars; ++varID)
     {
       auto tabnum = tableInqNum(vlistInqVarTable(vlistID1, varID));
       int index;
@@ -337,12 +337,12 @@ split_tabnum(bool swapObase, const std::string &fileSuffix, std::string &fileNam
   for (int index = 0; index < nsplit; ++index)
     {
       vlistClearFlag(vlistID1);
-      for (int varID = 0; varID < nvars; ++varID)
+      for (int varID = 0; varID < numVars; ++varID)
         {
           auto tabnum = tableInqNum(vlistInqVarTable(vlistID1, varID));
           if (tabnums[index] == tabnum)
             {
-              const auto &var = varList1[varID];
+              const auto &var = varList.vars[varID];
               for (int levelID = 0; levelID < var.nlevels; ++levelID)
                 {
                   vlistDefIndex(vlistID1, varID, levelID, index);
@@ -382,7 +382,6 @@ public:
     .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;
@@ -399,20 +398,17 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
-
     dataIsUnchanged = data_is_unchanged();
 
-    // clang-format off
-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
+    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");
 
     auto operatorID = cdo_operator_id();
 
@@ -431,7 +427,7 @@ SPLITTABNUM = module.get_id("splittabnum");
     streamID1 = cdo_open_read(0);
     vlistID1 = cdo_stream_inq_vlist(streamID1);
 
-    varList_init(varList1, vlistID1);
+    varList1 = VarList(vlistID1);
 
     std::string fileName;
     if (!swapObase) fileName = cdo_get_obase();
@@ -476,7 +472,7 @@ SPLITTABNUM = module.get_id("splittabnum");
   }
 
   void
-  run()
+  run() override
   {
     Field field;
     int tsID = 0;
@@ -502,7 +498,7 @@ SPLITTABNUM = module.get_id("splittabnum");
             if (dataIsUnchanged) { cdo_copy_record(streamIDs[index], streamID1); }
             else
               {
-                const auto &var = varList1[varID];
+                const auto &var = varList1.vars[varID];
                 field.init(var);
                 cdo_read_record(streamID1, field);
                 cdo_write_record(streamIDs[index], field);
@@ -514,7 +510,7 @@ SPLITTABNUM = module.get_id("splittabnum");
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID1);
 
diff --git a/src/Splitdate.cc b/src/Splitdate.cc
index 9c4d7d10eed36f03b9cc2b3cf6b78074d4dfe469..971e26499154d4884d2dc45ce5c7d39a1e52e210 100644
--- a/src/Splitdate.cc
+++ b/src/Splitdate.cc
@@ -15,7 +15,6 @@
 
 #include "cdo_options.h"
 #include "process_int.h"
-#include "param_conversion.h"
 #include "util_files.h"
 #include "util_string.h"
 
@@ -32,6 +31,7 @@ public:
     .constraints = { 1, OBASE, OnlyFirst },
   };
   inline static RegisterEntry<Splitdate> registration = RegisterEntry<Splitdate>(module);
+
   int SPLITDATE;
   CdoStreamID streamID1;
   int taxisID1;
@@ -52,7 +52,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
 
     dataIsUnchanged = data_is_unchanged();
@@ -71,7 +71,7 @@ public:
     taxisID2 = taxisDuplicate(taxisID1);
     vlistDefTaxis(vlistID2, taxisID2);
 
-    varList_init(varList1, vlistID1);
+    varList1 = VarList(vlistID1);
 
     fileSuffix = FileUtils::gen_suffix(cdo_inq_filetype(streamID1), vlistID1, cdo_get_stream_name(0));
 
@@ -82,16 +82,15 @@ public:
       array.resize(gridsizemax);
     }
 
-    haveConstVars = (varList_numConstVars(varList1) > 0);
-
+    haveConstVars = (varList1.numConstVars() > 0);
     if (haveConstVars)
       {
-        int numVars = varList1.size();
+        int numVars = varList1.numVars();
         vars.resize(numVars);
 
         for (int varID = 0; varID < numVars; ++varID)
           {
-            const auto &var = varList1[varID];
+            const auto &var = varList1.vars[varID];
             if (var.isConstant)
               {
                 vars[varID].resize(var.nlevels);
@@ -107,7 +106,7 @@ public:
   }
 
   void
-  run()
+  run() override
   {
     int64_t vDate0 = -1;
 
@@ -169,10 +168,10 @@ public:
 
         if (tsID > 0 && tsID2 == 0 && haveConstVars)
           {
-            int numVars = varList1.size();
+            int numVars = varList1.numVars();
             for (int varID = 0; varID < numVars; ++varID)
               {
-                const auto &var = varList1[varID];
+                const auto &var = varList1.vars[varID];
                 if (var.isConstant)
                   {
                     for (int levelID = 0; levelID < var.nlevels; ++levelID)
@@ -200,7 +199,7 @@ public:
 
                 if (tsID == 0 && haveConstVars)
                   {
-                    const auto &var = varList1[varID];
+                    const auto &var = varList1.vars[varID];
                     if (var.isConstant)
                       {
                         varray_copy(var.gridsize, array, vars[varID][levelID].vec_d);
@@ -216,7 +215,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     if (streamID2 != CDO_STREAM_UNDEF) cdo_stream_close(streamID2);
 
diff --git a/src/Splitrec.cc b/src/Splitrec.cc
index 7ee57a464295539d5f0d05f342f8fe062e5c17f5..206b8e5f5f1424c58a75ce748ed02b9016a9125a 100644
--- a/src/Splitrec.cc
+++ b/src/Splitrec.cc
@@ -42,9 +42,8 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
-
     operator_check_argc(0);
 
     dataIsUnchanged = data_is_unchanged();
@@ -54,10 +53,11 @@ public:
 
     fileSuffix = FileUtils::gen_suffix(cdo_inq_filetype(streamID1), vlistID1, cdo_get_stream_name(0));
 
-    varList_init(varList1, vlistID1);
+    varList1 = VarList(vlistID1);
   }
+
   void
-  run()
+  run() override
   {
     int index = 0;
     int tsID = 0;
@@ -95,7 +95,7 @@ public:
             if (dataIsUnchanged) { cdo_copy_record(streamID2, streamID1); }
             else
               {
-                field.init(varList1[varID]);
+                field.init(varList1.vars[varID]);
                 cdo_read_record(streamID1, field);
                 cdo_write_record(streamID2, field);
               }
@@ -109,7 +109,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID1);
   }
diff --git a/src/Splitsel.cc b/src/Splitsel.cc
index a50e891747122ecf1a35094aa4efcb003a8c333e..12bb0620aac552fc35912557c428f081185a200c 100644
--- a/src/Splitsel.cc
+++ b/src/Splitsel.cc
@@ -54,7 +54,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
 
     dataIsUnchanged = data_is_unchanged();
@@ -88,7 +88,7 @@ public:
     taxisID2 = taxisDuplicate(taxisID1);
     vlistDefTaxis(vlistID2, taxisID2);
 
-    varList_init(varList1, vlistID1);
+    varList1 = VarList(vlistID1);
 
     fileSuffix = FileUtils::gen_suffix(cdo_inq_filetype(streamID1), vlistID1, cdo_get_stream_name(0));
 
@@ -99,16 +99,15 @@ public:
       array.resize(gridsizemax);
     }
 
-    haveConstVars = (varList_numConstVars(varList1) > 0);
-
+    haveConstVars = (varList1.numConstVars() > 0);
     if (haveConstVars)
       {
-        int numVars = varList1.size();
+        int numVars = varList1.numVars();
         fields.resize(numVars);
 
         for (int varID = 0; varID < numVars; ++varID)
           {
-            const auto &var = varList1[varID];
+            const auto &var = varList1.vars[varID];
             if (var.isConstant)
               {
                 fields[varID].resize(var.nlevels);
@@ -123,7 +122,7 @@ public:
   }
 
   void
-  run()
+  run() override
   {
     int index = 1;
     int tsID;
@@ -145,7 +144,7 @@ public:
               int varID, levelID;
               cdo_inq_record(streamID1, &varID, &levelID);
 
-              const auto &var = varList1[varID];
+              const auto &var = varList1.vars[varID];
               if (var.isConstant)
                 {
                   auto &field = fields[varID][levelID];
@@ -181,10 +180,10 @@ public:
 
             if (tsID > 0 && tsID2 == 0 && haveConstVars)
               {
-                int numVars = varList1.size();
+                int numVars = varList1.numVars();
                 for (int varID = 0; varID < numVars; ++varID)
                   {
-                    const auto &var = varList1[varID];
+                    const auto &var = varList1.vars[varID];
                     if (var.isConstant)
                       {
                         for (int levelID = 0; levelID < var.nlevels; ++levelID)
@@ -212,7 +211,7 @@ public:
 
                     if (tsID == 0 && haveConstVars)
                       {
-                        const auto &var = varList1[varID];
+                        const auto &var = varList1.vars[varID];
                         if (var.isConstant)
                           {
                             auto &field = fields[varID][levelID];
@@ -245,7 +244,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID1);
 
diff --git a/src/Splittime.cc b/src/Splittime.cc
index f819a12280df8fdda2d66086572ec441c535d33d..27ac0857e8398ebff86b285ae5164c560c1deb37 100644
--- a/src/Splittime.cc
+++ b/src/Splittime.cc
@@ -67,6 +67,7 @@ public:
     .constraints = { 1, OBASE, OnlyFirst },
   };
   inline static RegisterEntry<Splittime> registration = RegisterEntry<Splittime>(module);
+
   int SPLITMON, SPLITSEAS;
   CdoStreamID streamID1;
   CdoStreamID streamID2;
@@ -95,15 +96,12 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
-
     dataIsUnchanged = data_is_unchanged();
 
-    // clang-format off
-SPLITMON = module.get_id("splitmon");
-SPLITSEAS = module.get_id("splitseas");
-    // clang-format on
+    SPLITMON = module.get_id("splitmon");
+    SPLITSEAS = module.get_id("splitseas");
 
     operatorID = cdo_operator_id();
     operfunc = cdo_operator_f1(operatorID);
@@ -128,7 +126,7 @@ SPLITSEAS = module.get_id("splitseas");
 
     fileSuffix = FileUtils::gen_suffix(cdo_inq_filetype(streamID1), vlistID1, cdo_get_stream_name(0));
 
-    varList_init(varList1, vlistID1);
+    varList1 = VarList(vlistID1);
 
     //  if (! dataIsUnchanged)
     {
@@ -137,16 +135,16 @@ SPLITSEAS = module.get_id("splitseas");
       array.resize(gridsizemax);
     }
 
-    haveConstVars = (varList_numConstVars(varList1) > 0);
+    haveConstVars = (varList1.numConstVars() > 0);
 
     if (haveConstVars)
       {
-        int numVars = varList1.size();
+        int numVars = varList1.numVars();
         vars.resize(numVars);
 
         for (int varID = 0; varID < numVars; ++varID)
           {
-            const auto &var = varList1[varID];
+            const auto &var = varList1.vars[varID];
             if (var.isConstant)
               {
                 vars[varID].resize(var.nlevels);
@@ -162,7 +160,7 @@ SPLITSEAS = module.get_id("splitseas");
   }
 
   void
-  run()
+  run() override
   {
     auto seasonNames = get_season_name();
 
@@ -223,10 +221,10 @@ SPLITSEAS = module.get_id("splitseas");
 
         if (tsID > 0 && tsIDs[index] == 0 && haveConstVars)
           {
-            int numVars = varList1.size();
+            int numVars = varList1.numVars();
             for (int varID = 0; varID < numVars; ++varID)
               {
-                const auto &var = varList1[varID];
+                const auto &var = varList1.vars[varID];
                 if (var.isConstant)
                   {
                     for (int levelID = 0; levelID < var.nlevels; ++levelID)
@@ -241,8 +239,7 @@ SPLITSEAS = module.get_id("splitseas");
 
         for (int recID = 0; recID < nrecs; ++recID)
           {
-            int varID, levelID;
-            cdo_inq_record(streamID1, &varID, &levelID);
+            auto [varID, levelID] = cdo_inq_record(streamID1);
             cdo_def_record(streamID2, varID, levelID);
 
             if (dataIsUnchanged && !(tsID == 0 && haveConstVars)) { cdo_copy_record(streamID2, streamID1); }
@@ -254,7 +251,7 @@ SPLITSEAS = module.get_id("splitseas");
 
                 if (tsID == 0 && haveConstVars)
                   {
-                    const auto &var = varList1[varID];
+                    const auto &var = varList1.vars[varID];
                     if (var.isConstant)
                       {
                         varray_copy(var.gridsize, array, vars[varID][levelID].vec_d);
@@ -270,7 +267,7 @@ SPLITSEAS = module.get_id("splitseas");
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID1);
 
diff --git a/src/Splityear.cc b/src/Splityear.cc
index 0d8f6ca169d68d10490f0517ed598dca265310aa..dfed54384cf6e10e93a65371003165ce58172aa1 100644
--- a/src/Splityear.cc
+++ b/src/Splityear.cc
@@ -20,7 +20,7 @@
 #include "util_files.h"
 #include "util_string.h"
 
-#define MAX_YEARS 99999
+constexpr int MaxYears = 99999;
 
 class Splityear : public Process
 {
@@ -35,10 +35,11 @@ public:
     .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);
+  Varray<int> cyear = Varray<int>(MaxYears, 0);
   std::string fileSuffix;
   int taxisID1;
   int taxisID2;
@@ -52,15 +53,13 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
-
     dataIsUnchanged = data_is_unchanged();
 
-    // clang-format off
-SPLITYEAR = module.get_id("splityear");
-SPLITYEARMON = module.get_id("splityearmon");
-    // clang-format on
+    SPLITYEAR = module.get_id("splityear");
+    SPLITYEARMON = module.get_id("splityearmon");
+
     operatorID = cdo_operator_id();
 
     operator_check_argc(0);
@@ -83,18 +82,18 @@ SPLITYEARMON = module.get_id("splityearmon");
       array.resize(gridsizemax);
     }
 
-    varList_init(varList1, vlistID1);
+    varList1 = VarList(vlistID1);
 
-    haveConstVars = (varList_numConstVars(varList1) > 0);
+    haveConstVars = (varList1.numConstVars() > 0);
 
     if (haveConstVars)
       {
-        int numVars = varList1.size();
+        int numVars = varList1.numVars();
         vars.resize(numVars);
 
         for (int varID = 0; varID < numVars; ++varID)
           {
-            const auto &var = varList1[varID];
+            const auto &var = varList1.vars[varID];
             if (var.isConstant)
               {
                 vars[varID].resize(var.nlevels);
@@ -110,7 +109,7 @@ SPLITYEARMON = module.get_id("splityearmon");
   }
 
   void
-  run()
+  run() override
   {
     int ic = 0;
     int index1 = -INT_MAX;
@@ -137,7 +136,7 @@ SPLITYEARMON = module.get_id("splityearmon");
                 tsID2 = 0;
 
                 ic = (year1 != year2) ? 0 : ic + 1;
-                if (year2 >= 0 && year2 < MAX_YEARS)
+                if (year2 >= 0 && year2 < MaxYears)
                   {
                     ic = cyear[year2];
                     cyear[year2]++;
@@ -184,10 +183,10 @@ SPLITYEARMON = module.get_id("splityearmon");
 
         if (tsID > 0 && tsID2 == 0 && haveConstVars)
           {
-            int numVars = varList1.size();
+            int numVars = varList1.numVars();
             for (int varID = 0; varID < numVars; ++varID)
               {
-                const auto &var = varList1[varID];
+                const auto &var = varList1.vars[varID];
                 if (var.isConstant)
                   {
                     for (int levelID = 0; levelID < var.nlevels; ++levelID)
@@ -202,8 +201,7 @@ SPLITYEARMON = module.get_id("splityearmon");
 
         for (int recID = 0; recID < nrecs; ++recID)
           {
-            int varID, levelID;
-            cdo_inq_record(streamID1, &varID, &levelID);
+            auto [varID, levelID] = cdo_inq_record(streamID1);
             cdo_def_record(streamID2, varID, levelID);
 
             if (dataIsUnchanged && !(tsID == 0 && haveConstVars)) { cdo_copy_record(streamID2, streamID1); }
@@ -215,7 +213,7 @@ SPLITYEARMON = module.get_id("splityearmon");
 
                 if (tsID == 0 && haveConstVars)
                   {
-                    const auto &var = varList1[varID];
+                    const auto &var = varList1.vars[varID];
                     if (var.isConstant)
                       {
                         varray_copy(var.gridsize, array, vars[varID][levelID].vec_d);
@@ -231,7 +229,7 @@ SPLITYEARMON = module.get_id("splityearmon");
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID1);
     cdo_stream_close(streamID2);
diff --git a/src/Tee.cc b/src/Tee.cc
index 51d58cd81a044a65fdeb411ceaed2e67f9aa0979..f83ecd0fcb01849716e23b7ab96315e460451806 100644
--- a/src/Tee.cc
+++ b/src/Tee.cc
@@ -22,6 +22,7 @@ public:
     .constraints = { 1, 1, NoRestriction },
   };
   inline static RegisterEntry<Tee> registration = RegisterEntry<Tee>(module);
+
   Field field;
   CdoStreamID streamID1;
   CdoStreamID streamID2;
@@ -33,7 +34,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
     operator_check_argc(1);
 
@@ -58,11 +59,11 @@ public:
     cdo_def_vlist(streamID2, vlistID2);
     streamDefVlist(streamID3, vlistID3);
 
-    varList_init(varList1, vlistID1);
+    varList1 = VarList(vlistID1);
   }
 
   void
-  run()
+  run() override
   {
     int tsID = 0;
     while (true)
@@ -78,9 +79,8 @@ public:
 
         for (int recID = 0; recID < nrecs; ++recID)
           {
-            int varID, levelID;
-            cdo_inq_record(streamID1, &varID, &levelID);
-            field.init(varList1[varID]);
+            auto [varID, levelID] = cdo_inq_record(streamID1);
+            field.init(varList1.vars[varID]);
             cdo_read_record(streamID1, field);
 
             cdo_def_record(streamID2, varID, levelID);
@@ -98,7 +98,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID1);
     cdo_stream_close(streamID2);
diff --git a/src/Templates.cc b/src/Templates.cc
index 5e7e79123a8530bf77ba208707b8360c70c3878f..3ddf62464699208cc7b8f59a5bf1606b060e27e0 100644
--- a/src/Templates.cc
+++ b/src/Templates.cc
@@ -30,7 +30,7 @@ public:
 
     streamID1 = cdo_open_read(0);
 
-    const auto vlistID1 = cdo_stream_inq_vlist(streamID1);
+    auto vlistID1 = cdo_stream_inq_vlist(streamID1);
     vlistID2 = vlistDuplicate(vlistID1);
 
     taxisID1 = vlistInqTaxis(vlistID1);
@@ -53,7 +53,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);
@@ -122,7 +122,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);
diff --git a/src/Test.cc b/src/Test.cc
index 568b343d19f49be0e9082a8aebb55ff65b7d52ee..9166626e3f576991ed295fd13bfb2012d265d73f 100644
--- a/src/Test.cc
+++ b/src/Test.cc
@@ -22,11 +22,11 @@ class Testdata : public Process
     .constraints = { 1, 1, NoRestriction },
   };
   inline static RegisterEntry<Testdata> registration = RegisterEntry<Testdata>(module);
+
   CdoStreamID streamID1;
+  CdoStreamID streamID2;
   int taxisID1;
   int vlistID1;
-
-  CdoStreamID streamID2;
   int taxisID2;
 
   size_t gridsize;
@@ -40,9 +40,8 @@ class Testdata : public Process
 
 public:
   void
-  init()
+  init() override
   {
-
     operator_check_argc(0);
 
     streamID1 = cdo_open_read(0);
@@ -67,14 +66,17 @@ public:
 
     fp = std::fopen("testdata", "w");
   }
+
   void
-  run()
+  run() override
   {
+    VarList varList1(vlistID1);
+
     int tsID2 = 0;
     int tsID1 = 0;
     while (true)
       {
-        const auto nrecs = cdo_stream_inq_timestep(streamID1, tsID1);
+        auto nrecs = cdo_stream_inq_timestep(streamID1, tsID1);
         if (nrecs == 0) break;
 
         cdo_taxis_copy_timestep(taxisID2, taxisID1);
@@ -89,7 +91,7 @@ public:
             size_t numMissVals;
             cdo_read_record(streamID1, array.data(), &numMissVals);
 
-            gridsize = gridInqSize(vlistInqVarGrid(vlistID1, varID));
+            gridsize = varList1.vars[varID].gridsize;
             for (size_t i = 0; i < gridsize; ++i)
               {
                 fval[i] = (float) array[i];
@@ -116,8 +118,9 @@ public:
         tsID2++;
       }
   }
+
   void
-  close()
+  close() override
   {
     std::fclose(fp);
     cdo_stream_close(streamID1);
diff --git a/src/Tests.cc b/src/Tests.cc
index 15d36625212c388b32b06b7339e84c8fbdb606b4..8db53ba8f2302780aaa0010b47c919bde079e466 100644
--- a/src/Tests.cc
+++ b/src/Tests.cc
@@ -46,7 +46,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
     NORMAL = module.get_id("normal");
     STUDENTT = module.get_id("studentt");
@@ -103,11 +103,11 @@ public:
     array1 = Varray<double>(gridsizemax);
     array2 = Varray<double>(gridsizemax);
 
-    varList_init(varList1, vlistID1);
+    varList1 = VarList(vlistID1);
   }
 
   void
-  run()
+  run() override
   {
     int tsID = 0;
     while (true)
@@ -120,13 +120,12 @@ public:
 
         for (int recID = 0; recID < nrecs; ++recID)
           {
-            int varID, levelID;
+            auto [varID, levelID] = cdo_inq_record(streamID1);
             size_t numMissVals;
-            cdo_inq_record(streamID1, &varID, &levelID);
             cdo_read_record(streamID1, array1.data(), &numMissVals);
 
-            auto gridsize = varList1[varID].gridsize;
-            auto missval = varList1[varID].missval;
+            auto gridsize = varList1.vars[varID].gridsize;
+            auto missval = varList1.vars[varID].missval;
 
             if (operatorID == NORMAL)
               {
@@ -168,7 +167,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID1);
     cdo_stream_close(streamID2);
diff --git a/src/Timcount.cc b/src/Timcount.cc
index 6cdf57c38de2adb408356f9e964a67f801170b24..d9137b4b3a067f1287866b03f7420addd10054c3 100644
--- a/src/Timcount.cc
+++ b/src/Timcount.cc
@@ -30,11 +30,11 @@ 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} },
+    .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
@@ -48,24 +48,17 @@ public:
   int taxisID2;
 
   int compareDate;
-  int maxrecs;
 
   CdiDateTime vDateTime0{};
   CdiDateTime vDateTimeN{};
 
   VarList varList1;
-  Field field;
-  FieldVector2D vars1;
-  std::vector<RecordInfo> recList;
+  FieldVector2D varsData1;
 
 public:
   void
-  init()
+  init() override
   {
-
-    // clang-format off
-    // clang-format on
-
     auto operatorID = cdo_operator_id();
     compareDate = cdo_operator_f2(operatorID);
 
@@ -88,16 +81,18 @@ public:
     streamID2 = cdo_open_write(1);
     cdo_def_vlist(streamID2, vlistID2);
 
-    maxrecs = vlistNrecs(vlistID1);
-    recList = std::vector<RecordInfo>(maxrecs);
-
-    varList_init(varList1, vlistID1);
+    varList1 = VarList(vlistID1);
 
-    fields_from_vlist(vlistID1, vars1, FIELD_VEC);
+    field2D_init(varsData1, varList1, FIELD_VEC);
   }
+
   void
-  run()
+  run() override
   {
+    Field field;
+    auto maxRecords = varList1.numRecords();
+    std::vector<RecordInfo> recordList(maxRecords);
+
     int tsID = 0;
     int otsID = 0;
     while (true)
@@ -121,23 +116,22 @@ public:
 
             for (int recID = 0; recID < nrecs; ++recID)
               {
-                int varID, levelID;
-                cdo_inq_record(streamID1, &varID, &levelID);
+                auto [varID, levelID] = cdo_inq_record(streamID1);
 
-                if (tsID == 0) recList[recID].set(varID, levelID);
+                if (tsID == 0) recordList[recID].set(varID, levelID);
 
-                const size_t fieldsize = vars1[varID][levelID].size;
+                size_t fieldsize = varsData1[varID][levelID].size;
 
                 if (numSets == 0)
                   {
-                    for (size_t i = 0; i < fieldsize; ++i) vars1[varID][levelID].vec_d[i] = vars1[varID][levelID].missval;
-                    vars1[varID][levelID].numMissVals = fieldsize;
+                    for (size_t i = 0; i < fieldsize; ++i) varsData1[varID][levelID].vec_d[i] = varsData1[varID][levelID].missval;
+                    varsData1[varID][levelID].numMissVals = fieldsize;
                   }
 
-                field.init(varList1[varID]);
+                field.init(varList1.vars[varID]);
                 cdo_read_record(streamID1, field);
 
-                field2_count(vars1[varID][levelID], field);
+                field2_count(varsData1[varID][levelID], field);
               }
 
             vDateTimeN = vDateTime;
@@ -150,13 +144,13 @@ public:
         taxisDefVdatetime(taxisID2, vDateTimeN);
         cdo_def_timestep(streamID2, otsID);
 
-        for (int recID = 0; recID < maxrecs; ++recID)
+        for (int recID = 0; recID < maxRecords; ++recID)
           {
-            auto [varID, levelID] = recList[recID].get();
-            if (otsID && varList1[varID].isConstant) continue;
+            auto [varID, levelID] = recordList[recID].get();
+            if (otsID && varList1.vars[varID].isConstant) continue;
 
             cdo_def_record(streamID2, varID, levelID);
-            cdo_write_record(streamID2, vars1[varID][levelID].vec_d.data(), vars1[varID][levelID].numMissVals);
+            cdo_write_record(streamID2, varsData1[varID][levelID].vec_d.data(), varsData1[varID][levelID].numMissVals);
           }
 
         if (nrecs == 0) break;
@@ -165,7 +159,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
diff --git a/src/Timcumsum.cc b/src/Timcumsum.cc
index 8792c1f9a6d86eb888a76a0e2118c19452ac2a5b..25fb2d644e15f030bf982be1ec85fb27b9ce67a5 100644
--- a/src/Timcumsum.cc
+++ b/src/Timcumsum.cc
@@ -29,6 +29,7 @@ public:
     .constraints = { 1, 1, NoRestriction },
   };
   inline static RegisterEntry<Timcumsum> registration = RegisterEntry<Timcumsum>(module);
+
   CdoStreamID streamID1;
   int taxisID1;
   CdoStreamID streamID2;
@@ -37,13 +38,12 @@ public:
   int varID, levelID;
 
   Field field;
-  FieldVector2D vars1;
+  FieldVector2D varsData1;
 
 public:
   void
-  init()
+  init() override
   {
-
     operator_check_argc(0);
 
     streamID1 = cdo_open_read(0);
@@ -63,10 +63,11 @@ public:
 
     field.resize(gridsizemax);
 
-    fields_from_vlist(vlistID1, vars1, FIELD_VEC);
+    VarList varList1(vlistID1);
+    field2D_init(varsData1, varList1, FIELD_VEC);
   }
   void
-  run()
+  run() override
   {
     int tsID = 0;
     while (true)
@@ -81,7 +82,7 @@ public:
           {
             cdo_inq_record(streamID1, &varID, &levelID);
 
-            auto &rvars1 = vars1[varID][levelID];
+            auto &rvars1 = varsData1[varID][levelID];
 
             auto fieldsize = rvars1.size;
 
@@ -113,8 +114,9 @@ public:
         tsID++;
       }
   }
+
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
diff --git a/src/Timfillmiss.cc b/src/Timfillmiss.cc
index 9c223f2738f1b2eee894789a404fc67988524361..3c998267b76bdd2b61f88a3c812f7b548ffab2d3 100644
--- a/src/Timfillmiss.cc
+++ b/src/Timfillmiss.cc
@@ -12,7 +12,6 @@
 #include <cdi.h>
 
 #include "process_int.h"
-#include "cdo_vlist.h"
 #include "param_conversion.h"
 #include "cdo_options.h"
 #include "datetime.h"
@@ -52,11 +51,11 @@ private:
   int taxisID1;
   int taxisID2;
 
-  VarList varList;
-  FieldVector3D vars;
+  VarList varList1;
+  FieldVector3D varsData;
 
   int calendar;
-  int nvars;
+  int numVars;
 
   FillMethod method{ FillMethod::Nearest };
   int limit{ 0 };
@@ -67,16 +66,6 @@ private:
   Varray<double> timeValues;
   int numSteps;
 
-  FillMethod
-  convert_fillmethod(const std::string &methodStr)
-  {
-    auto fillMethod = string_to_fillmethod(methodStr);
-
-    if (fillMethod == FillMethod::Undefined) cdo_abort("method=%s unsupported!", methodStr);
-
-    return fillMethod;
-  }
-
   void
   get_parameter()
   {
@@ -98,7 +87,7 @@ private:
             const auto &value = kv.values[0];
 
             // clang-format off
-            if      (key == "method")   method = convert_fillmethod(parameter_to_word(value));
+            if      (key == "method")   method = convert<FillMethod>(value);
             else if (key == "limit")    limit = parameter_to_int(value);
             else if (key == "max_gaps") maxGaps = parameter_to_int(value);
             else cdo_abort("Invalid parameter key >%s<!", key);
@@ -109,7 +98,7 @@ private:
 
 public:
   void
-  init()
+  init() override
   {
     get_parameter();
     limit = std::max(limit, 0);
@@ -129,15 +118,14 @@ public:
 
     calendar = taxisInqCalendar(taxisID1);
 
-    varList_init(varList, vlistID1);
-
-    nvars = vlistNvars(vlistID1);
+    varList1 = VarList(vlistID1);
+    numVars = varList1.numVars();
   }
 
   void
   step(int varID)
   {
-    const auto &var = varList[varID];
+    const auto &var = varList1.vars[varID];
     auto fieldMemType = var.memType;
     auto gridsize = var.gridsize;
     auto missval = var.missval;
@@ -152,9 +140,9 @@ public:
             auto &dataValues = dataValues2D[ompthID];
 
             if (fieldMemType == MemType::Float)
-              for (int t = 0; t < numSteps; ++t) dataValues[t] = vars[t][varID][levelID].vec_f[i];
+              for (int t = 0; t < numSteps; ++t) dataValues[t] = varsData[t][varID][levelID].vec_f[i];
             else
-              for (int t = 0; t < numSteps; ++t) dataValues[t] = vars[t][varID][levelID].vec_d[i];
+              for (int t = 0; t < numSteps; ++t) dataValues[t] = varsData[t][varID][levelID].vec_d[i];
 
             if (method == FillMethod::Nearest)
               fill_1d_nearest(numSteps, timeValues, dataValues, missval, limit, maxGaps);
@@ -166,15 +154,15 @@ public:
               fill_1d_backward(numSteps, dataValues, missval, limit, maxGaps);
 
             if (fieldMemType == MemType::Float)
-              for (int t = 0; t < numSteps; ++t) vars[t][varID][levelID].vec_f[i] = dataValues[t];
+              for (int t = 0; t < numSteps; ++t) varsData[t][varID][levelID].vec_f[i] = dataValues[t];
             else
-              for (int t = 0; t < numSteps; ++t) vars[t][varID][levelID].vec_d[i] = dataValues[t];
+              for (int t = 0; t < numSteps; ++t) varsData[t][varID][levelID].vec_d[i] = dataValues[t];
           }
       }
   }
 
   void
-  run()
+  run() override
   {
     int tsID = 0;
     while (true)
@@ -183,18 +171,17 @@ public:
         if (nrecs == 0) break;
 
         constexpr size_t NALLOC_INC = 1024;
-        if ((size_t) tsID >= vars.size()) vars.resize(vars.size() + NALLOC_INC);
+        if ((size_t) tsID >= varsData.size()) varsData.resize(varsData.size() + NALLOC_INC);
 
         dtlist.taxis_inq_timestep(taxisID1, tsID);
 
-        fields_from_vlist(vlistID1, vars[tsID]);
+        field2D_init(varsData[tsID], varList1);
 
         for (int recID = 0; recID < nrecs; ++recID)
           {
-            int varID, levelID;
-            cdo_inq_record(streamID1, &varID, &levelID);
-            auto &field = vars[tsID][varID][levelID];
-            field.init(varList[varID]);
+            auto [varID, levelID] = cdo_inq_record(streamID1);
+            auto &field = varsData[tsID][varID][levelID];
+            field.init(varList1.vars[varID]);
             cdo_read_record(streamID1, field);
           }
 
@@ -213,18 +200,18 @@ public:
     dataValues2D = Varray2D<double>(Threading::ompNumThreads);
     for (auto &dataValues : dataValues2D) dataValues.resize(numSteps);
 
-    for (int varID = 0; varID < nvars; ++varID) { step(varID); }
+    for (int varID = 0; varID < numVars; ++varID) { step(varID); }
 
     for (tsID = 0; tsID < numSteps; ++tsID)
       {
         dtlist.taxis_def_timestep(taxisID2, tsID);
         cdo_def_timestep(streamID2, tsID);
 
-        for (int varID = 0; varID < nvars; ++varID)
+        for (int varID = 0; varID < numVars; ++varID)
           {
-            for (int levelID = 0; levelID < varList[varID].nlevels; ++levelID)
+            for (int levelID = 0; levelID < varList1.vars[varID].nlevels; ++levelID)
               {
-                auto &field = vars[tsID][varID][levelID];
+                auto &field = varsData[tsID][varID][levelID];
                 if (field.hasData())
                   {
                     cdo_def_record(streamID2, varID, levelID);
@@ -237,7 +224,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
diff --git a/src/Timpctl.cc b/src/Timpctl.cc
index 3d0d4d5c6d3c813da67f4ad2a3ac3f1de627bf13..4c92d7ef09ba1fd66e2c23c8c1ebb70ce6a1de1c 100644
--- a/src/Timpctl.cc
+++ b/src/Timpctl.cc
@@ -45,26 +45,22 @@ public:
     .constraints = { 3, 1, NoRestriction },
   };
   inline static RegisterEntry<Timpctl> registration = RegisterEntry<Timpctl>(module);
+
   CdiDateTime vDateTime0{};
 
   CdoStreamID streamID1;
-  int taxisID1;
-
   CdoStreamID streamID2;
-  int taxisID2;
-
   CdoStreamID streamID3;
-  int taxisID3;
-
   CdoStreamID streamID4;
+
+  int taxisID1;
+  int taxisID2;
+  int taxisID3;
   int taxisID4;
 
   int compareDate;
-  int maxrecs;
   double pn;
 
-  std::vector<RecordInfo> recList;
-  FieldVector constFields;
   DateTimeList dtlist;
   Field field1, field2;
 
@@ -73,15 +69,12 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
-
-    // clang-format off
-    // clang-format on
     operator_input_arg("percentile number");
     pn = parameter_to_double(cdo_operator_argv(0));
 
-    int operatorID = cdo_operator_id();
+    auto operatorID = cdo_operator_id();
     compareDate = cdo_operator_f2(operatorID);
 
     streamID1 = cdo_open_read(0);
@@ -95,8 +88,12 @@ public:
 
     vlist_unpack(vlistID4);
 
-    vlist_compare(vlistID1, vlistID2, CmpVlist::All);
-    vlist_compare(vlistID1, vlistID3, CmpVlist::All);
+    varList1 = VarList(vlistID1);
+    VarList varList2(vlistID2);
+    VarList varList3(vlistID3);
+
+    varList_compare(varList1, varList2);
+    varList_compare(varList1, varList3);
 
     if (cdo_operator_f2(operatorID) == 16) vlistDefNtsteps(vlistID4, 1);
 
@@ -112,27 +109,25 @@ public:
     streamID4 = cdo_open_write(3);
     cdo_def_vlist(streamID4, vlistID4);
 
-    auto ntsteps = vlistNtsteps(vlistID1);
-    auto nvars = vlistNvars(vlistID1);
-
-    maxrecs = vlistNrecs(vlistID1);
-    recList = std::vector<RecordInfo>(maxrecs);
-
-    constFields = FieldVector(maxrecs);
+    auto numVars = varList1.numVars();
+    auto numSteps = varList1.numSteps();
 
     dtlist.set_stat(TimeStat::MEAN);
     dtlist.set_calendar(taxisInqCalendar(taxisID1));
 
-    varList_init(varList1, vlistID1);
-
-    hset = HistogramSet(nvars, ntsteps);
+    hset = HistogramSet(numVars, numSteps);
 
-    for (int varID = 0; varID < nvars; ++varID) hset.createVarLevels(varID, varList1[varID].nlevels, varList1[varID].gridsize);
+    for (const auto &var : varList1.vars) hset.createVarLevels(var.ID, var.nlevels, var.gridsize);
   }
 
   void
-  run()
+  run() override
   {
+    auto maxRecords = varList1.numRecords();
+    std::vector<RecordInfo> recordList(maxRecords);
+
+    FieldVector constFields(maxRecords);
+
     int tsID = 0;
     int otsID = 0;
     while (true)
@@ -150,13 +145,12 @@ public:
 
         for (int recID = 0; recID < nrecs; ++recID)
           {
-            int varID, levelID;
-            cdo_inq_record(streamID2, &varID, &levelID);
-            field1.init(varList1[varID]);
+            auto [varID, levelID] = cdo_inq_record(streamID2);
+            field1.init(varList1.vars[varID]);
             cdo_read_record(streamID2, field1);
 
-            cdo_inq_record(streamID3, &varID, &levelID);
-            field2.init(varList1[varID]);
+            (void) cdo_inq_record(streamID3, &varID, &levelID);
+            field2.init(varList1.vars[varID]);
             cdo_read_record(streamID3, field2);
 
             hset.defVarLevelBounds(varID, levelID, field1, field2);
@@ -177,19 +171,19 @@ public:
 
             for (int recID = 0; recID < nrecs; ++recID)
               {
-                int varID, levelID;
-                cdo_inq_record(streamID1, &varID, &levelID);
+                auto [varID, levelID] = cdo_inq_record(streamID1);
+                const auto &var1 = varList1.vars[varID];
 
-                if (tsID == 0) recList[recID].set(varID, levelID);
+                if (tsID == 0) recordList[recID].set(varID, levelID);
 
-                if (tsID == 0 && varList1[varID].isConstant)
+                if (tsID == 0 && var1.isConstant)
                   {
-                    constFields[recID].init(varList1[varID]);
+                    constFields[recID].init(var1);
                     cdo_read_record(streamID1, constFields[recID]);
                   }
                 else
                   {
-                    field1.init(varList1[varID]);
+                    field1.init(var1);
                     cdo_read_record(streamID1, field1);
 
                     hset.addVarLevelValues(varID, levelID, field1);
@@ -205,17 +199,18 @@ public:
         dtlist.stat_taxis_def_timestep(taxisID4, numSets);
         cdo_def_timestep(streamID4, otsID);
 
-        for (int recID = 0; recID < maxrecs; ++recID)
+        for (int recID = 0; recID < maxRecords; ++recID)
           {
-            auto [varID, levelID] = recList[recID].get();
-            if (otsID && varList1[varID].isConstant) continue;
+            auto [varID, levelID] = recordList[recID].get();
+            const auto &var1 = varList1.vars[varID];
+            if (otsID && var1.isConstant) continue;
 
             cdo_def_record(streamID4, varID, levelID);
 
-            if (varList1[varID].isConstant) { cdo_write_record(streamID4, constFields[recID]); }
+            if (var1.isConstant) { cdo_write_record(streamID4, constFields[recID]); }
             else
               {
-                field1.init(varList1[varID]);
+                field1.init(var1);
                 hset.getVarLevelPercentiles(field1, varID, levelID, pn);
                 cdo_write_record(streamID4, field1);
               }
@@ -227,7 +222,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID4);
     cdo_stream_close(streamID3);
diff --git a/src/Timselpctl.cc b/src/Timselpctl.cc
index 25a73642495b8a199d375135f6f617d55ba95633..9655d32700446c0e0f046a27d87471c9957432c7 100644
--- a/src/Timselpctl.cc
+++ b/src/Timselpctl.cc
@@ -52,12 +52,9 @@ public:
 
   int noffset = 0, nskip = 0;
   int ndates;
-  int maxrecs;
 
   double pn;
 
-  std::vector<RecordInfo> recList;
-  FieldVector constFields;
   DateTimeList dtlist;
   Field field1, field2;
   VarList varList1;
@@ -65,9 +62,8 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
-
     operator_input_arg("percentile number, numSets <,noffset <,nskip>>");
 
     auto nargc = cdo_operator_argc();
@@ -91,8 +87,12 @@ public:
 
     vlist_unpack(vlistID4);
 
-    vlist_compare(vlistID1, vlistID2, CmpVlist::All);
-    vlist_compare(vlistID1, vlistID3, CmpVlist::All);
+    varList1 = VarList(vlistID1);
+    VarList varList2(vlistID2);
+    VarList varList3(vlistID3);
+
+    varList_compare(varList1, varList2);
+    varList_compare(varList1, varList3);
 
     taxisID1 = vlistInqTaxis(vlistID1);
     taxisID2 = vlistInqTaxis(vlistID2);
@@ -106,27 +106,25 @@ public:
     streamID4 = cdo_open_write(3);
     cdo_def_vlist(streamID4, vlistID4);
 
-    auto ntsteps = vlistNtsteps(vlistID1);
-    auto nvars = vlistNvars(vlistID1);
-
-    maxrecs = vlistNrecs(vlistID1);
-    recList = std::vector<RecordInfo>(maxrecs);
-
-    constFields = FieldVector(maxrecs);
+    auto numVars = varList1.numVars();
+    auto numSteps = varList1.numSteps();
 
     dtlist.set_stat(TimeStat::MEAN);
     dtlist.set_calendar(taxisInqCalendar(taxisID1));
 
-    varList_init(varList1, vlistID1);
-
-    hset = HistogramSet(nvars, ntsteps);
+    hset = HistogramSet(numVars, numSteps);
 
-    for (int varID = 0; varID < nvars; ++varID) hset.createVarLevels(varID, varList1[varID].nlevels, varList1[varID].gridsize);
+    for (const auto &var : varList1.vars) hset.createVarLevels(var.ID, var.nlevels, var.gridsize);
   }
 
   void
-  run()
+  run() override
   {
+    auto maxRecords = varList1.numRecords();
+    std::vector<RecordInfo> recordList(maxRecords);
+
+    FieldVector constFields(maxRecords);
+
     int tsID;
     for (tsID = 0; tsID < noffset; ++tsID)
       {
@@ -135,10 +133,8 @@ public:
 
         for (int recID = 0; recID < nrecs; ++recID)
           {
-            int varID, levelID;
-            cdo_inq_record(streamID1, &varID, &levelID);
-
-            if (tsID == 0) recList[recID].set(varID, levelID);
+            auto [varID, levelID] = cdo_inq_record(streamID1);
+            if (tsID == 0) recordList[recID].set(varID, levelID);
           }
       }
     int otsID = 0;
@@ -165,11 +161,11 @@ public:
           {
             int varID, levelID;
             cdo_inq_record(streamID2, &varID, &levelID);
-            field1.init(varList1[varID]);
+            field1.init(varList1.vars[varID]);
             cdo_read_record(streamID2, field1);
 
             cdo_inq_record(streamID3, &varID, &levelID);
-            field2.init(varList1[varID]);
+            field2.init(varList1.vars[varID]);
             cdo_read_record(streamID3, field2);
 
             hset.defVarLevelBounds(varID, levelID, field1, field2);
@@ -186,19 +182,19 @@ public:
 
               for (int recID = 0; recID < nrecs; ++recID)
                 {
-                  int varID, levelID;
-                  cdo_inq_record(streamID1, &varID, &levelID);
+                  auto [varID, levelID] = cdo_inq_record(streamID1);
 
-                  if (tsID == 0) recList[recID].set(varID, levelID);
+                  if (tsID == 0) recordList[recID].set(varID, levelID);
 
-                  if (tsID == 0 && varList1[varID].isConstant)
+                  const auto &var1 = varList1.vars[varID];
+                  if (tsID == 0 && var1.isConstant)
                     {
-                      constFields[recID].init(varList1[varID]);
+                      constFields[recID].init(var1);
                       cdo_read_record(streamID1, constFields[recID]);
                     }
                   else
                     {
-                      field1.init(varList1[varID]);
+                      field1.init(var1);
                       cdo_read_record(streamID1, field1);
 
                       hset.addVarLevelValues(varID, levelID, field1);
@@ -213,17 +209,18 @@ public:
         dtlist.stat_taxis_def_timestep(taxisID4, numSets);
         cdo_def_timestep(streamID4, otsID);
 
-        for (int recID = 0; recID < maxrecs; ++recID)
+        for (int recID = 0; recID < maxRecords; ++recID)
           {
-            auto [varID, levelID] = recList[recID].get();
-            if (otsID && varList1[varID].isConstant) continue;
+            auto [varID, levelID] = recordList[recID].get();
+            const auto &var1 = varList1.vars[varID];
+            if (otsID && var1.isConstant) continue;
 
             cdo_def_record(streamID4, varID, levelID);
 
-            if (varList1[varID].isConstant) { cdo_write_record(streamID4, constFields[recID]); }
+            if (var1.isConstant) { cdo_write_record(streamID4, constFields[recID]); }
             else
               {
-                field1.init(varList1[varID]);
+                field1.init(var1);
                 hset.getVarLevelPercentiles(field1, varID, levelID, pn);
                 cdo_write_record(streamID4, field1);
               }
@@ -244,7 +241,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID4);
     cdo_stream_close(streamID3);
diff --git a/src/Timselstat.cc b/src/Timselstat.cc
index cf47fd3cae91b0213aa502b6d62714ad3aafb459..205f041989734c887f837130feca6b874c47ace2 100644
--- a/src/Timselstat.cc
+++ b/src/Timselstat.cc
@@ -23,9 +23,11 @@
 #include <cdi.h>
 
 #include "cdo_options.h"
+#include "cdo_stepstat.h"
 #include "process_int.h"
 #include "param_conversion.h"
 #include "datetime.h"
+#include "progress.h"
 #include "field_functions.h"
 
 class Timselstat : public Process
@@ -50,47 +52,33 @@ public:
     .constraints = { 1, 1, NoRestriction },
   };
   inline static RegisterEntry<Timselstat> registration = RegisterEntry<Timselstat>(module);
-  CdoStreamID streamID1;
-  int taxisID1;
 
+private:
+  CdoStreamID streamID1;
   CdoStreamID streamID2;
+  int taxisID1;
   int taxisID2;
 
   int noffset;
   int ndates;
   int nskip;
 
-  int operfunc;
-  int maxrecs;
+  int maxRecords;
 
-  bool lrange;
-  bool lmean;
-  bool lstd;
-  bool lvarstd;
-  bool lvars2;
-  int divisor;
+  cdo::StepStat2D stepStat;
 
-  std::vector<RecordInfo> recList;
+  std::vector<RecordInfo> recordList;
   DateTimeList dtlist;
-  VarList varList;
-  Field field;
-  FieldVector2D samp1, vars1, vars2;
+  VarList varList1;
 
 public:
   void
-  init()
+  init() override
   {
-
     auto operatorID = cdo_operator_id();
-    operfunc = cdo_operator_f1(operatorID);
+    auto operfunc = cdo_operator_f1(operatorID);
 
-    auto lminmax = (operfunc == FieldFunc_Min || operfunc == FieldFunc_Max);
-    lrange = (operfunc == FieldFunc_Range);
-    lmean = (operfunc == FieldFunc_Mean || operfunc == FieldFunc_Avg);
-    lstd = (operfunc == FieldFunc_Std || operfunc == FieldFunc_Std1);
-    lvarstd = (lstd || operfunc == FieldFunc_Var || operfunc == FieldFunc_Var1);
-    lvars2 = (lvarstd || lrange);
-    divisor = (operfunc == FieldFunc_Std1 || operfunc == FieldFunc_Var1);
+    stepStat.init(operfunc);
 
     operator_input_arg("numSets <noffset <nskip>>");
 
@@ -106,39 +94,31 @@ public:
     auto vlistID1 = cdo_stream_inq_vlist(streamID1);
     auto vlistID2 = vlistDuplicate(vlistID1);
 
-    if (!lminmax) vlist_unpack(vlistID2);
+    if (!stepStat.lminmax) vlist_unpack(vlistID2);
 
     taxisID1 = vlistInqTaxis(vlistID1);
     taxisID2 = taxisDuplicate(taxisID1);
     taxisWithBounds(taxisID2);
     vlistDefTaxis(vlistID2, taxisID2);
 
+    varList1 = VarList(vlistID1);
+
     streamID2 = cdo_open_write(1);
     cdo_def_vlist(streamID2, vlistID2);
 
-    maxrecs = vlistNrecs(vlistID1);
-    recList = std::vector<RecordInfo>(maxrecs);
+    maxRecords = varList1.numRecords();
+    recordList = std::vector<RecordInfo>(maxRecords);
 
     dtlist.set_stat(TimeStat::MEAN);
     dtlist.set_calendar(taxisInqCalendar(taxisID1));
 
-    varList_init(varList, vlistID1);
-
-    int VARS_MEMTYPE = 0;
-    if ((operfunc == FieldFunc_Min) || (operfunc == FieldFunc_Max)) VARS_MEMTYPE = FIELD_NAT;
-
-    fields_from_vlist(vlistID1, samp1);
-    fields_from_vlist(vlistID1, vars1, FIELD_VEC | VARS_MEMTYPE);
-    if (lvars2) fields_from_vlist(vlistID1, vars2, FIELD_VEC);
+    int VARS_MEMTYPE = stepStat.lminmax ? FIELD_NAT : 0;
+    stepStat.alloc(varList1, VARS_MEMTYPE);
   }
 
   void
-  run()
+  skip_noffset_steps(int &tsID)
   {
-    auto field2_stdvar_func = lstd ? field2_std : field2_var;
-    auto fieldc_stdvar_func = lstd ? fieldc_std : fieldc_var;
-
-    int tsID;
     for (tsID = 0; tsID < noffset; ++tsID)
       {
         auto nrecs = cdo_stream_inq_timestep(streamID1, tsID);
@@ -146,19 +126,37 @@ public:
 
         for (int recID = 0; recID < nrecs; ++recID)
           {
-            int varID, levelID;
-            cdo_inq_record(streamID1, &varID, &levelID);
+            auto [varID, levelID] = cdo_inq_record(streamID1);
 
-            if (tsID == 0) recList[recID].set(varID, levelID);
+            if (tsID == 0) recordList[recID].set(varID, levelID);
           }
       }
 
-    int otsID = 0;
-    if (tsID < noffset)
+    if (tsID < noffset) { cdo_abort("noffset is larger than number of timesteps!"); }
+  }
+
+  void
+  skip_nskip_steps(int &tsID, int &nrecs)
+  {
+    for (int i = 0; i < nskip; ++i)
       {
-        cdo_warning("noffset is larger than number of timesteps!");
-        return;
+        nrecs = cdo_stream_inq_timestep(streamID1, tsID);
+        if (nrecs == 0) break;
+        tsID++;
       }
+  }
+
+  void
+  run() override
+  {
+    Field field;
+    auto numSteps = varList1.numSteps();
+    cdo::Progress progress;
+
+    int tsID;
+    skip_noffset_steps(tsID);
+
+    int otsID = 0;
 
     while (true)
       {
@@ -169,121 +167,40 @@ public:
             nrecs = cdo_stream_inq_timestep(streamID1, tsID);
             if (nrecs == 0) break;
 
+            if (numSteps > 1) progress.update((tsID + 1.0) / numSteps);
+
             dtlist.taxis_inq_timestep(taxisID1, numSets);
 
             for (int recID = 0; recID < nrecs; ++recID)
               {
-                int varID, levelID;
-                cdo_inq_record(streamID1, &varID, &levelID);
-
-                if (tsID == 0) recList[recID].set(varID, levelID);
-
-                auto &rsamp1 = samp1[varID][levelID];
-                auto &rvars1 = vars1[varID][levelID];
-
-                if (numSets == 0)
-                  {
-                    cdo_read_record(streamID1, rvars1);
-                    if (lrange)
-                      {
-                        vars2[varID][levelID].numMissVals = rvars1.numMissVals;
-                        vars2[varID][levelID].vec_d = rvars1.vec_d;
-                      }
-
-                    if (rvars1.numMissVals || !rsamp1.empty())
-                      {
-                        if (rsamp1.empty()) rsamp1.resize(rvars1.size);
-                        field2_vinit(rsamp1, rvars1);
-                      }
-                  }
-                else
-                  {
-                    field.init(varList[varID]);
-                    cdo_read_record(streamID1, field);
-
-                    if (field.numMissVals || !rsamp1.empty())
-                      {
-                        if (rsamp1.empty()) rsamp1.resize(rvars1.size, numSets);
-                        field2_vincr(rsamp1, field);
-                      }
-
-                    // clang-format off
-                  if      (lvarstd) field2_sumsumq(rvars1, vars2[varID][levelID], field);
-                  else if (lrange)  field2_maxmin(rvars1, vars2[varID][levelID], field);
-                  else              field2_function(rvars1, field, operfunc);
-                    // clang-format on
-                  }
+                auto [varID, levelID] = cdo_inq_record(streamID1);
+                if (tsID == 0) recordList[recID].set(varID, levelID);
+                field.init(varList1.vars[varID]);
+                cdo_read_record(streamID1, field);
+                stepStat.add_field(field, varID, levelID, numSets);
               }
 
-            if (numSets == 0 && lvarstd)
-              for (int recID = 0; recID < maxrecs; ++recID)
-                {
-                  auto [varID, levelID] = recList[recID].get();
-                  if (varList[varID].isConstant) continue;
-
-                  field2_moq(vars2[varID][levelID], vars1[varID][levelID]);
-                }
-
             tsID++;
           }
 
         if (nrecs == 0 && numSets == 0) break;
 
-        for (int recID = 0; recID < maxrecs; ++recID)
-          {
-            auto [varID, levelID] = recList[recID].get();
-            if (varList[varID].isConstant) continue;
-
-            const auto &rsamp1 = samp1[varID][levelID];
-            auto &rvars1 = vars1[varID][levelID];
-
-            if (lmean)
-              {
-                if (!rsamp1.empty())
-                  field2_div(rvars1, rsamp1);
-                else
-                  fieldc_div(rvars1, (double) numSets);
-              }
-            else if (lvarstd)
-              {
-                if (!rsamp1.empty())
-                  field2_stdvar_func(rvars1, vars2[varID][levelID], rsamp1, divisor);
-                else
-                  fieldc_stdvar_func(rvars1, vars2[varID][levelID], numSets, divisor);
-              }
-            else if (lrange) { field2_sub(rvars1, vars2[varID][levelID]); }
-          }
+        cdo::records_process(recordList, varList1, stepStat, numSets);
 
         dtlist.stat_taxis_def_timestep(taxisID2, numSets);
-        cdo_def_timestep(streamID2, otsID);
-
-        for (int recID = 0; recID < maxrecs; ++recID)
-          {
-            auto [varID, levelID] = recList[recID].get();
-            if (otsID && varList[varID].isConstant) continue;
-
-            auto &rvars1 = vars1[varID][levelID];
-
-            cdo_def_record(streamID2, varID, levelID);
-            cdo_write_record(streamID2, rvars1);
-          }
+        cdo::write_out_stream(streamID2, recordList, varList1, stepStat, otsID);
 
         if (nrecs == 0) break;
         otsID++;
 
-        for (int i = 0; i < nskip; ++i)
-          {
-            nrecs = cdo_stream_inq_timestep(streamID1, tsID);
-            if (nrecs == 0) break;
-            tsID++;
-          }
+        skip_nskip_steps(tsID, nrecs);
 
         if (nrecs == 0) break;
       }
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
diff --git a/src/Timsort.cc b/src/Timsort.cc
index 58af4eaba4ee971d6aeca1e79fa3f8516236324c..342a2fb6c487cbeae16dff437a137265e2ccab1a 100644
--- a/src/Timsort.cc
+++ b/src/Timsort.cc
@@ -14,7 +14,6 @@
 #include <cdi.h>
 
 #include "process_int.h"
-#include "cdo_vlist.h"
 #include "cdo_options.h"
 #include "cimdOmp.h"
 #include "field_functions.h"
@@ -33,28 +32,26 @@ public:
   };
   inline static RegisterEntry<Timsort> registration = RegisterEntry<Timsort>(module);
 
-  int varID, levelID;
   int nalloc = 0;
 
   CdoStreamID streamID1;
-  int taxisID1;
-  int vlistID1;
-
   CdoStreamID streamID2;
+
+  int taxisID1;
   int taxisID2;
+  int vlistID1;
   int vlistID2;
 
-  int nvars;
+  int numVars;
 
-  VarList varList;
-  FieldVector3D vars;
+  VarList varList1;
+  FieldVector3D varsData;
   std::vector<CdiDateTime> vDateTimes;
 
 public:
   void
-  init()
+  init() override
   {
-
     operator_check_argc(0);
 
     streamID1 = cdo_open_read(0);
@@ -69,13 +66,12 @@ public:
     streamID2 = cdo_open_write(1);
     cdo_def_vlist(streamID2, vlistID2);
 
-    varList_init(varList, vlistID1);
-
-    nvars = vlistNvars(vlistID1);
+    varList1 = VarList(vlistID1);
+    numVars = varList1.numVars();
   }
 
   void
-  run()
+  run() override
   {
     int tsID = 0;
     while (true)
@@ -88,18 +84,18 @@ public:
             constexpr int NALLOC_INC = 1024;
             nalloc += NALLOC_INC;
             vDateTimes.resize(nalloc);
-            vars.resize(nalloc);
+            varsData.resize(nalloc);
           }
 
         vDateTimes[tsID] = taxisInqVdatetime(taxisID1);
 
-        fields_from_vlist(vlistID1, vars[tsID]);
+        field2D_init(varsData[tsID], varList1);
 
         for (int recID = 0; recID < nrecs; ++recID)
           {
-            cdo_inq_record(streamID1, &varID, &levelID);
-            auto &field = vars[tsID][varID][levelID];
-            field.init(varList[varID]);
+            auto [varID, levelID] = cdo_inq_record(streamID1);
+            auto &field = varsData[tsID][varID][levelID];
+            field.init(varList1.vars[varID]);
             cdo_read_record(streamID1, field);
           }
 
@@ -110,15 +106,15 @@ public:
 
     std::vector<Field> fields(Threading::ompNumThreads);
 
-    for (varID = 0; varID < nvars; ++varID)
+    for (int varID = 0; varID < numVars; ++varID)
       {
-        const auto &var = varList[varID];
+        const auto &var = varList1.vars[varID];
 
         if (var.isConstant) continue;
 
         auto memType = var.memType;
         auto gridsize = var.gridsize;
-        for (levelID = 0; levelID < var.nlevels; ++levelID)
+        for (int levelID = 0; levelID < var.nlevels; ++levelID)
           {
 #ifdef _OPENMP
 #pragma omp parallel for default(shared) schedule(static)
@@ -131,21 +127,21 @@ public:
                   {
                     auto &v = fields[ompthID].vec_f;
                     v.resize(nts);
-                    for (int t = 0; t < nts; ++t) v[t] = vars[t][varID][levelID].vec_f[i];
+                    for (int t = 0; t < nts; ++t) v[t] = varsData[t][varID][levelID].vec_f[i];
 
                     ranges::sort(v);
 
-                    for (int t = 0; t < nts; ++t) vars[t][varID][levelID].vec_f[i] = v[t];
+                    for (int t = 0; t < nts; ++t) varsData[t][varID][levelID].vec_f[i] = v[t];
                   }
                 else
                   {
                     auto &v = fields[ompthID].vec_d;
                     v.resize(nts);
-                    for (int t = 0; t < nts; ++t) v[t] = vars[t][varID][levelID].vec_d[i];
+                    for (int t = 0; t < nts; ++t) v[t] = varsData[t][varID][levelID].vec_d[i];
 
                     ranges::sort(v);
 
-                    for (int t = 0; t < nts; ++t) vars[t][varID][levelID].vec_d[i] = v[t];
+                    for (int t = 0; t < nts; ++t) varsData[t][varID][levelID].vec_d[i] = v[t];
                   }
               }
           }
@@ -156,12 +152,12 @@ public:
         taxisDefVdatetime(taxisID2, vDateTimes[tsID]);
         cdo_def_timestep(streamID2, tsID);
 
-        for (varID = 0; varID < nvars; ++varID)
+        for (int varID = 0; varID < numVars; ++varID)
           {
-            const auto &var = varList[varID];
-            for (levelID = 0; levelID < var.nlevels; ++levelID)
+            const auto &var = varList1.vars[varID];
+            for (int levelID = 0; levelID < var.nlevels; ++levelID)
               {
-                auto &field = vars[tsID][varID][levelID];
+                auto &field = varsData[tsID][varID][levelID];
                 if (field.hasData())
                   {
                     cdo_def_record(streamID2, varID, levelID);
@@ -173,7 +169,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
diff --git a/src/Timstat.cc b/src/Timstat.cc
index 4e920c585659f25334130a197167543cf90242d6..6ba8aef616ca8eab3a65ac011b136697d8b91686 100644
--- a/src/Timstat.cc
+++ b/src/Timstat.cc
@@ -62,15 +62,19 @@
 
 #include <cdi.h>
 
+#include <unordered_map>
+
 #include "cdo_options.h"
-#include "cdo_vlist.h"
+#include "cdo_stepstat.h"
+#include "cdo_task.h"
 #include "process_int.h"
 #include "datetime.h"
 #include "printinfo.h"
 #include "util_date.h"
-#include "param_conversion.h"
 #include "progress.h"
 #include "field_functions.h"
+#include "param_conversion.h"
+#include "pmlist.h"
 
 static void
 vlist_set_frequency(int vlistID, const std::string &frequency)
@@ -89,22 +93,32 @@ vlist_set_frequency(int vlistID, int compareDate)
 }
 
 static void
-set_missval(Field &field, const Field &samp, int numSets, double vfrac)
+get_parameter(double &vfraction, bool &completeOnly)
 {
-  auto fieldsize = field.size;
-  auto missval = field.missval;
-
-  size_t irun = 0;
-  for (size_t i = 0; i < fieldsize; ++i)
+  auto pargc = cdo_operator_argc();
+  if (pargc)
     {
-      if ((samp.vec_d[i] / numSets) < vfrac)
+      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)
         {
-          field.vec_d[i] = missval;
-          irun++;
+          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 == "vfraction")       vfraction = parameter_to_double(value);
+          else if (key == "complete_only")   completeOnly = parameter_to_bool(value);
+          else cdo_abort("Invalid parameter key >%s<!", key);
+          // clang-format on
         }
     }
-
-  if (irun) field.numMissVals = field_num_miss(field);
 }
 
 class Timstat : public Process
@@ -176,85 +190,99 @@ public:
   };
   inline static RegisterEntry<Timstat> registration = RegisterEntry<Timstat>(module);
 
+private:
   static const TimeStat timestatDate{ TimeStat::MEAN };
-  CdiDateTime vDateTime0{};
-  CdiDateTime vDateTimeN{};
 
   CdoStreamID streamID1;
-  int taxisID1;
-
   CdoStreamID streamID2;
-  int taxisID2;
-
   CdoStreamID streamID3;
-  int vlistID3, taxisID3{ -1 };
-
-  int ntsteps1;
-
-  bool lstd{ false };
-  bool lminidx{ false };
-  bool lmaxidx{ false };
-  bool lrange{ false };
-  bool lvarstd{ false };
-  bool lmean{ false };
-  double divisor;
+  int taxisID1;
+  int taxisID2;
 
-  int operfunc;
+  int vlistID3;
+  int taxisID3{ -1 };
 
   int compareDate;
-  int maxrecs;
+  int maxRecords;
 
-  bool lvfrac{ false };
-  double vfrac{ 1.0 };
+  bool completeOnly{ false };
+  bool handleVfraction{ false };
+  double vfraction{ -1.0 };
 
-  DateTimeList dtlist;
-  std::vector<RecordInfo> recList;
+  cdo::StepStat2D stepStat;
 
-  VarList varList;
-  Field field;
-  Varray<double> samp;
-  FieldVector2D samp1, vars1, vars2;
+  std::vector<RecordInfo> recordList;
+  DateTimeList dtlist;
+  VarList varList1;
 
-public:
   void
-  init()
+  create_diag_stream(int operatorID, int vlistID1, int numVars)
   {
+    char filename[8192];
+    std::strcpy(filename, cdo_operator_name(operatorID));
+    strcat(filename, "_");
+    strcat(filename, cdo_get_stream_name(1));
+    streamID3 = cdo_open_write(filename);
 
-    auto operatorID = cdo_operator_id();
-    operfunc = cdo_operator_f1(operatorID);
-    compareDate = cdo_operator_f2(operatorID);
+    vlistID3 = vlistDuplicate(vlistID1);
 
-    auto lminmax = (operfunc == FieldFunc_Min || operfunc == FieldFunc_Max);
-    lminidx = (operfunc == FieldFunc_Minidx);
-    lmaxidx = (operfunc == FieldFunc_Maxidx);
-    lrange = (operfunc == FieldFunc_Range);
-    lmean = (operfunc == FieldFunc_Mean || operfunc == FieldFunc_Avg);
-    lstd = (operfunc == FieldFunc_Std || operfunc == FieldFunc_Std1);
-    lvarstd = (lstd || operfunc == FieldFunc_Var || operfunc == FieldFunc_Var1);
-    auto lvars2 = (lvarstd || lrange || lminidx || lmaxidx);
-    divisor = (operfunc == FieldFunc_Std1 || operfunc == FieldFunc_Var1);
-
-    if (operfunc == FieldFunc_Mean)
+    for (int varID = 0; varID < numVars; ++varID)
       {
-        auto oargc = cdo_operator_argc();
-        if (oargc == 1)
+        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);
+
+    cdo_def_vlist(streamID3, vlistID3);
+  }
+
+  bool
+  check_numSets(std::vector<int> &numSetsList)
+  {
+    std::unordered_map<int, std::pair<double, std::string>> periodNameMap
+        = { { CMP_HOUR, { 23.0 / 24.0, "hour" } }, { CMP_MONTH, { 28 / 31.0, "month" } }, { CMP_YEAR, { 365 / 366.0, "year" } } };
+
+    if (numSetsList.size() > 1)
+      {
+        auto numSetsMin = std::min_element(numSetsList.begin(), numSetsList.end() - 1);
+        auto [periodFactor, periodName] = periodNameMap[compareDate];
+        if ((double) numSetsList.back() < *numSetsMin * periodFactor)
           {
-            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!");
+            if (Options::cdoVerbose)
+              cdo_warning("Last %s has less steps (%d) than all previous %ss (%d)!", periodName, numSetsList.back(), periodName,
+                          *numSetsMin);
+            return true;
           }
-        else if (oargc > 1)
-          cdo_abort("Too many arguments!");
       }
-    else { operator_check_argc(0); }
+
+    return false;
+  }
+
+public:
+  void
+  init() override
+  {
+    auto operatorID = cdo_operator_id();
+    auto operfunc = cdo_operator_f1(operatorID);
+    compareDate = cdo_operator_f2(operatorID);
+
+    stepStat.init(operfunc);
+
+    get_parameter(vfraction, completeOnly);
+    handleVfraction = (vfraction >= 0.0 && vfraction <= 1.0);
 
     streamID1 = cdo_open_read(0);
 
     auto vlistID1 = cdo_stream_inq_vlist(streamID1);
     auto vlistID2 = vlistDuplicate(vlistID1);
 
-    if (!lminmax) vlist_unpack(vlistID2);
+    if (!stepStat.lminmax) vlist_unpack(vlistID2);
 
     vlist_define_timestep_type(vlistID2, operfunc);
 
@@ -266,70 +294,41 @@ public:
     if (taxisInqType(taxisID2) == TAXIS_FORECAST) taxisDefType(taxisID2, TAXIS_RELATIVE);
     vlistDefTaxis(vlistID2, taxisID2);
 
-    auto nvars = vlistNvars(vlistID1);
+    varList1 = VarList(vlistID1);
 
     vlist_set_frequency(vlistID2, compareDate);
 
     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);
-
-        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);
-          }
-
-        taxisID3 = taxisDuplicate(taxisID1);
-        taxisWithBounds(taxisID3);
-        vlistDefTaxis(vlistID3, taxisID3);
-
-        cdo_def_vlist(streamID3, vlistID3);
-      }
+    if (Options::cdoDiag) create_diag_stream(operatorID, vlistID1, varList1.numVars());
 
-    maxrecs = vlistNrecs(vlistID1);
-    recList = std::vector<RecordInfo>(maxrecs);
+    maxRecords = varList1.numRecords();
+    recordList = std::vector<RecordInfo>(maxRecords);
 
     dtlist.set_stat(timestatDate);
     dtlist.set_calendar(taxisInqCalendar(taxisID1));
 
-    varList_init(varList, vlistID1);
-
-    int VARS_MEMTYPE = lminmax ? FIELD_NAT : 0;
-    // if ((Options::CDO_???(--single) == MemType::Float) && (operfunc == FieldFunc_Mean)) VARS_MEMTYPE = FIELD_NAT;
+    int VARS_MEMTYPE = stepStat.lminmax ? FIELD_NAT : 0;
+    // if ((Options::CDO_???(--single) == MemType::Float) && stepStat.lmean) VARS_MEMTYPE = FIELD_NAT;
     // if (Options::CDO_Memtype == MemType::Float) VARS_MEMTYPE = FIELD_FLT;
-    if (Options::cdoDiag || (lvfrac && operfunc == FieldFunc_Mean)) VARS_MEMTYPE = FIELD_DBL;
-
-    fields_from_vlist(vlistID1, samp1);
-    fields_from_vlist(vlistID1, vars1, FIELD_VEC | VARS_MEMTYPE);
-    if (lvars2) fields_from_vlist(vlistID1, vars2, FIELD_VEC);
-
-    auto numVars = varList.size();
-    for (size_t varID = 0; varID < numVars; ++varID) varList[varID].memType = vars1[varID][0].memType;
+    if (Options::cdoDiag || (handleVfraction && stepStat.lmean)) VARS_MEMTYPE = FIELD_DBL;
 
-    ntsteps1 = vlistNtsteps(vlistID1);
+    stepStat.alloc(varList1, VARS_MEMTYPE);
 
-    if (!Options::cdoVerbose && ntsteps1 > 1) progress::init();
+    // for (auto &var1 : varList1.vars) var1.memType = stepStat.var1(var1.ID, 0).memType;
   }
 
   void
-  run()
+  run_sync()
   {
+    std::vector<int> numSetsList;
+    CdiDateTime vDateTime0{};
+    CdiDateTime vDateTimeN{};
+    Field field;
 
-    auto field2_stdvar_func = lstd ? field2_std : field2_var;
-    auto fieldc_stdvar_func = lstd ? fieldc_std : fieldc_var;
+    auto numSteps1 = varList1.numSteps();
+    cdo::Progress progress;
 
     int tsID = 0;
     int otsID = 0;
@@ -342,7 +341,7 @@ public:
             nrecs = cdo_stream_inq_timestep(streamID1, tsID);
             if (nrecs == 0) break;
 
-            if (!Options::cdoVerbose && ntsteps1 > 1) progress::update(0, 1, (tsID + 1.0) / ntsteps1);
+            if (numSteps1 > 1) progress.update((tsID + 1.0) / numSteps1);
 
             dtlist.taxis_inq_timestep(taxisID1, numSets);
             auto vDateTime = dtlist.get_vDateTime(numSets);
@@ -357,61 +356,13 @@ public:
 
             for (int recID = 0; recID < nrecs; ++recID)
               {
-                int varID, levelID;
-                cdo_inq_record(streamID1, &varID, &levelID);
-
-                if (tsID == 0) recList[recID].set(varID, levelID);
-
-                auto &rsamp1 = samp1[varID][levelID];
-                auto &rvars1 = vars1[varID][levelID];
-
-                if (numSets == 0)
-                  {
-                    cdo_read_record(streamID1, rvars1);
-                    if (lrange || lminidx || lmaxidx)
-                      {
-                        vars2[varID][levelID].numMissVals = rvars1.numMissVals;
-                        vars2[varID][levelID].vec_d = rvars1.vec_d;
-                      }
-
-                    if (lminidx || lmaxidx) field_fill(rvars1, 0.0);
-
-                    if (rvars1.numMissVals || !rsamp1.empty())
-                      {
-                        if (rsamp1.empty()) rsamp1.resize(rvars1.size);
-                        field2_vinit(rsamp1, rvars1);
-                      }
-                  }
-                else
-                  {
-                    field.init(varList[varID]);
-                    cdo_read_record(streamID1, field);
-
-                    if (field.numMissVals || !rsamp1.empty())
-                      {
-                        if (rsamp1.empty()) rsamp1.resize(rvars1.size, numSets);
-                        field2_vincr(rsamp1, field);
-                      }
-
-                    // clang-format off
-                    if      (lvarstd) field2_sumsumq(rvars1, vars2[varID][levelID], field);
-                    else if (lrange)  field2_maxmin(rvars1, vars2[varID][levelID], field);
-                    else if (lminidx) field2_minidx(rvars1, vars2[varID][levelID], field, numSets);
-                    else if (lmaxidx) field2_maxidx(rvars1, vars2[varID][levelID], field, numSets);
-                    else              field2_function(rvars1, field, operfunc);
-                    // clang-format on
-                  }
+                auto [varID, levelID] = cdo_inq_record(streamID1);
+                if (tsID == 0) recordList[recID].set(varID, levelID);
+                field.init(varList1.vars[varID]);
+                cdo_read_record(streamID1, field);
+                stepStat.add_field(field, varID, levelID, numSets);
               }
 
-            if (numSets == 0 && lvarstd)
-              for (int recID = 0; recID < maxrecs; ++recID)
-                {
-                  auto [varID, levelID] = recList[recID].get();
-                  if (varList[varID].isConstant) continue;
-
-                  field2_moq(vars2[varID][levelID], vars1[varID][levelID]);
-                }
-
             vDateTimeN = vDateTime;
             numSets++;
             tsID++;
@@ -419,86 +370,147 @@ public:
 
         if (nrecs == 0 && numSets == 0) break;
 
-        for (int recID = 0; recID < maxrecs; ++recID)
+        if (compareDate == CMP_DAY || compareDate == CMP_MONTH || compareDate == CMP_YEAR)
           {
-            auto [varID, levelID] = recList[recID].get();
-            if (varList[varID].isConstant) continue;
-
-            const auto &rsamp1 = samp1[varID][levelID];
-            auto &rvars1 = vars1[varID][levelID];
-
-            if (lmean)
-              {
-                if (!rsamp1.empty())
-                  field2_div(rvars1, rsamp1);
-                else
-                  fieldc_div(rvars1, (double) numSets);
-              }
-            else if (lvarstd)
-              {
-                if (!rsamp1.empty())
-                  field2_stdvar_func(rvars1, vars2[varID][levelID], rsamp1, divisor);
-                else
-                  fieldc_stdvar_func(rvars1, vars2[varID][levelID], numSets, divisor);
-              }
-            else if (lrange) { field2_sub(rvars1, vars2[varID][levelID]); }
+            numSetsList.push_back(numSets);
+            if (nrecs == 0 && check_numSets(numSetsList) && completeOnly) break;
           }
 
-        if (Options::cdoVerbose) cdo_print("%s  vfrac = %g, numSets = %d", datetime_to_string(vDateTimeN), vfrac, numSets);
+        cdo::records_process(recordList, varList1, stepStat, numSets);
 
-        if (lvfrac && operfunc == FieldFunc_Mean)
-          for (int recID = 0; recID < maxrecs; ++recID)
-            {
-              auto [varID, levelID] = recList[recID].get();
-              if (varList[varID].isConstant) continue;
+        if (Options::cdoVerbose) cdo_print("%s  numSteps = %d", datetime_to_string(vDateTimeN), numSets);
 
-              const auto &rsamp1 = samp1[varID][levelID];
-
-              if (!rsamp1.empty()) set_missval(vars1[varID][levelID], rsamp1, numSets, vfrac);
-            }
+        if (handleVfraction && stepStat.lmean) cdo::records_set_missval(recordList, varList1, stepStat, numSets, vfraction);
 
         dtlist.stat_taxis_def_timestep(taxisID2, numSets);
-        cdo_def_timestep(streamID2, otsID);
+        cdo::write_out_stream(streamID2, recordList, varList1, stepStat, otsID);
 
         if (Options::cdoDiag)
           {
             dtlist.stat_taxis_def_timestep(taxisID3, numSets);
-            cdo_def_timestep(streamID3, otsID);
+            cdo::write_diag_stream(streamID3, recordList, varList1, stepStat, otsID, numSets);
           }
 
-        for (int recID = 0; recID < maxrecs; ++recID)
+        if (nrecs == 0) break;
+        otsID++;
+      }
+  }
+
+  static void
+  records_add_fields2D(const FieldVector2D &fields2D, const std::vector<RecordInfo> &recordList, const VarList &varList1,
+                       cdo::StepStat2D &stepStat, int numSets) noexcept
+  {
+    for (const auto &record : recordList)
+      {
+        auto [varID, levelID] = record.get();
+        if (varList1.vars[varID].isConstant) continue;
+
+        stepStat.add_field(fields2D[varID][levelID], varID, levelID, numSets);
+      }
+  }
+
+  void
+  run_async()
+  {
+    std::vector<int> numSetsList;
+    CdiDateTime vDateTime0{};
+    CdiDateTime vDateTimeN{};
+    auto numSteps = varList1.numSteps();
+    cdo::Progress progress;
+
+    FieldVector3D fields3D(2);
+    field2D_init(fields3D[0], varList1, FIELD_VEC | FIELD_NAT);
+    field2D_init(fields3D[1], varList1, FIELD_VEC | FIELD_NAT);
+
+    auto useTask = true;
+    auto task = useTask ? std::make_unique<cdo::Task>() : nullptr;
+
+    int tsID = 0;
+    int otsID = 0;
+    while (true)
+      {
+        int numSets = 0;
+        int nrecs = 0;
+        while (true)
           {
-            auto [varID, levelID] = recList[recID].get();
-            if (otsID && varList[varID].isConstant) continue;
+            nrecs = cdo_stream_inq_timestep(streamID1, tsID);
+            if (nrecs == 0) break;
 
-            auto &rvars1 = vars1[varID][levelID];
+            if (numSteps > 1) progress.update((tsID + 1.0) / numSteps);
 
-            cdo_def_record(streamID2, varID, levelID);
-            cdo_write_record(streamID2, rvars1);
+            dtlist.taxis_inq_timestep(taxisID1, numSets);
+            auto vDateTime = dtlist.get_vDateTime(numSets);
+
+            if (numSets == 0) vDateTime0 = vDateTime;
+
+            if (date_is_neq(vDateTime, vDateTime0, compareDate))
+              {
+                cdo_add_steps(-1);
+                break;
+              }
 
-            if (Options::cdoDiag)
+            for (int recID = 0; recID < nrecs; ++recID)
               {
-                auto &rsamp1 = samp1[varID][levelID];
-                samp.resize(field.size);
-                if (!rsamp1.empty())
-                  samp = rsamp1.vec_d;
-                else
-                  ranges::fill(samp, (double) numSets);
-
-                cdo_def_record(streamID3, varID, levelID);
-                cdo_write_record(streamID3, samp.data(), 0);
+                auto [varID, levelID] = cdo_inq_record(streamID1);
+                if (tsID == 0) recordList[recID].set(varID, levelID);
+                cdo_read_record(streamID1, fields3D[numSets % 2][varID][levelID]);
               }
+
+            if (useTask && numSets > 0) task->wait();
+
+            std::function<void()> records_add_fields2D_func
+                = std::bind(records_add_fields2D, std::cref(fields3D[numSets % 2]), std::cref(recordList), std::cref(varList1),
+                            std::ref(stepStat), numSets);
+
+            useTask ? task->doAsync(records_add_fields2D_func) : records_add_fields2D_func();
+
+            vDateTimeN = vDateTime;
+            numSets++;
+            tsID++;
+          }
+
+        if (nrecs == 0 && numSets == 0) break;
+
+        if (useTask) task->wait();
+
+        if (compareDate == CMP_DAY || compareDate == CMP_MONTH || compareDate == CMP_YEAR)
+          {
+            numSetsList.push_back(numSets);
+            if (nrecs == 0 && check_numSets(numSetsList) && completeOnly) break;
+          }
+
+        cdo::records_process(recordList, varList1, stepStat, numSets);
+
+        if (Options::cdoVerbose) cdo_print("%s  numSteps = %d", datetime_to_string(vDateTimeN), numSets);
+
+        if (handleVfraction && stepStat.lmean) cdo::records_set_missval(recordList, varList1, stepStat, numSets, vfraction);
+
+        dtlist.stat_taxis_def_timestep(taxisID2, numSets);
+        cdo::write_out_stream(streamID2, recordList, varList1, stepStat, otsID);
+
+        if (Options::cdoDiag)
+          {
+            dtlist.stat_taxis_def_timestep(taxisID3, numSets);
+            cdo::write_diag_stream(streamID3, recordList, varList1, stepStat, otsID, numSets);
           }
 
         if (nrecs == 0) break;
         otsID++;
       }
+  }
 
-    if (!Options::cdoVerbose && ntsteps1 > 1) progress::update(0, 1, 1);
+  void
+  run() override
+  {
+    auto runAsync = (Options::CDO_Parallel_Read > 0);
+    if (runAsync)
+      run_async();
+    else
+      run_sync();
   }
 
   void
-  close()
+  close() override
   {
     if (Options::cdoDiag) cdo_stream_close(streamID3);
     cdo_stream_close(streamID2);
diff --git a/src/Timstat2.cc b/src/Timstat2.cc
index b64fce1557d40b9ba906c57de43f51b4898331db..2f6b389dfa5925c8a59427976a17028c076e299b 100644
--- a/src/Timstat2.cc
+++ b/src/Timstat2.cc
@@ -15,7 +15,6 @@
 
 #include "arithmetic.h"
 #include "process_int.h"
-#include "cdo_vlist.h"
 #include "cimdOmp.h"
 #include "field_functions.h"
 
@@ -54,7 +53,7 @@ correlation_init(bool hasMissValues, size_t gridsize, const Varray<T1> &x, const
 #endif
       for (size_t i = 0; i < gridsize; ++i)
         {
-          if ((!dbl_is_equal(x[i], xmv)) && (!dbl_is_equal(y[i], ymv))) correlation_sum(i);
+          if ((dbl_is_not_equal(x[i], xmv)) && (dbl_is_not_equal(y[i], ymv))) correlation_sum(i);
         }
     }
   else
@@ -153,7 +152,7 @@ covariance_init(bool hasMissValues, size_t gridsize, const Varray<T1> &x, const
 #endif
       for (size_t i = 0; i < gridsize; ++i)
         {
-          if ((!dbl_is_equal(x[i], xmv)) && (!dbl_is_equal(y[i], ymv))) covariance_sum(i);
+          if ((dbl_is_not_equal(x[i], xmv)) && (dbl_is_not_equal(y[i], ymv))) covariance_sum(i);
         }
     }
   else
@@ -221,7 +220,7 @@ rmsd_init(size_t gridsize, const Varray<T1> &x, const Varray<T2> &y, T1 xmv, T2
 {
   for (size_t i = 0; i < gridsize; ++i)
     {
-      if ((!dbl_is_equal(x[i], xmv)) && (!dbl_is_equal(y[i], ymv)))
+      if ((dbl_is_not_equal(x[i], xmv)) && (dbl_is_not_equal(y[i], ymv)))
         {
           double xx = x[i];
           double yy = y[i];
@@ -269,7 +268,7 @@ public:
     .name = "Timstat2",
     .operators = { { "timcor", FieldFunc_Cor, 5, TimcorHelp },
                    { "timcovar", FieldFunc_Covar, 3, TimcovarHelp },
-                   { "timrmsd", FieldFunc_Rmsd, 1, nullptr} },
+                   { "timrmsd", FieldFunc_Rmsd, 1, nullptr } },
     .aliases = {},
     .mode = EXPOSED,     // Module mode: 0:intern 1:extern
     .number = CDI_REAL,  // Allowed number type
@@ -289,15 +288,14 @@ public:
 
   int operfunc{};
 
-  VarList varList1, varList2;
+  VarList varList1;
+  VarList varList2;
 
-  int nrecs1;
-
-  bool doWritePvalue{false};
+  bool doWritePvalue{ false };
 
 public:
   void
-  init()
+  init() override
   {
     auto operatorID = cdo_operator_id();
     operfunc = cdo_operator_f1(operatorID);
@@ -313,26 +311,25 @@ public:
     auto vlistID2 = cdo_stream_inq_vlist(streamID2);
     auto vlistID3 = vlistDuplicate(vlistID1);
 
-    vlist_compare(vlistID1, vlistID2, CmpVlist::All);
-
-    varList_init(varList1, vlistID1);
-    varList_init(varList2, vlistID2);
+    varList1 = VarList(vlistID1);
+    varList2 = VarList(vlistID2);
+    varList_compare(varList1, varList2);
 
-    auto nvars = vlistNvars(vlistID1);
+    auto numVars = varList1.numVars();
 
     taxisID1 = vlistInqTaxis(vlistID1);
     // auto taxisID2 = vlistInqTaxis(vlistID2);
     taxisID3 = taxisDuplicate(taxisID1);
 
     if (timeIsConst)
-      for (int varID = 0; varID < nvars; ++varID) vlistDefVarTimetype(vlistID3, varID, TIME_CONSTANT);
+      for (int varID = 0; varID < numVars; ++varID) vlistDefVarTimetype(vlistID3, varID, TIME_CONSTANT);
 
-    auto const&var = varList1[0];
-    doWritePvalue = (operfunc == FieldFunc_Cor && varList1.size() == 1 && var.nlevels == 1);
+    auto const &var = varList1.vars[0];
+    doWritePvalue = (operfunc == FieldFunc_Cor && numVars == 1 && var.nlevels == 1);
 
     if (doWritePvalue)
       {
-        auto varID = vlistDefVar(vlistID3, var.gridID, var.zaxisID, var.timetype);
+        auto varID = vlistDefVar(vlistID3, var.gridID, var.zaxisID, var.timeType);
         vlistDefVarName(vlistID3, varID, "pvalue");
       }
 
@@ -341,26 +338,25 @@ public:
     vlistDefTaxis(vlistID3, taxisID3);
     streamID3 = cdo_open_write(2);
     cdo_def_vlist(streamID3, vlistID3);
-
-    nrecs1 = vlistNrecs(vlistID1);
   }
 
   void
-  run()
+  run() override
   {
     Field field1, field2;
 
-    std::vector<int> recVarID(nrecs1);
-    std::vector<int> recLevelID(nrecs1);
+    auto numVars = varList1.numVars();
+    auto numRecords = varList1.numRecords();
 
-    int nvars = varList1.size();
+    std::vector<int> recVarID(numRecords);
+    std::vector<int> recLevelID(numRecords);
 
-    Varray4D<double> work(nvars);
-    Varray3D<size_t> nofvals(nvars);
+    Varray4D<double> work(numVars);
+    Varray3D<size_t> nofvals(numVars);
 
-    for (int varID = 0; varID < nvars; ++varID)
+    for (int varID = 0; varID < numVars; ++varID)
       {
-        const auto &var = varList1[varID];
+        const auto &var = varList1.vars[varID];
         auto gridsize = var.gridsize;
         auto nlevels = var.nlevels;
 
@@ -388,12 +384,11 @@ public:
 
         for (int recID = 0; recID < nrecs; ++recID)
           {
-            int varID, levelID;
-            cdo_inq_record(streamID1, &varID, &levelID);
-            cdo_inq_record(streamID2, &varID, &levelID);
+            (void) cdo_inq_record(streamID1);
+            auto [varID, levelID] = cdo_inq_record(streamID2);
 
-            field1.init(varList1[varID]);
-            field2.init(varList2[varID]);
+            field1.init(varList1.vars[varID]);
+            field2.init(varList2.vars[varID]);
 
             if (tsID == 0)
               {
@@ -401,7 +396,7 @@ public:
                 recLevelID[recID] = levelID;
               }
 
-            auto gridsize = varList1[varID].gridsize;
+            auto gridsize = varList1.vars[varID].gridsize;
 
             cdo_read_record(streamID1, field1);
             cdo_read_record(streamID2, field2);
@@ -421,13 +416,13 @@ public:
     taxisDefVdatetime(taxisID3, vDateTime);
     cdo_def_timestep(streamID3, tsID);
 
-    for (int recID = 0; recID < nrecs1; ++recID)
+    for (int recID = 0; recID < numRecords; ++recID)
       {
         auto varID = recVarID[recID];
         auto levelID = recLevelID[recID];
 
-        auto gridsize = varList1[varID].gridsize;
-        auto missval = varList1[varID].missval;
+        auto gridsize = varList1.vars[varID].gridsize;
+        auto missval = varList1.vars[varID].missval;
 
         auto &rwork = work[varID][levelID];
         const auto &rnofvals = nofvals[varID][levelID];
@@ -449,7 +444,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID3);
     cdo_stream_close(streamID2);
diff --git a/src/Timstat3.cc b/src/Timstat3.cc
index 332514710ff7105fe67faae912e1129b380aa4ee..4c8d47a5f0365f1745f4c488f827615fa8fd1e21 100644
--- a/src/Timstat3.cc
+++ b/src/Timstat3.cc
@@ -15,7 +15,6 @@
 #include <cdi.h>
 
 #include "process_int.h"
-#include "cdo_vlist.h"
 #include "param_conversion.h"
 #include "statistic.h"
 #include "arithmetic.h"
@@ -73,7 +72,7 @@ meandiff2test(double rconst, double risk, size_t gridsize, double missval, Varra
     temp0 = ADDM(temp0, DIVM(SUBM(work[4][i], tmp), varFactor[1]));
     degOfFreedom = ADDM(degOfFreedom, work[5][i]);
 
-    if (!dbl_is_equal(temp0, missval1) && temp0 < 0) temp0 = 0;  // This is possible because of rounding errors
+    if (dbl_is_not_equal(temp0, missval1) && temp0 < 0) temp0 = 0;  // This is possible because of rounding errors
 
     auto stddevEstimator = SQRTM(DIVM(temp0, degOfFreedom));
     auto meanEstimator = -rconst;
@@ -104,7 +103,7 @@ public:
   using Process::Process;
   inline static CdoModule module = {
     .name = "Timstat3",
-    .operators = { { "meandiff2test"}, { "varquot2test"} },
+    .operators = { { "meandiff2test" }, { "varquot2test" } },
     .aliases = {},
     .mode = EXPOSED,     // Module mode: 0:intern 1:extern
     .number = CDI_REAL,  // Allowed number type
@@ -127,11 +126,11 @@ public:
 
   int operatorID;
 
-  VarList varList0;
+  VarList varList1;
 
 public:
   void
-  init()
+  init() override
   {
     VARQUOT2TEST = module.get_id("varquot2test");
     MEANDIFF2TEST = module.get_id("meandiff2test");
@@ -146,23 +145,17 @@ public:
     if (rconst <= 0) cdo_abort("Constant must be positive!");
     if (risk <= 0 || risk >= 1) cdo_abort("Risk must be greater than 0 and lower than 1!");
 
-    for (int is = 0; is < NIN; ++is)
-      {
-        streamID[is] = cdo_open_read(is);
-        vlistID[is] = cdo_stream_inq_vlist(streamID[is]);
-        if (is > 0)
-          {
-            vlistID2 = cdo_stream_inq_vlist(streamID[is]);
-            vlist_compare(vlistID[0], vlistID2, CmpVlist::All);
-          }
-      }
+    for (int is = 0; is < NIN; ++is) { streamID[is] = cdo_open_read(is); }
+    for (int is = 0; is < NIN; ++is) { vlistID[is] = cdo_stream_inq_vlist(streamID[is]); }
+
+    varList1 = VarList(vlistID[0]);
+
+    for (int is = 1; is < NIN; ++is) { varList_compare(varList1, VarList(vlistID[is])); }
 
     auto vlistID3 = vlistDuplicate(vlistID[0]);
 
     gridsizemax = vlistGridsizeMax(vlistID[0]);
 
-    varList_init(varList0, vlistID[0]);
-
     taxisID1 = vlistInqTaxis(vlistID[0]);
     taxisID3 = taxisDuplicate(taxisID1);
 
@@ -172,24 +165,24 @@ public:
   }
 
   void
-  run()
+  run() override
   {
     CdiDateTime vDateTime{};
     int reachedEOF[NIN]{ 0 };
 
-    auto maxRecords = vlistNrecs(vlistID[0]);
-    auto recList = std::vector<RecordInfo>(maxRecords);
+    auto numVars = varList1.numVars();
+    auto maxRecords = varList1.numRecords();
+    auto recordList = std::vector<RecordInfo>(maxRecords);
 
     Field in[NIN], out[NOUT];
     for (int i = 0; i < NIN; ++i) in[i].resize(gridsizemax);
     for (int i = 0; i < NOUT; ++i) out[i].resize(gridsizemax);
 
-    int numVars = varList0.size();
     Varray4D<double> work(numVars);
 
     for (int varID = 0; varID < numVars; ++varID)
       {
-        auto const &var = varList0[varID];
+        auto const &var = varList1.vars[varID];
         auto gridsize = var.gridsize;
         auto nlevels = var.nlevels;
 
@@ -223,15 +216,14 @@ public:
 
             for (int recID = 0; recID < nrecs; ++recID)
               {
-                int varID, levelID;
-                cdo_inq_record(streamID[is], &varID, &levelID);
+                auto [varID, levelID] = cdo_inq_record(streamID[is]);
 
-                auto const &var = varList0[varID];
+                auto const &var = varList1.vars[varID];
 
                 auto gridsize = var.gridsize;
                 inField.missval = var.missval;
 
-                if (tsID == 0 && is == 0) recList[recID].set(varID, levelID);
+                if (tsID == 0 && is == 0) recordList[recID].set(varID, levelID);
 
                 cdo_read_record(streamID[is], inField.vec_d.data(), &inField.numMissVals);
 
@@ -261,9 +253,9 @@ public:
 
     for (int recID = 0; recID < maxRecords; ++recID)
       {
-        auto [varID, levelID] = recList[recID].get();
+        auto [varID, levelID] = recordList[recID].get();
 
-        auto const &var = varList0[varID];
+        auto const &var = varList1.vars[varID];
 
         auto const &rwork = work[varID][levelID];
 
@@ -277,7 +269,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID3);
     for (int is = 0; is < NIN; ++is) cdo_stream_close(streamID[is]);
diff --git a/src/Tinfo.cc b/src/Tinfo.cc
index 5994ca1f54185ae1061b10b5cc855c39ce8c66cd..6aeb74efe043be409bf717fd85ae429ab9dc71f6 100644
--- a/src/Tinfo.cc
+++ b/src/Tinfo.cc
@@ -146,7 +146,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
 
     operator_check_argc(0);
@@ -214,7 +214,7 @@ public:
   }
 
   void
-  run()
+  run() override
   {
     if (ntsteps == 0) return;
 
@@ -315,7 +315,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID);
 
diff --git a/src/Tocomplex.cc b/src/Tocomplex.cc
index 958b8385979d30a07979d14c1aa8913a94213268..dd0cd38e0ae25946eca40a82ba269163adb5c5f3 100644
--- a/src/Tocomplex.cc
+++ b/src/Tocomplex.cc
@@ -23,11 +23,11 @@ public:
     .constraints = { 1, 1, NoRestriction },
   };
   inline static RegisterEntry<Tocomplex> registration = RegisterEntry<Tocomplex>(module);
+
   int RETOCOMPLEX, IMTOCOMPLEX;
   CdoStreamID streamID1;
-  int taxisID1;
-
   CdoStreamID streamID2;
+  int taxisID1;
   int taxisID2;
   int vlistID2;
 
@@ -38,9 +38,8 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
-
     RETOCOMPLEX = module.get_id("retocomplex");
     IMTOCOMPLEX = module.get_id("imtocomplex");
 
@@ -73,11 +72,11 @@ public:
     array1 = Varray<double>(gridsizemax);
     array2 = Varray<double>(2 * gridsizemax);
 
-    varList_init(varList1, vlistID1);
+    varList1 = VarList(vlistID1);
   }
 
   void
-  run()
+  run() override
   {
     int tsID = 0;
     int tsID2 = 0;
@@ -91,14 +90,13 @@ public:
 
         for (int recID = 0; recID < nrecs; ++recID)
           {
-            size_t numMissVals;
-            int varID, levelID;
-            cdo_inq_record(streamID1, &varID, &levelID);
+            auto [varID, levelID] = cdo_inq_record(streamID1);
             cdo_def_record(streamID2, varID, levelID);
 
+            size_t numMissVals;
             cdo_read_record(streamID1, array1.data(), &numMissVals);
 
-            auto gridsize = varList1[varID].gridsize;
+            auto gridsize = varList1.vars[varID].gridsize;
             if (operatorID == RETOCOMPLEX)
               {
                 for (size_t i = 0; i < gridsize; ++i)
@@ -122,8 +120,9 @@ public:
         tsID++;
       }
   }
+
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
diff --git a/src/Transpose.cc b/src/Transpose.cc
index ef454755c28c53d86a7053a388c0cebea1018ffa..ff34ad2099aff320d0f047b33cd58cb60920cc1a 100644
--- a/src/Transpose.cc
+++ b/src/Transpose.cc
@@ -13,8 +13,15 @@
 
 #include <cdi.h>
 
-#include "process_int.h"
+#include "cpp_lib.h"
+/*
+#ifdef HAVE_LIB_MDSPAN
+#include <mdspan>
+#else
+*/
 #include "matrix_view.h"
+// #endif
+#include "process_int.h"
 
 void
 transxy(int gridID, const Varray<double> &v1, Varray<double> &v2)
@@ -25,11 +32,20 @@ transxy(int gridID, const Varray<double> &v1, Varray<double> &v2)
 
   if (gridsize == (nx * ny))
     {
+      /*
+#ifdef HAVE_LIB_MDSPAN
+    printf("using mdspan\n");
+    auto mV1 = std::mdspan(v1.data(), ny, nx);
+    auto mV2 = std::mdspan(v2.data(), nx, ny);
+    for (size_t j = 0; j < ny; ++j)
+      for (size_t i = 0; i < nx; ++i) mV2[i, j] = mV1[j, i];
+#else
+*/
       MatrixView<const double> mV1(v1.data(), ny, nx);
       MatrixView<double> mV2(v2.data(), nx, ny);
-
       for (size_t j = 0; j < ny; ++j)
         for (size_t i = 0; i < nx; ++i) mV2[i][j] = mV1[j][i];
+      // #endif
     }
   else
     {
@@ -43,7 +59,7 @@ public:
   using Process::Process;
   inline static CdoModule module = {
     .name = "Transpose",
-    .operators = { { "transxy"} },
+    .operators = { { "transxy" } },
     .aliases = {},
     .mode = EXPOSED,     // Module mode: 0:intern 1:extern
     .number = CDI_REAL,  // Allowed number type
@@ -59,13 +75,14 @@ private:
   int taxisID2;
 
   int vlistID1;
+  VarList varList1;
 
   Varray<double> array1;
   Varray<double> array2;
 
 public:
   void
-  init()
+  init() override
   {
     operator_check_argc(0);
 
@@ -74,7 +91,9 @@ public:
     vlistID1 = cdo_stream_inq_vlist(streamID1);
     auto vlistID2 = vlistDuplicate(vlistID1);
 
-    auto ngrids = vlistNgrids(vlistID1);
+    varList1 = VarList(vlistID1);
+
+    auto ngrids = vlistNumGrids(vlistID1);
     for (int index = 0; index < ngrids; ++index)
       {
         auto gridID1 = vlistGrid(vlistID1, index);
@@ -103,7 +122,7 @@ public:
   }
 
   void
-  run()
+  run() override
   {
     int tsID = 0;
     while (true)
@@ -121,7 +140,7 @@ public:
             cdo_inq_record(streamID1, &varID, &levelID);
             cdo_read_record(streamID1, array1.data(), &numMissVals);
 
-            auto gridID = vlistInqVarGrid(vlistID1, varID);
+            auto gridID = varList1.vars[varID].gridID;
             transxy(gridID, array1, array2);
 
             cdo_def_record(streamID2, varID, levelID);
@@ -133,7 +152,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
diff --git a/src/Trend.cc b/src/Trend.cc
index 6cda549976a6b8f49c51eaeb0b38eb95580b5ecb..6a3891e503a2226a81f713f542f3a6f849d4cf85 100644
--- a/src/Trend.cc
+++ b/src/Trend.cc
@@ -13,12 +13,17 @@
 
 #include <cdi.h>
 
+#include "field.h"
 #include "process_int.h"
 #include "cdo_vlist.h"
 #include "cdo_options.h"
+#include "cdo_task.h"
+#include "field_trend.h"
+#include "cimdOmp.h"
 #include "datetime.h"
 #include "pmlist.h"
 #include "param_conversion.h"
+#include "progress.h"
 #include "field_functions.h"
 #include "arithmetic.h"
 
@@ -64,30 +69,26 @@ public:
   };
   inline static RegisterEntry<Trend> registration = RegisterEntry<Trend>(module);
 
-  static const int nwork = 5;
-  FieldVector2D work[nwork];
+  static const int numWork = 5;
 
   CdoStreamID streamID1;
-  int taxisID1;
-
   CdoStreamID streamID2;
-  int taxisID2;
-
   CdoStreamID streamID3;
 
-  int maxrecs;
+  int taxisID1;
+  int taxisID2;
 
-  bool tstepIsEqual = true;
+  int maxRecords;
 
-  int calendar;
+  bool tstepIsEqual = true;
 
   VarList varList1;
-  Field field1, field2;
-  std::vector<RecordInfo> recList;
+  std::vector<RecordInfo> recordList;
+  size_t gridSizeMax{ 0 };
 
 public:
   void
-  init()
+  init() override
   {
     trendGetParameter(tstepIsEqual);
 
@@ -104,14 +105,13 @@ public:
     taxisID2 = taxisDuplicate(taxisID1);
     vlistDefTaxis(vlistID2, taxisID2);
 
-    varList_init(varList1, vlistID1);
-
-    auto nvars = vlistNvars(vlistID1);
+    varList1 = VarList(vlistID1);
 
-    maxrecs = vlistNrecs(vlistID1);
-    recList = std::vector<RecordInfo>(maxrecs);
+    maxRecords = varList1.numRecords();
+    recordList = std::vector<RecordInfo>(maxRecords);
 
-    for (int varID = 0; varID < nvars; ++varID) vlistDefVarDatatype(vlistID2, varID, CDI_DATATYPE_FLT64);
+    auto numVars = varList1.numVars();
+    for (int varID = 0; varID < numVars; ++varID) vlistDefVarDatatype(vlistID2, varID, CDI_DATATYPE_FLT64);
 
     streamID2 = cdo_open_write(1);
     streamID3 = cdo_open_write(2);
@@ -119,26 +119,55 @@ public:
     cdo_def_vlist(streamID2, vlistID2);
     cdo_def_vlist(streamID3, vlistID2);
 
-    auto gridsizemax = vlistGridsizeMax(vlistID1);
+    gridSizeMax = vlistGridsizeMax(vlistID1);
+  }
+
+  void
+  write_output(const FieldVector3D &work)
+  {
+    Field field2, field3;
+    field2.resize(gridSizeMax);
+    field3.resize(gridSizeMax);
+
+    cdo_def_timestep(streamID2, 0);
+    cdo_def_timestep(streamID3, 0);
+
+    for (int recID = 0; recID < maxRecords; ++recID)
+      {
+        auto [varID, levelID] = recordList[recID].get();
+
+        const auto &var = varList1.vars[varID];
+        field2.size = var.gridsize;
+        field2.missval = var.missval;
+        field3.size = var.gridsize;
+        field3.missval = var.missval;
 
-    field1.resize(gridsizemax);
-    field2.resize(gridsizemax);
+        calc_trend_param(work, field2, field3, varID, levelID);
 
-    for (auto &w : work) fields_from_vlist(vlistID1, w, FIELD_VEC, 0);
+        cdo_def_record(streamID2, varID, levelID);
+        cdo_write_record(streamID2, field2.vec_d.data(), field_num_miss(field2));
 
-    calendar = taxisInqCalendar(taxisID1);
+        cdo_def_record(streamID3, varID, levelID);
+        cdo_write_record(streamID3, field3.vec_d.data(), field_num_miss(field3));
+      }
   }
 
   void
-  run()
+  run_sync()
   {
+    auto calendar = taxisInqCalendar(taxisID1);
     CheckTimeIncr checkTimeIncr;
     JulianDate julianDate0;
-    double deltat1 = 0.0;
     CdiDateTime vDateTime{};
+    double deltat1 = 0.0;
+    auto numSteps = varList1.numSteps();
+    cdo::Progress progress;
+    Field field1;
 
-    int tsID = 0;
+    FieldVector3D work(numWork);
+    for (auto &w : work) field2D_init(w, varList1, FIELD_VEC, 0);
 
+    int tsID = 0;
     while (true)
       {
         auto nrecs = cdo_stream_inq_timestep(streamID1, tsID);
@@ -151,88 +180,106 @@ public:
 
         for (int recID = 0; recID < nrecs; ++recID)
           {
-            int varID, levelID;
-            cdo_inq_record(streamID1, &varID, &levelID);
-
-            recList[recID].set(varID, levelID);
-
-            cdo_read_record(streamID1, field1.vec_d.data(), &field1.numMissVals);
-
-            auto gridsize = varList1[varID].gridsize;
-            auto missval = varList1[varID].missval;
-
-            auto &sumj = work[0][varID][levelID].vec_d;
-            auto &sumjj = work[1][varID][levelID].vec_d;
-            auto &sumjx = work[2][varID][levelID].vec_d;
-            auto &sumx = work[3][varID][levelID].vec_d;
-            auto &zn = work[4][varID][levelID].vec_d;
-
-            auto trend_sum = [&](auto i, auto value, auto is_EQ) {
-              if (!is_EQ(value, missval))
-                {
-                  sumj[i] += zj;
-                  sumjj[i] += zj * zj;
-                  sumjx[i] += zj * value;
-                  sumx[i] += value;
-                  zn[i]++;
-                }
-            };
-
-            if (std::isnan(missval))
-              for (size_t i = 0; i < gridsize; ++i) trend_sum(i, field1.vec_d[i], dbl_is_equal);
-            else
-              for (size_t i = 0; i < gridsize; ++i) trend_sum(i, field1.vec_d[i], is_equal);
+            auto fstatus = (tsID + (recID + 1.0) / nrecs) / numSteps;
+            if (numSteps > 0) progress.update(fstatus);
+
+            auto [varID, levelID] = cdo_inq_record(streamID1);
+            recordList[recID].set(varID, levelID);
+            field1.init(varList1.vars[varID]);
+            cdo_read_record(streamID1, field1);
+
+            calc_trend_sum(work, field1, zj, varID, levelID);
           }
 
         tsID++;
       }
 
     taxisDefVdatetime(taxisID2, vDateTime);
-    cdo_def_timestep(streamID2, 0);
-    cdo_def_timestep(streamID3, 0);
+    write_output(work);
+  }
 
-    for (int recID = 0; recID < maxrecs; ++recID)
+  static void
+  records_calc_trend_sum(FieldVector3D &work, const FieldVector2D &fields2D, const std::vector<RecordInfo> &recordList,
+                         double zj) noexcept
+  {
+    for (const auto &record : recordList)
       {
-        auto [varID, levelID] = recList[recID].get();
-
-        auto gridsize = varList1[varID].gridsize;
-        auto missval = varList1[varID].missval;
-        auto missval1 = missval;
-        auto missval2 = missval;
-        field1.size = gridsize;
-        field1.missval = missval;
-        field2.size = gridsize;
-        field2.missval = missval;
-
-        const auto &sumj = work[0][varID][levelID].vec_d;
-        const auto &sumjj = work[1][varID][levelID].vec_d;
-        const auto &sumjx = work[2][varID][levelID].vec_d;
-        const auto &sumx = work[3][varID][levelID].vec_d;
-        const auto &zn = work[4][varID][levelID].vec_d;
-
-        auto trend_kernel = [&](auto i, auto is_EQ) {
-          auto temp1 = SUBM(sumjx[i], DIVM(MULM(sumj[i], sumx[i]), zn[i]));
-          auto temp2 = SUBM(sumjj[i], DIVM(MULM(sumj[i], sumj[i]), zn[i]));
-
-          field2.vec_d[i] = DIVM(temp1, temp2);
-          field1.vec_d[i] = SUBM(DIVM(sumx[i], zn[i]), MULM(DIVM(sumj[i], zn[i]), field2.vec_d[i]));
-        };
-
-        if (std::isnan(missval))
-          for (size_t i = 0; i < gridsize; ++i) trend_kernel(i, dbl_is_equal);
-        else
-          for (size_t i = 0; i < gridsize; ++i) trend_kernel(i, is_equal);
+        auto [varID, levelID] = record.get();
+        calc_trend_sum(work, fields2D[varID][levelID], zj, varID, levelID);
+      }
+  }
 
-        cdo_def_record(streamID2, varID, levelID);
-        cdo_write_record(streamID2, field1.vec_d.data(), field_num_miss(field1));
+  void
+  run_async()
+  {
+    auto calendar = taxisInqCalendar(taxisID1);
+    CheckTimeIncr checkTimeIncr;
+    JulianDate julianDate0;
+    CdiDateTime vDateTime{};
+    double deltat1 = 0.0;
+    auto numSteps = varList1.numSteps();
+    cdo::Progress progress;
 
-        cdo_def_record(streamID3, varID, levelID);
-        cdo_write_record(streamID3, field2.vec_d.data(), field_num_miss(field2));
+    FieldVector3D work(numWork);
+    for (auto &w : work) field2D_init(w, varList1, FIELD_VEC, 0);
+
+    FieldVector3D fields3D(2);
+    field2D_init(fields3D[0], varList1, FIELD_VEC | FIELD_NAT);
+    field2D_init(fields3D[1], varList1, FIELD_VEC | FIELD_NAT);
+
+    bool useTask = true;
+    auto task = useTask ? std::make_unique<cdo::Task>() : nullptr;
+
+    int tsID = 0;
+    while (true)
+      {
+        auto nrecs = cdo_stream_inq_timestep(streamID1, tsID);
+        if (nrecs == 0) break;
+
+        vDateTime = taxisInqVdatetime(taxisID1);
+
+        if (tstepIsEqual) check_time_increment(tsID, calendar, vDateTime, checkTimeIncr);
+        auto zj = tstepIsEqual ? (double) tsID : delta_time_step_0(tsID, calendar, vDateTime, julianDate0, deltat1);
+
+        for (int recID = 0; recID < nrecs; ++recID)
+          {
+            auto fstatus = (tsID + (recID + 1.0) / nrecs) / numSteps;
+            if (numSteps > 0) progress.update(fstatus);
+
+            auto [varID, levelID] = cdo_inq_record(streamID1);
+            recordList[recID].set(varID, levelID);
+            cdo_read_record(streamID1, fields3D[tsID % 2][varID][levelID]);
+          }
+
+        if (useTask && tsID > 0) task->wait();
+
+        std::function<void()> records_calc_trend_sum_func
+            = std::bind(records_calc_trend_sum, std::ref(work), std::ref(fields3D[tsID % 2]), std::cref(recordList), zj);
+
+        if (useTask) { task->doAsync(records_calc_trend_sum_func); }
+        else { records_calc_trend_sum_func(); }
+
+        tsID++;
       }
+
+    if (useTask) task->wait();
+
+    taxisDefVdatetime(taxisID2, vDateTime);
+    write_output(work);
+  }
+
+  void
+  run() override
+  {
+    auto runAsync = (Options::CDO_Parallel_Read > 0);
+    if (runAsync)
+      run_async();
+    else
+      run_sync();
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID3);
     cdo_stream_close(streamID2);
diff --git a/src/Trendarith.cc b/src/Trendarith.cc
index 56a5ea7d1cb4cb9a7500706390493e3ab45971ea..8e8cd123e6894a01f1f8022e8629f307dc307c9a 100644
--- a/src/Trendarith.cc
+++ b/src/Trendarith.cc
@@ -19,12 +19,13 @@
 #include "datetime.h"
 #include "pmlist.h"
 #include "param_conversion.h"
+#include "progress.h"
 #include "field_functions.h"
 #include "arithmetic.h"
 
 template <typename T>
 static void
-add_trend(double zj, const Varray<T> &v1, const Varray<double> &v2, const Varray<double> &v3, Varray<T> &v4, size_t n, double mv)
+add_trend(double zj, Varray<T> &v1, const Varray<double> &v2, const Varray<double> &v3, size_t n, double mv)
 {
   auto missval1 = mv;
   auto missval2 = mv;
@@ -32,25 +33,23 @@ add_trend(double zj, const Varray<T> &v1, const Varray<double> &v2, const Varray
   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);
+    for (size_t i = 0; i < n; ++i) v1[i] = add_kernel(i, dbl_is_equal);
   else
-    for (size_t i = 0; i < n; ++i) v4[i] = add_kernel(i, is_equal);
+    for (size_t i = 0; i < n; ++i) v1[i] = add_kernel(i, is_equal);
 }
 
 static void
-add_trend(double zj, const Field &field1, const Field &field2, const Field &field3, Field &field4)
+add_trend(double zj, Field &field1, const Field &field2, const Field &field3)
 {
-  if (field1.memType != field4.memType) cdo_abort("Interal error, memType of field1 and field2 differ!");
-
   if (field1.memType == MemType::Float)
-    add_trend(zj, field1.vec_f, field2.vec_d, field3.vec_d, field4.vec_f, field1.size, field1.missval);
+    add_trend(zj, field1.vec_f, field2.vec_d, field3.vec_d, field1.size, field1.missval);
   else
-    add_trend(zj, field1.vec_d, field2.vec_d, field3.vec_d, field4.vec_d, field1.size, field1.missval);
+    add_trend(zj, field1.vec_d, field2.vec_d, field3.vec_d, field1.size, field1.missval);
 }
 
 template <typename T>
 static void
-sub_trend(double zj, const Varray<T> &v1, const Varray<double> &v2, const Varray<double> &v3, Varray<T> &v4, size_t n, double mv)
+sub_trend(double zj, Varray<T> &v1, const Varray<double> &v2, const Varray<double> &v3, size_t n, double mv)
 {
   auto missval1 = mv;
   auto missval2 = mv;
@@ -58,20 +57,18 @@ sub_trend(double zj, const Varray<T> &v1, const Varray<double> &v2, const Varray
   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);
+    for (size_t i = 0; i < n; ++i) v1[i] = sub_kernel(i, dbl_is_equal);
   else
-    for (size_t i = 0; i < n; ++i) v4[i] = sub_kernel(i, is_equal);
+    for (size_t i = 0; i < n; ++i) v1[i] = sub_kernel(i, is_equal);
 }
 
 static void
-sub_trend(double zj, const Field &field1, const Field &field2, const Field &field3, Field &field4)
+sub_trend(double zj, Field &field1, const Field &field2, const Field &field3)
 {
-  if (field1.memType != field4.memType) cdo_abort("Interal error, memType of field1 and field2 differ!");
-
   if (field1.memType == MemType::Float)
-    sub_trend(zj, field1.vec_f, field2.vec_d, field3.vec_d, field4.vec_f, field1.size, field1.missval);
+    sub_trend(zj, field1.vec_f, field2.vec_d, field3.vec_d, field1.size, field1.missval);
   else
-    sub_trend(zj, field1.vec_d, field2.vec_d, field3.vec_d, field4.vec_d, field1.size, field1.missval);
+    sub_trend(zj, field1.vec_d, field2.vec_d, field3.vec_d, field1.size, field1.missval);
 }
 
 static void
@@ -120,28 +117,23 @@ public:
   inline static RegisterEntry<Trendarith> registration = RegisterEntry<Trendarith>(module);
 
   CdoStreamID streamID1;
-  int taxisID1;
-
   CdoStreamID streamID2;
-
   CdoStreamID streamID3;
-
   CdoStreamID streamID4;
+
+  int taxisID1;
   int taxisID4;
 
   int operfunc;
 
   bool tstepIsEqual;
 
-  int calendar;
-
-  VarList varList;
+  VarList varList1;
   FieldVector2D vars2, vars3;
-  Field field1, field4;
 
 public:
   void
-  init()
+  init() override
   {
     auto operatorID = cdo_operator_id();
     operfunc = cdo_operator_f1(operatorID);
@@ -160,28 +152,29 @@ public:
 
     vlist_unpack(vlistID4);
 
-    vlist_compare(vlistID1, vlistID2, CmpVlist::Dim);
-    vlist_compare(vlistID1, vlistID3, CmpVlist::Dim);
+    varList1 = VarList(vlistID1);
+    VarList varList2(vlistID2);
+    VarList varList3(vlistID3);
+
+    varList_compare(varList1, varList2);
+    varList_compare(varList1, varList3);
 
     taxisID1 = vlistInqTaxis(vlistID1);
     taxisID4 = taxisDuplicate(taxisID1);
     vlistDefTaxis(vlistID4, taxisID4);
 
-    varList_init(varList, vlistID1);
-
     streamID4 = cdo_open_write(3);
     cdo_def_vlist(streamID4, vlistID4);
 
-    fields_from_vlist(vlistID1, vars2, FIELD_VEC);
-    fields_from_vlist(vlistID1, vars3, FIELD_VEC);
+    field2D_init(vars2, varList1, FIELD_VEC);
+    field2D_init(vars3, varList1, FIELD_VEC);
 
     {
       auto nrecs = cdo_stream_inq_timestep(streamID2, 0);
       for (int recID = 0; recID < nrecs; ++recID)
         {
-          int varID, levelID;
-          cdo_inq_record(streamID2, &varID, &levelID);
-          cdo_read_record(streamID2, vars2[varID][levelID].vec_d.data(), &vars2[varID][levelID].numMissVals);
+          auto [varID, levelID] = cdo_inq_record(streamID2);
+          cdo_read_record(streamID2, vars2[varID][levelID]);
         }
     }
 
@@ -189,21 +182,23 @@ public:
       auto nrecs = cdo_stream_inq_timestep(streamID3, 0);
       for (int recID = 0; recID < nrecs; ++recID)
         {
-          int varID, levelID;
-          cdo_inq_record(streamID3, &varID, &levelID);
-          cdo_read_record(streamID3, vars3[varID][levelID].vec_d.data(), &vars3[varID][levelID].numMissVals);
+          auto [varID, levelID] = cdo_inq_record(streamID3);
+          cdo_read_record(streamID3, vars3[varID][levelID]);
         }
     }
-
-    calendar = taxisInqCalendar(taxisID1);
   }
 
   void
-  run()
+  run() override
   {
+    auto calendar = taxisInqCalendar(taxisID1);
     CheckTimeIncr checkTimeIncr;
     JulianDate julianDate0;
     double deltat1 = 0.0;
+    auto numSteps = varList1.numSteps();
+    cdo::Progress progress;
+    Field field;
+
     int tsID = 0;
     while (true)
       {
@@ -220,21 +215,21 @@ public:
 
         for (int recID = 0; recID < nrecs; ++recID)
           {
-            int varID, levelID;
-            cdo_inq_record(streamID1, &varID, &levelID);
-            field1.init(varList[varID]);
-            cdo_read_record(streamID1, field1);
+            auto fstatus = (tsID + (recID + 1.0) / nrecs) / numSteps;
+            if (numSteps > 0) progress.update(fstatus);
 
-            field4.init(varList[varID]);
+            auto [varID, levelID] = cdo_inq_record(streamID1);
+            const auto &var1 = varList1.vars[varID];
+            field.init(var1);
+            cdo_read_record(streamID1, field);
 
             if (operfunc == FieldFunc_Add)
-              add_trend(zj, field1, vars2[varID][levelID], vars3[varID][levelID], field4);
+              add_trend(zj, field, vars2[varID][levelID], vars3[varID][levelID]);
             else
-              sub_trend(zj, field1, vars2[varID][levelID], vars3[varID][levelID], field4);
+              sub_trend(zj, field, vars2[varID][levelID], vars3[varID][levelID]);
 
-            field4.numMissVals = field1.numMissVals;
             cdo_def_record(streamID4, varID, levelID);
-            cdo_write_record(streamID4, field4);
+            cdo_write_record(streamID4, field);
           }
 
         tsID++;
@@ -242,7 +237,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID4);
     cdo_stream_close(streamID3);
diff --git a/src/Tstepcount.cc b/src/Tstepcount.cc
index a8eb370dba52861c81f04391d06ab500864d1235..b1fced6689d21dd89490d3df11e18916ad958db3 100644
--- a/src/Tstepcount.cc
+++ b/src/Tstepcount.cc
@@ -23,14 +23,14 @@ template <typename T>
 static T
 tstepcount(long nts, T missval, const Varray<T> &v, T refval)
 {
-  if (DBL_IS_EQUAL(refval, missval)) return missval;
+  if (dbl_is_equal(refval, missval)) return missval;
 
   long j;
   long n = 0;
   for (j = 0; j < nts; ++j)
     {
       n++;
-      if (DBL_IS_EQUAL(v[j], refval)) break;
+      if (dbl_is_equal(v[j], refval)) break;
     }
 
   return (j == nts) ? missval : (T) n;
@@ -42,7 +42,7 @@ public:
   using Process::Process;
   inline static CdoModule module = {
     .name = "Tstepcount",
-    .operators = { { "tstepcount"} },
+    .operators = { { "tstepcount" } },
     .aliases = {},
     .mode = EXPOSED,     // Module mode: 0:intern 1:extern
     .number = CDI_REAL,  // Allowed number type
@@ -53,21 +53,21 @@ public:
   CdiDateTime vDateTime{};
 
   CdoStreamID streamID1;
-  int taxisID1;
-  int vlistID1;
-
   CdoStreamID streamID2;
+
+  int vlistID1;
+  int taxisID1;
   int taxisID2;
 
-  int nvars;
+  int numVars;
   double refval;
 
   VarList varList1;
-  FieldVector3D vars;
+  FieldVector3D varsData;
 
 public:
   void
-  init()
+  init() override
   {
     refval = (cdo_operator_argc() == 1) ? parameter_to_double(cdo_operator_argv(0)) : 0.0;
 
@@ -78,8 +78,8 @@ public:
 
     vlistDefNtsteps(vlistID2, 1);
 
-    nvars = vlistNvars(vlistID1);
-    for (int varID = 0; varID < nvars; ++varID) cdiDefKeyString(vlistID2, varID, CDI_KEY_UNITS, "steps");
+    numVars = vlistNvars(vlistID1);
+    for (int varID = 0; varID < numVars; ++varID) cdiDefKeyString(vlistID2, varID, CDI_KEY_UNITS, "steps");
 
     taxisID1 = vlistInqTaxis(vlistID1);
     taxisID2 = taxisDuplicate(taxisID1);
@@ -88,11 +88,11 @@ public:
     streamID2 = cdo_open_write(1);
     cdo_def_vlist(streamID2, vlistID2);
 
-    varList_init(varList1, vlistID1);
+    varList1 = VarList(vlistID1);
   }
-  
+
   void
-  run()
+  run() override
   {
     int tsID = 0;
     while (true)
@@ -101,18 +101,17 @@ public:
         if (nrecs == 0) break;
 
         constexpr size_t NALLOC_INC = 1024;
-        if ((size_t) tsID >= vars.size()) vars.resize(vars.size() + NALLOC_INC);
+        if ((size_t) tsID >= varsData.size()) varsData.resize(varsData.size() + NALLOC_INC);
 
         vDateTime = taxisInqVdatetime(taxisID1);
 
-        fields_from_vlist(vlistID1, vars[tsID]);
+        field2D_init(varsData[tsID], varList1);
 
         for (int recID = 0; recID < nrecs; ++recID)
           {
-            int varID, levelID;
-            cdo_inq_record(streamID1, &varID, &levelID);
-            auto &field = vars[tsID][varID][levelID];
-            field.init(varList1[varID]);
+            auto [varID, levelID] = cdo_inq_record(streamID1);
+            auto &field = varsData[tsID][varID][levelID];
+            field.init(varList1.vars[varID]);
             cdo_read_record(streamID1, field);
           }
 
@@ -123,12 +122,13 @@ public:
 
     std::vector<Field> fields(Threading::ompNumThreads);
 
-    for (int varID = 0; varID < nvars; ++varID)
+    for (int varID = 0; varID < numVars; ++varID)
       {
-        auto memType = varList1[varID].memType;
-        auto missval = varList1[varID].missval;
-        auto gridsize = varList1[varID].gridsize;
-        for (int levelID = 0; levelID < varList1[varID].nlevels; ++levelID)
+        const auto &var1 = varList1.vars[varID];
+        auto memType = var1.memType;
+        auto missval = var1.missval;
+        auto gridsize = var1.gridsize;
+        for (int levelID = 0; levelID < var1.nlevels; ++levelID)
           {
 #ifdef _OPENMP
 #pragma omp parallel for default(shared) schedule(dynamic, 1)
@@ -141,21 +141,21 @@ public:
                   {
                     auto &v = fields[ompthID].vec_f;
                     v.resize(nts);
-                    for (int t = 0; t < nts; ++t) v[t] = vars[t][varID][levelID].vec_f[i];
+                    for (int t = 0; t < nts; ++t) v[t] = varsData[t][varID][levelID].vec_f[i];
 
                     auto count = tstepcount(nts, (float) missval, v, (float) refval);
 
-                    vars[0][varID][levelID].vec_f[i] = count;
+                    varsData[0][varID][levelID].vec_f[i] = count;
                   }
                 else
                   {
                     auto &v = fields[ompthID].vec_d;
                     v.resize(nts);
-                    for (int t = 0; t < nts; ++t) v[t] = vars[t][varID][levelID].vec_d[i];
+                    for (int t = 0; t < nts; ++t) v[t] = varsData[t][varID][levelID].vec_d[i];
 
                     auto count = tstepcount(nts, missval, v, refval);
 
-                    vars[0][varID][levelID].vec_d[i] = count;
+                    varsData[0][varID][levelID].vec_d[i] = count;
                   }
               }
           }
@@ -164,12 +164,12 @@ public:
     taxisDefVdatetime(taxisID2, vDateTime);
     cdo_def_timestep(streamID2, 0);
 
-    for (int varID = 0; varID < nvars; ++varID)
+    for (int varID = 0; varID < numVars; ++varID)
       {
-        for (int levelID = 0; levelID < varList1[varID].nlevels; ++levelID)
+        for (int levelID = 0; levelID < varList1.vars[varID].nlevels; ++levelID)
           {
             cdo_def_record(streamID2, varID, levelID);
-            auto &field1 = vars[0][varID][levelID];
+            auto &field1 = varsData[0][varID][levelID];
             field_num_mv(field1);
             cdo_write_record(streamID2, field1);
           }
@@ -177,7 +177,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
diff --git a/src/Unpack.cc b/src/Unpack.cc
index 46165ad0f770a12a29e113cd6640570a2e7c7b84..b3c8a2c96892879e426b9502268a8fdb897208d1 100644
--- a/src/Unpack.cc
+++ b/src/Unpack.cc
@@ -24,16 +24,16 @@ public:
   inline static RegisterEntry<Unpack> registration = RegisterEntry<Unpack>(module);
 
   CdoStreamID streamID1;
-  int taxisID1;
-
   CdoStreamID streamID2;
+
+  int taxisID1;
   int taxisID2;
 
   VarList varList1;
 
 public:
   void
-  init()
+  init() override
   {
     operator_check_argc(0);
 
@@ -65,11 +65,11 @@ public:
     cdo_def_vlist(streamID2, vlistID2);
     vlistDestroy(vlistID2);
 
-    varList_init(varList1, vlistID1);
+    varList1 = VarList(vlistID1);
   }
 
   void
-  run()
+  run() override
   {
     Field field;
 
@@ -84,9 +84,8 @@ public:
 
         for (int recID = 0; recID < nrecs; ++recID)
           {
-            int varID, levelID;
-            cdo_inq_record(streamID1, &varID, &levelID);
-            field.init(varList1[varID]);
+            auto [varID, levelID] = cdo_inq_record(streamID1);
+            field.init(varList1.vars[varID]);
             cdo_read_record(streamID1, field);
 
             cdo_def_record(streamID2, varID, levelID);
@@ -98,7 +97,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID1);
     cdo_stream_close(streamID2);
diff --git a/src/Vargen.cc b/src/Vargen.cc
index 7b0d7d75ed964f9da4f0ea6b8b14b661688985d2..0971653141e18945808bb742cad7214c845bdcee 100644
--- a/src/Vargen.cc
+++ b/src/Vargen.cc
@@ -17,59 +17,33 @@
 #include <cstdlib>
 #include <cassert>
 #include <cdi.h>
-#include "julian_date.h"
 
 #include "cdo_options.h"
+#include "cdo_data.h"
 #include "process_int.h"
 #include "cdo_zaxis.h"
 #include "param_conversion.h"
 #include <mpim_grid.h>
 #include "grid_healpix.h"
 #include "griddes.h"
-#include "constants.h"
 #include "stdnametable.h"
 #include "param_conversion.h"
 
-static constexpr double etopo_scale = 3.0;
-static constexpr double etopo_offset = 11000.0;
-static constexpr unsigned short etopo[] = {
-#include "etopo.dat"
-};
-
-static constexpr double temp_scale = 500.0;
-static constexpr double temp_offset = -220.0;
-static constexpr unsigned short temp[] = {
-#include "temp.dat"
-};
-
-static constexpr double mask_scale = 1.0;
-static constexpr double mask_offset = 0.0;
-static constexpr unsigned short mask[] = {
-#include "mask.dat"
-};
-
-// Some Constants for creating temperatur and pressure for the standard atmosphere
-constexpr double T_ZERO = 213.0;
-constexpr double T_DELTA = 75.0;
-constexpr double SCALEHEIGHT = 10000.0;  // [m]
-
-static double
-std_atm_temperatur(double height)
-{
-  // Compute the temperatur for the given height (in meters) according to the solution of the hydrostatic atmosphere
-  return (T_ZERO + T_DELTA * std::exp((-1) * (height / SCALEHEIGHT)));
-}
-
-static double
-std_atm_pressure(double height)
+static int
+random_init(int operatorID)
 {
-  constexpr double P_ZERO = 1013.25;  // surface pressure [hPa]
-  constexpr double CC_R = 287.05;     // specific gas constant for air
-  constexpr double TMP4PRESSURE = (C_EARTH_GRAV * SCALEHEIGHT) / (CC_R * T_ZERO);
-
-  // Compute the pressure for the given height (in meters) according to the solution of the hydrostatic atmosphere
-  return (P_ZERO
-          * std::exp((-1) * TMP4PRESSURE * std::log((std::exp(height / SCALEHEIGHT) * T_ZERO + T_DELTA) / (T_ZERO + T_DELTA))));
+  unsigned int seed = Options::Random_Seed;
+  operator_input_arg(cdo_operator_enter(operatorID));
+  if (cdo_operator_argc() < 1) cdo_abort("Too few arguments!");
+  if (cdo_operator_argc() > 2) cdo_abort("Too many arguments!");
+  auto gridID = cdo_define_grid(cdo_operator_argv(0));
+  if (cdo_operator_argc() == 2)
+    {
+      auto idum = parameter_to_int(cdo_operator_argv(1));
+      if (idum >= 0 && idum < 0x7FFFFFFF) seed = idum;
+    }
+  std::srand(seed);
+  return gridID;
 }
 
 static void
@@ -95,6 +69,22 @@ conv_generic_grid(int gridID, size_t gridsize, Varray<double> &xvals2D, Varray<d
       }
 }
 
+static int
+generate_full_point_grid_radian(int gridID, Varray<double> &xvals, Varray<double> &yvals)
+{
+  gridID = generate_full_point_grid(gridID);
+  if (!gridHasCoordinates(gridID)) cdo_abort("Target cell center coordinates missing!");
+
+  gridInqXvals(gridID, xvals.data());
+  gridInqYvals(gridID, yvals.data());
+
+  // Convert lat/lon units if required
+  cdo_grid_to_radian(gridID, CDI_XAXIS, xvals, "grid center lon");
+  cdo_grid_to_radian(gridID, CDI_YAXIS, yvals, "grid center lat");
+
+  return gridID;
+}
+
 static size_t
 calc_index_ii(size_t nx, double xval)
 {
@@ -207,61 +197,6 @@ remap_nn_reg2d(size_t nx, size_t ny, const Varray<float> &data, int gridID, Varr
     remap_nn_reg2d_to_nonreg2d(nx, ny, data, gridID, array);
 }
 
-static int
-random_init(int operatorID)
-{
-  unsigned int seed = Options::Random_Seed;
-  operator_input_arg(cdo_operator_enter(operatorID));
-  if (cdo_operator_argc() < 1) cdo_abort("Too few arguments!");
-  if (cdo_operator_argc() > 2) cdo_abort("Too many arguments!");
-  auto gridID = cdo_define_grid(cdo_operator_argv(0));
-  if (cdo_operator_argc() == 2)
-    {
-      auto idum = parameter_to_int(cdo_operator_argv(1));
-      if (idum >= 0 && idum < 0x7FFFFFFF) seed = idum;
-    }
-  std::srand(seed);
-  return gridID;
-}
-
-static void
-random_compute(size_t gridsize, Varray<float> &array)
-{
-  for (size_t i = 0; i < gridsize; ++i) array[i] = ((double) std::rand()) / ((double) RAND_MAX);
-}
-
-static void
-sincos_compute(size_t gridsize, Varray<float> &array, const Varray<double> &xvals, const Varray<double> &yvals)
-{
-  for (size_t i = 0; i < gridsize; ++i) array[i] = std::cos(1.0 * xvals[i]) * std::sin(2.0 * yvals[i]);
-}
-
-static void
-coshill_compute(size_t gridsize, Varray<float> &array, const Varray<double> &xvals, const Varray<double> &yvals)
-{
-  for (size_t i = 0; i < gridsize; ++i) array[i] = 2.0 - std::cos(std::acos(std::cos(xvals[i]) * std::cos(yvals[i])) / 1.2);
-}
-
-static void
-testfield_compute(size_t gridsize, Varray<float> &array, const Varray<double> &xvals, const Varray<double> &yvals)
-{
-  double xyz[3];
-  for (size_t i = 0; i < gridsize; ++i)
-    {
-      gcLLtoXYZ(xvals[i], yvals[i], xyz);
-      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;
-    }
-}
-
-static void
-unpack_data(size_t datasize, Varray<float> &data, double scale, double offset, const unsigned short *zdata)
-{
-  for (size_t i = 0; i < datasize; ++i) data[i] = zdata[i] / scale - offset;
-}
-
 static int
 define_point_grid()
 {
@@ -319,6 +254,7 @@ public:
   using Process::Process;
   inline static CdoModule module = {
     .name = "Vargen",
+    // clang-format off
     .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 },
@@ -329,6 +265,7 @@ public:
                    { "temp", VargenHelp },
                    { "mask", VargenHelp },
                    { "stdatm", 0, 0, "height levels[m]", VargenHelp } },
+    // clang-format on
     .aliases = { { "for", "seq" } },
     .mode = EXPOSED,     // Module mode: 0:intern 1:extern
     .number = CDI_REAL,  // Allowed number type
@@ -341,28 +278,24 @@ public:
 private:
   static constexpr size_t nlat = 360, nlon = 720;
   double lon[nlon], lat[nlat];
-  int nlevels = 1;
   int gridID = -1, gridIDdata = -1;
   double rstart = 0.0, rstop = 0.0, rinc = 0.0;
   double rconst = 0.0;
   std::vector<double> levels;
   int ntimesteps;
-  int julday;
 
   int taxisID;
   CdoStreamID streamID;
-  int varID;
   int varID2;
   int vlistID;
   int operatorID;
-  size_t gridsize;
-  Varray<float> array;
-  int nvars;
 
 public:
   void
-  init()
+  init() override
   {
+    int nlevels = 1;
+
     RANDOM = module.get_id("random");
     SINCOS = module.get_id("sincos");
     COSHILL = module.get_id("coshill");
@@ -439,7 +372,7 @@ public:
 
     auto timetype = (operatorID == SEQ) ? TIME_VARYING : TIME_CONSTANT;
 
-    varID = vlistDefVar(vlistID, gridID, zaxisID, timetype);
+    auto varID = vlistDefVar(vlistID, gridID, zaxisID, timetype);
     /*
        For the standard atmosphere two output variables are generated: pressure and temperature.
        The first (varID) is pressure, second (varID2) is temperature. Add an additional variable for the standard atmosphere.
@@ -469,20 +402,20 @@ public:
 
     cdo_def_vlist(streamID, vlistID);
 
-    gridsize = gridInqSize(gridID);
-    array = Varray<float>(gridsize);
-
     ntimesteps = (operatorID == SEQ) ? 1.001 + ((rstop - rstart) / rinc) : 1;
     if (operatorID != SEQ) vlistDefNtsteps(vlistID, 0);
-
-    julday = date_to_julday(CALENDAR_PROLEPTIC, 10101);
-
-    nvars = vlistNvars(vlistID);
   }
 
   void
-  run()
+  run() override
   {
+    VarList varList(vlistID);
+
+    auto julday = date_to_julday(CALENDAR_PROLEPTIC, 10101);
+
+    size_t gridsize = gridInqSize(gridID);
+    Varray<float> array(gridsize);
+
     for (int tsID = 0; tsID < ntimesteps; ++tsID)
       {
         auto rval = rstart + rinc * tsID;
@@ -492,63 +425,45 @@ public:
         cdo_def_timestep(streamID, tsID);
 
         // this should either be 1 or 2, two for atmosphere
-        for (varID = 0; varID < nvars; ++varID)
+        for (int varID = 0; varID < varList.numVars(); ++varID)
           {
-            nlevels = zaxisInqSize(vlistInqVarZaxis(vlistID, varID));
-            for (int levelID = 0; levelID < nlevels; ++levelID)
+            for (int levelID = 0; levelID < varList.vars[varID].nlevels; ++levelID)
               {
                 cdo_def_record(streamID, varID, levelID);
 
-                if (operatorID == RANDOM) { random_compute(gridsize, array); }
+                if (operatorID == RANDOM) { cdo::fill_random(array); }
                 else if (operatorID == SINCOS || operatorID == COSHILL || operatorID == TESTFIELD)
                   {
                     Varray<double> xvals(gridsize), yvals(gridsize);
 
                     if (grid_is_distance_generic(gridID)) { conv_generic_grid(gridID, gridsize, xvals, yvals); }
-                    else
-                      {
-                        gridID = generate_full_point_grid(gridID);
-                        if (!gridHasCoordinates(gridID)) cdo_abort("Target cell center coordinates missing!");
-
-                        gridInqXvals(gridID, xvals.data());
-                        gridInqYvals(gridID, yvals.data());
-
-                        // Convert lat/lon units if required
-                        cdo_grid_to_radian(gridID, CDI_XAXIS, xvals, "grid center lon");
-                        cdo_grid_to_radian(gridID, CDI_YAXIS, yvals, "grid center lat");
-                      }
+                    else { gridID = generate_full_point_grid_radian(gridID, xvals, yvals); }
 
                     // clang-format off
-                    if      (operatorID == SINCOS)    sincos_compute(gridsize, array, xvals, yvals);
-                    else if (operatorID == COSHILL)   coshill_compute(gridsize, array, xvals, yvals);
-                    else if (operatorID == TESTFIELD) testfield_compute(gridsize, array, xvals, yvals);
+                    if      (operatorID == SINCOS)    cdo::fill_sincos(array, xvals, yvals);
+                    else if (operatorID == COSHILL)   cdo::fill_coshill(array, xvals, yvals);
+                    else if (operatorID == TESTFIELD) cdo::fill_testfield(array, xvals, yvals);
                     // clang-format on
                   }
-                else if (operatorID == CONST)
-                  {
-                    for (size_t i = 0; i < gridsize; ++i) array[i] = rconst;
-                  }
+                else if (operatorID == CONST) { ranges::fill(array, rconst); }
                 else if (operatorID == TOPO || operatorID == TEMP || operatorID == MASK)
                   {
-                    auto datasize = gridInqSize(gridIDdata);
-                    Varray<float> data(datasize);
+                    Varray<float> data;
 
                     // clang-format off
-                    if      (operatorID == TOPO) unpack_data(datasize, data, etopo_scale, etopo_offset, etopo);
-                    else if (operatorID == TEMP) unpack_data(datasize, data, temp_scale, temp_offset, temp);
-                    else if (operatorID == MASK) unpack_data(datasize, data, mask_scale, mask_offset, mask);
+                    if      (operatorID == TOPO) data = cdo::unpack_data(cdo::topoData);
+                    else if (operatorID == TEMP) data = cdo::unpack_data(cdo::tempData);
+                    else if (operatorID == MASK) data = cdo::unpack_data(cdo::maskData);
                     // clang-format on
 
                     if (gridID != gridIDdata && gridIDdata != -1) { remap_nn_reg2d(nlon, nlat, data, gridID, array); }
-                    else
-                      {
-                        for (size_t i = 0; i < gridsize; ++i) array[i] = data[i];
-                      }
+                    else { array = data; }
                   }
                 else if (operatorID == SEQ) { array[0] = rval; }
                 else if (operatorID == STDATM)
                   {
-                    array[0] = (varID == varID2) ? std_atm_temperatur(levels[levelID]) : std_atm_pressure(levels[levelID]);
+                    array[0]
+                        = (varID == varID2) ? cdo::std_atm_temperatur(levels[levelID]) : cdo::std_atm_pressure(levels[levelID]);
                   }
 
                 cdo_write_record_f(streamID, array.data(), 0);
@@ -558,7 +473,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID);
 
diff --git a/src/Varrms.cc b/src/Varrms.cc
index 8a6ed153c68d8eab726836a19665ffb77f09928b..7934d02c4d4ddf846c4a70872a2a70a4ff0d628d 100644
--- a/src/Varrms.cc
+++ b/src/Varrms.cc
@@ -38,7 +38,8 @@ var_rms(const Varray<double> &w, const FieldVector &field1, const FieldVector &f
       {
         auto array1 = field1[k].vec_d;
         auto array2 = field2[k].vec_d;
-        for (size_t i = 0; i < len; ++i) /*	  if ( !is_EQ(w[i], missval1) ) */
+        for (size_t i = 0; i < len; ++i)
+          //	  if ( !is_EQ(w[i], missval1) )
           {
             rsum = ADDM(rsum, MULM(w[i], MULM(SUBM(array2[i], array1[i]), SUBM(array2[i], array1[i]))));
             rsumw = ADDM(rsumw, w[i]);
@@ -71,7 +72,7 @@ public:
   using Process::Process;
   inline static CdoModule module = {
     .name = "Varrms",
-    .operators = { { "varrms"} },
+    .operators = { { "varrms" } },
     .aliases = {},
     .mode = EXPOSED,     // Module mode: 0:intern 1:extern
     .number = CDI_REAL,  // Allowed number type
@@ -90,8 +91,8 @@ private:
   int vlistID1;
   int vlistID3;
 
-  FieldVector2D vars1;
-  FieldVector2D vars2;
+  FieldVector2D varsData1;
+  FieldVector2D varsData2;
 
   int nvars;
   int lastgrid = -1;
@@ -101,12 +102,12 @@ private:
 
   Field field3;
   Varray<double> weights;
+  VarList varList1;
 
 public:
   void
-  init()
+  init() override
   {
-
     operator_check_argc(0);
 
     streamID1 = cdo_open_read(0);
@@ -133,7 +134,7 @@ public:
     taxisID3 = taxisDuplicate(taxisID1);
     vlistDefTaxis(vlistID3, taxisID3);
 
-    auto ngrids = vlistNgrids(vlistID1);
+    auto ngrids = vlistNumGrids(vlistID1);
     int index = 0;
     auto gridID1 = vlistGrid(vlistID1, index);
 
@@ -146,8 +147,10 @@ public:
     streamID3 = cdo_open_write(2);
     cdo_def_vlist(streamID3, vlistID3);
 
-    fields_from_vlist(vlistID1, vars1, FIELD_VEC);
-    fields_from_vlist(vlistID2, vars2, FIELD_VEC);
+    varList1 = VarList(vlistID1);
+    VarList varList2(vlistID2);
+    field2D_init(varsData1, varList1, FIELD_VEC);
+    field2D_init(varsData2, varList2, FIELD_VEC);
 
     auto gridsizemax = vlistGridsizeMax(vlistID1);
     if (needWeights) weights.resize(gridsizemax);
@@ -157,7 +160,7 @@ public:
   }
 
   void
-  run()
+  run() override
   {
     int tsID = 0;
     while (true)
@@ -176,18 +179,18 @@ public:
             size_t numMissVals;
             int varID, levelID;
             cdo_inq_record(streamID1, &varID, &levelID);
-            cdo_read_record(streamID1, vars1[varID][levelID].vec_d.data(), &numMissVals);
+            cdo_read_record(streamID1, varsData1[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(), &numMissVals);
+            cdo_read_record(streamID2, varsData2[varID][levelID].vec_d.data(), &numMissVals);
             if (numMissVals) cdo_abort("Missing values unsupported for this operator!");
           }
 
         for (int varID = 0; varID < nvars; ++varID)
           {
             auto wstatus = false;
-            auto gridID = vars1[varID][0].grid;
+            auto gridID = varsData1[varID][0].grid;
             if (needWeights && gridID != lastgrid)
               {
                 lastgrid = gridID;
@@ -197,8 +200,8 @@ public:
             if (wstatus != 0 && tsID == 0 && code != oldcode)
               cdo_warning("Using constant area weights for code %d!", oldcode = code);
 
-            field3.missval = vars1[varID][0].missval;
-            var_rms(weights, vars1[varID], vars2[varID], field3);
+            field3.missval = varsData1[varID][0].missval;
+            var_rms(weights, varsData1[varID], varsData2[varID], field3);
 
             cdo_def_record(streamID3, varID, 0);
             cdo_write_record(streamID3, field3.vec_d.data(), field3.numMissVals);
@@ -209,7 +212,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID3);
     cdo_stream_close(streamID2);
diff --git a/src/Varsstat.cc b/src/Varsstat.cc
index 21d525bae67b84aae87776ce9ffb918894fb473c..a304a6a30b4b6bfc0a9b7c1e2797112861bf0590 100644
--- a/src/Varsstat.cc
+++ b/src/Varsstat.cc
@@ -7,13 +7,14 @@
 
 #include <cdi.h>
 
+#include "cdo_stepstat.h"
 #include "process_int.h"
 #include "field_functions.h"
 
 static void
 check_unique_zaxis(int vlistID)
 {
-  auto nzaxis = vlistNzaxis(vlistID);
+  auto nzaxis = vlistNumZaxis(vlistID);
   auto zaxisID = vlistZaxis(vlistID, 0);
   auto nlevels = zaxisInqSize(zaxisID);
   for (int index = 1; index < nzaxis; ++index)
@@ -25,7 +26,7 @@ check_unique_zaxis(int vlistID)
 static void
 check_unique_gridsize(int vlistID)
 {
-  auto ngrids = vlistNgrids(vlistID);
+  auto ngrids = vlistNumGrids(vlistID);
   auto gridID = vlistGrid(vlistID, 0);
   auto gridsize = gridInqSize(gridID);
   for (int index = 0; index < ngrids; ++index)
@@ -35,16 +36,16 @@ check_unique_gridsize(int vlistID)
 }
 
 static void
-set_attributes(const VarList &varList1, int vlistID2, int varID2, int operatorID)
+set_attributes(const CdoVars &cdoVars1, int vlistID2, int varID2, int operatorID)
 {
-  const auto &var0 = varList1[0];
+  const auto &var0 = cdoVars1[0];
   auto paramIsEqual = true;
   auto name = var0.name;
   auto param = var0.param;
-  int nvars = varList1.size();
+  int nvars = cdoVars1.size();
   for (int varID = 1; varID < nvars; ++varID)
     {
-      if (param != varList1[varID].param || name != varList1[varID].name)
+      if (param != cdoVars1[varID].param || name != cdoVars1[varID].name)
         {
           paramIsEqual = false;
           break;
@@ -85,69 +86,55 @@ public:
     .constraints = { 1, 1, NoRestriction },
   };
   inline static RegisterEntry<Varsstat> registration = RegisterEntry<Varsstat>(module);
-  CdoStreamID streamID1;
-  int taxisID1;
 
+private:
+  CdoStreamID streamID1;
   CdoStreamID streamID2;
+  int taxisID1;
   int taxisID2;
-  int vlistID2;
-
-  bool lrange;
-  bool lvarstd;
-  bool lmean;
-  bool lstd;
-  double divisor;
 
-  int nlevels;
+  int vlistID2;
 
-  int operfunc;
+  int numLevels;
 
-  Field field;
-  FieldVector vars1, samp1, vars2;
   VarList varList1;
 
+  cdo::StepStat1Dlevels stepStat;
+
 public:
   void
-  init()
+  init() override
   {
-
     auto operatorID = cdo_operator_id();
-    operfunc = cdo_operator_f1(operatorID);
+    auto operfunc = cdo_operator_f1(operatorID);
 
     operator_check_argc(0);
 
-    lrange = (operfunc == FieldFunc_Range);
-    lmean = (operfunc == FieldFunc_Mean || operfunc == FieldFunc_Avg);
-    lstd = (operfunc == FieldFunc_Std || operfunc == FieldFunc_Std1);
-    lvarstd = (lstd || operfunc == FieldFunc_Var || operfunc == FieldFunc_Var1);
-    auto lvars2 = (lvarstd || lrange);
-    divisor = (operfunc == FieldFunc_Std1 || operfunc == FieldFunc_Var1);
+    stepStat.init(operfunc);
 
     streamID1 = cdo_open_read(0);
     auto vlistID1 = cdo_stream_inq_vlist(streamID1);
 
-    varList_init(varList1, vlistID1);
+    varList1 = VarList(vlistID1);
 
     check_unique_zaxis(vlistID1);
     auto zaxisID = vlistZaxis(vlistID1, 0);
-    nlevels = zaxisInqSize(zaxisID);
+    numLevels = zaxisInqSize(zaxisID);
 
     check_unique_gridsize(vlistID1);
-    auto gridID = vlistGrid(vlistID1, 0);
-    auto gridsize = gridInqSize(gridID);
 
-    auto timetype = varList1[0].timetype;
-    auto nvars = vlistNvars(vlistID1);
-    for (int varID = 1; varID < nvars; ++varID)
+    auto timetype = varList1.vars[0].timeType;
+    for (const auto &var : varList1.vars)
       {
-        if (timetype != varList1[varID].timetype) cdo_abort("Number of timesteps differ!");
+        if (timetype != var.timeType) cdo_abort("Number of timesteps differ!");
       }
 
     vlistID2 = vlistCreate();
     vlistDefNtsteps(vlistID2, vlistNtsteps(vlistID1));
 
+    auto gridID = vlistGrid(vlistID1, 0);
     auto varID2 = vlistDefVar(vlistID2, gridID, zaxisID, timetype);
-    set_attributes(varList1, vlistID2, varID2, operatorID);
+    set_attributes(varList1.vars, vlistID2, varID2, operatorID);
 
     taxisID1 = vlistInqTaxis(vlistID1);
     taxisID2 = taxisDuplicate(taxisID1);
@@ -156,36 +143,14 @@ public:
     streamID2 = cdo_open_write(1);
     cdo_def_vlist(streamID2, vlistID2);
 
-    vars1 = FieldVector(nlevels);
-    samp1 = FieldVector(nlevels);
-    if (lvars2) vars2.resize(nlevels);
-
-    for (int levelID = 0; levelID < nlevels; ++levelID)
-      {
-        auto missval = varList1[0].missval;
-
-        samp1[levelID].grid = gridID;
-        samp1[levelID].missval = missval;
-        samp1[levelID].memType = MemType::Double;
-        vars1[levelID].grid = gridID;
-        vars1[levelID].missval = missval;
-        vars1[levelID].memType = MemType::Double;
-        vars1[levelID].resize(gridsize);
-        if (lvars2)
-          {
-            vars2[levelID].grid = gridID;
-            vars2[levelID].missval = missval;
-            vars2[levelID].memType = MemType::Double;
-            vars2[levelID].resize(gridsize);
-          }
-      }
+    int VARS_MEMTYPE = stepStat.lminmax ? FIELD_NAT : 0;
+    stepStat.alloc(varList1, VARS_MEMTYPE);
   }
 
   void
-  run()
+  run() override
   {
-    auto field2_stdvar_func = lstd ? field2_std : field2_var;
-    auto fieldc_stdvar_func = lstd ? fieldc_std : fieldc_var;
+    Field field;
 
     int tsID = 0;
     while (true)
@@ -198,77 +163,27 @@ public:
 
         for (int recID = 0; recID < nrecs; ++recID)
           {
-            int varID, levelID;
-            cdo_inq_record(streamID1, &varID, &levelID);
+            auto [varID, levelID] = cdo_inq_record(streamID1);
 
-            auto &rsamp1 = samp1[levelID];
-            auto &rvars1 = vars1[levelID];
+            field.init(varList1.vars[varID]);
+            cdo_read_record(streamID1, field);
 
-            rvars1.nsamp++;
-            if (lrange) vars2[levelID].nsamp++;
+            auto numSets = stepStat.var1(levelID).nsamp;
+            stepStat.add_field(field, levelID, numSets);
 
-            if (varID == 0)
-              {
-                cdo_read_record(streamID1, rvars1);
-                if (lrange)
-                  {
-                    vars2[levelID].numMissVals = rvars1.numMissVals;
-                    vars2[levelID].vec_d = rvars1.vec_d;
-                  }
-
-                if (lvarstd) field2_moq(vars2[levelID], rvars1);
-
-                if (rvars1.numMissVals || !rsamp1.empty())
-                  {
-                    if (rsamp1.empty()) rsamp1.resize(rvars1.size);
-                    field2_vinit(rsamp1, rvars1);
-                  }
-              }
-            else
-              {
-                field.init(varList1[varID]);
-                cdo_read_record(streamID1, field);
-
-                if (field.numMissVals || !rsamp1.empty())
-                  {
-                    if (rsamp1.empty()) rsamp1.resize(rvars1.size, rvars1.nsamp);
-                    field2_vincr(rsamp1, field);
-                  }
-
-                // clang-format off
-                if      (lvarstd) field2_sumsumq(rvars1, vars2[levelID], field);
-                else if (lrange)  field2_maxmin(rvars1, vars2[levelID], field);
-                else              field2_function(rvars1, field, operfunc);
-                // clang-format on
-              }
+            if (varID == 0 && stepStat.lvarstd) stepStat.moq(levelID);
           }
 
-        for (int levelID = 0; levelID < nlevels; ++levelID)
+        for (int levelID = 0; levelID < numLevels; ++levelID)
           {
-            const auto &rsamp1 = samp1[levelID];
-            auto &rvars1 = vars1[levelID];
-
-            if (rvars1.nsamp)
+            auto numSets = stepStat.var1(levelID).nsamp;
+            if (numSets)
               {
-                if (lmean)
-                  {
-                    if (!rsamp1.empty())
-                      field2_div(rvars1, rsamp1);
-                    else
-                      fieldc_div(rvars1, (double) rvars1.nsamp);
-                  }
-                else if (lvarstd)
-                  {
-                    if (!rsamp1.empty())
-                      field2_stdvar_func(rvars1, vars2[levelID], rsamp1, divisor);
-                    else
-                      fieldc_stdvar_func(rvars1, vars2[levelID], rvars1.nsamp, divisor);
-                  }
-                else if (lrange) { field2_sub(rvars1, vars2[levelID]); }
+                stepStat.process(levelID, numSets);
 
                 cdo_def_record(streamID2, 0, levelID);
-                cdo_write_record(streamID2, rvars1);
-                rvars1.nsamp = 0;
+                cdo_write_record(streamID2, stepStat.var1(levelID));
+                stepStat.var1(levelID).nsamp = 0;
               }
           }
 
@@ -277,7 +192,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
diff --git a/src/Verifygrid.cc b/src/Verifygrid.cc
index 8c8ca7a7cc4f273db8d97fd36b622606c9efe43d..9da2bd7307b1f2d3b41c6b0c50de69aa385b6f59 100644
--- a/src/Verifygrid.cc
+++ b/src/Verifygrid.cc
@@ -792,7 +792,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
 
     operator_check_argc(0);
@@ -800,10 +800,10 @@ public:
     streamID = cdo_open_read(0);
     vlistID = cdo_stream_inq_vlist(streamID);
 
-    ngrids = vlistNgrids(vlistID);
+    ngrids = vlistNumGrids(vlistID);
   }
   void
-  run()
+  run() override
   {
     for (int gridno = 0; gridno < ngrids; ++gridno)
       {
@@ -886,7 +886,7 @@ public:
       }
   }
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID);
   }
diff --git a/src/Verifyweights.cc b/src/Verifyweights.cc
index 910cea4664589a5f1bcb3458059a7557cadee05c..949b4203926f5812fcb0d6841a38606c9bcd3f11 100644
--- a/src/Verifyweights.cc
+++ b/src/Verifyweights.cc
@@ -18,7 +18,6 @@
 #include "griddes.h"
 #include <mpim_grid.h>
 #include "remap_vars.h"
-#include "cdo_zaxis.h"
 
 #ifdef HAVE_LIBNETCDF
 static void
@@ -94,7 +93,7 @@ cdf_read_var_double(int ncfileid, const char *name, double *array)
 }
 
 static void
-cdf_read_coordinate_radian(int ncfileid, const char *name, size_t size, Varray<double> &varray)
+cdf_read_coordinate_radian(int ncfileid, const char *name, Varray<double> &varray)
 {
   int ncvarid;
   nce(nc_inq_varid(ncfileid, name, &ncvarid));
@@ -209,17 +208,15 @@ read_remapgrid_scrip(int ncfileid, const std::string &prefix, bool lgridarea, Re
 {
   // Read all variables of the grid
   cdf_read_var_size(ncfileid, (prefix + "_dims").c_str(), 2, grid.dims);
-
   cdf_read_var_int(ncfileid, (prefix + "_imask").c_str(), grid.mask.data());
 
-  cdf_read_coordinate_radian(ncfileid, (prefix + "_center_lat").c_str(), grid.ncells, grid.cell_center_lat);
-  cdf_read_coordinate_radian(ncfileid, (prefix + "_center_lon").c_str(), grid.ncells, grid.cell_center_lon);
+  cdf_read_coordinate_radian(ncfileid, (prefix + "_center_lat").c_str(), grid.cell_center_lat);
+  cdf_read_coordinate_radian(ncfileid, (prefix + "_center_lon").c_str(), grid.cell_center_lon);
 
   if (grid.ncorners)
     {
-      size_t size = grid.ncorners * grid.ncells;
-      cdf_read_coordinate_radian(ncfileid, (prefix + "_corner_lat").c_str(), size, grid.cell_corner_lat);
-      cdf_read_coordinate_radian(ncfileid, (prefix + "_corner_lon").c_str(), size, grid.cell_corner_lon);
+      cdf_read_coordinate_radian(ncfileid, (prefix + "_corner_lat").c_str(), grid.cell_corner_lat);
+      cdf_read_coordinate_radian(ncfileid, (prefix + "_corner_lon").c_str(), grid.cell_corner_lon);
     }
 
   if (lgridarea) cdf_read_var_double(ncfileid, (prefix + "_area").c_str(), grid.cell_area.data());
@@ -701,7 +698,7 @@ public:
   using Process::Process;
   inline static CdoModule module = {
     .name = "Verifyweights",
-    .operators = { { "verifyweights", 0, 1, "remap filename"}, { "writeremapscrip", 0, 2, "input and output remap filename"} },
+    .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
@@ -715,7 +712,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
 #ifndef HAVE_LIBNETCDF
     cdo_abort("NetCDF support not compiled in!");
@@ -734,7 +731,7 @@ public:
   }
 
   void
-  run()
+  run() override
   {
 #ifdef HAVE_LIBNETCDF
     if (operatorID == VERIFYWEIGHTS)
@@ -745,7 +742,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
   }
 };
diff --git a/src/Vertcum.cc b/src/Vertcum.cc
index 67593e5a6a97012eb382ba8ce929ee795167d379..592f35c7996d1313e381eedfe4796f7d96230566 100644
--- a/src/Vertcum.cc
+++ b/src/Vertcum.cc
@@ -15,7 +15,6 @@
 #include <cdi.h>
 
 #include "process_int.h"
-#include "cdo_vlist.h"
 
 #define IS_SURFACE_LEVEL(zaxisID) (zaxisInqType(zaxisID) == ZAXIS_SURFACE && zaxisInqSize(zaxisID) == 1)
 
@@ -43,13 +42,14 @@ public:
   using Process::Process;
   inline static CdoModule module = {
     .name = "Vertcum",
-    .operators = { { "vertcum"}, { "vertcumhl"} },
+    .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;
 
@@ -62,20 +62,19 @@ public:
 
   int operatorID;
 
-  int nvars;
+  int numVars;
+
+  VarList varList1;
+  VarList varList2;
 
-  VarList varList1, varList2;
   std::vector<std::vector<size_t>> varnumMissVals;
   Varray3D<double> vardata1, vardata2;
 
 public:
   void
-  init()
+  init() override
   {
-
-    // clang-format off
-VERTCUMHL = module.get_id("vertcumhl");
-    // clang-format on
+    VERTCUMHL = module.get_id("vertcumhl");
 
     operatorID = cdo_operator_id();
 
@@ -92,7 +91,7 @@ VERTCUMHL = module.get_id("vertcumhl");
       {
         std::vector<double> vct;
         bool lhybrid = false;
-        auto nzaxis = vlistNzaxis(vlistID1);
+        auto nzaxis = vlistNumZaxis(vlistID1);
         for (int i = 0; i < nzaxis; ++i)
           {
             auto zaxisID = vlistZaxis(vlistID1, i);
@@ -128,19 +127,19 @@ VERTCUMHL = module.get_id("vertcumhl");
           }
       }
 
-    varList_init(varList1, vlistID1);
-    varList_init(varList2, vlistID2);
+    varList1 = VarList(vlistID1);
+    varList2 = VarList(vlistID2);
 
-    nvars = vlistNvars(vlistID1);
-    varnumMissVals = std::vector<std::vector<size_t>>(nvars);
-    vardata1 = Varray3D<double>(nvars);
-    vardata2 = Varray3D<double>(nvars);
+    numVars = vlistNvars(vlistID1);
+    varnumMissVals = std::vector<std::vector<size_t>>(numVars);
+    vardata1 = Varray3D<double>(numVars);
+    vardata2 = Varray3D<double>(numVars);
 
-    for (int varID = 0; varID < nvars; ++varID)
+    for (int varID = 0; varID < numVars; ++varID)
       {
-        auto gridsize = varList1[varID].gridsize;
-        auto nlevs = varList1[varID].nlevels;
-        auto nlevs2 = varList2[varID].nlevels;
+        auto gridsize = varList1.vars[varID].gridsize;
+        auto nlevs = varList1.vars[varID].nlevels;
+        auto nlevs2 = varList2.vars[varID].nlevels;
 
         varnumMissVals[varID].resize(nlevs);
         vardata1[varID].resize(nlevs);
@@ -158,7 +157,7 @@ VERTCUMHL = module.get_id("vertcumhl");
   }
 
   void
-  run()
+  run() override
   {
     int tsID = 0;
     while (true)
@@ -171,16 +170,16 @@ VERTCUMHL = module.get_id("vertcumhl");
 
         for (int recID = 0; recID < nrecs; ++recID)
           {
-            int varID, levelID;
-            cdo_inq_record(streamID1, &varID, &levelID);
+            auto [varID, levelID] = cdo_inq_record(streamID1);
             cdo_read_record(streamID1, &vardata1[varID][levelID][0], &varnumMissVals[varID][levelID]);
           }
 
-        for (int varID = 0; varID < nvars; ++varID)
+        for (int varID = 0; varID < numVars; ++varID)
           {
-            auto missval = varList2[varID].missval;
-            auto gridsize = varList2[varID].gridsize;
-            auto nlevs2 = varList2[varID].nlevels;
+            const auto &var2 = varList2.vars[varID];
+            auto missval = var2.missval;
+            auto gridsize = var2.gridsize;
+            auto nlevs2 = var2.nlevels;
 
             if (operatorID == VERTCUMHL && nlevs2 == nlevshl)
               {
@@ -202,26 +201,27 @@ VERTCUMHL = module.get_id("vertcumhl");
 
             if (operatorID == VERTCUMHL && nlevs2 == nlevshl)
               {
-                const auto &var1 = vardata2[varID][nlevs2 - 1];
+                const auto &var1data = vardata2[varID][nlevs2 - 1];
                 for (int levelID = 0; levelID < nlevs2; ++levelID)
                   {
-                    auto &var2 = vardata2[varID][levelID];
+                    auto &var2data = vardata2[varID][levelID];
                     for (size_t i = 0; i < gridsize; ++i)
                       {
-                        if (is_not_equal(var1[i], 0))
-                          var2[i] /= var1[i];
+                        if (is_not_equal(var1data[i], 0))
+                          var2data[i] /= var1data[i];
                         else
-                          var2[i] = 0;
+                          var2data[i] = 0;
                       }
                   }
               }
           }
 
-        for (int varID = 0; varID < nvars; ++varID)
+        for (int varID = 0; varID < numVars; ++varID)
           {
-            auto missval = varList2[varID].missval;
-            auto gridsize = varList2[varID].gridsize;
-            auto nlevs2 = varList2[varID].nlevels;
+            const auto &var2 = varList2.vars[varID];
+            auto missval = var2.missval;
+            auto gridsize = var2.gridsize;
+            auto nlevs2 = var2.nlevels;
             for (int levelID = 0; levelID < nlevs2; ++levelID)
               {
                 auto &single = vardata2[varID][levelID];
@@ -236,7 +236,7 @@ VERTCUMHL = module.get_id("vertcumhl");
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
diff --git a/src/Vertfillmiss.cc b/src/Vertfillmiss.cc
index 63934629c2ac036ac18a717c24e145c79c551906..eddba20d1a250479cd2706659e2410d6edc6a1aa 100644
--- a/src/Vertfillmiss.cc
+++ b/src/Vertfillmiss.cc
@@ -5,14 +5,9 @@
 
 */
 
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
 #include <cdi.h>
 
 #include "process_int.h"
-#include "cdo_vlist.h"
 #include "param_conversion.h"
 #include "cdo_options.h"
 #include "cimdOmp.h"
@@ -43,11 +38,10 @@ private:
   int taxisID1;
   int taxisID2;
 
-  VarList varList;
-  FieldVector2D vars;
+  VarList varList1;
+  FieldVector2D varsData;
 
   int calendar;
-  int nvars;
 
   FillMethod method{ FillMethod::Nearest };
   int limit{ 0 };
@@ -56,16 +50,6 @@ private:
   Varray2D<double> dataValues2D;
   Varray<double> levelValues;
 
-  FillMethod
-  convert_fillmethod(const std::string &methodStr)
-  {
-    auto fillMethod = string_to_fillmethod(methodStr);
-
-    if (fillMethod == FillMethod::Undefined) cdo_abort("method=%s unsupported!", methodStr);
-
-    return fillMethod;
-  }
-
   void
   get_parameter()
   {
@@ -87,7 +71,7 @@ private:
             const auto &value = kv.values[0];
 
             // clang-format off
-            if      (key == "method")   method = convert_fillmethod(parameter_to_word(value));
+            if      (key == "method")   method = convert<FillMethod>(value);
             else if (key == "limit")    limit = parameter_to_int(value);
             else if (key == "max_gaps") maxGaps = parameter_to_int(value);
             else cdo_abort("Invalid parameter key >%s<!", key);
@@ -98,7 +82,7 @@ private:
 
 public:
   void
-  init()
+  init() override
   {
     get_parameter();
     limit = std::max(limit, 0);
@@ -118,25 +102,23 @@ public:
 
     calendar = taxisInqCalendar(taxisID1);
 
-    varList_init(varList, vlistID1);
-
-    nvars = vlistNvars(vlistID1);
+    varList1 = VarList(vlistID1);
 
     int maxLevels = 0;
-    for (auto &var : varList) maxLevels = std::max(maxLevels, var.nlevels);
+    for (auto &var : varList1.vars) maxLevels = std::max(maxLevels, var.nlevels);
 
     dataValues2D = Varray2D<double>(Threading::ompNumThreads);
     for (auto &dataValues : dataValues2D) dataValues.resize(maxLevels);
 
     levelValues.resize(maxLevels);
 
-    fields_from_vlist(vlistID1, vars);
+    field2D_init(varsData, varList1);
   }
 
   void
   fillmiss(int varID)
   {
-    const auto &var = varList[varID];
+    const auto &var = varList1.vars[varID];
     auto fieldMemType = var.memType;
     auto gridsize = var.gridsize;
     auto numLevels = var.nlevels;
@@ -153,9 +135,9 @@ public:
         auto &dataValues = dataValues2D[ompthID];
 
         if (fieldMemType == MemType::Float)
-          for (int levelID = 0; levelID < numLevels; ++levelID) dataValues[levelID] = vars[varID][levelID].vec_f[i];
+          for (int levelID = 0; levelID < numLevels; ++levelID) dataValues[levelID] = varsData[varID][levelID].vec_f[i];
         else
-          for (int levelID = 0; levelID < numLevels; ++levelID) dataValues[levelID] = vars[varID][levelID].vec_d[i];
+          for (int levelID = 0; levelID < numLevels; ++levelID) dataValues[levelID] = varsData[varID][levelID].vec_d[i];
 
         if (method == FillMethod::Nearest)
           fill_1d_nearest(numLevels, levelValues, dataValues, missval, limit, maxGaps);
@@ -167,15 +149,16 @@ public:
           fill_1d_backward(numLevels, dataValues, missval, limit, maxGaps);
 
         if (fieldMemType == MemType::Float)
-          for (int levelID = 0; levelID < numLevels; ++levelID) vars[varID][levelID].vec_f[i] = dataValues[levelID];
+          for (int levelID = 0; levelID < numLevels; ++levelID) varsData[varID][levelID].vec_f[i] = dataValues[levelID];
         else
-          for (int levelID = 0; levelID < numLevels; ++levelID) vars[varID][levelID].vec_d[i] = dataValues[levelID];
+          for (int levelID = 0; levelID < numLevels; ++levelID) varsData[varID][levelID].vec_d[i] = dataValues[levelID];
       }
   }
 
   void
-  run()
+  run() override
   {
+    auto numVars = varList1.numVars();
     int tsID = 0;
     while (true)
       {
@@ -187,29 +170,28 @@ public:
 
         for (int recID = 0; recID < nrecs; ++recID)
           {
-            int varID, levelID;
-            cdo_inq_record(streamID1, &varID, &levelID);
-            auto &field = vars[varID][levelID];
-            field.init(varList[varID]);
+            auto [varID, levelID] = cdo_inq_record(streamID1);
+            auto &field = varsData[varID][levelID];
+            field.init(varList1.vars[varID]);
             cdo_read_record(streamID1, field);
           }
 
-        for (int varID = 0; varID < nvars; ++varID)
+        for (int varID = 0; varID < numVars; ++varID)
           {
-            int numLevels = varList[varID].nlevels;
+            int numLevels = varList1.vars[varID].nlevels;
             if (numLevels > 1)
               {
                 size_t numMissVals = 0;
-                for (int levelID = 0; levelID < numLevels; ++levelID) { numMissVals += vars[varID][levelID].numMissVals; }
+                for (int levelID = 0; levelID < numLevels; ++levelID) { numMissVals += varsData[varID][levelID].numMissVals; }
                 if (numMissVals > 0) fillmiss(varID);
               }
           }
 
-        for (int varID = 0; varID < nvars; ++varID)
+        for (int varID = 0; varID < numVars; ++varID)
           {
-            for (int levelID = 0; levelID < varList[varID].nlevels; ++levelID)
+            for (int levelID = 0; levelID < varList1.vars[varID].nlevels; ++levelID)
               {
-                auto &field = vars[varID][levelID];
+                auto &field = varsData[varID][levelID];
                 if (field.hasData())
                   {
                     cdo_def_record(streamID2, varID, levelID);
@@ -224,7 +206,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
diff --git a/src/Vertintap.cc b/src/Vertintap.cc
index 3cef326b6c549817ba1ae2601fb64b6ad46b15c8..60ff888e4b10cbe76203ca6eccedee227fb6af7d 100644
--- a/src/Vertintap.cc
+++ b/src/Vertintap.cc
@@ -18,7 +18,6 @@
 #include "stdnametable.h"
 #include "util_string.h"
 #include "const.h"
-#include "cdo_zaxis.h"
 #include "param_conversion.h"
 #include "vertint_util.h"
 
@@ -110,7 +109,7 @@ public:
   size_t gridsize;
 
   int numPL;
-  int nvars;
+  int numVars;
   int zaxisID_FL;
   int zaxisID_HL;
   int numFullLevels;
@@ -136,7 +135,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
     AP2PLX = module.get_id("ap2plx");
     AP2HLX = module.get_id("ap2hlx");
@@ -176,12 +175,12 @@ public:
     auto zaxisIDp = zaxisCreate(zaxistype, numPL);
     zaxisDefLevels(zaxisIDp, levels.data());
 
-    varList_init(varList1, vlistID1);
-    varListSetUniqueMemtype(varList1);
-    auto memtype = varList1[0].memType;
+    varList1 = VarList(vlistID1);
+    varList_set_unique_memtype(varList1);
+    auto memtype = varList1.vars[0].memType;
 
-    nvars = vlistNvars(vlistID1);
-    for (int varID = 0; varID < nvars; ++varID)
+    numVars = varList1.numVars();
+    for (int varID = 0; varID < numVars; ++varID)
       {
         auto stdname = string_to_lower(cdo::inq_key_string(vlistID1, varID, CDI_KEY_STDNAME));
 
@@ -195,28 +194,28 @@ public:
 
     if (-1 != apressID_FL && -1 != apressID_HL)
       {
-        if (varList1[apressID_FL].nlevels == (varList1[apressID_HL].nlevels + 1)) std::swap(apressID_FL, apressID_HL);
+        if (varList1.vars[apressID_FL].nlevels == (varList1.vars[apressID_HL].nlevels + 1)) std::swap(apressID_FL, apressID_HL);
       }
 
     if (Options::cdoVerbose)
       {
         cdo_print("Found:");
         // clang-format off
-        if (-1 != psID)        cdo_print("  %s -> %s", var_stdname(surface_air_pressure), varList1[psID].name);
-        if (-1 != dpressID)    cdo_print("  %s -> %s", var_stdname(pressure_thickness), varList1[dpressID].name);
-        if (-1 != apressID_FL) cdo_print("  %s (full) -> %s", var_stdname(air_pressure), varList1[apressID_FL].name);
-        if (-1 != apressID_HL) cdo_print("  %s (half) -> %s", var_stdname(air_pressure), varList1[apressID_HL].name);
+        if (-1 != psID)        cdo_print("  %s -> %s", var_stdname(surface_air_pressure), varList1.vars[psID].name);
+        if (-1 != dpressID)    cdo_print("  %s -> %s", var_stdname(pressure_thickness), varList1.vars[dpressID].name);
+        if (-1 != apressID_FL) cdo_print("  %s (full) -> %s", var_stdname(air_pressure), varList1.vars[apressID_FL].name);
+        if (-1 != apressID_HL) cdo_print("  %s (half) -> %s", var_stdname(air_pressure), varList1.vars[apressID_HL].name);
         // clang-format on
       }
 
     if (-1 == apressID_FL) cdo_abort("%s not found!", var_stdname(air_pressure));
 
-    zaxisID_FL = (-1 == apressID_FL) ? -1 : varList1[apressID_FL].zaxisID;
-    zaxisID_HL = (-1 == apressID_HL) ? -1 : varList1[apressID_HL].zaxisID;
-    numFullLevels = (-1 == zaxisID_FL) ? 0 : varList1[apressID_FL].nlevels;
-    numHalfLevels = (-1 == zaxisID_HL) ? numFullLevels + 1 : varList1[apressID_HL].nlevels;
+    zaxisID_FL = (-1 == apressID_FL) ? -1 : varList1.vars[apressID_FL].zaxisID;
+    zaxisID_HL = (-1 == apressID_HL) ? -1 : varList1.vars[apressID_HL].zaxisID;
+    numFullLevels = (-1 == zaxisID_FL) ? 0 : varList1.vars[apressID_FL].nlevels;
+    numHalfLevels = (-1 == zaxisID_HL) ? numFullLevels + 1 : varList1.vars[apressID_HL].nlevels;
 
-    auto nzaxis = vlistNzaxis(vlistID1);
+    auto nzaxis = vlistNumZaxis(vlistID1);
     for (int index = 0; index < nzaxis; ++index)
       {
         auto zaxisID = vlistZaxis(vlistID1, index);
@@ -226,13 +225,13 @@ public:
           vlistChangeZaxis(vlistID2, zaxisID, zaxisIDp);
       }
 
-    varList_init(varList2, vlistID2);
-    varListSetMemtype(varList2, memtype);
+    varList2 = VarList(vlistID2);
+    varList_set_memtype(varList2, memtype);
 
-    processVars = std::vector<bool>(nvars);
-    interpVars = std::vector<bool>(nvars);
-    varnumMissVals = Varray2D<size_t>(nvars);
-    vardata1 = Field3DVector(nvars), vardata2 = Field3DVector(nvars);
+    processVars = std::vector<bool>(numVars);
+    interpVars = std::vector<bool>(numVars);
+    varnumMissVals = Varray2D<size_t>(numVars);
+    vardata1 = Field3DVector(numVars), vardata2 = Field3DVector(numVars);
 
     auto maxLevels = std::max(std::max(numFullLevels, numHalfLevels), numPL);
 
@@ -263,9 +262,9 @@ public:
         levels = phlev;
       }
 
-    for (int varID = 0; varID < nvars; ++varID)
+    for (int varID = 0; varID < numVars; ++varID)
       {
-        const auto &var = varList1[varID];
+        const auto &var = varList1.vars[varID];
         auto isHeightAxis = is_height_axis(var.zaxisID);
 
         if (gridInqType(var.gridID) == GRID_SPECTRAL) cdo_abort("Spectral data unsupported!");
@@ -279,7 +278,7 @@ public:
         if (interpVars[varID])
           {
             varnumMissVals[varID].resize(maxLevels, 0);
-            vardata2[varID].init(varList2[varID]);
+            vardata2[varID].init(varList2.vars[varID]);
           }
         else
           {
@@ -299,9 +298,9 @@ public:
           cdo_warning("Surface pressure not found - set to lower bound of %s!", var_stdname(air_pressure));
       }
 
-    for (int varID = 0; varID < nvars; ++varID)
+    for (int varID = 0; varID < numVars; ++varID)
       {
-        if (interpVars[varID] && varList1[varID].isConstant) vlistDefVarTimetype(vlistID2, varID, TIME_VARYING);
+        if (interpVars[varID] && varList1.vars[varID].isConstant) vlistDefVarTimetype(vlistID2, varID, TIME_VARYING);
       }
 
     streamID2 = cdo_open_write(1);
@@ -310,7 +309,7 @@ public:
   }
 
   void
-  run()
+  run() override
   {
     int tsID = 0;
     while (true)
@@ -318,10 +317,10 @@ public:
         auto nrecs = cdo_stream_inq_timestep(streamID1, tsID);
         if (nrecs == 0) break;
 
-        for (int varID = 0; varID < nvars; ++varID)
+        for (int varID = 0; varID < numVars; ++varID)
           {
             processVars[varID] = false;
-            const auto &var = varList1[varID];
+            const auto &var = varList1.vars[varID];
             for (int levelID = 0; levelID < var.nlevels; ++levelID) varnumMissVals[varID][levelID] = 0;
           }
 
@@ -330,34 +329,33 @@ public:
 
         for (int recID = 0; recID < nrecs; ++recID)
           {
-            int varID, levelID;
-            cdo_inq_record(streamID1, &varID, &levelID);
+            auto [varID, levelID] = cdo_inq_record(streamID1);
             cdo_read_record(streamID1, vardata1[varID], levelID, &varnumMissVals[varID][levelID]);
             processVars[varID] = true;
           }
 
-        for (int varID = 0; varID < nvars; ++varID)
+        for (int varID = 0; varID < numVars; ++varID)
           if (interpVars[varID]) processVars[varID] = true;
 
         if (zaxisID_FL != -1)
           {
-            if (tsID == 1 && varList1[apressID_FL].timetype == TIME_CONSTANT)
+            if (tsID == 1 && varList1.vars[apressID_FL].timeType == TIME_CONSTANT)
               cdo_warning("%s does not vary in time!", var_stdname(air_pressure));
 
             if (psID != -1)
               {
-                psProg.init(varList1[psID]);
+                psProg.init(varList1.vars[psID]);
                 field_copy(vardata1[psID], psProg);
               }
             else if (dpressID != -1)
               {
-                psProg.init(varList1[dpressID]);
+                psProg.init(varList1.vars[dpressID]);
                 field_fill(psProg, 0);
                 for (int k = 0; k < numFullLevels; ++k) field_add(psProg, vardata1[dpressID], k);
               }
             else
               {
-                psProg.init(varList1[apressID_FL]);
+                psProg.init(varList1.vars[apressID_FL]);
                 field_copy(vardata1[apressID_FL], numFullLevels - 1, psProg);
               }
 
@@ -378,11 +376,11 @@ public:
             if (!extrapolate) gen_vert_index_mv(vertIndex_HL, levels, gridsize, psProg, numMiss_HL);
           }
 
-        for (int varID = 0; varID < nvars; ++varID)
+        for (int varID = 0; varID < numVars; ++varID)
           {
             if (processVars[varID])
               {
-                const auto &var = varList1[varID];
+                const auto &var = varList1.vars[varID];
 
                 if (tsID > 0 && !interpVars[varID] && var.isConstant) continue;
 
@@ -407,7 +405,7 @@ public:
                       }
                   }
 
-                for (int levelID = 0; levelID < varList2[varID].nlevels; ++levelID)
+                for (int levelID = 0; levelID < varList2.vars[varID].nlevels; ++levelID)
                   {
                     cdo_def_record(streamID2, varID, levelID);
                     cdo_write_record(streamID2, interpVars[varID] ? vardata2[varID] : vardata1[varID], levelID,
@@ -421,7 +419,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
diff --git a/src/Vertintgh.cc b/src/Vertintgh.cc
index 9815053619a44c9c2ba1b8c707a84ede3ec19460..cc634d77433cbfcd9f248c9dfb29809f4d368d6e 100644
--- a/src/Vertintgh.cc
+++ b/src/Vertintgh.cc
@@ -19,7 +19,6 @@
 #include "field_vinterp.h"
 #include "stdnametable.h"
 #include "util_string.h"
-#include "const.h"
 #include "cdo_zaxis.h"
 #include "param_conversion.h"
 #include "vertint_util.h"
@@ -97,7 +96,7 @@ public:
   int taxisID2;
 
   size_t gridsize;
-  int nvars;
+  int numVars;
   int numFullLevels;
   int numHalfLevels;
 
@@ -121,7 +120,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
     GH2HLX = module.get_id("gh2hlx");
 
@@ -145,15 +144,15 @@ public:
 
     gridsize = vlist_check_gridsize(vlistID1);
 
-    varList_init(varList1, vlistID1);
-    varListSetUniqueMemtype(varList1);
-    auto memtype = varList1[0].memType;
+    varList1 = VarList(vlistID1);
+    varList_set_unique_memtype(varList1);
+    auto memtype = varList1.vars[0].memType;
 
     auto stdnameHeight_FL = var_stdname(geometric_height_at_full_level_center);
     auto stdnameHeight_HL = var_stdname(geometric_height_at_half_level_center);
 
-    nvars = vlistNvars(vlistID1);
-    for (int varID = 0; varID < nvars; ++varID)
+    numVars = varList1.numVars();
+    for (int varID = 0; varID < numVars; ++varID)
       {
         auto stdname = string_to_lower(cdo::inq_key_string(vlistID1, varID, CDI_KEY_STDNAME));
         if (stdname == stdnameHeight_FL) heightID_FL = varID;
@@ -162,9 +161,9 @@ public:
 
     if (-1 == heightID_FL && -1 == heightID_HL)
       {
-        for (int varID = 0; varID < nvars; ++varID)
+        for (int varID = 0; varID < numVars; ++varID)
           {
-            const auto &var = varList1[varID];
+            const auto &var = varList1.vars[varID];
             auto stdname = string_to_lower(cdo::inq_key_string(vlistID1, varID, CDI_KEY_STDNAME));
             if (stdname == "height" && var.units == "m")
               {
@@ -179,18 +178,18 @@ public:
     if (Options::cdoVerbose)
       {
         cdo_print("Found:");
-        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);
+        if (-1 != heightID_FL) cdo_print("  %s -> %s", stdnameHeight_FL, varList1.vars[heightID_FL].name);
+        if (-1 != heightID_HL) cdo_print("  %s -> %s", stdnameHeight_HL, varList1.vars[heightID_HL].name);
       }
 
     if (-1 == heightID_FL && -1 == heightID_HL) cdo_abort("%s not found!", stdnameHeight_FL);
 
-    auto zaxisID_FL = (-1 == heightID_FL) ? -1 : varList1[heightID_FL].zaxisID;
-    auto zaxisID_HL = (-1 == heightID_HL) ? -1 : varList1[heightID_HL].zaxisID;
-    numFullLevels = (-1 == zaxisID_FL) ? 0 : varList1[heightID_FL].nlevels;
-    numHalfLevels = (-1 == zaxisID_HL) ? 0 : varList1[heightID_HL].nlevels;
+    auto zaxisID_FL = (-1 == heightID_FL) ? -1 : varList1.vars[heightID_FL].zaxisID;
+    auto zaxisID_HL = (-1 == heightID_HL) ? -1 : varList1.vars[heightID_HL].zaxisID;
+    numFullLevels = (-1 == zaxisID_FL) ? 0 : varList1.vars[heightID_FL].nlevels;
+    numHalfLevels = (-1 == zaxisID_HL) ? 0 : varList1.vars[heightID_HL].nlevels;
 
-    auto nzaxis = vlistNzaxis(vlistID1);
+    auto nzaxis = vlistNumZaxis(vlistID1);
     for (int index = 0; index < nzaxis; ++index)
       {
         auto zaxisID = vlistZaxis(vlistID1, index);
@@ -200,8 +199,8 @@ public:
           vlistChangeZaxis(vlistID2, zaxisID, zaxisID2);
       }
 
-    varList_init(varList2, vlistID2);
-    varListSetMemtype(varList2, memtype);
+    varList2 = VarList(vlistID2);
+    varList_set_memtype(varList2, memtype);
 
     if (!extrapolate) numMiss_FL.resize(heightLevels.size());
     if (!extrapolate) numMiss_HL.resize(heightLevels.size());
@@ -209,17 +208,17 @@ public:
     if (-1 != heightID_FL) vertIndex_FL.resize(gridsize * heightLevels.size());
     if (-1 != heightID_HL) vertIndex_HL.resize(gridsize * heightLevels.size());
 
-    processVars = std::vector<bool>(nvars);
-    interpVars = std::vector<bool>(nvars);
-    varnumMissVals = Varray2D<size_t>(nvars);
-    vardata1 = Field3DVector(nvars);
-    vardata2 = Field3DVector(nvars);
+    processVars = std::vector<bool>(numVars);
+    interpVars = std::vector<bool>(numVars);
+    varnumMissVals = Varray2D<size_t>(numVars);
+    vardata1 = Field3DVector(numVars);
+    vardata2 = Field3DVector(numVars);
 
     auto maxLevels = std::max(std::max(numFullLevels, numHalfLevels), (int) heightLevels.size());
 
-    for (int varID = 0; varID < nvars; ++varID)
+    for (int varID = 0; varID < numVars; ++varID)
       {
-        const auto &var = varList1[varID];
+        const auto &var = varList1.vars[varID];
         auto isHeightAxis = is_height_axis(var.zaxisID);
 
         if (gridInqType(var.gridID) == GRID_SPECTRAL) cdo_abort("Spectral data unsupported!");
@@ -232,7 +231,7 @@ public:
         if (interpVars[varID])
           {
             varnumMissVals[varID].resize(maxLevels, 0);
-            vardata2[varID].init(varList2[varID]);
+            vardata2[varID].init(varList2.vars[varID]);
           }
         else
           {
@@ -250,9 +249,9 @@ public:
           }
       }
 
-    for (int varID = 0; varID < nvars; ++varID)
+    for (int varID = 0; varID < numVars; ++varID)
       {
-        const auto &var = varList1[varID];
+        const auto &var = varList1.vars[varID];
         if (interpVars[varID] && var.isConstant) vlistDefVarTimetype(vlistID2, varID, TIME_VARYING);
       }
 
@@ -261,7 +260,7 @@ public:
   }
 
   void
-  run()
+  run() override
   {
     Field heightBottom;
 
@@ -271,10 +270,10 @@ public:
         auto nrecs = cdo_stream_inq_timestep(streamID1, tsID);
         if (nrecs == 0) break;
 
-        for (int varID = 0; varID < nvars; ++varID)
+        for (int varID = 0; varID < numVars; ++varID)
           {
             processVars[varID] = false;
-            const auto &var = varList1[varID];
+            const auto &var = varList1.vars[varID];
             for (int levelID = 0; levelID < var.nlevels; ++levelID) varnumMissVals[varID][levelID] = 0;
           }
 
@@ -283,43 +282,42 @@ public:
 
         for (int recID = 0; recID < nrecs; ++recID)
           {
-            int varID, levelID;
-            cdo_inq_record(streamID1, &varID, &levelID);
+            auto [varID, levelID] = cdo_inq_record(streamID1);
             cdo_read_record(streamID1, vardata1[varID], levelID, &varnumMissVals[varID][levelID]);
             processVars[varID] = true;
           }
 
-        for (int varID = 0; varID < nvars; ++varID)
+        for (int varID = 0; varID < numVars; ++varID)
           if (interpVars[varID]) processVars[varID] = true;
 
         auto lreverse = true;
-        if (-1 != heightID_FL && (tsID == 0 || !varList1[heightID_FL].isConstant))
+        if (-1 != heightID_FL && (tsID == 0 || !varList1.vars[heightID_FL].isConstant))
           {
             gen_vert_index(vertIndex_FL, heightLevels, vardata1[heightID_FL], gridsize, lreverse);
             if (!extrapolate)
               {
-                heightBottom.init(varList1[heightID_FL]);
+                heightBottom.init(varList1.vars[heightID_FL]);
                 field_copy(vardata1[heightID_FL], numFullLevels - 1, heightBottom);
                 gen_vert_index_mv(vertIndex_FL, heightLevels, gridsize, heightBottom, numMiss_FL, lreverse);
               }
           }
 
-        if (-1 != heightID_HL && (tsID == 0 || !varList1[heightID_HL].isConstant))
+        if (-1 != heightID_HL && (tsID == 0 || !varList1.vars[heightID_HL].isConstant))
           {
             gen_vert_index(vertIndex_HL, heightLevels, vardata1[heightID_HL], gridsize, lreverse);
             if (!extrapolate)
               {
-                heightBottom.init(varList1[heightID_HL]);
+                heightBottom.init(varList1.vars[heightID_HL]);
                 field_copy(vardata1[heightID_HL], numHalfLevels - 1, heightBottom);
                 gen_vert_index_mv(vertIndex_HL, heightLevels, gridsize, heightBottom, numMiss_HL, lreverse);
               }
           }
 
-        for (int varID = 0; varID < nvars; ++varID)
+        for (int varID = 0; varID < numVars; ++varID)
           {
             if (processVars[varID])
               {
-                const auto &var = varList1[varID];
+                const auto &var = varList1.vars[varID];
                 if (tsID > 0 && !interpVars[varID] && var.isConstant) continue;
 
                 if (interpVars[varID])
@@ -343,7 +341,7 @@ public:
                       }
                   }
 
-                for (int levelID = 0; levelID < varList2[varID].nlevels; ++levelID)
+                for (int levelID = 0; levelID < varList2.vars[varID].nlevels; ++levelID)
                   {
                     cdo_def_record(streamID2, varID, levelID);
                     cdo_write_record(streamID2, interpVars[varID] ? vardata2[varID] : vardata1[varID], levelID,
@@ -357,7 +355,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
diff --git a/src/Vertintml.cc b/src/Vertintml.cc
index 7cc5ac8811c935cb58701685f24ab7d278c472af..5d52ca6a50f6596f7475075948414ac456c8ac10 100644
--- a/src/Vertintml.cc
+++ b/src/Vertintml.cc
@@ -21,7 +21,6 @@
 #include "stdnametable.h"
 #include "constants.h"
 #include "const.h"
-#include "cdo_zaxis.h"
 #include "param_conversion.h"
 #include "vertint_util.h"
 
@@ -113,7 +112,7 @@ zaxis_is_hybrid(int zaxisType)
 static void
 change_hybrid_zaxis(int vlistID1, int vlistID2, int vctSize, double *vct, int zaxisID2, int numFullLevels, int numHalfLevels)
 {
-  auto nzaxis = vlistNzaxis(vlistID1);
+  auto nzaxis = vlistNumZaxis(vlistID1);
   for (int iz = 0; iz < nzaxis; ++iz)
     {
       auto zaxisID = vlistZaxis(vlistID1, iz);
@@ -129,6 +128,20 @@ change_hybrid_zaxis(int vlistID1, int vlistID2, int vctSize, double *vct, int za
     }
 }
 
+static void
+print_vars_found(const VarIDs &varIDs, const CdoVars &vars)
+{
+  cdo_print("Found:");
+  // clang-format off
+  if (-1 != varIDs.tempID)    cdo_print("  %s -> %s", var_stdname(air_temperature), vars[varIDs.tempID].name);
+  if (-1 != varIDs.psID)      cdo_print("  %s -> %s", var_stdname(surface_air_pressure), vars[varIDs.psID].name);
+  if (-1 != varIDs.lnpsID)    cdo_print("  LOG(%s) -> %s", var_stdname(surface_air_pressure), vars[varIDs.lnpsID].name);
+  if (-1 != varIDs.sgeopotID) cdo_print("  %s -> %s", var_stdname(surface_geopotential), vars[varIDs.sgeopotID].name);
+  if (-1 != varIDs.geopotID)  cdo_print("  %s -> %s", var_stdname(geopotential), vars[varIDs.geopotID].name);
+  if (-1 != varIDs.gheightID) cdo_print("  %s -> %s", var_stdname(geopotential_height), vars[varIDs.gheightID].name);
+  // clang-format on
+}
+
 static void
 pressure_level_interpolation(Varray<double> &pressureLevels, bool useHeightLevel, bool extrapolate)
 {
@@ -139,10 +152,9 @@ pressure_level_interpolation(Varray<double> &pressureLevels, bool useHeightLevel
   auto vlistID1 = cdo_stream_inq_vlist(streamID1);
   auto vlistID2 = vlistDuplicate(vlistID1);
 
-  VarList varList1;
-  varList_init(varList1, vlistID1);
-  varListSetUniqueMemtype(varList1);
-  auto memType = varList1[0].memType;
+  VarList varList1(vlistID1);
+  varList_set_unique_memtype(varList1);
+  auto memType = varList1.vars[0].memType;
 
   auto taxisID1 = vlistInqTaxis(vlistID1);
   auto taxisID2 = taxisDuplicate(taxisID1);
@@ -163,9 +175,8 @@ pressure_level_interpolation(Varray<double> &pressureLevels, bool useHeightLevel
 
   change_hybrid_zaxis(vlistID1, vlistID2, vctSize, vct.data(), zaxisID_PL, numFullLevels, numHalfLevels);
 
-  VarList varList2;
-  varList_init(varList2, vlistID2);
-  varListSetMemtype(varList2, memType);
+  VarList varList2(vlistID2);
+  varList_set_memtype(varList2, memType);
 
   int psvarID = -1;
   auto vctIsInverted = false;
@@ -183,11 +194,11 @@ pressure_level_interpolation(Varray<double> &pressureLevels, bool useHeightLevel
 
   if (vctIsInverted) invert_vct(vct);
 
-  auto nvars = vlistNvars(vlistID1);
+  auto numVars = varList1.numVars();
 
-  std::vector<bool> processVars(nvars), interpVars(nvars);
-  Varray2D<size_t> varnumMissVals(nvars);
-  Field3DVector vardata1(nvars), vardata2(nvars);
+  std::vector<bool> processVars(numVars), interpVars(numVars);
+  Varray2D<size_t> varnumMissVals(numVars);
+  Field3DVector vardata1(numVars), vardata2(numVars);
 
   auto maxLevels = std::max(numHalfLevels, numPL);
 
@@ -228,32 +239,21 @@ pressure_level_interpolation(Varray<double> &pressureLevels, bool useHeightLevel
       pressureLevels = phlev;
     }
 
-  VarIDs varIDs = search_varIDs(varList1, vlistID1, numFullLevels);
+  auto varIDs = varList_search_varIDs(varList1, numFullLevels);
 
   // vertical_interp_Z() is implemented for gheight on model half levels only
-  if (-1 != varIDs.gheightID && varList1[varIDs.gheightID].nlevels == numFullLevels)
+  if (-1 != varIDs.gheightID && varList1.vars[varIDs.gheightID].nlevels == numFullLevels)
     {
       if (Options::cdoVerbose)
-        cdo_print("%s(%s) on model full levels found!", var_stdname(geopotential_height), varList1[varIDs.gheightID].name);
+        cdo_print("%s(%s) on model full levels found!", var_stdname(geopotential_height), varList1.vars[varIDs.gheightID].name);
       varIDs.gheightID = -1;
     }
 
-  if (Options::cdoVerbose)
-    {
-      cdo_print("Found:");
-      // clang-format off
-      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
-    }
+  if (Options::cdoVerbose) print_vars_found(varIDs, varList1.vars);
 
-  for (int varID = 0; varID < nvars; ++varID)
+  for (int varID = 0; varID < numVars; ++varID)
     {
-      auto &var1 = varList1[varID];
+      auto &var1 = varList1.vars[varID];
       auto nlevels = var1.nlevels;
 
       if (var1.gridType == GRID_SPECTRAL && zaxis_is_hybrid(var1.zaxisType)) cdo_abort("Spectral data on model level unsupported!");
@@ -272,7 +272,7 @@ pressure_level_interpolation(Varray<double> &pressureLevels, bool useHeightLevel
       if (interpVars[varID])
         {
           varnumMissVals[varID].resize(maxLevels, 0);
-          vardata2[varID].init(varList2[varID]);
+          vardata2[varID].init(varList2.vars[varID]);
         }
       else
         {
@@ -283,11 +283,11 @@ pressure_level_interpolation(Varray<double> &pressureLevels, bool useHeightLevel
     }
 
   auto needVertIndexHalf{ false };
-  for (int varID = 0; varID < nvars; ++varID)
+  for (int varID = 0; varID < numVars; ++varID)
     {
       if (interpVars[varID])
         {
-          auto &var1 = varList1[varID];
+          auto &var1 = varList1.vars[varID];
           if (var1.nlevels == numHalfLevels && varID != varIDs.gheightID) needVertIndexHalf = true;
         }
     }
@@ -315,20 +315,20 @@ pressure_level_interpolation(Varray<double> &pressureLevels, bool useHeightLevel
   if (Options::cdoVerbose && presID != -1)
     {
       if (presID == varIDs.lnpsID)
-        cdo_print("Using LOG(%s) from %s", var_stdname(surface_air_pressure), varList1[presID].name);
+        cdo_print("Using LOG(%s) from %s", var_stdname(surface_air_pressure), varList1.vars[presID].name);
       else
-        cdo_print("Using %s from %s", var_stdname(surface_air_pressure), varList1[presID].name);
+        cdo_print("Using %s from %s", var_stdname(surface_air_pressure), varList1.vars[presID].name);
     }
 
   Field psProg;
-  if (zaxisID_ML != -1 && presID != -1) psProg.init(varList1[presID]);
+  if (zaxisID_ML != -1 && presID != -1) psProg.init(varList1.vars[presID]);
 
   auto sgeopotNeeded = (varIDs.tempID != -1 || varIDs.gheightID != -1);
 
   Field sgeopot;
   if (zaxisID_ML != -1 && sgeopotNeeded)
     {
-      sgeopot.init(varList1[presID]);
+      sgeopot.init(varList1.vars[presID]);
       if (varIDs.sgeopotID == -1)
         {
           if (extrapolate)
@@ -351,16 +351,15 @@ pressure_level_interpolation(Varray<double> &pressureLevels, bool useHeightLevel
       auto nrecs = cdo_stream_inq_timestep(streamID1, tsID);
       if (nrecs == 0) break;
 
-      for (int varID = 0; varID < nvars; ++varID) processVars[varID] = false;
+      for (int varID = 0; varID < numVars; ++varID) processVars[varID] = false;
 
       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);
-          const auto &var = varList1[varID];
+          auto [varID, levelID] = cdo_inq_record(streamID1);
+          const auto &var = varList1.vars[varID];
 
           if (vctIsInverted && zaxisID_ML != -1 && var.zaxisID == zaxisID_ML) levelID = var.nlevels - 1 - levelID;
 
@@ -402,11 +401,11 @@ pressure_level_interpolation(Varray<double> &pressureLevels, bool useHeightLevel
             }
         }
 
-      for (int varID = 0; varID < nvars; ++varID)
+      for (int varID = 0; varID < numVars; ++varID)
         {
           if (processVars[varID])
             {
-              const auto &var = varList1[varID];
+              const auto &var = varList1.vars[varID];
 
               if (tsID > 0 && var.isConstant) continue;
 
@@ -445,7 +444,7 @@ pressure_level_interpolation(Varray<double> &pressureLevels, bool useHeightLevel
                   if (!extrapolate) varray_copy(numPL, pnumMissVals, varnumMissVals[varID]);
                 }
 
-              for (int levelID = 0; levelID < varList2[varID].nlevels; ++levelID)
+              for (int levelID = 0; levelID < varList2.vars[varID].nlevels; ++levelID)
                 {
                   cdo_def_record(streamID2, varID, levelID);
                   cdo_write_record(streamID2, interpVars[varID] ? vardata2[varID] : vardata1[varID], levelID,
@@ -461,7 +460,7 @@ pressure_level_interpolation(Varray<double> &pressureLevels, bool useHeightLevel
   cdo_stream_close(streamID1);
 }
 
-//#define ENABLE_HEIGHT_LEVEL_INTERPOLATION
+// #define ENABLE_HEIGHT_LEVEL_INTERPOLATION
 
 #ifdef ENABLE_HEIGHT_LEVEL_INTERPOLATION
 // obsolete, use intlevel!!!
@@ -475,10 +474,9 @@ height_level_interpolation(Varray<double> &heightLevels, bool extrapolate)
   auto vlistID1 = cdo_stream_inq_vlist(streamID1);
   auto vlistID2 = vlistDuplicate(vlistID1);
 
-  VarList varList1;
-  varList_init(varList1, vlistID1);
-  varListSetUniqueMemtype(varList1);
-  auto memType = varList1[0].memType;
+  VarList varList1(vlistID1);
+  varList_setUniqueMemtype(varList1);
+  auto memType = varList1.vars[0].memType;
 
   auto taxisID1 = vlistInqTaxis(vlistID1);
   auto taxisID2 = taxisDuplicate(taxisID1);
@@ -499,14 +497,13 @@ height_level_interpolation(Varray<double> &heightLevels, bool extrapolate)
 
   change_hybrid_zaxis(vlistID1, vlistID2, vctSize, vct.data(), zaxisID_HL, numFullLevels, numHalfLevels);
 
-  VarList varList2;
-  varList_init(varList2, vlistID2);
-  varListSetMemtype(varList2, memType);
+  VarList varList2(vlistID2);
+  varList_setMemtype(varList2, memType);
   /*
   auto vctIsInverted = false;
   if (vctSize && vctSize % 2 == 0)
     {
-      (void) varList_get_psvarid(varList1, zaxisID_ML);
+      (void) cdoVars_get_psvarid(varList1.vars, zaxisID_ML);
 
       int i;
       for (i = vctSize / 2 + 1; i < vctSize; ++i)
@@ -518,11 +515,11 @@ height_level_interpolation(Varray<double> &heightLevels, bool extrapolate)
 
   if (vctIsInverted) invert_vct(vct);
   */
-  auto nvars = vlistNvars(vlistID1);
+  auto numVars = varList1.numVars();
 
-  std::vector<bool> processVars(nvars), interpVars(nvars);
-  Varray2D<size_t> varnumMissVals(nvars);
-  Field3DVector vardata1(nvars), vardata2(nvars);
+  std::vector<bool> processVars(numVars), interpVars(numVars);
+  Varray2D<size_t> varnumMissVals(numVars);
+  Field3DVector vardata1(numVars), vardata2(numVars);
 
   auto maxLevels = std::max(numHalfLevels, numHeightLevels);
 
@@ -541,24 +538,13 @@ height_level_interpolation(Varray<double> &heightLevels, bool extrapolate)
   else
     cdo_warning("No 3D variable with hybrid sigma pressure coordinate found!");
 
-  VarIDs varIDs = search_varIDs(varList1, vlistID1, numFullLevels);
+  VarIDs varIDs = search_varIDs(varList1.vars, vlistID1, numFullLevels);
 
-  if (Options::cdoVerbose)
-    {
-      cdo_print("Found:");
-      // clang-format off
-      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
-    }
+  if (Options::cdoVerbose) print_vars_found(varIDs, varList1.vars);
 
-  for (int varID = 0; varID < nvars; ++varID)
+  for (int varID = 0; varID < numVars; ++varID)
     {
-      const auto &var1 = varList1[varID];
+      const auto &var1 = varList1.vars[varID];
       auto nlevels = var1.nlevels;
 
       if (var1.gridType == GRID_SPECTRAL && zaxis_is_hybrid(var1.zaxisType)) cdo_abort("Spectral data on model level unsupported!");
@@ -575,7 +561,7 @@ height_level_interpolation(Varray<double> &heightLevels, bool extrapolate)
       if (interpVars[varID])
         {
           varnumMissVals[varID].resize(maxLevels, 0);
-          vardata2[varID].init(varList2[varID]);
+          vardata2[varID].init(varList2.vars[varID]);
         }
       else
         {
@@ -602,7 +588,7 @@ 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.vars[varIDs.sgeopotID]); }
 
       field_fill(sgeopot, 0.0);
     }
@@ -618,15 +604,14 @@ height_level_interpolation(Varray<double> &heightLevels, bool extrapolate)
       auto nrecs = cdo_stream_inq_timestep(streamID1, tsID);
       if (nrecs == 0) break;
 
-      for (int varID = 0; varID < nvars; ++varID) processVars[varID] = false;
+      for (int varID = 0; varID < numVars; ++varID) processVars[varID] = false;
 
       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);
+          auto [varID, levelID] = cdo_inq_record(streamID1);
           cdo_read_record(streamID1, vardata1[varID], levelID, &varnumMissVals[varID][levelID]);
 
           processVars[varID] = true;
@@ -638,17 +623,17 @@ height_level_interpolation(Varray<double> &heightLevels, bool extrapolate)
           gen_vert_index(vertIndex, heightLevels, vardata1[varIDs.gheightID], gridsize, lreverse);
           if (!extrapolate)
             {
-              heightBottom.init(varList1[varIDs.gheightID]);
+              heightBottom.init(varList1.vars[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)
+      for (int varID = 0; varID < numVars; ++varID)
         {
           if (processVars[varID])
             {
-              const auto &var = varList1[varID];
+              const auto &var = varList1.vars[varID];
               if (tsID > 0 && var.isConstant) continue;
 
               if (interpVars[varID])
@@ -667,7 +652,7 @@ height_level_interpolation(Varray<double> &heightLevels, bool extrapolate)
                   if (!extrapolate) varray_copy(numHeightLevels, pnumMissVals, varnumMissVals[varID]);
                 }
 
-              for (int levelID = 0; levelID < varList2[varID].nlevels; ++levelID)
+              for (int levelID = 0; levelID < varList2.vars[varID].nlevels; ++levelID)
                 {
                   cdo_def_record(streamID2, varID, levelID);
                   cdo_write_record(streamID2, interpVars[varID] ? vardata2[varID] : vardata1[varID], levelID,
@@ -724,7 +709,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
     ML2PLX = module.get_id("ml2plx");
     ML2HLX = module.get_id("ml2hlx");
@@ -753,7 +738,7 @@ public:
   }
 
   void
-  run()
+  run() override
   {
 #ifdef ENABLE_HEIGHT_LEVEL_INTERPOLATION
     if (doHeightInterpolation)
@@ -764,7 +749,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
   }
 };
diff --git a/src/Vertintzs.cc b/src/Vertintzs.cc
index 5f69b64e55e3865969c56ef7e57343ca36c2f600..d4e3f5b7a20d19a261ab4f636182482090be4b76 100644
--- a/src/Vertintzs.cc
+++ b/src/Vertintzs.cc
@@ -17,9 +17,7 @@
 #include "process_int.h"
 #include "cdo_vlist.h"
 #include "field_vinterp.h"
-#include "stdnametable.h"
 #include "util_string.h"
-#include "const.h"
 #include "cdo_zaxis.h"
 #include "param_conversion.h"
 
@@ -69,7 +67,7 @@ public:
   using Process::Process;
   inline static CdoModule module = {
     .name = "Vertintzs",
-    .operators = { { "zs2zl", 0, 0, "depth levels in meter"}, { "zs2zlx"} },
+    .operators = { { "zs2zl", 0, 0, "depth levels in meter" }, { "zs2zlx" } },
     .aliases = {},
     .mode = EXPOSED,     // Module mode: 0:intern 1:extern
     .number = CDI_REAL,  // Allowed number type
@@ -112,7 +110,7 @@ private:
 
 public:
   void
-  init()
+  init() override
   {
 
     auto operatorID = cdo_operator_id();
@@ -132,31 +130,30 @@ public:
 
     gridsize = vlist_check_gridsize(vlistID1);
 
-    varList_init(varList1, vlistID1);
-    varListSetUniqueMemtype(varList1);
-    auto memType = varList1[0].memType;
-
-    nvars = vlistNvars(vlistID1);
+    varList1 = VarList(vlistID1);
+    varList_set_unique_memtype(varList1);
+    auto memType = varList1.vars[0].memType;
 
+    nvars = varList1.numVars();
     for (int varID = 0; varID < nvars; ++varID)
       {
-        if (string_to_lower(varList1[varID].name) == "depth_c") depthID = varID;
+        if (string_to_lower(varList1.vars[varID].name) == "depth_c") depthID = varID;
       }
 
     if (Options::cdoVerbose)
       {
         cdo_print("Found:");
         // clang-format off
-        if (-1 != depthID)   cdo_print("  %s -> %s", "zstar depth at cell center", varList1[depthID].name);
+        if (-1 != depthID)   cdo_print("  %s -> %s", "zstar depth at cell center", varList1.vars[depthID].name);
         // clang-format on
       }
 
     if (-1 == depthID) cdo_abort("depth_c not found!");
 
-    auto zaxisIDfull = (-1 == depthID) ? -1 : varList1[depthID].zaxisID;
+    auto zaxisIDfull = (-1 == depthID) ? -1 : varList1.vars[depthID].zaxisID;
     numFullLevels = (-1 == zaxisIDfull) ? 0 : zaxisInqSize(zaxisIDfull);
 
-    auto nzaxis = vlistNzaxis(vlistID1);
+    auto nzaxis = vlistNumZaxis(vlistID1);
     for (int index = 0; index < nzaxis; ++index)
       {
         auto zaxisID = vlistZaxis(vlistID1, index);
@@ -165,8 +162,8 @@ public:
           vlistChangeZaxis(vlistID2, zaxisID, zaxisID2);
       }
 
-    varList_init(varList2, vlistID2);
-    varListSetMemtype(varList2, memType);
+    varList2 = VarList(vlistID2);
+    varList_set_memtype(varList2, memType);
 
     if (!extrapolate) pnumMissVals.resize(depthLevels.size());
 
@@ -182,7 +179,7 @@ public:
 
     for (int varID = 0; varID < nvars; ++varID)
       {
-        const auto &var = varList1[varID];
+        const auto &var = varList1.vars[varID];
 
         if (gridInqType(var.gridID) == GRID_SPECTRAL) cdo_abort("Spectral data unsupported!");
 
@@ -191,7 +188,7 @@ public:
 
         interpVars[varID] = (var.zaxisID == zaxisIDfull || (is_depth_axis(var.zaxisID) && (var.nlevels == numFullLevels)));
 
-        if (interpVars[varID]) { vardata2[varID].init(varList2[varID]); }
+        if (interpVars[varID]) { vardata2[varID].init(varList2.vars[varID]); }
         else if (is_depth_axis(var.zaxisID) && var.nlevels > 1)
           {
 
@@ -199,11 +196,11 @@ public:
           }
       }
 
-    fullDepth.init(varList1[depthID]);
+    fullDepth.init(varList1.vars[depthID]);
 
     for (int varID = 0; varID < nvars; ++varID)
       {
-        if (interpVars[varID] && varList1[varID].isConstant) vlistDefVarTimetype(vlistID2, varID, TIME_VARYING);
+        if (interpVars[varID] && varList1.vars[varID].isConstant) vlistDefVarTimetype(vlistID2, varID, TIME_VARYING);
       }
 
     streamID2 = cdo_open_write(1);
@@ -212,7 +209,7 @@ public:
   }
 
   void
-  run()
+  run() override
   {
     int tsID = 0;
     while (true)
@@ -223,7 +220,7 @@ public:
         for (int varID = 0; varID < nvars; ++varID)
           {
             processVars[varID] = false;
-            const auto &var = varList1[varID];
+            const auto &var = varList1.vars[varID];
             for (int levelID = 0; levelID < var.nlevels; ++levelID) varnumMissVals[varID][levelID] = 0;
           }
 
@@ -232,8 +229,7 @@ public:
 
         for (int recID = 0; recID < nrecs; ++recID)
           {
-            int varID, levelID;
-            cdo_inq_record(streamID1, &varID, &levelID);
+            auto [varID, levelID] = cdo_inq_record(streamID1);
             cdo_read_record(streamID1, vardata1[varID], levelID, &varnumMissVals[varID][levelID]);
             processVars[varID] = true;
           }
@@ -241,14 +237,14 @@ public:
         for (int varID = 0; varID < nvars; ++varID)
           if (interpVars[varID]) processVars[varID] = true;
 
-        if (tsID == 0 || !varList1[depthID].isConstant)
+        if (tsID == 0 || !varList1.vars[depthID].isConstant)
           {
             constexpr auto lreverse = false;
             field_copy(vardata1[depthID], fullDepth);
             gen_vert_index(vertIndexFull, depthLevels, fullDepth, gridsize, lreverse);
             if (!extrapolate)
               {
-                depthBottom.init(varList1[depthID]);
+                depthBottom.init(varList1.vars[depthID]);
                 field_copy(fullDepth, numFullLevels - 1, depthBottom);
                 gen_vert_index_mv(vertIndexFull, depthLevels, gridsize, depthBottom, pnumMissVals, lreverse);
               }
@@ -258,7 +254,7 @@ public:
           {
             if (processVars[varID])
               {
-                const auto &var = varList1[varID];
+                const auto &var = varList1.vars[varID];
 
                 if (tsID > 0 && !interpVars[varID] && var.isConstant) continue;
 
@@ -277,7 +273,7 @@ public:
                     if (!extrapolate) varray_copy(depthLevels.size(), pnumMissVals, varnumMissVals[varID]);
                   }
 
-                for (int levelID = 0; levelID < varList2[varID].nlevels; ++levelID)
+                for (int levelID = 0; levelID < varList2.vars[varID].nlevels; ++levelID)
                   {
                     cdo_def_record(streamID2, varID, levelID);
                     auto varout = (interpVars[varID] ? vardata2[varID] : vardata1[varID]);
@@ -291,7 +287,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
diff --git a/src/Vertstat.cc b/src/Vertstat.cc
index 01c4a7afea39ae3a2ce7064dd8610d6a1a8cd404..e3e33a1f498fe4a2ec662fe76a1f1061b15bdf73 100644
--- a/src/Vertstat.cc
+++ b/src/Vertstat.cc
@@ -24,8 +24,8 @@
 #include <cdi.h>
 
 #include "cdo_options.h"
+#include "cdo_stepstat.h"
 #include "process_int.h"
-#include "cdo_vlist.h"
 #include "cdo_zaxis.h"
 #include "param_conversion.h"
 #include "pmlist.h"
@@ -39,7 +39,7 @@ get_surface_ID(int vlistID)
 {
   int surfID = -1;
 
-  auto nzaxis = vlistNzaxis(vlistID);
+  auto nzaxis = vlistNumZaxis(vlistID);
   for (int index = 0; index < nzaxis; ++index)
     {
       auto zaxisID = vlistZaxis(vlistID, index);
@@ -58,7 +58,7 @@ get_surface_ID(int vlistID)
 static void
 set_surface_ID(const int vlistID, const int surfID)
 {
-  auto nzaxis = vlistNzaxis(vlistID);
+  auto nzaxis = vlistNumZaxis(vlistID);
   for (int index = 0; index < nzaxis; ++index)
     {
       auto zaxisID = vlistZaxis(vlistID, index);
@@ -119,7 +119,7 @@ public:
   };
   inline static RegisterEntry<Vertstat> registration = RegisterEntry<Vertstat>(module);
 
-  int VERTINT;
+private:
   struct VertInfo
   {
     int zaxisID = -1;
@@ -129,52 +129,37 @@ public:
     Varray<double> weights;
   };
 
+  int VERTINT;
   int operatorID;
-  int operfunc;
 
   CdoStreamID streamID1;
   CdoStreamID streamID2;
-
-  int vlistID2;
-
   int taxisID1;
   int taxisID2;
 
-  int nzaxis;
-  int nvars;
-
-  bool needWeights;
-  bool lrange;
-  bool lmean;
-  bool lstd;
-  bool lvarstd;
-  int divisor;
-
-  VarList varList;
+  int vlistID2;
 
-  FieldVector vars1;
-  FieldVector vars2;
-  FieldVector samp1;
+  int nzaxis;
+  int numVars;
 
-  Field field;
+  VarList varList1;
 
   std::vector<VertInfo> vert;
 
+  bool needWeights;
+  cdo::StepStat1Dvars stepStat;
+
 public:
   void
-  init()
+  init() override
   {
     VERTINT = module.get_id("vertint");
 
     operatorID = cdo_operator_id();
-    operfunc = cdo_operator_f1(operatorID);
+    auto operfunc = cdo_operator_f1(operatorID);
     needWeights = cdo_operator_f2(operatorID);
 
-    lrange = (operfunc == FieldFunc_Range);
-    lmean = (operfunc == FieldFunc_Mean || operfunc == FieldFunc_Avg);
-    lstd = (operfunc == FieldFunc_Std || operfunc == FieldFunc_Std1);
-    lvarstd = (lstd || operfunc == FieldFunc_Var || operfunc == FieldFunc_Var1);
-    divisor = (operfunc == FieldFunc_Std1 || operfunc == FieldFunc_Var1);
+    stepStat.init(operfunc);
 
     // int applyWeights = lmean;
 
@@ -182,8 +167,8 @@ public:
     auto vlistID1 = cdo_stream_inq_vlist(streamID1);
 
     vlistClearFlag(vlistID1);
-    nvars = vlistNvars(vlistID1);
-    for (int varID = 0; varID < nvars; ++varID) vlistDefFlag(vlistID1, varID, 0, true);
+    numVars = vlistNvars(vlistID1);
+    for (int varID = 0; varID < numVars; ++varID) vlistDefFlag(vlistID1, varID, 0, true);
 
     vlistID2 = vlistCreate();
     cdo_vlist_copy_flag(vlistID2, vlistID1);
@@ -196,7 +181,7 @@ public:
     auto surfID = get_surface_ID(vlistID1);
     set_surface_ID(vlistID2, surfID);
 
-    nzaxis = vlistNzaxis(vlistID1);
+    nzaxis = vlistNumZaxis(vlistID1);
     vert = std::vector<VertInfo>(nzaxis);
     if (needWeights)
       {
@@ -232,38 +217,17 @@ public:
     streamID2 = cdo_open_write(1);
     cdo_def_vlist(streamID2, vlistID2);
 
-    varList_init(varList, vlistID1);
-
-    vars1 = FieldVector(nvars);
-    vars2 = FieldVector(nvars);
-    samp1 = FieldVector(nvars);
-    if (lvarstd || lrange) vars2.resize(nvars);
+    varList1 = VarList(vlistID1);
 
-    for (int varID = 0; varID < nvars; ++varID)
-      {
-        const auto &var = varList[varID];
-        samp1[varID].grid = var.gridID;
-        samp1[varID].missval = var.missval;
-        samp1[varID].memType = MemType::Double;
-        vars1[varID].grid = var.gridID;
-        vars1[varID].missval = var.missval;
-        vars1[varID].memType = MemType::Double;
-        vars1[varID].resize(var.gridsize);
-        if (lvarstd || lrange)
-          {
-            vars2[varID].grid = var.gridID;
-            vars2[varID].missval = var.missval;
-            vars2[varID].memType = MemType::Double;
-            vars2[varID].resize(var.gridsize);
-          }
-      }
+    int VARS_MEMTYPE = stepStat.lminmax ? FIELD_NAT : 0;
+    stepStat.alloc(varList1, VARS_MEMTYPE);
   }
 
   void
-  run()
+  run() override
   {
-    auto field2_stdvar_func = lstd ? field2_std : field2_var;
-    auto fieldc_stdvar_func = lstd ? fieldc_std : fieldc_var;
+    Field field;
+
     int tsID = 0;
     while (true)
       {
@@ -273,20 +237,20 @@ public:
         cdo_taxis_copy_timestep(taxisID2, taxisID1);
         cdo_def_timestep(streamID2, tsID);
 
-        std::vector<bool> varsLevelInit(nvars, false);
+        std::vector<bool> varsLevelInit(numVars, false);
 
         for (int recID = 0; recID < nrecs; ++recID)
           {
-            int varID, levelID;
-            cdo_inq_record(streamID1, &varID, &levelID);
+            auto [varID, levelID] = cdo_inq_record(streamID1);
 
-            const auto &var = varList[varID];
+            const auto &var = varList1.vars[varID];
 
-            auto &rsamp1 = samp1[varID];
-            auto &rvars1 = vars1[varID];
+            auto &rsamp1 = stepStat.samp(varID);
+            auto &rvar1 = stepStat.var1(varID);
+            auto &rvar2 = stepStat.var2(varID);
 
-            rvars1.nsamp++;
-            if (lrange) vars2[varID].nsamp++;
+            rvar1.nsamp++;
+            if (stepStat.lrange) rvar2.nsamp++;
 
             auto gridsize = var.gridsize;
 
@@ -311,101 +275,82 @@ public:
                     }
               }
 
+            field.init(var);
+            cdo_read_record(streamID1, field);
+
             if (varsLevelInit[varID] == false)
               {
                 varsLevelInit[varID] = true;
-                cdo_read_record(streamID1, rvars1);
-                if (lrange)
-                  {
-                    vars2[varID].numMissVals = rvars1.numMissVals;
-                    vars2[varID].vec_d = rvars1.vec_d;
-                  }
+                field_copy(field, rvar1);
 
-                if (operatorID == VERTINT && is_not_equal(layerThickness, 1.0)) fieldc_mul(rvars1, layerThickness);
-                if (lmean && is_not_equal(layerWeight, 1.0)) fieldc_mul(rvars1, layerWeight);
+                if (stepStat.lrange) field_copy(field, rvar2);
 
-                if (lvarstd)
+                if (operatorID == VERTINT && is_not_equal(layerThickness, 1.0)) fieldc_mul(rvar1, layerThickness);
+                if (stepStat.lmean && is_not_equal(layerWeight, 1.0)) fieldc_mul(rvar1, layerWeight);
+
+                if (stepStat.lvarstd)
                   {
                     if (is_not_equal(layerWeight, 1.0))
                       {
-                        field2_moqw(vars2[varID], rvars1, layerWeight);
-                        fieldc_mul(rvars1, layerWeight);
+                        field2_moqw(rvar2, rvar1, layerWeight);
+                        fieldc_mul(rvar1, layerWeight);
                       }
-                    else { field2_moq(vars2[varID], rvars1); }
+                    else { field2_moq(rvar2, rvar1); }
                   }
 
-                if (rvars1.numMissVals || !rsamp1.empty() || needWeights)
+                if (rvar1.numMissVals || !rsamp1.empty() || needWeights)
                   {
                     if (rsamp1.empty()) rsamp1.resize(gridsize);
 
                     for (size_t i = 0; i < gridsize; ++i)
-                      rsamp1.vec_d[i] = (dbl_is_equal(rvars1.vec_d[i], rvars1.missval)) ? 0.0 : layerWeight;
+                      rsamp1.vec_d[i] = (dbl_is_equal(rvar1.vec_d[i], rvar1.missval)) ? 0.0 : layerWeight;
                   }
               }
             else
               {
-                field.init(var);
-                cdo_read_record(streamID1, field);
-
                 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 (stepStat.lmean && is_not_equal(layerWeight, 1.0)) fieldc_mul(field, layerWeight);
 
                 if (field.numMissVals || !rsamp1.empty())
                   {
-                    if (rsamp1.empty()) rsamp1.resize(gridsize, rvars1.nsamp);
+                    if (rsamp1.empty()) rsamp1.resize(gridsize, rvar1.nsamp);
 
                     if (field.memType == MemType::Float)
                       {
                         for (size_t i = 0; i < gridsize; ++i)
-                          if (!dbl_is_equal(field.vec_f[i], (float) rvars1.missval)) rsamp1.vec_d[i] += layerWeight;
+                          if (dbl_is_not_equal(field.vec_f[i], (float) rvar1.missval)) rsamp1.vec_d[i] += layerWeight;
                       }
                     else
                       {
                         for (size_t i = 0; i < gridsize; ++i)
-                          if (!dbl_is_equal(field.vec_d[i], rvars1.missval)) rsamp1.vec_d[i] += layerWeight;
+                          if (dbl_is_not_equal(field.vec_d[i], rvar1.missval)) rsamp1.vec_d[i] += layerWeight;
                       }
                   }
 
-                if (lvarstd)
+                if (stepStat.lvarstd)
                   {
                     if (is_not_equal(layerWeight, 1.0))
                       {
-                        field2_sumqw(vars2[varID], field, layerWeight);
-                        field2_sumw(rvars1, field, layerWeight);
+                        field2_sumqw(rvar2, field, layerWeight);
+                        field2_sumw(rvar1, field, layerWeight);
                       }
-                    else { field2_sumsumq(rvars1, vars2[varID], field); }
+                    else { field2_sumsumq(rvar1, rvar2, field); }
                   }
-                else if (lrange) { field2_maxmin(rvars1, vars2[varID], field); }
-                else { field2_function(rvars1, field, operfunc); }
+                else if (stepStat.lrange) { field2_maxmin(rvar1, rvar2, field); }
+                else { field2_function(rvar1, field, stepStat.operfunc); }
               }
           }
 
-        for (int varID = 0; varID < nvars; ++varID)
+        for (int varID = 0; varID < numVars; ++varID)
           {
-            const auto &rsamp1 = samp1[varID];
-            auto &rvars1 = vars1[varID];
-
-            if (rvars1.nsamp)
+            auto numSets = stepStat.var1(varID).nsamp;
+            if (numSets)
               {
-                if (lmean)
-                  {
-                    if (!rsamp1.empty())
-                      field2_div(rvars1, rsamp1);
-                    else
-                      fieldc_div(rvars1, (double) rvars1.nsamp);
-                  }
-                else if (lvarstd)
-                  {
-                    if (!rsamp1.empty())
-                      field2_stdvar_func(rvars1, vars2[varID], rsamp1, divisor);
-                    else
-                      fieldc_stdvar_func(rvars1, vars2[varID], rvars1.nsamp, divisor);
-                  }
-                else if (lrange) { field2_sub(rvars1, vars2[varID]); }
+                stepStat.process(varID, numSets);
 
                 cdo_def_record(streamID2, varID, 0);
-                cdo_write_record(streamID2, rvars1);
-                rvars1.nsamp = 0;
+                cdo_write_record(streamID2, stepStat.var1(varID));
+                stepStat.var1(varID).nsamp = 0;
               }
           }
 
@@ -414,9 +359,8 @@ public:
   }
 
   void
-  close()
+  close() override
   {
-
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
 
diff --git a/src/Vertwind.cc b/src/Vertwind.cc
index c79e0008be82e4311a0d3e5a823a872084debbac..4f11b9ac6ed4905540d4e2451ce21cc76cdf8272 100644
--- a/src/Vertwind.cc
+++ b/src/Vertwind.cc
@@ -36,6 +36,7 @@ public:
     .constraints = { 1, 1, NoRestriction },
   };
   inline static RegisterEntry<Vertwind> registration = RegisterEntry<Vertwind>(module);
+
   CdoStreamID streamID1;
   CdoStreamID streamID2;
 
@@ -65,9 +66,8 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
-
     operator_check_argc(0);
 
     streamID1 = cdo_open_read(0);
@@ -75,8 +75,7 @@ public:
 
     vlist_check_gridsize(vlistID1);
 
-    VarList varList1;
-    varList_init(varList1, vlistID1);
+    VarList varList1(vlistID1);
 
     int temp_code = 130;
     int sq_code = 133;
@@ -86,11 +85,11 @@ public:
     int nvars = vlistNvars(vlistID1);
     for (int varID = 0; varID < nvars; ++varID)
       {
-        int code = varList1[varID].code;
+        int code = varList1.vars[varID].code;
 
         if (code <= 0)
           {
-            auto varname = string_to_lower(varList1[varID].name);
+            auto varname = string_to_lower(varList1.vars[varID].name);
 
             if (varname == "st") { code = temp_code; }
             else if (varname == "sq") { code = sq_code; }
@@ -114,18 +113,19 @@ public:
       }
 
     // Get missing values
-    missval_t = varList1[tempID].missval;
-    missval_sq = varList1[sqID].missval;
-    missval_wap = varList1[omegaID].missval;
+    missval_t = varList1.vars[tempID].missval;
+    missval_sq = varList1.vars[sqID].missval;
+
+    const auto &varOmega = varList1.vars[omegaID];
+    missval_wap = varOmega.missval;
     missval_out = missval_wap;
 
-    auto gridID = varList1[omegaID].gridID;
-    zaxisID = varList1[omegaID].zaxisID;
+    zaxisID = varOmega.zaxisID;
 
-    if (psID == -1 && zaxisInqType(zaxisID) == ZAXIS_HYBRID) cdo_abort("Surface pressure (code 134) not found!");
+    if (psID == -1 && varOmega.zaxisType == ZAXIS_HYBRID) cdo_abort("Surface pressure (code 134) not found!");
 
-    gridsize = gridInqSize(gridID);
-    nlevels = zaxisInqSize(zaxisID);
+    gridsize = varOmega.gridsize;
+    nlevels = varOmega.nlevels;
     Varray<double> levels(nlevels);
     cdo_zaxis_inq_levels(zaxisID, levels.data());
 
@@ -183,7 +183,7 @@ public:
   }
 
   void
-  run()
+  run() override
   {
     int tsID = 0;
     while (true)
@@ -197,8 +197,7 @@ public:
         for (int recID = 0; recID < nrecs; ++recID)
           {
             size_t numMissVals;
-            int varID, levelID;
-            cdo_inq_record(streamID1, &varID, &levelID);
+            auto[varID, levelID] = cdo_inq_record(streamID1);
 
             auto offset = (size_t) levelID * gridsize;
 
@@ -259,7 +258,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
diff --git a/src/Wct.cc b/src/Wct.cc
index b6abb14fcee1df8af71e79e57bbf642b860612e3..a5ef6a9db99df68c47d29e1830a6b99b79b7c49e 100644
--- a/src/Wct.cc
+++ b/src/Wct.cc
@@ -17,7 +17,6 @@
 
 #include "cdo_options.h"
 #include "process_int.h"
-#include "cdo_vlist.h"
 
 static const char WCT_NAME[] = "wind_chill_temperature";
 static const char WCT_LONGNAME[] = "Windchill temperature describes the fact that low temperatures are felt "
@@ -90,13 +89,16 @@ public:
   int vlistID1;
   int vlistID2;
 
+  VarList varList1;
+  VarList varList2;
+
   int varID3;
 
   Field field1, field2;
 
 public:
   void
-  init()
+  init() override
   {
     operator_check_argc(0);
 
@@ -106,9 +108,12 @@ public:
     vlistID1 = cdo_stream_inq_vlist(streamID1);
     vlistID2 = cdo_stream_inq_vlist(streamID2);
 
+    varList1 = VarList(vlistID1);
+    varList2 = VarList(vlistID2);
+
     taxisID1 = vlistInqTaxis(vlistID1);
 
-    vlist_compare(vlistID1, vlistID2, CmpVlist::Dim);
+    vlist_compare(vlistID1, vlistID2, CmpVarList::Dim);
 
     auto gridsizemax = vlistGridsizeMax(vlistID1);
 
@@ -118,8 +123,8 @@ public:
     if (Options::cdoVerbose) cdo_print("Number of timesteps: file1 %d, file2 %d", vlistNtsteps(vlistID1), vlistNtsteps(vlistID2));
 
     auto vlistID3 = vlistCreate();
-    auto gridID = vlistInqVarGrid(vlistID1, FIRST_VAR);
-    auto zaxisID = vlistInqVarZaxis(vlistID1, FIRST_VAR);
+    auto gridID = varList1.vars[FIRST_VAR].gridID;
+    auto zaxisID = varList1.vars[FIRST_VAR].zaxisID;
     varID3 = vlistDefVar(vlistID3, gridID, zaxisID, TIME_VARYING);
 
     taxisID3 = cdo_taxis_create(TAXIS_RELATIVE);
@@ -138,7 +143,7 @@ public:
   }
 
   void
-  run()
+  run() override
   {
     int tsID = 0;
     while (true)
@@ -166,8 +171,8 @@ public:
 
             if (varID1 != FIRST_VAR) continue;
 
-            field1.missval = vlistInqVarMissval(vlistID1, varID1);
-            field2.missval = vlistInqVarMissval(vlistID2, varID2);
+            field1.missval = varList1.vars[varID1].missval;
+            field2.missval = varList2.vars[varID2].missval;
 
             farexpr(field1, field2, windchillTemperature);
 
@@ -180,7 +185,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID3);
     cdo_stream_close(streamID2);
diff --git a/src/Wind.cc b/src/Wind.cc
index 0b62441ae8d4036792be08c2db14377df72aae5f..580cc93f2b072bb6b50a011a9c76a883af68ebb1 100644
--- a/src/Wind.cc
+++ b/src/Wind.cc
@@ -72,11 +72,8 @@ public:
   using Process::Process;
   inline static CdoModule module = {
     .name = "Wind",
-    .operators = { { "uv2dv", WindHelp },
-                   { "uv2dvl", WindHelp },
-                   { "dv2uv", WindHelp },
-                   { "dv2uvl", WindHelp },
-                   { "dv2ps", Wind2Help } },
+    .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
@@ -113,7 +110,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
     dataIsUnchanged = data_is_unchanged();
 
@@ -148,6 +145,8 @@ public:
     auto vlistID1 = cdo_stream_inq_vlist(streamID1);
     auto vlistID2 = vlistDuplicate(vlistID1);
 
+    VarList varList1(vlistID1);
+
     taxisID1 = vlistInqTaxis(vlistID1);
     taxisID2 = taxisDuplicate(taxisID1);
     vlistDefTaxis(vlistID2, taxisID2);
@@ -156,10 +155,10 @@ public:
     auto nvars = vlistNvars(vlistID2);
     for (int varID = 0; varID < nvars; ++varID)
       {
-        auto varname = string_to_lower(cdo::inq_var_name(vlistID1, varID));
-        auto param = vlistInqVarParam(vlistID2, varID);
+        const auto &var = varList1.vars[varID];
+        auto varname = string_to_lower(var.name);
         int pnum, pcat, pdis;
-        cdiDecodeParam(param, &pnum, &pcat, &pdis);
+        cdiDecodeParam(var.param, &pnum, &pcat, &pdis);
         int code = pnum;
         if (operatorID == UV2DV || operatorID == UV2DVL)
           {
@@ -205,10 +204,12 @@ public:
 
         if (varID1 != -1 && varID2 != -1)
           {
-            gridID1 = vlistInqVarGrid(vlistID1, varID1);
+            const auto &var1 = varList1.vars[varID1];
+            const auto &var2 = varList1.vars[varID2];
+            gridID1 = var1.gridID;
 
-            if (gridInqType(gridID1) != GRID_GAUSSIAN) cdo_abort("U-wind is not on Gaussian grid!");
-            if (gridID1 != vlistInqVarGrid(vlistID1, varID2)) cdo_abort("U and V wind must have the same grid represention!");
+            if (var1.gridType != GRID_GAUSSIAN) cdo_abort("U-wind is not on Gaussian grid!");
+            if (var1.gridID != var2.gridID) cdo_abort("U and V wind must have the same grid represention!");
 
             auto numLPE = gridInqNP(gridID1);
             long nlon = gridInqXsize(gridID1);
@@ -234,7 +235,7 @@ public:
             defineAttributesDV(vlistID2, gridID2, varID1, varID2);
 
             ntr = gridInqTrunc(gridID2);
-            nlev = zaxisInqSize(vlistInqVarZaxis(vlistID1, varID1));
+            nlev = var1.nlevels;
 
             spTrans.init(nlon, nlat, ntr, PolFlag::UV2DV, nlev);
           }
@@ -246,12 +247,13 @@ public:
 
         if (varID1 != -1 && varID2 != -1)
           {
-            gridID1 = vlistInqVarGrid(vlistID1, varID2);
+            const auto &var1 = varList1.vars[varID1];
+            const auto &var2 = varList1.vars[varID2];
+            gridID1 = var2.gridID;
 
-            if (gridInqType(gridID1) != GRID_SPECTRAL) cdo_abort("Vorticity is not on spectral grid!");
+            if (var2.gridType != GRID_SPECTRAL) cdo_abort("Vorticity is not on spectral grid!");
 
-            if (gridID1 != vlistInqVarGrid(vlistID1, varID1))
-              cdo_abort("Divergence and vorticity must have the same grid represention!");
+            if (gridID1 != var1.gridID) cdo_abort("Divergence and vorticity must have the same grid represention!");
 
             if (gridIDgp != -1)
               {
@@ -274,7 +276,7 @@ public:
             long nlon = gridInqXsize(gridID2);
             long nlat = gridInqYsize(gridID2);
             ntr = gridInqTrunc(gridID1);
-            nlev = zaxisInqSize(vlistInqVarZaxis(vlistID1, varID1));
+            nlev = var1.nlevels;
 
             spTrans.init(nlon, nlat, ntr, PolFlag::SP2FC, nlev);
             dvTrans.init(ntr);
@@ -287,12 +289,13 @@ public:
 
         if (varID1 != -1 && varID2 != -1)
           {
-            gridID1 = vlistInqVarGrid(vlistID1, varID2);
+            const auto &var1 = varList1.vars[varID1];
+            const auto &var2 = varList1.vars[varID2];
+            gridID1 = var2.gridID;
 
-            if (gridInqType(gridID1) != GRID_SPECTRAL) cdo_abort("Vorticity is not on spectral grid!");
+            if (var2.gridType != GRID_SPECTRAL) cdo_abort("Vorticity is not on spectral grid!");
 
-            if (gridID1 != vlistInqVarGrid(vlistID1, varID1))
-              cdo_abort("Divergence and vorticity must have the same grid represention!");
+            if (gridID1 != var1.gridID) cdo_abort("Divergence and vorticity must have the same grid represention!");
 
             defineAttributesPS(vlistID2, varID1, varID2);
 
@@ -309,7 +312,8 @@ public:
 
     if (varID1 != -1 && varID2 != -1)
       {
-        nlev = zaxisInqSize(vlistInqVarZaxis(vlistID1, varID1));
+        const auto &var1 = varList1.vars[varID1];
+        nlev = var1.nlevels;
 
         auto gridsize = gridInqSize(gridID1);
         ivar1.resize(nlev * gridsize);
@@ -322,7 +326,7 @@ public:
   }
 
   void
-  run()
+  run() override
   {
     int tsID = 0;
     while (true)
@@ -366,9 +370,9 @@ public:
         if (varID1 != -1 && varID2 != -1)
           {
             if (luv2dv)
-              trans_uv2dv(spTrans, nlev, gridID1, ivar1.data(), ivar2.data(), gridID2, ovar1.data(), ovar2.data());
+              trans_uv2dv(spTrans, nlev, gridID1, ivar1, ivar2, gridID2, ovar1, ovar2);
             else if (ldv2uv)
-              trans_dv2uv(spTrans, dvTrans, nlev, gridID1, ivar1.data(), ivar2.data(), gridID2, ovar1.data(), ovar2.data());
+              trans_dv2uv(spTrans, dvTrans, nlev, gridID1, ivar1, ivar2, gridID2, ovar1, ovar2);
             else if (operatorID == DV2PS)
               {
                 dv2ps(ivar1.data(), ovar1.data(), nlev, ntr);
@@ -413,7 +417,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
diff --git a/src/WindTrans.cc b/src/WindTrans.cc
index 7808239d5445026ebd072eb8416ea277ba4f30a4..75165382d93d358dbfe90f32a2a73307cfe2b247 100644
--- a/src/WindTrans.cc
+++ b/src/WindTrans.cc
@@ -50,8 +50,8 @@
 #define CheckUVisStaggered(varID1, varID2, zaxisID1, zaxisID2) \
   {                                                            \
     VarsUVareStaggered = 1;                                    \
-    auto gridID1tmp = varList1[varID1].gridID;                 \
-    auto gridID2tmp = varList1[varID2].gridID;                 \
+    auto gridID1tmp = varList1.vars[varID1].gridID;            \
+    auto gridID2tmp = varList1.vars[varID2].gridID;            \
     if (zaxisID1 != zaxisID2)                                  \
       VarsUVareStaggered = 0;                                  \
     else if (gridID1tmp == gridID2tmp)                         \
@@ -174,7 +174,6 @@ destaggerUorV_positiveOrder(double *fu, double *fuOut, int klev, long nlat, long
 static void *
 DestaggerUV()
 {
-  int varID, levelID;
   int varID1 = CDI_UNDEFID, varID2 = CDI_UNDEFID;
   int zaxisID1 = CDI_UNDEFID, zaxisID2 = CDI_UNDEFID;
   int varID1stg = CDI_UNDEFID, varID2stg = CDI_UNDEFID;
@@ -241,8 +240,8 @@ DestaggerUV()
   auto taxisID2 = taxisDuplicate(taxisID1);
   vlistDefTaxis(vlistID2, taxisID2);
 
-  VarList varList1;
-  varList_init(varList1, vlistID1);
+  VarList varList1(vlistID1);
+  VarList varList2(vlistID2);
 
   // Find the first occurance of staggered U and V variables (for example codes 33,34). We assume that one (grib-)file
   // contains only 1 sort of horizontal grids and at most 2 staggered grids.
@@ -287,9 +286,9 @@ DestaggerUV()
 
   // Search for staggered u and v wind:
   int nvars = vlistNvars(vlistID1);
-  for (varID = 0; varID < nvars; ++varID)
+  for (int varID = 0; varID < nvars; ++varID)
     {
-      const auto &var = varList1[varID];
+      const auto &var = varList1.vars[varID];
 
       int pnum, pcat, pdis;
       cdiDecodeParam(var.param, &pnum, &pcat, &pdis);
@@ -321,8 +320,8 @@ DestaggerUV()
           CheckUVisStaggered(varID1, varID2, zaxisID1, zaxisID2);
           if (VarsUVareStaggered)
             {
-              gridID1 = varList1[varID1].gridID;
-              gridID2 = varList1[varID2].gridID;
+              gridID1 = varList1.vars[varID1].gridID;
+              gridID2 = varList1.vars[varID2].gridID;
               if (cdoDebugExt)
                 cdo_print("Found STAGGERED U & V: varID1=%d (gridID1=%d), varID2=%d (gridID2=%d)", varID1, gridID2, varID2,
                           gridID1);
@@ -360,7 +359,7 @@ DestaggerUV()
       gridID0 = gridID1;
     }
 
-  int ngrids = vlistNgrids(vlistID1);
+  int ngrids = vlistNumGrids(vlistID1);
 
   double xincU = 0.0, yincU = 0.0, xincV = 0.0, yincV = 0.0;
 
@@ -445,9 +444,9 @@ DestaggerUV()
       if (gridID0 == -1)
         {
           Debug(cdoDebugExt, "Last trial to find a reference grid for destaggered wind.");
-          for (varID = 0; varID < nvars; ++varID)
+          for (int varID = 0; varID < nvars; ++varID)
             {
-              const auto &var = varList1[varID];
+              const auto &var = varList1.vars[varID];
 
               gridID = var.gridID;
               Debug(cdoDebugExt, "Var.id %d has grid nr:%d", varID, gridID);
@@ -505,9 +504,9 @@ DestaggerUV()
 
       for (int recID = 0; recID < nrecs; ++recID)
         {
-          cdo_inq_record(streamID1, &varID, &levelID);
+          auto [varID, levelID] = cdo_inq_record(streamID1);
 
-          const auto &var = varList1[varID];
+          const auto &var = varList1.vars[varID];
 
           int pnum, pcat, pdis;
           cdiDecodeParam(var.param, &pnum, &pcat, &pdis);
@@ -541,8 +540,7 @@ DestaggerUV()
                           if (cdoDebugExt >= 10)
                             cdo_print("Destaggering U-wind record: %05d (timestep:%d); Var.id [%4d]; (code=%3d; "
                                       "ltype=%3d; level=%4d; levelID=%3d); GridID %d => %d  *** <===",
-                                      recID, tsID, varID, code, ltype, level, levelID, var.gridID,
-                                      vlistInqVarGrid(vlistID2, varID));
+                                      recID, tsID, varID, code, ltype, level, levelID, var.gridID, varList2.vars[varID].gridID);
                         }
                       else
                         UorV = -1;  // this U is not staggered, just copy the record..
@@ -554,8 +552,7 @@ DestaggerUV()
                           if (cdoDebugExt >= 10)
                             cdo_print("Destaggering V-wind record: %05d (timestep:%d); Var.id [%4d]; (code=%3d; "
                                       "ltype=%3d; level=%4d; levelID=%3d); GridID %d => %d  *** <===",
-                                      recID, tsID, varID, code, ltype, level, levelID, var.gridID,
-                                      vlistInqVarGrid(vlistID2, varID));
+                                      recID, tsID, varID, code, ltype, level, levelID, var.gridID, varList2.vars[varID].gridID);
                         }
                       else
                         UorV = -1;  // this V is not staggered, just copy the record..
@@ -591,8 +588,7 @@ DestaggerUV()
                   //                          (xfirst_U = -30.15; yfirst_U = -30.80);
                   //                          (xfirst_V = -30.20; yfirst_V = -30.75);
                   // cdo uvDestag: Grid info: (dxU; dyU) = (0.00; 0.05); (dxV; dyV) = (0.05; 0.00)
-                  if (cdoDebugExt >= 20)
-                    cdo_print("Setting GRID id from: %d => to: %d", var.gridID, vlistInqVarGrid(vlistID2, varID));
+                  if (cdoDebugExt >= 20) cdo_print("Setting GRID id from: %d => to: %d", var.gridID, varList2.vars[varID].gridID);
 
                   cdo_def_record(streamID2, varID, levelID);
                   cdo_write_record(streamID2, ovar.data(), numMissVals);
@@ -1126,7 +1122,6 @@ public:
   void *
   TransformUV()
   {
-    int varID, levelID;
     int varID1, varID2, nlevel1, nlevel2;
     size_t gridsize = 0;
     int code, gridID;
@@ -1169,40 +1164,39 @@ public:
     auto vlistID1 = cdo_stream_inq_vlist(streamID1);
     auto vlistID2 = vlistDuplicate(vlistID1);
 
-    auto nvars = vlistNvars(vlistID1);
-    auto num_recs = vlistNrecs(vlistID1);
+    VarList varList1(vlistID1);
+    VarList varList2(vlistID2);
 
-    std::vector<RecordInfo> recList(num_recs);
+    auto numVars = varList1.numVars();
+    auto numRecords = varList1.numRecords();
 
-    std::vector<std::vector<size_t>> varnumMissVals(nvars);
-    Varray3D<double> vardata(nvars);
+    std::vector<RecordInfo> recordList(numRecords);
 
-    // U & V are NOT grid relative
-    for (varID = 0; varID < nvars; ++varID) cdiDefKeyInt(vlistID2, varID, CDI_KEY_UVRELATIVETOGRID, 0);
+    std::vector<std::vector<size_t>> varnumMissVals(numVars);
+    Varray3D<double> vardata(numVars);
 
-    VarList varList1, varList2;
-    varList_init(varList1, vlistID1);
-    varList_init(varList2, vlistID2);
+    // U & V are NOT grid relative
+    for (int varID = 0; varID < numVars; ++varID) cdiDefKeyInt(vlistID2, varID, CDI_KEY_UVRELATIVETOGRID, 0);
 
     bool lfound[MAXARG];
     for (int i = 0; i < nch; ++i) lfound[i] = false;
 
     if (lvar)
       {
-        for (varID = 0; varID < nvars; ++varID)
+        for (int varID = 0; varID < numVars; ++varID)
           {
             for (int i = 0; i < nch; ++i)
-              if (varList2[varID].name == chvars[i]) lfound[i] = true;
+              if (varList1.vars[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 varID = 0; varID < numVars; ++varID)
           {
             for (int i = 0; i < nch; ++i)
-              if (varList2[varID].code == chcodes[i]) lfound[i] = true;
+              if (varList2.vars[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]);
@@ -1212,9 +1206,9 @@ public:
 
     // 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)
+    for (int varID = 0; varID < numVars; ++varID)
       {
-        const auto &var = varList1[varID];
+        const auto &var = varList1.vars[varID];
 
         cdiDecodeParam(var.param, &pnum, &pcat, &pdis);
         code = pnum;
@@ -1238,11 +1232,11 @@ public:
             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,
+                        varList2.vars[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);
+            for (int levelID = 0; levelID < nlevs; ++levelID) vardata[varID][levelID].resize(gridsize);
           }
       }
 
@@ -1270,16 +1264,16 @@ public:
 
         for (int recID = 0; recID < nrecs; ++recID)
           {
-            cdo_inq_record(streamID1, &varID, &levelID);
-            code = varList1[varID].code;
-            zaxisID = varList1[varID].zaxisID;
+            auto [varID, levelID] = cdo_inq_record(streamID1);
+            code = varList1.vars[varID].code;
+            zaxisID = varList1.vars[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;
+              {                                // This means that it is not eighter U neither V.
+                recordList[recID].varID = -1;  // We will NOT record/store this field in memory
+                recordList[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);
@@ -1291,8 +1285,8 @@ public:
               }
             else
               {
-                recList[recID].varID = varID;
-                recList[recID].levelID = levelID;
+                recordList[recID].varID = varID;
+                recordList[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)",
@@ -1306,63 +1300,64 @@ public:
 
         int code1, zaxisID1, ltype1;
         int code2, zaxisID2, ltype2;
-        Debug(cdoDebugExt, "Looping over %d variables to look for U-wind..", nvars);
+        Debug(cdoDebugExt, "Looping over %d variables to look for U-wind..", numVars);
 
         // find u-variables:
-        for (varID1 = 0; varID1 < nvars; ++varID1)
+        for (varID1 = 0; varID1 < numVars; ++varID1)
           if (vardata[varID1].empty())
             {
               // 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;
+              code1 = varList2.vars[varID1].code;
+              zaxisID1 = varList2.vars[varID1].zaxisID;
               ltype1 = zaxis_to_ltype(zaxisID1);
               nlevel1 = zaxisInqSize(zaxisID1);
-              CheckVarIsV(varID1, varList2[varID1].name, code1);
+              CheckVarIsV(varID1, varList2.vars[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);
+                          varID1, code1, varList2.vars[varID1].name, ltype1, nlevel1, zaxisID1);
+              CheckVarIsU(varID1, varList2.vars[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);
+                          varID1, code1, varList2.vars[varID1].name, ltype1, nlevel1, zaxisID1);
               auto usvarID = varID1;
               // find corresponding v-variable to u-variable:
-              for (varID2 = 0; varID2 < nvars; ++varID2)
+              for (varID2 = 0; varID2 < numVars; ++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;
+                    const auto &var2 = varList2.vars[varID2];
+                    code2 = var2.code;
+                    zaxisID2 = var2.zaxisID;
                     ltype2 = zaxis_to_ltype(zaxisID2);
-                    nlevel2 = zaxisInqSize(zaxisID2);
-                    CheckVarIsU(varID2, varList2[varID2].name, code2);
+                    nlevel2 = var2.nlevels;
+                    CheckVarIsU(varID2, var2.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);
+                                varID2, code2, var2.name, ltype2, nlevel2, zaxisID2);
+                    CheckVarIsV(varID2, var2.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);
+                                varID2, code2, var2.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;
+                      cdo_print("Using code %d [%d](u) and code %d [%d](v)", varList1.vars[varID1].code, code1,
+                                varList1.vars[varID2].code, code2);
+                    gridID = varList1.vars[varID1].gridID;
                     if (operatorID != ROTUVN)  // ROTUVN operator does not need creation of gridIDcurvl ...
                       {
                         if ((gridIDcurvl != -1) && (gridIDlastused != gridID))
@@ -1445,7 +1440,7 @@ public:
                       }
                     else
                       {
-                        for (levelID = 0; levelID < nlevel1; ++levelID)
+                        for (int levelID = 0; levelID < nlevel1; ++levelID)
                           {
                             if (cdoDebugExt)
                               cdo_print("RotuvNorth(): processing  level type: %d; level %d (out of [0:%d])", ltype1, levelID,
@@ -1472,12 +1467,12 @@ public:
 
         for (int recID = 0; recID < nrecs; ++recID)
           {
-            varID = recList[recID].varID;
-            levelID = recList[recID].levelID;
+            auto varID = recordList[recID].varID;
+            auto levelID = recordList[recID].levelID;
             if (varID != -1)
               {
-                code = varList1[varID].code;
-                zaxisID = varList1[varID].zaxisID;
+                code = varList1.vars[varID].code;
+                zaxisID = varList1.vars[varID].zaxisID;
                 ltype = zaxis_to_ltype(zaxisID);
                 level = zaxisInqLevel(zaxisID, levelID);
                 if (cdoDebugExt >= 10)
@@ -1503,7 +1498,7 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
 
     // clang-format off
@@ -1518,7 +1513,7 @@ PROJUVLATLON = module.get_id("projuvLatLon");
   }
 
   void
-  run()
+  run() override
   {
 
     // will be calling: rot_uv_north(int gridID, double *us, double *vs);
@@ -1536,7 +1531,7 @@ PROJUVLATLON = module.get_id("projuvLatLon");
   }
 
   void
-  close()
+  close() override
   {
     // TODO: split the TransformUV and DestaggerUV into seperate classes and fix their close
   }
diff --git a/src/Writegrid.cc b/src/Writegrid.cc
index 0d83790d28a312ea67495eb23f5e8f0659adbe3a..d2a778f6a022d0b6868cbb0137557935729bda2b 100644
--- a/src/Writegrid.cc
+++ b/src/Writegrid.cc
@@ -36,13 +36,13 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
 
     operator_check_argc(0);
 
     streamID = cdo_open_read(0);
-    const auto vlistID = cdo_stream_inq_vlist(streamID);
+    auto vlistID = cdo_stream_inq_vlist(streamID);
 
     gridID = vlistGrid(vlistID, 0);
 
@@ -61,12 +61,12 @@ public:
       }
   }
   void
-  run()
+  run() override
   {
     write_nc_grid(cdo_get_stream_name(1), gridID, mask.data());
   }
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID);
   }
diff --git a/src/Writerandom.cc b/src/Writerandom.cc
index 69dd2e21930726662afafe64665741164ade4cde..4c86af6a023edada45cd7a9f027cb692b7ae4ea6 100644
--- a/src/Writerandom.cc
+++ b/src/Writerandom.cc
@@ -29,6 +29,7 @@ public:
     .constraints = { 1, 1, NoRestriction },
   };
   inline static RegisterEntry<Writerandom> registration = RegisterEntry<Writerandom>(module);
+
   CdoStreamID streamID1;
   CdoStreamID streamID2;
 
@@ -39,15 +40,14 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
-
     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);
@@ -57,15 +57,16 @@ public:
 
     cdo_def_vlist(streamID2, vlistID2);
 
-    varList_init(varList1, vlistID1);
+    varList1 = VarList(vlistID1);
   }
+
   void
-  run()
+  run() override
   {
     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);
@@ -77,11 +78,10 @@ public:
 
         for (int recID = 0; recID < nrecs; ++recID)
           {
-            int varID, levelID;
-            cdo_inq_record(streamID1, &varID, &levelID);
+            auto[varID, levelID] = cdo_inq_record(streamID1);
             recvarID[recID] = varID;
             reclevelID[recID] = levelID;
-            recdata[recID].resize(varList1[varID].gridsize);
+            recdata[recID].resize(varList1.vars[varID].gridsize);
             cdo_read_record(streamID1, recdata[recID].data(), &recnumMissVals[recID]);
           }
 
@@ -89,7 +89,7 @@ public:
 
         for (int rindex = nrecs - 1; rindex >= 0; rindex--)
           {
-            const auto index = (int) (rindex * ((double) std::rand()) / ((double) RAND_MAX));
+            auto index = (int) (rindex * ((double) std::rand()) / ((double) RAND_MAX));
             //	printf("rindex %d %d\n", rindex, index);
             int ipos = -1;
             for (int recID = 0; recID < nrecs; ++recID)
@@ -110,9 +110,9 @@ public:
 
         for (int recID = 0; recID < nrecs; ++recID)
           {
-            const auto rindex = recindex[recID];
-            const auto varID = recvarID[rindex];
-            const auto levelID = reclevelID[rindex];
+            auto rindex = recindex[recID];
+            auto varID = recvarID[rindex];
+            auto levelID = reclevelID[rindex];
             cdo_def_record(streamID2, varID, levelID);
             cdo_write_record(streamID2, recdata[rindex].data(), recnumMissVals[rindex]);
           }
@@ -120,8 +120,9 @@ public:
         tsID++;
       }
   }
+
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
diff --git a/src/XTimstat.cc b/src/XTimstat.cc
deleted file mode 100644
index b8f7b5996914cc088b1c0d14f92c050ce2975e5e..0000000000000000000000000000000000000000
--- a/src/XTimstat.cc
+++ /dev/null
@@ -1,575 +0,0 @@
-/*
-  This file is part of CDO. CDO is a collection of Operators to manipulate and analyse Climate model Data.
-
-  Author: Uwe Schulzweida
-
-*/
-
-/*
-   This module contains the following operators:
-
-      Timstat    timmin          Time minimum
-      Timstat    timmax          Time maximum
-      Timstat    timsum          Time sum
-      Timstat    timmean         Time mean
-      Timstat    timavg          Time average
-      Timstat    timvar          Time variance
-      Timstat    timvar1         Time variance [Normalize by (n-1)]
-      Timstat    timstd          Time standard deviation
-      Timstat    timstd1         Time standard deviation [Normalize by (n-1)]
-      Hourstat   hourmin         Hourly minimum
-      Hourstat   hourmax         Hourly maximum
-      Hourstat   hoursum         Hourly sum
-      Hourstat   hourmean        Hourly mean
-      Hourstat   houravg         Hourly average
-      Hourstat   hourvar         Hourly variance
-      Hourstat   hourvar1        Hourly variance [Normalize by (n-1)]
-      Hourstat   hourstd         Hourly standard deviation
-      Hourstat   hourstd1        Hourly standard deviation [Normalize by (n-1)]
-      Daystat    daymin          Daily minimum
-      Daystat    daymax          Daily maximum
-      Daystat    daysum          Daily sum
-      Daystat    daymean         Daily mean
-      Daystat    dayavg          Daily average
-      Daystat    dayvar          Daily variance
-      Daystat    dayvar1         Daily variance [Normalize by (n-1)]
-      Daystat    daystd          Daily standard deviation
-      Daystat    daystd1         Daily standard deviation [Normalize by (n-1)]
-      Monstat    monmin          Monthly minimum
-      Monstat    monmax          Monthly maximum
-      Monstat    monsum          Monthly sum
-      Monstat    monmean         Monthly mean
-      Monstat    monavg          Monthly average
-      Monstat    monvar          Monthly variance
-      Monstat    monvar1         Monthly variance [Normalize by (n-1)]
-      Monstat    monstd          Monthly standard deviation
-      Monstat    monstd1         Monthly standard deviation [Normalize by (n-1)]
-      Yearstat   yearmin         Yearly minimum
-      Yearstat   yearmax         Yearly maximum
-      Yearstat   yearsum         Yearly sum
-      Yearstat   yearmean        Yearly mean
-      Yearstat   yearavg         Yearly average
-      Yearstat   yearvar         Yearly variance
-      Yearstat   yearvar1        Yearly variance [Normalize by (n-1)]
-      Yearstat   yearstd         Yearly standard deviation
-      Yearstat   yearstd1        Yearly standard deviation [Normalize by (n-1)]
-*/
-
-#include <cdi.h>
-
-#include <utility>
-
-#include "cdo_options.h"
-#include "cdo_vlist.h"
-#include "process_int.h"
-#include "cdo_task.h"
-#include "datetime.h"
-#include "printinfo.h"
-#include "util_date.h"
-#include "param_conversion.h"
-#include "field_functions.h"
-
-#define USE_CDI_STREAM 0
-
-struct ReadArguments
-{
-  int tsIDnext;
-#ifdef USE_CDI_STREAM
-  int streamID;
-#else
-  CdoStreamID streamID;
-#endif
-  int nrecs;
-  RecordInfo *recList;
-  FieldVector2D &vars;
-
-  ReadArguments(FieldVector2D &input_vars) : tsIDnext(0), nrecs(0), recList(nullptr), vars(input_vars){};
-};
-
-static int num_recs = 0;
-
-static void *
-cdoReadTimestep(void *rarg)
-{
-  ReadArguments *readarg = (ReadArguments *) rarg;
-  auto &input_vars = readarg->vars;
-  RecordInfo *recList = readarg->recList;
-  auto streamID = readarg->streamID;
-  int tsIDnext = readarg->tsIDnext;
-  int nrecs = readarg->nrecs;
-
-  for (int recID = 0; recID < nrecs; ++recID)
-    {
-      int varID, levelID;
-#ifdef USE_CDI_STREAM
-      streamInqRecord(streamID, &varID, &levelID);
-#else
-      cdo_inq_record(streamID, &varID, &levelID);
-#endif
-
-      if (tsIDnext == 1 && recList)
-        {
-          recList[recID].varID = varID;
-          recList[recID].levelID = levelID;
-        }
-
-      size_t numMissVals;
-
-#ifdef USE_CDI_STREAM
-      if (Options::CDO_Memtype == MemType::Float)
-        streamReadRecordF(streamID, input_vars[varID][levelID].vec_f.data(), &numMissVals);
-      else
-        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(), &numMissVals);
-      else
-        cdo_read_record(streamID, input_vars[varID][levelID].vec_d.data(), &numMissVals);
-#endif
-      input_vars[varID][levelID].numMissVals = numMissVals;
-    }
-
-#ifdef USE_CDI_STREAM
-  num_recs = streamInqTimestep(streamID, tsIDnext);
-#else
-  num_recs = cdo_stream_inq_timestep(streamID, tsIDnext);
-#endif
-
-  return ((void *) &num_recs);
-}
-
-static void
-vlistSetFrequency(int vlistID, int compareDate)
-{
-  const char *freq = nullptr;
-  // clang-format off
-  if      (compareDate == CMP_DAY)   freq = "day";
-  else if (compareDate == CMP_MONTH) freq = "mon";
-  else if (compareDate == CMP_YEAR)  freq = "year";
-  // clang-format on
-  if (freq) cdiDefAttTxt(vlistID, CDI_GLOBAL, "frequency", (int) strlen(freq), freq);
-}
-
-class XTimstat : public Process
-{
-  TimeStat timestatDate{ TimeStat::MEAN };
-  CdiDateTime vDateTime0{};
-  CdiDateTime vDateTimeN{};
-  CdoStreamID streamID3 = CDO_STREAM_UNDEF;
-  bool lvfrac = false;
-  double vfrac = 1;
-
-  int tsID = 0;
-  int otsID = 0;
-  int nrecs;
-
-  int compareDate;
-
-#ifdef USE_CDI_STREAM
-  int streamID1;
-#else
-  CdoStreamID streamID1;
-#endif
-  CdoStreamID streamID2;
-
-  int vlistID1;
-  int vlistID3;
-
-  int taxisID1;
-  int taxisID2;
-  int taxisID3 = -1;
-
-  DateTimeList dtlist;
-  std::vector<RecordInfo> recList;
-  FieldVector3D input_vars = FieldVector3D(2);
-
-  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
-    streamID1 = streamOpenRead(cdo_get_stream_name(0));
-    vlistID1 = streamInqVlist(streamID1);
-#else
-    streamID1 = cdo_open_read(0);
-    vlistID1 = cdo_stream_inq_vlist(streamID1);
-#endif
-
-    auto vlistID2 = vlistDuplicate(vlistID1);
-    vlist_define_timestep_type(vlistID2, operfunc);
-
-    vlistDefNtsteps(vlistID2, (compareDate == CMP_DATE) ? 1 : -1);
-
-    taxisID1 = vlistInqTaxis(vlistID1);
-    taxisID2 = taxisDuplicate(taxisID1);
-    taxisWithBounds(taxisID2);
-    if (taxisInqType(taxisID2) == TAXIS_FORECAST) taxisDefType(taxisID2, TAXIS_RELATIVE);
-    vlistDefTaxis(vlistID2, taxisID2);
-
-    auto nvars = vlistNvars(vlistID1);
-
-    vlistSetFrequency(vlistID2, compareDate);
-
-    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);
-
-        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);
-          }
-
-        taxisID3 = taxisDuplicate(taxisID1);
-        taxisWithBounds(taxisID3);
-        vlistDefTaxis(vlistID3, taxisID3);
-
-        cdo_def_vlist(streamID3, vlistID3);
-      }
-
-    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;
-    fields_from_vlist(vlistID1, input_vars[0], FIELD_VEC | FIELD_MEMTYPE);
-    fields_from_vlist(vlistID1, input_vars[1], FIELD_VEC | FIELD_MEMTYPE);
-
-    fields_from_vlist(vlistID1, samp1);
-    fields_from_vlist(vlistID1, vars1, FIELD_VEC);
-    if (lvars2) fields_from_vlist(vlistID1, vars2, FIELD_VEC);
-
-    lparallelread = (Options::CDO_Parallel_Read > 0);
-    if (lparallelread) read_task = new cdo::Task;
-
-#ifdef USE_CDI_STREAM
-    nrecs = streamInqTimestep(streamID1, tsID);
-#else
-    nrecs = cdo_stream_inq_timestep(streamID1, tsID);
-#endif
-
-    maxrecs = nrecs;
-    recList = std::vector<RecordInfo>(maxrecs);
-  }
-
-  void
-  run()
-  {
-    auto field2_stdvar_func = lstd ? field2_std : field2_var;
-    auto fieldc_stdvar_func = lstd ? fieldc_std : fieldc_var;
-
-    tsID++;
-
-    ReadArguments readarg(input_vars[curFirst]);
-    readarg.streamID = streamID1;
-
-    while (true)
-      {
-        int numSets = 0;
-        while (nrecs > 0)
-          {
-            dtlist.taxis_inq_timestep(taxisID1, numSets);
-            auto vDateTime = dtlist.get_vDateTime(numSets);
-
-            if (numSets == 0) vDateTime0 = vDateTime;
-
-            if (date_is_neq(vDateTime, vDateTime0, compareDate))
-              {
-                cdo_add_steps(-1);
-                break;
-              }
-
-            readarg.tsIDnext = tsID;
-            readarg.nrecs = nrecs;
-            readarg.recList = recList.data();
-            readarg.vars = input_vars[curFirst];
-
-            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;
-
-                    auto &rsamp1 = samp1[varID][levelID];
-                    auto &rvars1 = vars1[varID][levelID];
-                    auto &rinput_var = input_vars[curFirst][varID][levelID];
-
-                    auto numMissVals = rinput_var.numMissVals;
-
-                    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
-              {
-#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;
-
-                  if (vlistInqVarTimetype(vlistID1, varID) == TIME_CONSTANT) continue;
-
-                  field2_moq(vars2[varID][levelID], vars1[varID][levelID]);
-                }
-
-            vDateTimeN = vDateTime;
-            numSets++;
-            tsID++;
-          }
-
-        if (nrecs == 0 && numSets == 0) break;
-
-        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 (vlistInqVarTimetype(vlistID1, varID) == TIME_CONSTANT) continue;
-
-                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;
-
-                if (vlistInqVarTimetype(vlistID1, varID) == TIME_CONSTANT) continue;
-
-                const auto &rsamp1 = samp1[varID][levelID];
-                auto &rvars1 = vars1[varID][levelID];
-                const auto &rvars2 = vars2[varID][levelID];
-
-                if (!rsamp1.empty())
-                  field2_stdvar_func(rvars1, rvars2, rsamp1, divisor);
-                else
-                  fieldc_stdvar_func(rvars1, rvars2, numSets, divisor);
-              }
-          }
-
-        if (Options::cdoVerbose) cdo_print("%s  vfrac = %g, numSets = %d", datetime_to_string(vDateTimeN), vfrac, numSets);
-
-        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;
-
-              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 (irun) rvars1.numMissVals = field_num_miss(rvars1);
-                }
-            }
-
-        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);
-          }
-
-        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;
-
-            cdo_def_record(streamID2, varID, levelID);
-            cdo_write_record(streamID2, rvars1.vec_d.data(), rvars1.numMissVals);
-
-            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++;
-      }
-  }
-
-  void
-  close()
-  {
-
-    if (Options::cdoDiag) cdo_stream_close(streamID3);
-    cdo_stream_close(streamID2);
-#ifdef USE_CDI_STREAM
-    streamClose(streamID1);
-#else
-    cdo_stream_close(streamID1);
-#endif
-
-    if (read_task) delete read_task;
-  }
-};
diff --git a/src/Ydayarith.cc b/src/Ydayarith.cc
index 7ea26ccc18b087b25fa252a7cb5e87b6d841e4e1..cd2aed585a2e0d85e6338a623c8646327753b16d 100644
--- a/src/Ydayarith.cc
+++ b/src/Ydayarith.cc
@@ -41,9 +41,10 @@ public:
 
 private:
   static const int MaxDays = 373;  //~31*12
-  Field field;
-  FieldVector2D vars2[MaxDays];
+
+  FieldVector2D varsData2[MaxDays];
   VarList varList1;
+  VarList varList2;
 
   int operfunc;
 
@@ -60,9 +61,8 @@ private:
 
 public:
   void
-  init()
+  init() override
   {
-
     auto operatorID = cdo_operator_id();
     operfunc = cdo_operator_f1(operatorID);
 
@@ -77,9 +77,9 @@ public:
 
     vlist_unpack(vlistID3);
 
-    vlist_compare(vlistID1, vlistID2, CmpVlist::All);
-
-    varList_init(varList1, vlistID1);
+    varList1 = VarList(vlistID1);
+    varList2 = VarList(vlistID2);
+    varList_compare(varList1, varList2);
 
     taxisID1 = vlistInqTaxis(vlistID1);
     taxisID2 = vlistInqTaxis(vlistID2);
@@ -91,8 +91,10 @@ public:
   }
 
   void
-  run()
+  run() override
   {
+    Field field;
+
     int tsID = 0;
     while (true)
       {
@@ -107,20 +109,19 @@ public:
             cdo_error("Day of year %d out of range (date=%s)!", dayOfYear, date_to_string(vDateTime.date));
             return;
           }
-        if (vars2[dayOfYear].size() > 0)
+        if (varsData2[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;
           }
 
-        fields_from_vlist(vlistID2, vars2[dayOfYear], FIELD_VEC | FIELD_NAT);
+        field2D_init(varsData2[dayOfYear], varList2, FIELD_VEC | FIELD_NAT);
 
         for (int recID = 0; recID < nrecs; ++recID)
           {
-            int varID, levelID;
-            cdo_inq_record(streamID2, &varID, &levelID);
-            cdo_read_record(streamID2, vars2[dayOfYear][varID][levelID]);
+            auto [varID, levelID] = cdo_inq_record(streamID2);
+            cdo_read_record(streamID2, varsData2[dayOfYear][varID][levelID]);
           }
 
         tsID++;
@@ -140,7 +141,7 @@ public:
             cdo_error("Day of year %d out of range (date=%s)!", dayOfYear, date_to_string(vDateTime.date));
             return;
           }
-        if (vars2[dayOfYear].size() == 0)
+        if (varsData2[dayOfYear].size() == 0)
           {
             cdo_error("Day of year index %d not found (date=%s)!", dayOfYear, date_to_string(vDateTime.date));
             return;
@@ -151,12 +152,11 @@ public:
 
         for (int recID = 0; recID < nrecs; ++recID)
           {
-            int varID, levelID;
-            cdo_inq_record(streamID1, &varID, &levelID);
-            field.init(varList1[varID]);
+            auto [varID, levelID] = cdo_inq_record(streamID1);
+            field.init(varList1.vars[varID]);
             cdo_read_record(streamID1, field);
 
-            field2_function(field, vars2[dayOfYear][varID][levelID], operfunc);
+            field2_function(field, varsData2[dayOfYear][varID][levelID], operfunc);
 
             cdo_def_record(streamID3, varID, levelID);
             cdo_write_record(streamID3, field);
@@ -167,7 +167,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID3);
diff --git a/src/Ydaypctl.cc b/src/Ydaypctl.cc
index 0c7726d47e44d39c93097432185dca29fa0523e9..33a711121f7acca3379db90978c2319478ad9926 100644
--- a/src/Ydaypctl.cc
+++ b/src/Ydaypctl.cc
@@ -16,7 +16,6 @@
 
 #include <cdi.h>
 
-#include "cdo_options.h"
 #include "cdo_vlist.h"
 #include "datetime.h"
 #include "process_int.h"
@@ -37,42 +36,28 @@ public:
     .constraints = { 3, 1, NoRestriction },
   };
   inline static RegisterEntry<Ydaypctl> registration = RegisterEntry<Ydaypctl>(module);
+
   static const int MaxDays = 373;
-  CdiDateTime vDateTimes1[MaxDays]{};
-  CdiDateTime vDateTimes2[MaxDays]{};
-  long numSets[MaxDays] = { 0 };
-  std::vector<bool> vars1;
-  HistogramSet hsets[MaxDays];
 
   CdoStreamID streamID1;
-  int taxisID1;
-
   CdoStreamID streamID2;
-  int taxisID2;
-
   CdoStreamID streamID3;
-  int taxisID3;
-
   CdoStreamID streamID4;
-  int taxisID4;
 
-  int nvars;
-  int ntsteps;
-  int maxrecs;
+  int vlistID1;
+  int taxisID1;
+  int taxisID2;
+  int taxisID3;
+  int taxisID4;
 
   double pn;
 
-  Field field1, field2;
-
   VarList varList1;
-  FieldVector constFields;
-  std::vector<RecordInfo> recList;
 
 public:
   void
-  init()
+  init() override
   {
-
     operator_input_arg("percentile number");
     pn = parameter_to_double(cdo_operator_argv(0));
 
@@ -80,15 +65,19 @@ public:
     streamID2 = cdo_open_read(1);
     streamID3 = cdo_open_read(2);
 
-    const auto vlistID1 = cdo_stream_inq_vlist(streamID1);
-    const auto vlistID2 = cdo_stream_inq_vlist(streamID2);
-    const auto vlistID3 = cdo_stream_inq_vlist(streamID3);
-    const auto vlistID4 = vlistDuplicate(vlistID1);
+    vlistID1 = cdo_stream_inq_vlist(streamID1);
+    auto vlistID2 = cdo_stream_inq_vlist(streamID2);
+    auto vlistID3 = cdo_stream_inq_vlist(streamID3);
+    auto vlistID4 = vlistDuplicate(vlistID1);
 
     vlist_unpack(vlistID4);
 
-    vlist_compare(vlistID1, vlistID2, CmpVlist::All);
-    vlist_compare(vlistID1, vlistID3, CmpVlist::All);
+    varList1 = VarList(vlistID1);
+    VarList varList2(vlistID2);
+    VarList varList3(vlistID3);
+
+    varList_compare(varList1, varList2);
+    varList_compare(varList1, varList3);
 
     taxisID1 = vlistInqTaxis(vlistID1);
     taxisID2 = vlistInqTaxis(vlistID2);
@@ -101,28 +90,29 @@ public:
 
     streamID4 = cdo_open_write(3);
     cdo_def_vlist(streamID4, vlistID4);
-
-    ntsteps = vlistNtsteps(vlistID1);
-    nvars = vlistNvars(vlistID1);
-
-    maxrecs = vlistNrecs(vlistID1);
-    recList = std::vector<RecordInfo>(maxrecs);
-
-    constFields = FieldVector(maxrecs);
-
-    varList_init(varList1, vlistID1);
-
-    vars1 = std::vector<bool>(MaxDays, false);
   }
 
   void
-  run()
+  run() override
   {
+    Field field1, field2;
+    std::vector<bool> vars1(MaxDays, false);
+    CdiDateTime vDateTimes1[MaxDays]{};
+    CdiDateTime vDateTimes2[MaxDays]{};
+    HistogramSet hsets[MaxDays];
+    long numSets[MaxDays] = { 0 };
+
+    auto numVars = varList1.numVars();
+    auto numSteps = varList1.numSteps();
+    auto maxRecords = varList1.numRecords();
+    std::vector<RecordInfo> recordList(maxRecords);
+
+    FieldVector constFields(maxRecords);
 
     int tsID = 0;
     while (true)
       {
-        const auto nrecs = cdo_stream_inq_timestep(streamID2, tsID);
+        auto nrecs = cdo_stream_inq_timestep(streamID2, tsID);
         if (nrecs == 0) break;
 
         if (nrecs != cdo_stream_inq_timestep(streamID3, tsID))
@@ -145,24 +135,19 @@ public:
         if (!vars1[dayOfYear])
           {
             vars1[dayOfYear] = true;
-            hsets[dayOfYear].create(nvars, ntsteps);
+            hsets[dayOfYear].create(numVars, numSteps);
 
-            for (int varID = 0; varID < nvars; ++varID)
-              {
-                const auto &var = varList1[varID];
-                hsets[dayOfYear].createVarLevels(varID, var.nlevels, var.gridsize);
-              }
+            for (const auto &var : varList1.vars) hsets[dayOfYear].createVarLevels(var.ID, var.nlevels, var.gridsize);
           }
 
         for (int recID = 0; recID < nrecs; ++recID)
           {
-            int varID, levelID;
-            cdo_inq_record(streamID2, &varID, &levelID);
-            const auto &var = varList1[varID];
+            auto [varID, levelID] = cdo_inq_record(streamID2);
+            const auto &var = varList1.vars[varID];
             field1.init(var);
             cdo_read_record(streamID2, field1);
 
-            cdo_inq_record(streamID3, &varID, &levelID);
+            (void) cdo_inq_record(streamID3);
             field2.init(var);
             cdo_read_record(streamID3, field2);
 
@@ -175,7 +160,7 @@ public:
     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;
 
         const auto vDateTime = taxisInqVdatetime(taxisID1);
@@ -192,11 +177,10 @@ public:
 
         for (int recID = 0; recID < nrecs; ++recID)
           {
-            int varID, levelID;
-            cdo_inq_record(streamID1, &varID, &levelID);
-            const auto &var = varList1[varID];
+            auto [varID, levelID] = cdo_inq_record(streamID1);
+            const auto &var = varList1.vars[varID];
 
-            if (tsID == 0) recList[recID].set(varID, levelID);
+            if (tsID == 0) recordList[recID].set(varID, levelID);
 
             if (tsID == 0 && var.isConstant)
               {
@@ -227,17 +211,18 @@ public:
           taxisDefVdatetime(taxisID4, vDateTimes1[dayOfYear]);
           cdo_def_timestep(streamID4, otsID);
 
-          for (int recID = 0; recID < maxrecs; ++recID)
+          for (int recID = 0; recID < maxRecords; ++recID)
             {
-              auto [varID, levelID] = recList[recID].get();
-              if (otsID && varList1[varID].isConstant) continue;
+              auto [varID, levelID] = recordList[recID].get();
+              const auto &var = varList1.vars[varID];
+              if (otsID && var.isConstant) continue;
 
               cdo_def_record(streamID4, varID, levelID);
 
-              if (varList1[varID].isConstant) { cdo_write_record(streamID4, constFields[recID]); }
+              if (var.isConstant) { cdo_write_record(streamID4, constFields[recID]); }
               else
                 {
-                  field1.init(varList1[varID]);
+                  field1.init(var);
                   hsets[dayOfYear].getVarLevelPercentiles(field1, varID, levelID, pn);
                   cdo_write_record(streamID4, field1);
                 }
@@ -248,7 +233,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID4);
     cdo_stream_close(streamID3);
diff --git a/src/Ydaystat.cc b/src/Ydaystat.cc
index 913534130dd43a67d974e493d5ec8e9c7597d2d0..7c432609c4a1df88ab93cd24b7dd2d3f0224dc18 100644
--- a/src/Ydaystat.cc
+++ b/src/Ydaystat.cc
@@ -23,20 +23,26 @@
 #include <cdi.h>
 
 #include "cdo_options.h"
-#include "cdo_vlist.h"
+#include "cdo_stepstat.h"
 #include "datetime.h"
 #include "process_int.h"
 #include "param_conversion.h"
 #include "pmlist.h"
 #include "printinfo.h"
+#include "progress.h"
 #include "field_functions.h"
 
-static int yearMode = 0;
+struct YstatParam
+{
+  int year = 0;
+  bool yearMode{ false };
+};
 
-static void
+static YstatParam
 setParameter(void)
 {
-  const auto pargc = cdo_operator_argc();
+  YstatParam params;
+  auto pargc = cdo_operator_argc();
   if (pargc)
     {
       const auto &pargv = cdo_get_oper_argv();
@@ -54,11 +60,15 @@ setParameter(void)
           const auto &value = kv.values[0];
 
           if (key == "yearMode")
-            yearMode = parameter_to_int(value);
+            params.yearMode = parameter_to_bool(value);
+          else if (key == "year")
+            params.year = parameter_to_int(value);
           else
             cdo_abort("Invalid parameter key >%s<!", key);
         }
     }
+
+  return params;
 }
 
 class Ydaystat : public Process
@@ -85,80 +95,75 @@ public:
   inline static RegisterEntry<Ydaystat> registration = RegisterEntry<Ydaystat>(module);
 
 private:
-  static const int MaxDays = 373;
-
   CdoStreamID streamID1;
   CdoStreamID streamID2;
-
   int taxisID1;
   int taxisID2;
-  int vlistID1;
 
-  int dayOfYear_numSets[MaxDays]{ 0 };
-  int VARS_MEMTYPE = 0;
-
-  CdiDateTime vDateTimes[MaxDays]{};
-  FieldVector2D vars1[MaxDays], vars2[MaxDays], samp1[MaxDays];
-
-  bool lmean;
-  bool lrange;
-  bool lstd;
-  bool lvars2;
-  bool lvarstd;
+  int vlistID1;
+  VarList varList1;
 
-  int divisor;
-  int maxrecs;
-  int operfunc;
+  int maxRecords;
+  std::vector<RecordInfo> recordList;
 
-  std::vector<RecordInfo> recList;
-  VarList varList;
-  Field field;
+  cdo::StepStat3D stepStat;
+  YstatParam params;
 
 public:
   void
-  init()
+  init() override
   {
-    setParameter();
-
     auto operatorID = cdo_operator_id();
-    operfunc = cdo_operator_f1(operatorID);
+    auto operfunc = cdo_operator_f1(operatorID);
+
+    stepStat.init(operfunc);
 
-    auto lminmax = (operfunc == FieldFunc_Min || operfunc == FieldFunc_Max);
-    lrange = (operfunc == FieldFunc_Range);
-    lmean = (operfunc == FieldFunc_Mean || operfunc == FieldFunc_Avg);
-    lstd = (operfunc == FieldFunc_Std || operfunc == FieldFunc_Std1);
-    lvarstd = (lstd || operfunc == FieldFunc_Var || operfunc == FieldFunc_Var1);
-    lvars2 = (lvarstd || lrange);
-    divisor = (operfunc == FieldFunc_Std1 || operfunc == FieldFunc_Var1);
+    params = setParameter();
 
     streamID1 = cdo_open_read(0);
 
     vlistID1 = cdo_stream_inq_vlist(streamID1);
     auto vlistID2 = vlistDuplicate(vlistID1);
 
-    if (!lminmax) vlist_unpack(vlistID2);
+    varList1 = VarList(vlistID1);
+
+    if (!stepStat.lminmax) vlist_unpack(vlistID2);
 
     taxisID1 = vlistInqTaxis(vlistID1);
     taxisID2 = taxisDuplicate(taxisID1);
-    if (taxisHasBounds(taxisID2)) taxisDeleteBounds(taxisID2);
+    taxisWithBounds(taxisID2);
+    if (taxisInqType(taxisID2) == TAXIS_FORECAST) taxisDefType(taxisID2, TAXIS_RELATIVE);
     vlistDefTaxis(vlistID2, taxisID2);
 
     streamID2 = cdo_open_write(1);
     cdo_def_vlist(streamID2, vlistID2);
 
-    maxrecs = vlistNrecs(vlistID1);
-    recList = std::vector<RecordInfo>(maxrecs);
-
-    varList_init(varList, vlistID1);
-
-    if ((operfunc == FieldFunc_Min) || (operfunc == FieldFunc_Max)) VARS_MEMTYPE = FIELD_NAT;
+    maxRecords = varList1.numRecords();
+    recordList = std::vector<RecordInfo>(maxRecords);
   }
 
   void
-  run()
+  run() override
   {
-    auto field2_stdvar_func = lstd ? field2_std : field2_var;
-    auto fieldc_stdvar_func = lstd ? fieldc_std : fieldc_var;
+    constexpr auto timestatDate{ TimeStat::LAST };
+    constexpr int MaxDays = 373;
+    constexpr int MaxSteps = MaxDays;
+    std::vector<DateTimeList> dtLists(MaxSteps);
+    std::vector<int> rangeNumSets(MaxSteps, 0);
+    Field field;
+
+    stepStat.set_dimlen0(MaxSteps);
+    int VARS_MEMTYPE = stepStat.lminmax ? FIELD_NAT : 0;
+
+    auto calendar = taxisInqCalendar(taxisID1);
+    for (int stepIndex = 0; stepIndex < MaxSteps; ++stepIndex)
+      {
+        dtLists[stepIndex].set_stat(timestatDate);
+        dtLists[stepIndex].set_calendar(calendar);
+      }
+
+    auto numSteps = varList1.numSteps();
+    cdo::Progress progress;
 
     int tsID = 0;
     int otsID = 0;
@@ -167,150 +172,76 @@ public:
         auto nrecs = cdo_stream_inq_timestep(streamID1, tsID);
         if (nrecs == 0) break;
 
-        auto vDateTime = taxisInqVdatetime(taxisID1);
+        if (numSteps > 1) progress.update((tsID + 1.0) / numSteps);
 
+        auto vDateTime = taxisInqVdatetime(taxisID1);
         if (Options::cdoVerbose) cdo_print("process timestep: %d %s", tsID + 1, datetime_to_string(vDateTime));
 
-        auto dayOfYear = decode_day_of_year(vDateTime.date);
-        if (dayOfYear < 0 || dayOfYear >= MaxDays)
-          cdo_abort("Day of year %d out of range (%s)!", dayOfYear, datetime_to_string(vDateTime));
+        auto stepIndex = decode_day_of_year(vDateTime.date);
+        if (stepIndex < 0 || stepIndex >= MaxSteps)
+          cdo_abort("Day of year %d out of range (%s)!", stepIndex, datetime_to_string(vDateTime));
 
-        vDateTimes[dayOfYear] = vDateTime;
+        dtLists[stepIndex].taxis_set_next_timestep(taxisID1);
 
-        if (!vars1[dayOfYear].size())
-          {
-            fields_from_vlist(vlistID1, samp1[dayOfYear]);
-            fields_from_vlist(vlistID1, vars1[dayOfYear], FIELD_VEC | VARS_MEMTYPE);
-            if (lvars2) fields_from_vlist(vlistID1, vars2[dayOfYear], FIELD_VEC);
-          }
+        if (!stepStat.var1(stepIndex).size()) { stepStat.alloc(stepIndex, varList1, VARS_MEMTYPE); }
 
+        auto numSets = rangeNumSets[stepIndex];
         for (int recID = 0; recID < nrecs; ++recID)
           {
-            int varID, levelID;
-            cdo_inq_record(streamID1, &varID, &levelID);
-            const auto &var = varList[varID];
-
-            if (tsID == 0) recList[recID].set(varID, levelID);
-
-            auto &rsamp1 = samp1[dayOfYear][varID][levelID];
-            auto &rvars1 = vars1[dayOfYear][varID][levelID];
-
-            auto numSets = dayOfYear_numSets[dayOfYear];
-
-            if (numSets == 0)
-              {
-                cdo_read_record(streamID1, rvars1);
-                if (lrange)
-                  {
-                    vars2[dayOfYear][varID][levelID].numMissVals = rvars1.numMissVals;
-                    vars2[dayOfYear][varID][levelID].vec_d = rvars1.vec_d;
-                  }
-
-                if (rvars1.numMissVals || !rsamp1.empty())
-                  {
-                    if (rsamp1.empty()) rsamp1.resize(rvars1.size);
-                    field2_vinit(rsamp1, rvars1);
-                  }
-              }
-            else
-              {
-                field.init(var);
-                cdo_read_record(streamID1, field);
-
-                if (field.numMissVals || !rsamp1.empty())
-                  {
-                    if (rsamp1.empty()) rsamp1.resize(rvars1.size, numSets);
-                    field2_vincr(rsamp1, field);
-                  }
-
-                // clang-format off
-                if      (lvarstd) field2_sumsumq(rvars1, vars2[dayOfYear][varID][levelID], field);
-                else if (lrange)  field2_maxmin(rvars1, vars2[dayOfYear][varID][levelID], field);
-                else              field2_function(rvars1, field, operfunc);
-                // clang-format on
-              }
+            auto [varID, levelID] = cdo_inq_record(streamID1);
+            if (tsID == 0) recordList[recID].set(varID, levelID);
+            field.init(varList1.vars[varID]);
+            cdo_read_record(streamID1, field);
+            stepStat.add_field(field, stepIndex, varID, levelID, numSets);
           }
 
-        if (dayOfYear_numSets[dayOfYear] == 0 && lvarstd)
-          for (int recID = 0; recID < maxrecs; ++recID)
-            {
-              auto [varID, levelID] = recList[recID].get();
-              if (varList[varID].isConstant) continue;
-
-              field2_moq(vars2[dayOfYear][varID][levelID], vars1[dayOfYear][varID][levelID]);
-            }
-
-        dayOfYear_numSets[dayOfYear]++;
+        rangeNumSets[stepIndex]++;
         tsID++;
       }
 
     // set the year to the minimum of years found on output timestep
-    if (yearMode)
+    if (params.yearMode)
       {
         int outyear = 1e9;
-        for (int dayOfYear = 0; dayOfYear < MaxDays; dayOfYear++)
-          if (dayOfYear_numSets[dayOfYear])
-            {
-              auto year = vDateTimes[dayOfYear].date.year;
-              if (year < outyear) outyear = year;
-            }
-        for (int dayOfYear = 0; dayOfYear < MaxDays; dayOfYear++)
-          if (dayOfYear_numSets[dayOfYear])
-            {
-              auto year = vDateTimes[dayOfYear].date.year;
-              if (year > outyear) vDateTimes[dayOfYear].date.year = outyear;
-            }
+        for (int stepIndex = 0; stepIndex < MaxSteps; stepIndex++)
+          {
+            if (rangeNumSets[stepIndex])
+              {
+                auto numEntries = dtLists[stepIndex].get_size();
+                const auto &dtInfo = dtLists[stepIndex].get_info();
+                outyear = std::min(outyear, dtInfo[numEntries - 1].v.date.year);
+              }
+          }
+        params.year = outyear;
       }
 
-    for (int dayOfYear = 0; dayOfYear < MaxDays; dayOfYear++)
-      if (dayOfYear_numSets[dayOfYear])
-        {
-          auto numSets = dayOfYear_numSets[dayOfYear];
-          for (int recID = 0; recID < maxrecs; ++recID)
-            {
-              auto [varID, levelID] = recList[recID].get();
-              if (varList[varID].isConstant) continue;
-
-              const auto &rsamp1 = samp1[dayOfYear][varID][levelID];
-              auto &rvars1 = vars1[dayOfYear][varID][levelID];
-
-              if (lmean)
-                {
-                  if (!rsamp1.empty())
-                    field2_div(rvars1, rsamp1);
-                  else
-                    fieldc_div(rvars1, (double) numSets);
-                }
-              else if (lvarstd)
-                {
-                  if (!rsamp1.empty())
-                    field2_stdvar_func(rvars1, vars2[dayOfYear][varID][levelID], rsamp1, divisor);
-                  else
-                    fieldc_stdvar_func(rvars1, vars2[dayOfYear][varID][levelID], numSets, divisor);
-                }
-              else if (lrange) { field2_sub(rvars1, vars2[dayOfYear][varID][levelID]); }
-            }
-
-          taxisDefVdatetime(taxisID2, vDateTimes[dayOfYear]);
-          cdo_def_timestep(streamID2, otsID);
-
-          for (int recID = 0; recID < maxrecs; ++recID)
-            {
-              auto [varID, levelID] = recList[recID].get();
-              if (otsID && varList[varID].isConstant) continue;
-
-              auto &rvars1 = vars1[dayOfYear][varID][levelID];
-
-              cdo_def_record(streamID2, varID, levelID);
-              cdo_write_record(streamID2, rvars1);
-            }
-
-          otsID++;
-        }
+    for (int stepIndex = 0; stepIndex < MaxSteps; stepIndex++)
+      {
+        auto numSets = rangeNumSets[stepIndex];
+        if (numSets)
+          {
+            cdo::records_process_3D(stepIndex, recordList, varList1, stepStat, numSets);
+
+            if (params.year) dtLists[stepIndex].set_year(params.year);
+            dtLists[stepIndex].stat_taxis_def_timestep(taxisID2);
+            cdo_def_timestep(streamID2, otsID);
+
+            for (int recID = 0; recID < maxRecords; ++recID)
+              {
+                auto [varID, levelID] = recordList[recID].get();
+                if (otsID && varList1.vars[varID].isConstant) continue;
+
+                cdo_def_record(streamID2, varID, levelID);
+                cdo_write_record(streamID2, stepStat.var1(stepIndex, varID, levelID));
+              }
+
+            otsID++;
+          }
+      }
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
diff --git a/src/Ydrunpctl.cc b/src/Ydrunpctl.cc
index 036aa3f5a5939a4ef9f93c5c0c1d7fb97bb9d40c..75f01d355be6380816432febeeaf91fc3c1f3920 100644
--- a/src/Ydrunpctl.cc
+++ b/src/Ydrunpctl.cc
@@ -48,43 +48,29 @@ private:
   CdoStreamID streamID3;
   CdoStreamID streamID4;
 
+  int vlistID1;
   int taxisID1;
   int taxisID2;
   int taxisID3;
   int taxisID4;
 
-  int nvars;
-  int ntsteps;
-  int ndates;
+  int numDates;
   int dpy;
   double pn;
-  int maxrecs;
 
   std::string percMethod, readMethod;
 
-  CdiDateTime vDateTimes1[MaxDays]{};
-  CdiDateTime vDateTimes2[MaxDays]{};
-  HistogramSet hsets[MaxDays];
-  int numSets[MaxDays] = { 0 };
-
-  std::vector<bool> vars2;
-
-  Field field1, field2;
+  FieldVector3D varsData1;
+  std::vector<CdiDateTime> cdiDateTimes;
 
-  FieldVector constFields;
   VarList varList1;
-  FieldVector3D vars1;
-  std::vector<CdiDateTime> cdiDateTimes;
-  std::vector<RecordInfo> recList;
 
 public:
   void
-  init()
+  init() override
   {
-    vars2 = std::vector<bool>(MaxDays, false);
-
     pn = parameter_to_double(cdo_operator_argv(0));
-    ndates = parameter_to_int(cdo_operator_argv(1));
+    numDates = parameter_to_int(cdo_operator_argv(1));
 
     if (cdo_operator_argc() > 2)
       {
@@ -104,15 +90,19 @@ public:
     streamID2 = cdo_open_read(1);
     streamID3 = cdo_open_read(2);
 
-    auto vlistID1 = cdo_stream_inq_vlist(streamID1);
+    vlistID1 = cdo_stream_inq_vlist(streamID1);
     auto vlistID2 = cdo_stream_inq_vlist(streamID2);
     auto vlistID3 = cdo_stream_inq_vlist(streamID3);
     auto vlistID4 = vlistDuplicate(vlistID1);
 
     vlist_unpack(vlistID4);
 
-    vlist_compare(vlistID1, vlistID2, CmpVlist::All);
-    vlist_compare(vlistID1, vlistID3, CmpVlist::All);
+    varList1 = VarList(vlistID1);
+    VarList varList2(vlistID2);
+    VarList varList3(vlistID3);
+
+    varList_compare(varList1, varList2);
+    varList_compare(varList1, varList3);
 
     taxisID1 = vlistInqTaxis(vlistID1);
     taxisID2 = vlistInqTaxis(vlistID2);
@@ -128,28 +118,31 @@ public:
     streamID4 = cdo_open_write(3);
     cdo_def_vlist(streamID4, vlistID4);
 
-    ntsteps = vlistNtsteps(vlistID1);
-    nvars = vlistNvars(vlistID1);
-
-    maxrecs = vlistNrecs(vlistID1);
-    recList = std::vector<RecordInfo>(maxrecs);
-
-    constFields = FieldVector(maxrecs);
+    cdiDateTimes = std::vector<CdiDateTime>(numDates + 1);
 
-    varList_init(varList1, vlistID1);
-
-    cdiDateTimes = std::vector<CdiDateTime>(ndates + 1);
-
-    vars1 = FieldVector3D(ndates + 1);
-    for (int its = 0; its < ndates; its++) fields_from_vlist(vlistID1, vars1[its], FIELD_VEC | FIELD_NAT);
+    varsData1 = FieldVector3D(numDates + 1);
+    for (int its = 0; its < numDates; its++) field2D_init(varsData1[its], varList1, FIELD_VEC | FIELD_NAT);
   }
 
   void
-  run()
+  run() override
   {
+    Field field1, field2;
+    std::vector<bool> vars2(MaxDays, false);
+    CdiDateTime vDateTimes1[MaxDays]{};
+    CdiDateTime vDateTimes2[MaxDays]{};
+    HistogramSet hsets[MaxDays];
+    int numSets[MaxDays] = { 0 };
+
+    auto numVars = varList1.numVars();
+    auto numSteps = varList1.numSteps();
+    auto maxRecords = varList1.numRecords();
+    std::vector<RecordInfo> recordList(maxRecords);
+
+    FieldVector constFields(maxRecords);
+
     int startYear = 0;
     int tsID = 0;
-
     while (true)
       {
         auto nrecs = cdo_stream_inq_timestep(streamID2, tsID);
@@ -175,24 +168,19 @@ public:
         if (!vars2[dayOfYear])
           {
             vars2[dayOfYear] = true;
-            hsets[dayOfYear].create(nvars, ntsteps);
+            hsets[dayOfYear].create(numVars, numSteps);
 
-            for (int varID = 0; varID < nvars; ++varID)
-              {
-                const auto &var = varList1[varID];
-                hsets[dayOfYear].createVarLevels(varID, var.nlevels, var.gridsize);
-              }
+            for (const auto &var : varList1.vars) hsets[dayOfYear].createVarLevels(var.ID, var.nlevels, var.gridsize);
           }
 
         for (int recID = 0; recID < nrecs; ++recID)
           {
-            int varID, levelID;
-            cdo_inq_record(streamID2, &varID, &levelID);
-            const auto &var = varList1[varID];
+            auto [varID, levelID] = cdo_inq_record(streamID2);
+            const auto &var = varList1.vars[varID];
             field1.init(var);
             cdo_read_record(streamID2, field1);
 
-            cdo_inq_record(streamID3, &varID, &levelID);
+            (void) cdo_inq_record(streamID3);
             field2.init(var);
             cdo_read_record(streamID3, field2);
 
@@ -202,35 +190,34 @@ public:
         tsID++;
       }
 
-    for (tsID = 0; tsID < ndates; ++tsID)
+    for (tsID = 0; tsID < numDates; ++tsID)
       {
         auto nrecs = cdo_stream_inq_timestep(streamID1, tsID);
-        if (nrecs == 0) cdo_abort("File has less then %d timesteps!", ndates);
+        if (nrecs == 0) cdo_abort("File has less then %d timesteps!", numDates);
 
         cdiDateTimes[tsID] = taxisInqVdatetime(taxisID1);
 
         for (int recID = 0; recID < nrecs; ++recID)
           {
-            int varID, levelID;
-            cdo_inq_record(streamID1, &varID, &levelID);
-            const auto &var = varList1[varID];
+            auto [varID, levelID] = cdo_inq_record(streamID1);
+            const auto &var = varList1.vars[varID];
 
-            if (tsID == 0) recList[recID].set(varID, levelID);
+            if (tsID == 0) recordList[recID].set(varID, levelID);
 
             if (tsID == 0 && var.isConstant)
               {
                 constFields[recID].init(var);
                 cdo_read_record(streamID1, constFields[recID]);
               }
-            else { cdo_read_record(streamID1, vars1[tsID][varID][levelID]); }
+            else { cdo_read_record(streamID1, varsData1[tsID][varID][levelID]); }
           }
       }
 
     while (true)
       {
-        cdiDateTimes[ndates] = datetime_avg(dpy, ndates, cdiDateTimes);
+        cdiDateTimes[numDates] = datetime_avg(dpy, numDates, cdiDateTimes);
 
-        auto vDateTime = cdiDateTimes[ndates];
+        auto vDateTime = cdiDateTimes[numDates];
 
         auto dayOfYear = decode_day_of_year(vDateTime.date);
         if (dayOfYear < 0 || dayOfYear >= MaxDays) cdo_abort("Day %d out of range!", dayOfYear);
@@ -240,96 +227,96 @@ public:
         if (!vars2[dayOfYear])
           cdo_abort("No data for day %d in %s and %s", dayOfYear, cdo_get_stream_name(1), cdo_get_stream_name(2));
 
-        for (int varID = 0; varID < nvars; ++varID)
+        for (int varID = 0; varID < numVars; ++varID)
           {
-            const auto &var = varList1[varID];
+            const auto &var = varList1.vars[varID];
             if (var.isConstant) continue;
 
             for (int levelID = 0; levelID < var.nlevels; ++levelID)
-              for (int inp = 0; inp < ndates; ++inp) hsets[dayOfYear].addVarLevelValues(varID, levelID, vars1[inp][varID][levelID]);
+              for (int inp = 0; inp < numDates; ++inp)
+                hsets[dayOfYear].addVarLevelValues(varID, levelID, varsData1[inp][varID][levelID]);
           }
 
-        cdiDateTimes[ndates] = cdiDateTimes[0];
-        vars1[ndates] = vars1[0];
+        cdiDateTimes[numDates] = cdiDateTimes[0];
+        varsData1[numDates] = varsData1[0];
 
-        for (int inp = 0; inp < ndates; ++inp)
+        for (int inp = 0; inp < numDates; ++inp)
           {
             cdiDateTimes[inp] = cdiDateTimes[inp + 1];
-            vars1[inp] = vars1[inp + 1];
+            varsData1[inp] = varsData1[inp + 1];
           }
 
         auto nrecs = cdo_stream_inq_timestep(streamID1, tsID);
         if (nrecs == 0) break;
 
-        cdiDateTimes[ndates - 1] = taxisInqVdatetime(taxisID1);
+        cdiDateTimes[numDates - 1] = taxisInqVdatetime(taxisID1);
 
         for (int recID = 0; recID < nrecs; ++recID)
           {
-            int varID, levelID;
-            cdo_inq_record(streamID1, &varID, &levelID);
-            cdo_read_record(streamID1, vars1[ndates - 1][varID][levelID]);
+            auto [varID, levelID] = cdo_inq_record(streamID1);
+            cdo_read_record(streamID1, varsData1[numDates - 1][varID][levelID]);
           }
 
-        numSets[dayOfYear] += ndates;
+        numSets[dayOfYear] += numDates;
         tsID++;
       }
 
     if (readMethod == "c" && cdo_assert_files_only())
       {
-        auto endYear = cdiDateTimes[ndates - 1].date.year;
+        auto endYear = cdiDateTimes[numDates - 1].date.year;
         auto cdiStream = streamOpenRead(cdo_get_stream_name(0));
         auto cdiVlistID = streamInqVlist(cdiStream);
         auto cdiTaxisID = vlistInqTaxis(cdiVlistID);
         int missTimes = 0;
-        for (missTimes = 0; missTimes < ndates - 1; missTimes++)
+        for (missTimes = 0; missTimes < numDates - 1; missTimes++)
           {
             auto nrecs = streamInqTimestep(cdiStream, missTimes);
             if (nrecs == 0) break;
 
-            cdiDateTimes[ndates - 1] = taxisInqVdatetime(cdiTaxisID);
-            cdiDateTimes[ndates - 1].date.year = endYear + 1;
+            cdiDateTimes[numDates - 1] = taxisInqVdatetime(cdiTaxisID);
+            cdiDateTimes[numDates - 1].date.year = endYear + 1;
 
             for (int recID = 0; recID < nrecs; ++recID)
               {
                 int varID, levelID;
                 streamInqRecord(cdiStream, &varID, &levelID);
-                auto &pvars1 = vars1[ndates - 1][varID][levelID];
+                auto &pvars1 = varsData1[numDates - 1][varID][levelID];
                 if (pvars1.memType == MemType::Float)
                   streamReadRecordF(cdiStream, pvars1.vec_f.data(), &pvars1.numMissVals);
                 else
                   streamReadRecord(cdiStream, pvars1.vec_d.data(), &pvars1.numMissVals);
               }
 
-            cdiDateTimes[ndates] = datetime_avg(dpy, ndates, cdiDateTimes);
-            auto vDateTime = cdiDateTimes[ndates];
+            cdiDateTimes[numDates] = datetime_avg(dpy, numDates, cdiDateTimes);
+            auto vDateTime = cdiDateTimes[numDates];
             if (vDateTime.date.year > endYear) vDateTime.date.year = startYear;
 
             auto dayOfYear = decode_day_of_year(vDateTime.date);
             if (dayOfYear < 0 || dayOfYear >= MaxDays) cdo_abort("Day %d out of range!", dayOfYear);
 
-            numSets[dayOfYear] += ndates;
+            numSets[dayOfYear] += numDates;
 
-            for (int varID = 0; varID < nvars; ++varID)
+            for (int varID = 0; varID < numVars; ++varID)
               {
-                const auto &var = varList1[varID];
+                const auto &var = varList1.vars[varID];
                 if (var.isConstant) continue;
 
                 for (int levelID = 0; levelID < var.nlevels; ++levelID)
-                  for (int inp = 0; inp < ndates; ++inp)
-                    hsets[dayOfYear].addVarLevelValues(varID, levelID, vars1[inp][varID][levelID]);
+                  for (int inp = 0; inp < numDates; ++inp)
+                    hsets[dayOfYear].addVarLevelValues(varID, levelID, varsData1[inp][varID][levelID]);
               }
 
-            cdiDateTimes[ndates] = cdiDateTimes[0];
-            vars1[ndates] = vars1[0];
+            cdiDateTimes[numDates] = cdiDateTimes[0];
+            varsData1[numDates] = varsData1[0];
 
-            for (int inp = 0; inp < ndates; ++inp)
+            for (int inp = 0; inp < numDates; ++inp)
               {
                 cdiDateTimes[inp] = cdiDateTimes[inp + 1];
-                vars1[inp] = vars1[inp + 1];
+                varsData1[inp] = varsData1[inp + 1];
               }
           }
 
-        if (missTimes != ndates - 1) cdo_abort("Addding the missing values when using the 'readMethod' method was not possible");
+        if (missTimes != numDates - 1) cdo_abort("Addding the missing values when using the 'readMethod' method was not possible");
 
         streamClose(cdiStream);
       }
@@ -365,17 +352,18 @@ public:
           taxisDefVdatetime(taxisID4, vDateTimes1[dayOfYear]);
           cdo_def_timestep(streamID4, otsID);
 
-          for (int recID = 0; recID < maxrecs; ++recID)
+          for (int recID = 0; recID < maxRecords; ++recID)
             {
-              auto [varID, levelID] = recList[recID].get();
-              if (otsID && varList1[varID].isConstant) continue;
+              auto [varID, levelID] = recordList[recID].get();
+              const auto &var = varList1.vars[varID];
+              if (otsID && var.isConstant) continue;
 
               cdo_def_record(streamID4, varID, levelID);
 
-              if (varList1[varID].isConstant) { cdo_write_record(streamID4, constFields[recID]); }
+              if (var.isConstant) { cdo_write_record(streamID4, constFields[recID]); }
               else
                 {
-                  field1.init(varList1[varID]);
+                  field1.init(var);
                   hsets[dayOfYear].getVarLevelPercentiles(field1, varID, levelID, pn);
                   cdo_write_record(streamID4, field1);
                 }
@@ -386,7 +374,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID4);
     cdo_stream_close(streamID3);
diff --git a/src/Ydrunstat.cc b/src/Ydrunstat.cc
index 8f4fabc0c0c24c250598452dadcd64b47ea165a5..43afdaa0d0ca60eb7f4dcd89ec536986bbbb551f 100644
--- a/src/Ydrunstat.cc
+++ b/src/Ydrunstat.cc
@@ -34,13 +34,14 @@ constexpr int MaxDays = 373;
 
 struct YdayStats
 {
-  CdiDateTime vDateTime[MaxDays]{};
   int numSets[MaxDays]{};
-  FieldVector2D vars1[MaxDays];
-  FieldVector2D vars2[MaxDays];
-  int vlist;
+  CdiDateTime vDateTime[MaxDays]{};
+  FieldVector2D varsData1[MaxDays];
+  FieldVector2D varsData2[MaxDays];
+  int vlistID;
+  VarList varList;
 
-  explicit YdayStats(int vlistID) : vlist(vlistID) {}
+  explicit YdayStats(int _vlistID) : vlistID(_vlistID), varList(VarList(_vlistID)) {}
 };
 
 static void
@@ -54,33 +55,33 @@ ydstat_update(YdayStats &stats, CdiDateTime vDateTime, const FieldVector2D &vars
 
   stats.vDateTime[dayOfYear] = vDateTime;
 
-  if (!stats.vars1[dayOfYear].size())
+  if (!stats.varsData1[dayOfYear].size())
     {
-      fields_from_vlist(stats.vlist, stats.vars1[dayOfYear], FIELD_VEC);
-      if (lvarstd) fields_from_vlist(stats.vlist, stats.vars2[dayOfYear], FIELD_VEC);
+      field2D_init(stats.varsData1[dayOfYear], stats.varList, FIELD_VEC);
+      if (lvarstd) field2D_init(stats.varsData2[dayOfYear], stats.varList, FIELD_VEC);
     }
 
-  auto nvars = vlistNvars(stats.vlist);
-  for (int varID = 0; varID < nvars; ++varID)
+  auto numVars = stats.varList.numVars();
+  for (int varID = 0; varID < numVars; ++varID)
     {
-      if (vlistInqVarTimetype(stats.vlist, varID) == TIME_CONSTANT) continue;
+      const auto &var = stats.varList.vars[varID];
+      if (var.timeType == TIME_CONSTANT) continue;
 
-      auto nlevels = zaxisInqSize(vlistInqVarZaxis(stats.vlist, varID));
-      for (int levelID = 0; levelID < nlevels; ++levelID)
+      for (int levelID = 0; levelID < var.nlevels; ++levelID)
         {
           if (stats.numSets[dayOfYear] == 0)
             {
-              field_copy(vars1[varID][levelID], stats.vars1[dayOfYear][varID][levelID]);
-              if (lvarstd) field_copy(vars2[varID][levelID], stats.vars2[dayOfYear][varID][levelID]);
+              field_copy(vars1[varID][levelID], stats.varsData1[dayOfYear][varID][levelID]);
+              if (lvarstd) field_copy(vars2[varID][levelID], stats.varsData2[dayOfYear][varID][levelID]);
             }
           else
             {
               if (lvarstd)
                 {
-                  field2_sum(stats.vars1[dayOfYear][varID][levelID], vars1[varID][levelID]);
-                  field2_sum(stats.vars2[dayOfYear][varID][levelID], vars2[varID][levelID]);
+                  field2_sum(stats.varsData1[dayOfYear][varID][levelID], vars1[varID][levelID]);
+                  field2_sum(stats.varsData2[dayOfYear][varID][levelID], vars2[varID][levelID]);
                 }
-              else { field2_function(stats.vars1[dayOfYear][varID][levelID], vars1[varID][levelID], operfunc); }
+              else { field2_function(stats.varsData1[dayOfYear][varID][levelID], vars1[varID][levelID], operfunc); }
             }
         }
     }
@@ -101,21 +102,21 @@ ydstat_finalize(YdayStats &stats, int operfunc)
   for (int dayOfYear = 0; dayOfYear < MaxDays; dayOfYear++)
     if (stats.numSets[dayOfYear])
       {
-        auto nvars = vlistNvars(stats.vlist);
-        for (int varID = 0; varID < nvars; ++varID)
+        auto numVars = stats.varList.numVars();
+        for (int varID = 0; varID < numVars; ++varID)
           {
-            if (vlistInqVarTimetype(stats.vlist, varID) == TIME_CONSTANT) continue;
+            const auto &var = stats.varList.vars[varID];
+            if (var.timeType == TIME_CONSTANT) continue;
 
-            auto nlevels = zaxisInqSize(vlistInqVarZaxis(stats.vlist, varID));
-            for (int levelID = 0; levelID < nlevels; ++levelID)
+            for (int levelID = 0; levelID < var.nlevels; ++levelID)
               {
                 auto numSets = stats.numSets[dayOfYear];
-                auto &rvars1 = stats.vars1[dayOfYear][varID][levelID];
+                auto &rvars1 = stats.varsData1[dayOfYear][varID][levelID];
 
                 if (lmean) { fieldc_div(rvars1, (double) numSets); }
                 else if (lvarstd)
                   {
-                    const auto &rvars2 = stats.vars2[dayOfYear][varID][levelID];
+                    const auto &rvars2 = stats.varsData2[dayOfYear][varID][levelID];
                     fieldc_stdvar_func(rvars1, rvars2, numSets, divisor);
                   }
               }
@@ -157,28 +158,25 @@ private:
 
   char readMethod;
   bool lvarstd;
-  int maxrecs;
-  int ndates;
+  int numDates;
   int dpy;
 
+  FieldVector3D varsData1;
+  FieldVector3D varsData2;
+
   VarList varList1;
-  FieldVector3D vars1;
-  FieldVector3D vars2;
-  std::vector<RecordInfo> recList;
-  std::vector<CdiDateTime> cdiDateTimes;
 
 public:
   void
-  init()
+  init() override
   {
-
     auto operatorID = cdo_operator_id();
     operfunc = cdo_operator_f1(operatorID);
 
     operator_input_arg("number of timesteps");
-    auto nparams = cdo_operator_argc();
-    ndates = parameter_to_int(cdo_operator_argv(0));
-    readMethod = (nparams == 2) ? cdo_operator_argv(1)[0] : '0';
+    auto numParams = cdo_operator_argc();
+    numDates = parameter_to_int(cdo_operator_argv(0));
+    readMethod = (numParams == 2) ? cdo_operator_argv(1)[0] : '0';
 
     auto lminmax = (operfunc == FieldFunc_Min || operfunc == FieldFunc_Max);
     lvarstd = (operfunc == FieldFunc_Std || operfunc == FieldFunc_Var || operfunc == FieldFunc_Std1 || operfunc == FieldFunc_Var1);
@@ -199,34 +197,33 @@ public:
     streamID2 = cdo_open_write(1);
     cdo_def_vlist(streamID2, vlistID2);
 
-    maxrecs = vlistNrecs(vlistID1);
-    recList = std::vector<RecordInfo>(maxrecs);
-
-    varList_init(varList1, vlistID1);
+    varsData1 = FieldVector3D(numDates + 1);
+    varsData2 = FieldVector3D(numDates + 1);
 
-    cdiDateTimes = std::vector<CdiDateTime>(ndates + 1);
-
-    vars1 = FieldVector3D(ndates + 1);
-    vars2 = FieldVector3D(ndates + 1);
-
-    for (int its = 0; its < ndates; its++)
+    varList1 = VarList(vlistID1);
+    for (int its = 0; its < numDates; its++)
       {
-        fields_from_vlist(vlistID1, vars1[its], FIELD_VEC);
-        if (lvarstd) fields_from_vlist(vlistID1, vars2[its], FIELD_VEC);
+        field2D_init(varsData1[its], varList1, FIELD_VEC);
+        if (lvarstd) field2D_init(varsData2[its], varList1, FIELD_VEC);
       }
   }
 
   void
-  run()
+  run() override
   {
     YdayStats stats = YdayStats(vlistID1);
+    std::vector<CdiDateTime> cdiDateTimes(numDates + 1);
+
+    auto maxRecords = varList1.numRecords();
+    std::vector<RecordInfo> recordList(maxRecords);
+
     int startYear = 0;
     int tsID = 0;
 
-    for (tsID = 0; tsID < ndates; ++tsID)
+    for (tsID = 0; tsID < numDates; ++tsID)
       {
         auto nrecs = cdo_stream_inq_timestep(streamID1, tsID);
-        if (nrecs == 0) cdo_abort("File has less then %d timesteps!", ndates);
+        if (nrecs == 0) cdo_abort("File has less then %d timesteps!", numDates);
 
         cdiDateTimes[tsID] = taxisInqVdatetime(taxisID1);
 
@@ -234,67 +231,66 @@ public:
 
         for (int recID = 0; recID < nrecs; ++recID)
           {
-            int varID, levelID;
-            cdo_inq_record(streamID1, &varID, &levelID);
+            auto [varID, levelID] = cdo_inq_record(streamID1);
 
-            if (tsID == 0) recList[recID].set(varID, levelID);
+            if (tsID == 0) recordList[recID].set(varID, levelID);
 
-            auto &rvars1 = vars1[tsID][varID][levelID];
+            auto &rvars1 = varsData1[tsID][varID][levelID];
 
             cdo_read_record(streamID1, rvars1);
 
             if (lvarstd)
               {
-                field2_moq(vars2[tsID][varID][levelID], rvars1);
-                for (int inp = 0; inp < tsID; ++inp) field2_sumsumq(vars1[inp][varID][levelID], vars2[inp][varID][levelID], rvars1);
+                field2_moq(varsData2[tsID][varID][levelID], rvars1);
+                for (int inp = 0; inp < tsID; ++inp)
+                  field2_sumsumq(varsData1[inp][varID][levelID], varsData2[inp][varID][levelID], rvars1);
               }
             else
               {
-                for (int inp = 0; inp < tsID; ++inp) field2_function(vars1[inp][varID][levelID], rvars1, operfunc);
+                for (int inp = 0; inp < tsID; ++inp) field2_function(varsData1[inp][varID][levelID], rvars1, operfunc);
               }
           }
       }
 
     while (true)
       {
-        cdiDateTimes[ndates] = datetime_avg(dpy, ndates, cdiDateTimes);
+        cdiDateTimes[numDates] = datetime_avg(dpy, numDates, cdiDateTimes);
 
-        ydstat_update(stats, cdiDateTimes[ndates], vars1[0], vars2[0], ndates, operfunc);
+        ydstat_update(stats, cdiDateTimes[numDates], varsData1[0], varsData2[0], numDates, operfunc);
 
-        cdiDateTimes[ndates] = cdiDateTimes[0];
-        vars1[ndates] = vars1[0];
-        if (lvarstd) vars2[ndates] = vars2[0];
+        cdiDateTimes[numDates] = cdiDateTimes[0];
+        varsData1[numDates] = varsData1[0];
+        if (lvarstd) varsData2[numDates] = varsData2[0];
 
-        for (int inp = 0; inp < ndates; ++inp)
+        for (int inp = 0; inp < numDates; ++inp)
           {
             cdiDateTimes[inp] = cdiDateTimes[inp + 1];
-            vars1[inp] = vars1[inp + 1];
-            if (lvarstd) vars2[inp] = vars2[inp + 1];
+            varsData1[inp] = varsData1[inp + 1];
+            if (lvarstd) varsData2[inp] = varsData2[inp + 1];
           }
 
         auto nrecs = cdo_stream_inq_timestep(streamID1, tsID);
         if (nrecs == 0) break;
 
-        cdiDateTimes[ndates - 1] = taxisInqVdatetime(taxisID1);
+        cdiDateTimes[numDates - 1] = taxisInqVdatetime(taxisID1);
 
         for (int recID = 0; recID < nrecs; ++recID)
           {
-            int varID, levelID;
-            cdo_inq_record(streamID1, &varID, &levelID);
+            auto [varID, levelID] = cdo_inq_record(streamID1);
 
-            auto &rvars1 = vars1[ndates - 1][varID][levelID];
+            auto &rvars1 = varsData1[numDates - 1][varID][levelID];
 
             cdo_read_record(streamID1, rvars1);
 
             if (lvarstd)
               {
-                field2_moq(vars2[ndates - 1][varID][levelID], rvars1);
-                for (int inp = 0; inp < ndates - 1; ++inp)
-                  field2_sumsumq(vars1[inp][varID][levelID], vars2[inp][varID][levelID], rvars1);
+                field2_moq(varsData2[numDates - 1][varID][levelID], rvars1);
+                for (int inp = 0; inp < numDates - 1; ++inp)
+                  field2_sumsumq(varsData1[inp][varID][levelID], varsData2[inp][varID][levelID], rvars1);
               }
             else
               {
-                for (int inp = 0; inp < ndates - 1; ++inp) field2_function(vars1[inp][varID][levelID], rvars1, operfunc);
+                for (int inp = 0; inp < numDates - 1; ++inp) field2_function(varsData1[inp][varID][levelID], rvars1, operfunc);
               }
           }
 
@@ -303,59 +299,59 @@ public:
 
     if (readMethod == 'c' && cdo_assert_files_only())
       {
-        auto endYear = cdiDateTimes[ndates - 1].date.year;
+        auto endYear = cdiDateTimes[numDates - 1].date.year;
         auto cdiStream = streamOpenRead(cdo_get_stream_name(0));
         auto cdiVlistID = streamInqVlist(cdiStream);
         auto cdiTaxisID = vlistInqTaxis(cdiVlistID);
         int missTimes = 0;
-        for (missTimes = 0; missTimes < ndates - 1; missTimes++)
+        for (missTimes = 0; missTimes < numDates - 1; missTimes++)
           {
             auto nrecs = streamInqTimestep(cdiStream, missTimes);
             if (nrecs == 0) break;
 
-            cdiDateTimes[ndates - 1] = taxisInqVdatetime(cdiTaxisID);
-            cdiDateTimes[ndates - 1].date.year = endYear + 1;
+            cdiDateTimes[numDates - 1] = taxisInqVdatetime(cdiTaxisID);
+            cdiDateTimes[numDates - 1].date.year = endYear + 1;
 
             for (int recID = 0; recID < nrecs; ++recID)
               {
                 int varID, levelID;
                 streamInqRecord(cdiStream, &varID, &levelID);
 
-                auto &rvars1 = vars1[ndates - 1][varID][levelID];
+                auto &rvars1 = varsData1[numDates - 1][varID][levelID];
 
                 streamReadRecord(cdiStream, rvars1.vec_d.data(), &rvars1.numMissVals);
 
                 if (lvarstd)
                   {
-                    field2_moq(vars2[ndates - 1][varID][levelID], rvars1);
-                    for (int inp = 0; inp < ndates - 1; ++inp)
-                      field2_sumsumq(vars1[inp][varID][levelID], vars2[inp][varID][levelID], rvars1);
+                    field2_moq(varsData2[numDates - 1][varID][levelID], rvars1);
+                    for (int inp = 0; inp < numDates - 1; ++inp)
+                      field2_sumsumq(varsData1[inp][varID][levelID], varsData2[inp][varID][levelID], rvars1);
                   }
                 else
                   {
-                    for (int inp = 0; inp < ndates - 1; ++inp) field2_function(vars1[inp][varID][levelID], rvars1, operfunc);
+                    for (int inp = 0; inp < numDates - 1; ++inp) field2_function(varsData1[inp][varID][levelID], rvars1, operfunc);
                   }
               }
 
-            cdiDateTimes[ndates] = datetime_avg(dpy, ndates, cdiDateTimes);
-            auto vDateTime = cdiDateTimes[ndates];
+            cdiDateTimes[numDates] = datetime_avg(dpy, numDates, cdiDateTimes);
+            auto vDateTime = cdiDateTimes[numDates];
             if (vDateTime.date.year > endYear) vDateTime.date.year = startYear;
 
-            ydstat_update(stats, vDateTime, vars1[0], vars2[0], ndates, operfunc);
+            ydstat_update(stats, vDateTime, varsData1[0], varsData2[0], numDates, operfunc);
 
-            cdiDateTimes[ndates] = cdiDateTimes[0];
-            vars1[ndates] = vars1[0];
-            if (lvarstd) vars2[ndates] = vars2[0];
+            cdiDateTimes[numDates] = cdiDateTimes[0];
+            varsData1[numDates] = varsData1[0];
+            if (lvarstd) varsData2[numDates] = varsData2[0];
 
-            for (int inp = 0; inp < ndates; ++inp)
+            for (int inp = 0; inp < numDates; ++inp)
               {
                 cdiDateTimes[inp] = cdiDateTimes[inp + 1];
-                vars1[inp] = vars1[inp + 1];
-                if (lvarstd) vars2[inp] = vars2[inp + 1];
+                varsData1[inp] = varsData1[inp + 1];
+                if (lvarstd) varsData2[inp] = varsData2[inp + 1];
               }
           }
 
-        if (missTimes != ndates - 1) cdo_abort("Addding the missing values when using the 'readMethod' method was not possible");
+        if (missTimes != numDates - 1) cdo_abort("Addding the missing values when using the 'readMethod' method was not possible");
 
         streamClose(cdiStream);
       }
@@ -372,12 +368,12 @@ public:
           taxisDefVdatetime(taxisID2, stats.vDateTime[dayOfYear]);
           cdo_def_timestep(streamID2, otsID);
 
-          for (int recID = 0; recID < maxrecs; ++recID)
+          for (int recID = 0; recID < maxRecords; ++recID)
             {
-              auto [varID, levelID] = recList[recID].get();
-              if (otsID && varList1[varID].isConstant) continue;
+              auto [varID, levelID] = recordList[recID].get();
+              if (otsID && varList1.vars[varID].isConstant) continue;
 
-              auto &rvars1 = stats.vars1[dayOfYear][varID][levelID];
+              auto &rvars1 = stats.varsData1[dayOfYear][varID][levelID];
 
               cdo_def_record(streamID2, varID, levelID);
               cdo_write_record(streamID2, rvars1);
@@ -388,7 +384,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
diff --git a/src/Yeararith.cc b/src/Yeararith.cc
index c6901503010d83f875e3259019fc82a890121b55..e47afafad2310d1b62985ee55ebb9c2576267133 100644
--- a/src/Yeararith.cc
+++ b/src/Yeararith.cc
@@ -19,7 +19,6 @@
 #include <climits>
 
 #include "cdo_vlist.h"
-#include "cdo_season.h"
 #include "process_int.h"
 #include "field_functions.h"
 
@@ -39,6 +38,7 @@ public:
     .constraints = { 2, 1, NoRestriction },
   };
   inline static RegisterEntry<Yeararith> registration = RegisterEntry<Yeararith>(module);
+
   CdoStreamID streamID1;
   CdoStreamID streamID2;
   CdoStreamID streamID3;
@@ -47,17 +47,16 @@ public:
   int taxisID2;
   int taxisID3;
 
-  FieldVector2D vars2;
-  Field field;
+  FieldVector2D varsData2;
 
   int operfunc;
   VarList varList1;
+  VarList varList2;
 
 public:
   void
-  init()
+  init() override
   {
-
     auto operatorID = cdo_operator_id();
     operfunc = cdo_operator_f1(operatorID);
 
@@ -72,9 +71,9 @@ public:
 
     vlist_unpack(vlistID3);
 
-    vlist_compare(vlistID1, vlistID2, CmpVlist::All);
-
-    varList_init(varList1, vlistID1);
+    varList1 = VarList(vlistID1);
+    varList2 = VarList(vlistID2);
+    varList_compare(varList1, varList2);
 
     taxisID1 = vlistInqTaxis(vlistID1);
     taxisID2 = vlistInqTaxis(vlistID2);
@@ -84,12 +83,14 @@ public:
     streamID3 = cdo_open_write(2);
     cdo_def_vlist(streamID3, vlistID3);
 
-    fields_from_vlist(vlistID2, vars2, FIELD_VEC | FIELD_NAT);
+    field2D_init(varsData2, varList2, FIELD_VEC | FIELD_NAT);
   }
 
   void
-  run()
+  run() override
   {
+    Field field;
+
     int year0 = -INT_MAX + 1;
     int year2last = 0;
     int tsID2 = 0;
@@ -116,9 +117,8 @@ public:
                     year0 = year;
                     for (int recID = 0; recID < nrecs2; ++recID)
                       {
-                        int varID, levelID;
-                        cdo_inq_record(streamID2, &varID, &levelID);
-                        cdo_read_record(streamID2, vars2[varID][levelID]);
+                        auto [varID, levelID] = cdo_inq_record(streamID2);
+                        cdo_read_record(streamID2, varsData2[varID][levelID]);
                       }
                     break;
                   }
@@ -135,12 +135,11 @@ public:
 
         for (int recID = 0; recID < nrecs; ++recID)
           {
-            int varID, levelID;
-            cdo_inq_record(streamID1, &varID, &levelID);
-            field.init(varList1[varID]);
+            auto [varID, levelID] = cdo_inq_record(streamID1);
+            field.init(varList1.vars[varID]);
             cdo_read_record(streamID1, field);
 
-            field2_function(field, vars2[varID][levelID], operfunc);
+            field2_function(field, varsData2[varID][levelID], operfunc);
 
             cdo_def_record(streamID3, varID, levelID);
             cdo_write_record(streamID3, field);
@@ -151,7 +150,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID3);
     cdo_stream_close(streamID2);
diff --git a/src/Yearmonstat.cc b/src/Yearmonstat.cc
index d7205917507bbed4af0faf42d5461e1f4a5b891a..8c4f88f2afae527d5cd9422ff2bc6fbaabc48ae0 100644
--- a/src/Yearmonstat.cc
+++ b/src/Yearmonstat.cc
@@ -16,7 +16,7 @@
 #include "calendar.h"
 
 #include "cdo_options.h"
-#include "cdo_vlist.h"
+#include "cdo_stepstat.h"
 #include "process_int.h"
 #include "datetime.h"
 #include "printinfo.h"
@@ -28,8 +28,7 @@ public:
   using Process::Process;
   inline static CdoModule module = {
     .name = "Yearmonstat",
-    .operators = { { "yearmonmean", FieldFunc_Mean, 0, YearmonstatHelp },
-                   { "yearmonavg", FieldFunc_Avg, 0, YearmonstatHelp } },
+    .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
@@ -40,40 +39,26 @@ public:
 private:
   CdoStreamID streamID1;
   CdoStreamID streamID2;
+  int taxisID1;
+  int taxisID2;
 
   int vlistID1;
   int vlistID2;
 
-  int taxisID1;
-  int taxisID2;
-
   int year0 = 0, month0 = 0;
   int year, month, day;
 
   DateTimeList dtlist;
 
-  VarList varList;
-  Field field;
-
-  FieldVector2D samp1, vars1;
   CdiDateTime vDateTime0{};
 
   int calendar;
-
-  std::vector<RecordInfo> recList;
   int operfunc;
 
-  int nvars;
-  int maxrecs;
-
 public:
   void
-  init()
+  init() override
   {
-
-    // clang-format off
-    // clang-format on
-
     auto operatorID = cdo_operator_id();
 
     operfunc = cdo_operator_f1(operatorID);
@@ -95,31 +80,31 @@ public:
     streamID2 = cdo_open_write(1);
     cdo_def_vlist(streamID2, vlistID2);
 
-    nvars = vlistNvars(vlistID1);
-
-    maxrecs = vlistNrecs(vlistID1);
-    recList = std::vector<RecordInfo>(maxrecs);
-
     calendar = taxisInqCalendar(taxisID1);
     constexpr auto timestatDate{ TimeStat::MEAN };
     dtlist.set_stat(timestatDate);
     dtlist.set_calendar(calendar);
-
-    varList_init(varList, vlistID1);
-
-    fields_from_vlist(vlistID1, samp1);
-    fields_from_vlist(vlistID1, vars1, FIELD_VEC);
   }
 
   void
-  run()
+  run() override
   {
+    Field field;
+    VarList varList1(vlistID1);
+
+    auto maxRecords = varList1.numRecords();
+    std::vector<RecordInfo> recordList(maxRecords);
+
+    FieldVector2D samp1, varsData1;
+    field2D_init(samp1, varList1);
+    field2D_init(varsData1, varList1, FIELD_VEC);
+
     int tsID = 0;
     int otsID = 0;
     while (true)
       {
         int nrecs = 0;
-        long numSets = 0;
+        int numSets = 0;
         double dsets = 0.0;
         while (true)
           {
@@ -149,14 +134,13 @@ public:
 
             for (int recID = 0; recID < nrecs; ++recID)
               {
-                int varID, levelID;
-                cdo_inq_record(streamID1, &varID, &levelID);
-                const auto &var = varList[varID];
+                auto [varID, levelID] = cdo_inq_record(streamID1);
+                const auto &var = varList1.vars[varID];
 
-                if (tsID == 0) recList[recID].set(varID, levelID);
+                if (tsID == 0) recordList[recID].set(varID, levelID);
 
                 auto &rsamp1 = samp1[varID][levelID];
-                auto &rvars1 = vars1[varID][levelID];
+                auto &rvars1 = varsData1[varID][levelID];
 
                 if (numSets == 0)
                   {
@@ -196,14 +180,15 @@ public:
 
         if (nrecs == 0 && numSets == 0) break;
 
-        for (int varID = 0; varID < nvars; ++varID)
+        const auto numVars = varList1.numVars();
+        for (int varID = 0; varID < numVars; ++varID)
           {
-            const auto &var = varList[varID];
+            const auto &var = varList1.vars[varID];
             if (var.isConstant) continue;
             for (int levelID = 0; levelID < var.nlevels; ++levelID)
               {
                 const auto &rsamp1 = samp1[varID][levelID];
-                auto &rvars1 = vars1[varID][levelID];
+                auto &rvars1 = varsData1[varID][levelID];
                 if (!rsamp1.empty())
                   field2_div(rvars1, rsamp1);
                 else
@@ -216,12 +201,12 @@ public:
         dtlist.stat_taxis_def_timestep(taxisID2, numSets);
         cdo_def_timestep(streamID2, otsID);
 
-        for (int recID = 0; recID < maxrecs; ++recID)
+        for (int recID = 0; recID < maxRecords; ++recID)
           {
-            auto [varID, levelID] = recList[recID].get();
-            if (otsID && varList[varID].isConstant) continue;
+            auto [varID, levelID] = recordList[recID].get();
+            if (otsID && varList1.vars[varID].isConstant) continue;
 
-            auto &rvars1 = vars1[varID][levelID];
+            auto &rvars1 = varsData1[varID][levelID];
             cdo_def_record(streamID2, varID, levelID);
             cdo_write_record(streamID2, rvars1);
           }
@@ -232,7 +217,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
diff --git a/src/Yhourarith.cc b/src/Yhourarith.cc
index 5a4dc590ee358b94ec487d9f2a3c129420d2c913..6d1dcc2cf917d4049e61c57729f1506337cfd678 100644
--- a/src/Yhourarith.cc
+++ b/src/Yhourarith.cc
@@ -18,23 +18,12 @@
 
 #include "cdo_vlist.h"
 #include "datetime.h"
-#include "field.h"
 #include "process_int.h"
 #include "printinfo.h"
 #include "field_functions.h"
 
 constexpr int MaxHours = 9301;  // 31*12*25 + 1
 
-static int
-getHourOfYearIndex(const CdiDateTime &vDateTime)
-{
-  auto houroy = decode_hour_of_year(vDateTime);
-
-  if (houroy < 0 || houroy >= MaxHours) cdo_abort("Hour of year %d out of range (%s)!", houroy, datetime_to_string(vDateTime));
-
-  return houroy;
-}
-
 class Yhourarith : public Process
 {
 public:
@@ -51,6 +40,7 @@ public:
     .constraints = { 2, 1, NoRestriction },
   };
   inline static RegisterEntry<Yhourarith> registration = RegisterEntry<Yhourarith>(module);
+
   int operfunc;
   CdoStreamID streamID1;
   CdoStreamID streamID2;
@@ -62,15 +52,13 @@ public:
 
   int vlistID2;
 
-  Field field;
-  FieldVector2D vars2[MaxHours];
   VarList varList1;
+  VarList varList2;
 
 public:
   void
-  init()
+  init() override
   {
-
     auto operatorID = cdo_operator_id();
     operfunc = cdo_operator_f1(operatorID);
 
@@ -85,9 +73,9 @@ public:
 
     vlist_unpack(vlistID3);
 
-    vlist_compare(vlistID1, vlistID2, CmpVlist::All);
-
-    varList_init(varList1, vlistID1);
+    varList1 = VarList(vlistID1);
+    varList2 = VarList(vlistID2);
+    varList_compare(varList1, varList2);
 
     taxisID1 = vlistInqTaxis(vlistID1);
     taxisID2 = vlistInqTaxis(vlistID2);
@@ -99,24 +87,26 @@ public:
   }
 
   void
-  run()
+  run() override
   {
+    Field field;
+    FieldVector2D varsData2[MaxHours];
+
     int tsID = 0;
     while (true)
       {
         auto nrecs = cdo_stream_inq_timestep(streamID2, tsID);
         if (nrecs == 0) break;
 
-        auto houroy = getHourOfYearIndex(taxisInqVdatetime(taxisID2));
-        if (vars2[houroy].size() > 0) cdo_abort("Hour of year index %d already allocated!", houroy);
+        auto hourOfYear = decode_hour_of_year(taxisInqVdatetime(taxisID2), MaxHours);
+        if (varsData2[hourOfYear].size() > 0) cdo_abort("Hour of year index %d already allocated!", hourOfYear);
 
-        fields_from_vlist(vlistID2, vars2[houroy], FIELD_VEC | FIELD_NAT);
+        field2D_init(varsData2[hourOfYear], varList2, FIELD_VEC | FIELD_NAT);
 
         for (int recID = 0; recID < nrecs; ++recID)
           {
-            int varID, levelID;
-            cdo_inq_record(streamID2, &varID, &levelID);
-            cdo_read_record(streamID2, vars2[houroy][varID][levelID]);
+            auto [varID, levelID] = cdo_inq_record(streamID2);
+            cdo_read_record(streamID2, varsData2[hourOfYear][varID][levelID]);
           }
 
         tsID++;
@@ -130,20 +120,19 @@ public:
         auto nrecs = cdo_stream_inq_timestep(streamID1, tsID);
         if (nrecs == 0) break;
 
-        auto houroy = getHourOfYearIndex(taxisInqVdatetime(taxisID1));
-        if (vars2[houroy].size() == 0) cdo_abort("Hour of year index %d not found!", houroy);
+        auto hourOfYear = decode_hour_of_year(taxisInqVdatetime(taxisID1), MaxHours);
+        if (varsData2[hourOfYear].size() == 0) cdo_abort("Hour of year index %d not found!", hourOfYear);
 
         cdo_taxis_copy_timestep(taxisID3, taxisID1);
         cdo_def_timestep(streamID3, tsID);
 
         for (int recID = 0; recID < nrecs; ++recID)
           {
-            int varID, levelID;
-            cdo_inq_record(streamID1, &varID, &levelID);
-            field.init(varList1[varID]);
+            auto [varID, levelID] = cdo_inq_record(streamID1);
+            field.init(varList1.vars[varID]);
             cdo_read_record(streamID1, field);
 
-            field2_function(field, vars2[houroy][varID][levelID], operfunc);
+            field2_function(field, varsData2[hourOfYear][varID][levelID], operfunc);
 
             cdo_def_record(streamID3, varID, levelID);
             cdo_write_record(streamID3, field);
@@ -154,9 +143,8 @@ public:
   }
 
   void
-  close()
+  close() override
   {
-
     cdo_stream_close(streamID3);
     cdo_stream_close(streamID1);
   }
diff --git a/src/Yhourstat.cc b/src/Yhourstat.cc
index 42db06594a7fc35dcd4e0a3aea1c8c3f07660d8f..7bcf78e96d42d8f999ccafd23cc1a9011845c57d 100644
--- a/src/Yhourstat.cc
+++ b/src/Yhourstat.cc
@@ -34,9 +34,11 @@
 #include <cdi.h>
 
 #include "cdo_options.h"
+#include "cdo_stepstat.h"
 #include "datetime.h"
 #include "process_int.h"
 #include "printinfo.h"
+#include "progress.h"
 #include "field_functions.h"
 
 class Yhourstat : public Process
@@ -56,7 +58,7 @@ public:
                    { "yhourstd1", FieldFunc_Std1, 0, YhourstatHelp },
                    { "yhourvar", FieldFunc_Var, 0, YhourstatHelp },
                    { "yhourvar1", FieldFunc_Var1, 0, YhourstatHelp },
-                   { "dhourrange", FieldFunc_Range, 0, DhourstatHelp },
+                   { "dhourrange", FieldFunc_Range, 1, DhourstatHelp },
                    { "dhourmin", FieldFunc_Min, 1, DhourstatHelp },
                    { "dhourmax", FieldFunc_Max, 1, DhourstatHelp },
                    { "dhoursum", FieldFunc_Sum, 1, DhourstatHelp },
@@ -65,7 +67,17 @@ public:
                    { "dhourstd", FieldFunc_Std, 1, DhourstatHelp },
                    { "dhourstd1", FieldFunc_Std1, 1, DhourstatHelp },
                    { "dhourvar", FieldFunc_Var, 1, DhourstatHelp },
-                   { "dhourvar1", FieldFunc_Var1, 1, DhourstatHelp } },
+                   { "dhourvar1", FieldFunc_Var1, 1, DhourstatHelp },
+                   { "dminuterange", FieldFunc_Range, 3, DminutestatHelp },
+                   { "dminutemin", FieldFunc_Min, 3, DminutestatHelp },
+                   { "dminutemax", FieldFunc_Max, 3, DminutestatHelp },
+                   { "dminutesum", FieldFunc_Sum, 3, DminutestatHelp },
+                   { "dminutemean", FieldFunc_Mean, 3, DminutestatHelp },
+                   { "dminuteavg", FieldFunc_Avg, 3, DminutestatHelp },
+                   { "dminutestd", FieldFunc_Std, 3, DminutestatHelp },
+                   { "dminutestd1", FieldFunc_Std1, 3, DminutestatHelp },
+                   { "dminutevar", FieldFunc_Var, 3, DminutestatHelp },
+                   { "dminutevar1", FieldFunc_Var1, 3, DminutestatHelp } },
     // clang-format on
     .aliases = {},
     .mode = EXPOSED,     // Module mode: 0:intern 1:extern
@@ -75,56 +87,34 @@ public:
   inline static RegisterEntry<Yhourstat> registration = RegisterEntry<Yhourstat>(module);
 
 private:
-  int MaxHours;
-
-  std::vector<int> hourot_numSets;  // hour of time
-  FieldVector3D vars1;
-  FieldVector3D vars2;
-  FieldVector3D samp1;
-
-  std::vector<DateTimeList> dtlist;
-
   CdoStreamID streamID1;
   CdoStreamID streamID2;
-  int vlistID1;
   int taxisID1;
   int taxisID2;
 
-  std::vector<RecordInfo> recList;
-  VarList varList;
+  int vlistID1;
+  VarList varList1;
+
+  int maxRecords;
+  std::vector<RecordInfo> recordList;
 
-  int VARS_MEMTYPE = 0;
+  cdo::StepStat3D stepStat;
 
-  int maxrecs;
-  int operfunc;
   bool ldaily;
-  bool lrange;
-  bool lmean;
-  bool lstd;
-  bool lvarstd;
-  bool lvars2;
-  int divisor;
-
-  Field field;
+  bool lminute;
 
 public:
   void
-  init()
+  init() override
   {
-    constexpr auto timestatDate{ TimeStat::LAST };
-
     auto operatorID = cdo_operator_id();
-    operfunc = cdo_operator_f1(operatorID);
+    auto operfunc = cdo_operator_f1(operatorID);
 
-    ldaily = (cdo_operator_f2(operatorID) == 1);
+    auto f2 = cdo_operator_f2(operatorID);
+    ldaily = (f2 == 1 || f2 == 3);
+    lminute = (f2 == 3);
 
-    auto lminmax = (operfunc == FieldFunc_Min || operfunc == FieldFunc_Max);
-    lrange = (operfunc == FieldFunc_Range);
-    lmean = (operfunc == FieldFunc_Mean || operfunc == FieldFunc_Avg);
-    lstd = (operfunc == FieldFunc_Std || operfunc == FieldFunc_Std1);
-    lvarstd = (lstd || operfunc == FieldFunc_Var || operfunc == FieldFunc_Var1);
-    lvars2 = (lvarstd || lrange);
-    divisor = (operfunc == FieldFunc_Std1 || operfunc == FieldFunc_Var1);
+    stepStat.init(operfunc);
 
     operator_check_argc(0);
 
@@ -133,45 +123,54 @@ public:
     vlistID1 = cdo_stream_inq_vlist(streamID1);
     auto vlistID2 = vlistDuplicate(vlistID1);
 
-    if (!lminmax) vlist_unpack(vlistID2);
+    varList1 = VarList(vlistID1);
+
+    if (!stepStat.lminmax) vlist_unpack(vlistID2);
 
     taxisID1 = vlistInqTaxis(vlistID1);
     taxisID2 = taxisDuplicate(taxisID1);
     taxisWithBounds(taxisID2);
     if (taxisInqType(taxisID2) == TAXIS_FORECAST) taxisDefType(taxisID2, TAXIS_RELATIVE);
-
-    MaxHours = ldaily ? 25 : 9301;                   // year: 31*12*25 + 1
-    hourot_numSets = std::vector<int>(MaxHours, 0);  // hour of time
-    vars1 = FieldVector3D(MaxHours);
-    vars2 = FieldVector3D(MaxHours);
-    samp1 = FieldVector3D(MaxHours);
-
-    dtlist = std::vector<DateTimeList>(MaxHours);
-    for (int hourot = 0; hourot < MaxHours; ++hourot)
-      {
-        dtlist[hourot].set_stat(timestatDate);
-        dtlist[hourot].set_calendar(taxisInqCalendar(taxisID1));
-      }
-
     vlistDefTaxis(vlistID2, taxisID2);
 
     streamID2 = cdo_open_write(1);
     cdo_def_vlist(streamID2, vlistID2);
 
-    maxrecs = vlistNrecs(vlistID1);
-    recList = std::vector<RecordInfo>(maxrecs);
-
-    varList_init(varList, vlistID1);
+    maxRecords = varList1.numRecords();
+    recordList = std::vector<RecordInfo>(maxRecords);
+  }
 
-    if ((operfunc == FieldFunc_Min) || (operfunc == FieldFunc_Max)) VARS_MEMTYPE = FIELD_NAT;
+  int
+  get_stepIndex(CdiDateTime vDateTime, int MaxSteps)
+  {
+    return lminute ? decode_minute_of_day(vDateTime, MaxSteps)
+                   : (ldaily ? decode_hour_of_day(vDateTime, MaxSteps) : decode_hour_of_year(vDateTime, MaxSteps));
   }
 
   void
-  run()
+  run() override
   {
+    constexpr auto timestatDate{ TimeStat::LAST };
+    int MaxHours = ldaily ? 25 : 9301;  // year: 31*12*25 + 1
+    int MaxMinutes = 1501;              // 25*60 + 1;
+    int MaxSteps = lminute ? MaxMinutes : MaxHours;
+    std::vector<DateTimeList> dtlist(MaxSteps);
+    std::vector<int> rangeNumSets(MaxSteps, 0);
+    Field field;
+
+    stepStat.set_dimlen0(MaxSteps);
+    int VARS_MEMTYPE = stepStat.lminmax ? FIELD_NAT : 0;
+
+    auto calendar = taxisInqCalendar(taxisID1);
+    for (int stepIndex = 0; stepIndex < MaxHours; ++stepIndex)
+      {
+        dtlist[stepIndex].set_stat(timestatDate);
+        dtlist[stepIndex].set_calendar(calendar);
+      }
+
+    auto numSteps = varList1.numSteps();
+    cdo::Progress progress;
 
-    auto field2_stdvar_func = lstd ? field2_std : field2_var;
-    auto fieldc_stdvar_func = lstd ? fieldc_std : fieldc_var;
     int tsID = 0;
     int otsID = 0;
     while (true)
@@ -179,132 +178,57 @@ public:
         auto nrecs = cdo_stream_inq_timestep(streamID1, tsID);
         if (nrecs == 0) break;
 
-        auto vDateTime = taxisInqVdatetime(taxisID1);
+        if (numSteps > 1) progress.update((tsID + 1.0) / numSteps);
 
+        auto vDateTime = taxisInqVdatetime(taxisID1);
         if (Options::cdoVerbose) cdo_print("process timestep: %d %s", tsID + 1, datetime_to_string(vDateTime));
 
-        auto hourot = ldaily ? decode_hour_of_day(vDateTime) : decode_hour_of_year(vDateTime);
-        if (hourot < 0 || hourot >= MaxHours)
-          cdo_abort("Hour of year %d out of range (%s)!", hourot, datetime_to_string(vDateTime));
+        auto stepIndex = get_stepIndex(vDateTime, MaxSteps);
 
-        dtlist[hourot].taxis_inq_timestep(taxisID1, hourot_numSets[hourot]);
+        dtlist[stepIndex].taxis_inq_timestep(taxisID1, rangeNumSets[stepIndex]);
 
-        if (!vars1[hourot].size())
-          {
-            fields_from_vlist(vlistID1, samp1[hourot]);
-            fields_from_vlist(vlistID1, vars1[hourot], FIELD_VEC | VARS_MEMTYPE);
-            if (lvars2) fields_from_vlist(vlistID1, vars2[hourot], FIELD_VEC);
-          }
+        if (!stepStat.var1(stepIndex).size()) { stepStat.alloc(stepIndex, varList1, VARS_MEMTYPE); }
 
+        auto numSets = rangeNumSets[stepIndex];
         for (int recID = 0; recID < nrecs; ++recID)
           {
-            int varID, levelID;
-            cdo_inq_record(streamID1, &varID, &levelID);
-            const auto &var = varList[varID];
+            auto [varID, levelID] = cdo_inq_record(streamID1);
+            if (tsID == 0) recordList[recID].set(varID, levelID);
+            field.init(varList1.vars[varID]);
+            cdo_read_record(streamID1, field);
+            stepStat.add_field(field, stepIndex, varID, levelID, numSets);
+          }
 
-            if (tsID == 0) recList[recID].set(varID, levelID);
+        rangeNumSets[stepIndex]++;
+        tsID++;
+      }
 
-            auto &rsamp1 = samp1[hourot][varID][levelID];
-            auto &rvars1 = vars1[hourot][varID][levelID];
+    for (int stepIndex = 0; stepIndex < MaxSteps; stepIndex++)
+      {
+        auto numSets = rangeNumSets[stepIndex];
+        if (numSets)
+          {
+            cdo::records_process_3D(stepIndex, recordList, varList1, stepStat, numSets);
 
-            auto numSets = hourot_numSets[hourot];
+            dtlist[stepIndex].stat_taxis_def_timestep(taxisID2, rangeNumSets[stepIndex]);
+            cdo_def_timestep(streamID2, otsID);
 
-            if (numSets == 0)
-              {
-                cdo_read_record(streamID1, rvars1);
-                if (lrange)
-                  {
-                    vars2[hourot][varID][levelID].numMissVals = rvars1.numMissVals;
-                    vars2[hourot][varID][levelID].vec_d = rvars1.vec_d;
-                  }
-
-                if (rvars1.numMissVals || !rsamp1.empty())
-                  {
-                    if (rsamp1.empty()) rsamp1.resize(rvars1.size);
-                    field2_vinit(rsamp1, rvars1);
-                  }
-              }
-            else
+            for (int recID = 0; recID < maxRecords; ++recID)
               {
-                field.init(var);
-                cdo_read_record(streamID1, field);
-
-                if (field.numMissVals || !rsamp1.empty())
-                  {
-                    if (rsamp1.empty()) rsamp1.resize(rvars1.size, numSets);
-                    field2_vincr(rsamp1, field);
-                  }
-
-                // clang-format off
-                if      (lvarstd) field2_sumsumq(rvars1, vars2[hourot][varID][levelID], field);
-                else if (lrange)  field2_maxmin(rvars1, vars2[hourot][varID][levelID], field);
-                else              field2_function(rvars1, field, operfunc);
-                // clang-format on
-              }
-          }
+                auto [varID, levelID] = recordList[recID].get();
+                if (otsID && varList1.vars[varID].isConstant) continue;
 
-        if (hourot_numSets[hourot] == 0 && lvarstd)
-          for (int recID = 0; recID < maxrecs; ++recID)
-            {
-              auto [varID, levelID] = recList[recID].get();
-              if (varList[varID].isConstant) continue;
-
-              field2_moq(vars2[hourot][varID][levelID], vars1[hourot][varID][levelID]);
-            }
+                cdo_def_record(streamID2, varID, levelID);
+                cdo_write_record(streamID2, stepStat.var1(stepIndex, varID, levelID));
+              }
 
-        hourot_numSets[hourot]++;
-        tsID++;
+            otsID++;
+          }
       }
-
-    for (int hourot = 0; hourot < MaxHours; ++hourot)
-      if (hourot_numSets[hourot])
-        {
-          auto numSets = hourot_numSets[hourot];
-          for (int recID = 0; recID < maxrecs; ++recID)
-            {
-              auto [varID, levelID] = recList[recID].get();
-              if (varList[varID].isConstant) continue;
-
-              const auto &rsamp1 = samp1[hourot][varID][levelID];
-              auto &rvars1 = vars1[hourot][varID][levelID];
-
-              if (lmean)
-                {
-                  if (!rsamp1.empty())
-                    field2_div(rvars1, rsamp1);
-                  else
-                    fieldc_div(rvars1, (double) numSets);
-                }
-              else if (lvarstd)
-                {
-                  if (!rsamp1.empty())
-                    field2_stdvar_func(rvars1, vars2[hourot][varID][levelID], rsamp1, divisor);
-                  else
-                    fieldc_stdvar_func(rvars1, vars2[hourot][varID][levelID], numSets, divisor);
-                }
-              else if (lrange) { field2_sub(rvars1, vars2[hourot][varID][levelID]); }
-            }
-
-          dtlist[hourot].stat_taxis_def_timestep(taxisID2, hourot_numSets[hourot]);
-          cdo_def_timestep(streamID2, otsID);
-
-          for (int recID = 0; recID < maxrecs; ++recID)
-            {
-              auto [varID, levelID] = recList[recID].get();
-              if (otsID && varList[varID].isConstant) continue;
-
-              auto &rvars1 = vars1[hourot][varID][levelID];
-
-              cdo_def_record(streamID2, varID, levelID);
-              cdo_write_record(streamID2, rvars1);
-            }
-
-          otsID++;
-        }
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
diff --git a/src/Ymonarith.cc b/src/Ymonarith.cc
index c0040be55f05ca9431a001f22075ca2aa3d14a72..bc57e7ae57dfb5a44cf32f912097c43382de3444 100644
--- a/src/Ymonarith.cc
+++ b/src/Ymonarith.cc
@@ -65,6 +65,7 @@ public:
     .constraints = { 2, 1, NoRestriction },
   };
   inline static RegisterEntry<Ymonarith> registration = RegisterEntry<Ymonarith>(module);
+
   int operfunc;
   int opertype;
 
@@ -79,8 +80,9 @@ public:
   int vlistID2;
 
   VarList varList1;
+  VarList varList2;
   Field field;
-  FieldVector2D vars2[MaxMonths];
+  FieldVector2D varsData2[MaxMonths];
 
   void
   process_data(int tsID, int nrecs, int mon)
@@ -90,12 +92,11 @@ public:
 
     for (int recID = 0; recID < nrecs; ++recID)
       {
-        int varID, levelID;
-        cdo_inq_record(streamID1, &varID, &levelID);
-        field.init(varList1[varID]);
+        auto [varID, levelID] = cdo_inq_record(streamID1);
+        field.init(varList1.vars[varID]);
         cdo_read_record(streamID1, field);
 
-        field2_function(field, vars2[mon][varID][levelID], operfunc);
+        field2_function(field, varsData2[mon][varID][levelID], operfunc);
 
         cdo_def_record(streamID3, varID, levelID);
         cdo_write_record(streamID3, field);
@@ -105,13 +106,12 @@ public:
   void
   load_data_from_stream(int mon, int nrecs)
   {
-    fields_from_vlist(vlistID2, vars2[mon], FIELD_VEC | FIELD_NAT);
+    field2D_init(varsData2[mon], varList2, FIELD_VEC | FIELD_NAT);
 
     for (int recID = 0; recID < nrecs; ++recID)
       {
-        int varID, levelID;
-        cdo_inq_record(streamID2, &varID, &levelID);
-        cdo_read_record(streamID2, vars2[mon][varID][levelID]);
+        auto [varID, levelID] = cdo_inq_record(streamID2);
+        cdo_read_record(streamID2, varsData2[mon][varID][levelID]);
       }
   }
 
@@ -121,7 +121,7 @@ public:
     for (int nrecs, tsID = 0; (nrecs = cdo_stream_inq_timestep(streamID2, tsID)) != 0; tsID++)
       {
         auto mon = get_month_index(taxisInqVdatetime(taxisID2).date);
-        if (vars2[mon].size())
+        if (varsData2[mon].size())
           cdo_abort("%s already allocated! The second input file must contain monthly mean values for a maximum of one year.",
                     monthNames[mon]);
 
@@ -131,7 +131,7 @@ public:
     for (int nrecs, tsID = 0; (nrecs = cdo_stream_inq_timestep(streamID1, tsID)) != 0; tsID++)
       {
         auto mon = get_month_index(taxisInqVdatetime(taxisID1).date);
-        if (vars2[mon].size() == 0)
+        if (varsData2[mon].size() == 0)
           cdo_abort("%s not found! The second input file must contain monthly mean values for a maximum of one year.",
                     monthNames[mon]);
 
@@ -147,16 +147,16 @@ public:
       {
         auto mon = get_month_index(taxisInqVdatetime(taxisID2).date);
         auto season = month_to_season(mon + 1);
-        if (vars2[season].size()) cdo_abort("Season %s already allocated!", seasonNames[season]);
+        if (varsData2[season].size()) cdo_abort("Season %s already allocated!", seasonNames[season]);
 
-        load_data_from_stream(mon, nrecs);
+        load_data_from_stream(season, nrecs);
       }
 
     for (int nrecs, tsID = 0; (nrecs = cdo_stream_inq_timestep(streamID1, tsID)) != 0; tsID++)
       {
         auto mon = get_month_index(taxisInqVdatetime(taxisID1).date);
         auto season = month_to_season(mon + 1);
-        if (vars2[season].size() == 0) cdo_abort("Season %s not found!", seasonNames[season]);
+        if (varsData2[season].size() == 0) cdo_abort("Season %s not found!", seasonNames[season]);
 
         process_data(tsID, nrecs, season);
       }
@@ -164,9 +164,8 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
-
     auto operatorID = cdo_operator_id();
     opertype = cdo_operator_f2(operatorID);
     operfunc = cdo_operator_f1(operatorID);
@@ -182,9 +181,9 @@ public:
 
     vlist_unpack(vlistID3);
 
-    vlist_compare(vlistID1, vlistID2, CmpVlist::All);
-
-    varList_init(varList1, vlistID1);
+    varList1 = VarList(vlistID1);
+    varList2 = VarList(vlistID2);
+    varList_compare(varList1, varList2);
 
     taxisID1 = vlistInqTaxis(vlistID1);
     taxisID2 = vlistInqTaxis(vlistID2);
@@ -196,7 +195,7 @@ public:
   }
 
   void
-  run()
+  run() override
   {
     if (opertype == SEASONAL)
       run_seasonal();
@@ -205,7 +204,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID3);
diff --git a/src/Ymoncomp.cc b/src/Ymoncomp.cc
index 7c867606378d2fb1103605b21e18847040b767db..b9571d2269ad9afa44dd73e6dba950ff0650343e 100644
--- a/src/Ymoncomp.cc
+++ b/src/Ymoncomp.cc
@@ -24,8 +24,8 @@
 
 #include <cdi.h>
 
-#include "cdo_vlist.h"
 #include "cdo_season.h"
+#include "cdo_varlist.h"
 #include "process_int.h"
 #include "field_functions.h"
 
@@ -106,18 +106,19 @@ public:
                    { "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} },
+                   { "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;
 
@@ -132,8 +133,9 @@ public:
   int vlistID2;
 
   VarList varList1;
+  VarList varList2;
   Field field;
-  FieldVector2D vars2[MaxMonths];
+  FieldVector2D varsData2[MaxMonths];
 
   void
   process_data(int tsID, int nrecs, int mon)
@@ -143,12 +145,11 @@ public:
 
     for (int recID = 0; recID < nrecs; ++recID)
       {
-        int varID, levelID;
-        cdo_inq_record(streamID1, &varID, &levelID);
-        field.init(varList1[varID]);
+        auto [varID, levelID] = cdo_inq_record(streamID1);
+        field.init(varList1.vars[varID]);
         cdo_read_record(streamID1, field);
 
-        comp_function(field, vars2[mon][varID][levelID], operfunc);
+        comp_function(field, varsData2[mon][varID][levelID], operfunc);
 
         cdo_def_record(streamID3, varID, levelID);
         cdo_write_record(streamID3, field);
@@ -158,13 +159,12 @@ public:
   void
   load_data_from_stream(int mon, int nrecs)
   {
-    fields_from_vlist(vlistID2, vars2[mon], FIELD_VEC | FIELD_NAT);
+    field2D_init(varsData2[mon], varList2, FIELD_VEC | FIELD_NAT);
 
     for (int recID = 0; recID < nrecs; ++recID)
       {
-        int varID, levelID;
-        cdo_inq_record(streamID2, &varID, &levelID);
-        cdo_read_record(streamID2, vars2[mon][varID][levelID]);
+        auto [varID, levelID] = cdo_inq_record(streamID2);
+        cdo_read_record(streamID2, varsData2[mon][varID][levelID]);
       }
   }
 
@@ -174,7 +174,7 @@ public:
     for (int nrecs, tsID = 0; (nrecs = cdo_stream_inq_timestep(streamID2, tsID)) != 0; tsID++)
       {
         auto mon = get_month_index(taxisInqVdatetime(taxisID2).date);
-        if (vars2[mon].size())
+        if (varsData2[mon].size())
           cdo_abort("%s already allocated! The second input file must contain monthly mean values for a maximum of one year.",
                     monthNames[mon]);
 
@@ -184,7 +184,7 @@ public:
     for (int nrecs, tsID = 0; (nrecs = cdo_stream_inq_timestep(streamID1, tsID)) != 0; tsID++)
       {
         auto mon = get_month_index(taxisInqVdatetime(taxisID1).date);
-        if (vars2[mon].size() == 0)
+        if (varsData2[mon].size() == 0)
           cdo_abort("%s not found! The second input file must contain monthly mean values for a maximum of one year.",
                     monthNames[mon]);
 
@@ -200,7 +200,7 @@ public:
       {
         auto mon = get_month_index(taxisInqVdatetime(taxisID2).date);
         auto season = month_to_season(mon + 1);
-        if (vars2[season].size()) cdo_abort("Season %s already allocated!", seasonNames[season]);
+        if (varsData2[season].size()) cdo_abort("Season %s already allocated!", seasonNames[season]);
 
         load_data_from_stream(mon, nrecs);
       }
@@ -209,7 +209,7 @@ public:
       {
         auto mon = get_month_index(taxisInqVdatetime(taxisID1).date);
         auto season = month_to_season(mon + 1);
-        if (vars2[season].size() == 0) cdo_abort("Season %s not found!", seasonNames[season]);
+        if (varsData2[season].size() == 0) cdo_abort("Season %s not found!", seasonNames[season]);
 
         process_data(tsID, nrecs, season);
       }
@@ -217,9 +217,8 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
-
     auto operatorID = cdo_operator_id();
     operfunc = cdo_operator_f1(operatorID);
     opertype = cdo_operator_f2(operatorID);
@@ -233,9 +232,9 @@ public:
     vlistID2 = cdo_stream_inq_vlist(streamID2);
     auto vlistID3 = vlistDuplicate(vlistID1);
 
-    vlist_compare(vlistID1, vlistID2, CmpVlist::All);
-
-    varList_init(varList1, vlistID1);
+    varList1 = VarList(vlistID1);
+    varList2 = VarList(vlistID2);
+    varList_compare(varList1, varList2);
 
     taxisID1 = vlistInqTaxis(vlistID1);
     taxisID2 = vlistInqTaxis(vlistID2);
@@ -249,7 +248,7 @@ public:
   }
 
   void
-  run()
+  run() override
   {
     if (opertype == SEASONAL)
       run_seasonal();
@@ -258,7 +257,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID3);
diff --git a/src/Ymonpctl.cc b/src/Ymonpctl.cc
index 8586fa91158da9bef3abf06fbb37632515bb349f..ca33057a03099afb69a345a1271585d0aacb1441 100644
--- a/src/Ymonpctl.cc
+++ b/src/Ymonpctl.cc
@@ -16,9 +16,7 @@
 
 #include <cdi.h>
 
-#include "cdo_options.h"
 #include "cdo_vlist.h"
-#include "field.h"
 #include "datetime.h"
 #include "process_int.h"
 #include "param_conversion.h"
@@ -45,33 +43,20 @@ public:
   CdoStreamID streamID3;
   CdoStreamID streamID4;
 
+  int vlistID1;
   int taxisID1;
   int taxisID2;
   int taxisID3;
   int taxisID4;
 
-  CdiDateTime vDateTimes1[MaxMonths]{};
-  CdiDateTime vDateTimes2[MaxMonths]{};
+  double pn;
 
-  std::vector<bool> vars1 = std::vector<bool>(MaxMonths, false);
-  HistogramSet hsets[MaxMonths];
-  long numSets[MaxMonths] = { 0 };
   VarList varList1;
-  Field field1, field2;
-  FieldVector constFields;
-  std::vector<RecordInfo> recList;
-
-  int maxrecs;
-  int nvars;
-  int ntsteps;
-
-  double pn;
 
 public:
   void
-  init()
+  init() override
   {
-
     operator_input_arg("percentile number");
     pn = parameter_to_double(cdo_operator_argv(0));
 
@@ -79,15 +64,19 @@ public:
     streamID2 = cdo_open_read(1);
     streamID3 = cdo_open_read(2);
 
-    auto vlistID1 = cdo_stream_inq_vlist(streamID1);
+    vlistID1 = cdo_stream_inq_vlist(streamID1);
     auto vlistID2 = cdo_stream_inq_vlist(streamID2);
     auto vlistID3 = cdo_stream_inq_vlist(streamID3);
     auto vlistID4 = vlistDuplicate(vlistID1);
 
     vlist_unpack(vlistID4);
 
-    vlist_compare(vlistID1, vlistID2, CmpVlist::All);
-    vlist_compare(vlistID1, vlistID3, CmpVlist::All);
+    varList1 = VarList(vlistID1);
+    VarList varList2(vlistID2);
+    VarList varList3(vlistID3);
+
+    varList_compare(varList1, varList2);
+    varList_compare(varList1, varList3);
 
     taxisID1 = vlistInqTaxis(vlistID1);
     taxisID2 = vlistInqTaxis(vlistID2);
@@ -100,21 +89,25 @@ public:
 
     streamID4 = cdo_open_write(3);
     cdo_def_vlist(streamID4, vlistID4);
-
-    ntsteps = vlistNtsteps(vlistID1);
-    nvars = vlistNvars(vlistID1);
-
-    maxrecs = vlistNrecs(vlistID1);
-    recList = std::vector<RecordInfo>(maxrecs);
-
-    constFields = FieldVector(maxrecs);
-
-    varList_init(varList1, vlistID1);
   }
 
   void
-  run()
+  run() override
   {
+    Field field1, field2;
+    std::vector<bool> varsData1(MaxMonths, false);
+    CdiDateTime vDateTimes1[MaxMonths]{};
+    CdiDateTime vDateTimes2[MaxMonths]{};
+    HistogramSet hsets[MaxMonths];
+    long numSets[MaxMonths] = { 0 };
+
+    auto numVars = varList1.numVars();
+    auto numSteps = varList1.numSteps();
+    auto maxRecords = varList1.numRecords();
+    std::vector<RecordInfo> recordList(maxRecords);
+
+    FieldVector constFields(maxRecords);
+
     int tsID = 0;
     while (true)
       {
@@ -139,26 +132,21 @@ public:
 
         vDateTimes2[month] = vDateTime2;
 
-        if (!vars1[month])
+        if (!varsData1[month])
           {
-            vars1[month] = true;
-            hsets[month].create(nvars, ntsteps);
-            for (int varID = 0; varID < nvars; ++varID)
-              {
-                const auto &var = varList1[varID];
-                hsets[month].createVarLevels(varID, var.nlevels, var.gridsize);
-              }
+            varsData1[month] = true;
+            hsets[month].create(numVars, numSteps);
+            for (const auto &var : varList1.vars) hsets[month].createVarLevels(var.ID, var.nlevels, var.gridsize);
           }
 
         for (int recID = 0; recID < nrecs; ++recID)
           {
-            int varID, levelID;
-            cdo_inq_record(streamID2, &varID, &levelID);
-            const auto &var = varList1[varID];
+            auto [varID, levelID] = cdo_inq_record(streamID2);
+            const auto &var = varList1.vars[varID];
             field1.init(var);
             cdo_read_record(streamID2, field1);
 
-            cdo_inq_record(streamID3, &varID, &levelID);
+            (void) cdo_inq_record(streamID3);
             field2.init(var);
             cdo_read_record(streamID3, field2);
 
@@ -183,15 +171,15 @@ public:
 
         vDateTimes1[month] = vDateTime;
 
-        if (!vars1[month]) cdo_abort("No data for month %d in %s and %s", month, cdo_get_stream_name(1), cdo_get_stream_name(2));
+        if (!varsData1[month])
+          cdo_abort("No data for month %d in %s and %s", month, cdo_get_stream_name(1), cdo_get_stream_name(2));
 
         for (int recID = 0; recID < nrecs; ++recID)
           {
-            int varID, levelID;
-            cdo_inq_record(streamID1, &varID, &levelID);
-            const auto &var = varList1[varID];
+            auto [varID, levelID] = cdo_inq_record(streamID1);
+            const auto &var = varList1.vars[varID];
 
-            if (tsID == 0) recList[recID].set(varID, levelID);
+            if (tsID == 0) recordList[recID].set(varID, levelID);
 
             if (tsID == 0 && var.isConstant)
               {
@@ -222,17 +210,18 @@ public:
           taxisDefVdatetime(taxisID4, vDateTimes1[month]);
           cdo_def_timestep(streamID4, otsID);
 
-          for (int recID = 0; recID < maxrecs; ++recID)
+          for (int recID = 0; recID < maxRecords; ++recID)
             {
-              auto [varID, levelID] = recList[recID].get();
-              if (otsID && varList1[varID].isConstant) continue;
+              auto [varID, levelID] = recordList[recID].get();
+              const auto &var = varList1.vars[varID];
+              if (otsID && var.isConstant) continue;
 
               cdo_def_record(streamID4, varID, levelID);
 
-              if (varList1[varID].isConstant) { cdo_write_record(streamID4, constFields[recID]); }
+              if (var.isConstant) { cdo_write_record(streamID4, constFields[recID]); }
               else
                 {
-                  field1.init(varList1[varID]);
+                  field1.init(var);
                   hsets[month].getVarLevelPercentiles(field1, varID, levelID, pn);
                   cdo_write_record(streamID4, field1);
                 }
@@ -243,7 +232,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID4);
     cdo_stream_close(streamID3);
diff --git a/src/Ymonstat.cc b/src/Ymonstat.cc
index 46ef57077ace52981f60a8c4ecf2994a41049cb1..d1639b8c8ba40791b91f428c696260cfd987e91e 100644
--- a/src/Ymonstat.cc
+++ b/src/Ymonstat.cc
@@ -23,9 +23,11 @@
 #include <cdi.h>
 
 #include "cdo_options.h"
+#include "cdo_stepstat.h"
 #include "datetime.h"
 #include "process_int.h"
 #include "printinfo.h"
+#include "progress.h"
 #include "field_functions.h"
 
 class Ymonstat : public Process
@@ -50,50 +52,29 @@ public:
     .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 };
-  int nmon = 0;
-  FieldVector2D vars1[MaxMonths], vars2[MaxMonths], samp1[MaxMonths];
-  int VARS_MEMTYPE = 0;
-  DateTimeList dtlists[MaxMonths];
 
+private:
   CdoStreamID streamID1;
   CdoStreamID streamID2;
-  int vlistID1;
   int taxisID1;
   int taxisID2;
 
-  std::vector<RecordInfo> recList;
-  VarList varList;
+  int vlistID1;
+  VarList varList1;
 
-  int operfunc;
-  int maxrecs;
-  bool lrange;
-  bool lmean;
-  bool lstd;
-  bool lvarstd;
-  bool lvars2;
-  int divisor;
+  int maxRecords;
+  std::vector<RecordInfo> recordList;
 
-  Field field;
+  cdo::StepStat3D stepStat;
 
 public:
   void
-  init()
+  init() override
   {
-    constexpr auto timestatDate{ TimeStat::LAST };
-
     auto operatorID = cdo_operator_id();
-    operfunc = cdo_operator_f1(operatorID);
+    auto operfunc = cdo_operator_f1(operatorID);
 
-    auto lminmax = (operfunc == FieldFunc_Min || operfunc == FieldFunc_Max);
-    lrange = (operfunc == FieldFunc_Range);
-    lmean = (operfunc == FieldFunc_Mean || operfunc == FieldFunc_Avg);
-    lstd = (operfunc == FieldFunc_Std || operfunc == FieldFunc_Std1);
-    lvarstd = (lstd || operfunc == FieldFunc_Var || operfunc == FieldFunc_Var1);
-    lvars2 = (lvarstd || lrange);
-    divisor = (operfunc == FieldFunc_Std1 || operfunc == FieldFunc_Var1);
+    stepStat.init(operfunc);
 
     operator_check_argc(0);
 
@@ -102,7 +83,9 @@ public:
     vlistID1 = cdo_stream_inq_vlist(streamID1);
     auto vlistID2 = vlistDuplicate(vlistID1);
 
-    if (!lminmax) vlist_unpack(vlistID2);
+    varList1 = VarList(vlistID1);
+
+    if (!stepStat.lminmax) vlist_unpack(vlistID2);
 
     taxisID1 = vlistInqTaxis(vlistID1);
     taxisID2 = taxisDuplicate(taxisID1);
@@ -113,25 +96,33 @@ public:
     streamID2 = cdo_open_write(1);
     cdo_def_vlist(streamID2, vlistID2);
 
-    maxrecs = vlistNrecs(vlistID1);
-    recList = std::vector<RecordInfo>(maxrecs);
+    maxRecords = varList1.numRecords();
+    recordList = std::vector<RecordInfo>(maxRecords);
+  }
+
+  void
+  run() override
+  {
+    constexpr auto timestatDate{ TimeStat::LAST };
+    constexpr int MaxMonths = 17;
+    constexpr int MaxSteps = MaxMonths;
+    std::vector<DateTimeList> dtLists(MaxSteps);
+    std::vector<int> rangeNumSets(MaxSteps, 0);
+    Field field;
+
+    stepStat.set_dimlen0(MaxSteps);
+    int VARS_MEMTYPE = stepStat.lminmax ? FIELD_NAT : 0;
 
-    for (int month = 0; month < MaxMonths; ++month)
+    auto calendar = taxisInqCalendar(taxisID1);
+    for (int stepIndex = 0; stepIndex < MaxSteps; ++stepIndex)
       {
-        dtlists[month].set_stat(timestatDate);
-        dtlists[month].set_calendar(taxisInqCalendar(taxisID1));
+        dtLists[stepIndex].set_stat(timestatDate);
+        dtLists[stepIndex].set_calendar(calendar);
       }
 
-    varList_init(varList, vlistID1);
+    auto numSteps = varList1.numSteps();
+    cdo::Progress progress;
 
-    if ((operfunc == FieldFunc_Min) || (operfunc == FieldFunc_Max)) VARS_MEMTYPE = FIELD_NAT;
-  }
-
-  void
-  run()
-  {
-    auto field2_stdvar_func = lstd ? field2_std : field2_var;
-    auto fieldc_stdvar_func = lstd ? fieldc_std : fieldc_var;
     int tsID = 0;
     int otsID = 0;
     while (true)
@@ -139,143 +130,58 @@ public:
         auto nrecs = cdo_stream_inq_timestep(streamID1, tsID);
         if (nrecs == 0) break;
 
-        auto vDateTime = taxisInqVdatetime(taxisID1);
+        if (numSteps > 1) progress.update((tsID + 1.0) / numSteps);
 
+        auto vDateTime = taxisInqVdatetime(taxisID1);
         if (Options::cdoVerbose) cdo_print("process timestep: %d %s", tsID + 1, datetime_to_string(vDateTime));
 
-        int month = vDateTime.date.month;
-        if (month < 0 || month >= MaxMonths) cdo_abort("Month %d out of range!", month);
+        int stepIndex = vDateTime.date.month;
+        if (stepIndex < 0 || stepIndex >= MaxSteps) cdo_abort("Month %d out of range!", stepIndex);
 
-        dtlists[month].taxis_set_next_timestep(taxisID1);
+        dtLists[stepIndex].taxis_set_next_timestep(taxisID1);
 
-        if (!vars1[month].size())
-          {
-            mon[nmon++] = month;
-            fields_from_vlist(vlistID1, samp1[month]);
-            fields_from_vlist(vlistID1, vars1[month], FIELD_VEC | VARS_MEMTYPE);
-            if (lvars2) fields_from_vlist(vlistID1, vars2[month], FIELD_VEC);
-          }
+        if (!stepStat.var1(stepIndex).size()) { stepStat.alloc(stepIndex, varList1, VARS_MEMTYPE); }
 
+        auto numSets = rangeNumSets[stepIndex];
         for (int recID = 0; recID < nrecs; ++recID)
           {
-            int varID, levelID;
-            cdo_inq_record(streamID1, &varID, &levelID);
-            const auto &var = varList[varID];
-
-            if (tsID == 0) recList[recID].set(varID, levelID);
-
-            auto &rsamp1 = samp1[month][varID][levelID];
-            auto &rvars1 = vars1[month][varID][levelID];
-
-            auto numSets = month_numSets[month];
-
-            if (numSets == 0)
-              {
-                cdo_read_record(streamID1, rvars1);
-                if (lrange)
-                  {
-                    vars2[month][varID][levelID].numMissVals = rvars1.numMissVals;
-                    vars2[month][varID][levelID].vec_d = rvars1.vec_d;
-                  }
-
-                if (rvars1.numMissVals || !rsamp1.empty())
-                  {
-                    if (rsamp1.empty()) rsamp1.resize(rvars1.size);
-                    field2_vinit(rsamp1, rvars1);
-                  }
-              }
-            else
-              {
-                field.init(var);
-                cdo_read_record(streamID1, field);
-
-                if (field.numMissVals || !rsamp1.empty())
-                  {
-                    if (rsamp1.empty()) rsamp1.resize(rvars1.size, numSets);
-                    field2_vincr(rsamp1, field);
-                  }
-
-                // clang-format off
-                if      (lvarstd) field2_sumsumq(rvars1, vars2[month][varID][levelID], field);
-                else if (lrange)  field2_maxmin(rvars1, vars2[month][varID][levelID], field);
-                else              field2_function(rvars1, field, operfunc);
-                // clang-format on
-              }
+            auto [varID, levelID] = cdo_inq_record(streamID1);
+            if (tsID == 0) recordList[recID].set(varID, levelID);
+            field.init(varList1.vars[varID]);
+            cdo_read_record(streamID1, field);
+            stepStat.add_field(field, stepIndex, varID, levelID, numSets);
           }
 
-        if (month_numSets[month] == 0 && lvarstd)
-          for (int recID = 0; recID < maxrecs; ++recID)
-            {
-              auto [varID, levelID] = recList[recID].get();
-              if (varList[varID].isConstant) continue;
-
-              field2_moq(vars2[month][varID][levelID], vars1[month][varID][levelID]);
-            }
-
-        month_numSets[month]++;
+        rangeNumSets[stepIndex]++;
         tsID++;
       }
 
-    if (nmon == 12)
-      {
-        int smon = 0;
-        for (int month = 1; month <= 12; ++month)
-          if (month_numSets[month]) smon++;
-        if (smon == 12)
-          for (int month = 1; month <= 12; ++month) mon[month - 1] = month;
-      }
-
-    for (int i = 0; i < nmon; ++i)
+    for (int stepIndex = 0; stepIndex < MaxSteps; stepIndex++)
       {
-        auto month = mon[i];
-        auto numSets = month_numSets[month];
-        if (numSets == 0) cdo_abort("Internal problem, numSets[%d] not defined!", month);
-
-        for (int recID = 0; recID < maxrecs; ++recID)
+        auto numSets = rangeNumSets[stepIndex];
+        if (numSets)
           {
-            auto [varID, levelID] = recList[recID].get();
-            if (varList[varID].isConstant) continue;
+            cdo::records_process_3D(stepIndex, recordList, varList1, stepStat, numSets);
 
-            const auto &rsamp1 = samp1[month][varID][levelID];
-            auto &rvars1 = vars1[month][varID][levelID];
+            dtLists[stepIndex].stat_taxis_def_timestep(taxisID2);
+            cdo_def_timestep(streamID2, otsID);
 
-            if (lmean)
-              {
-                if (!rsamp1.empty())
-                  field2_div(rvars1, rsamp1);
-                else
-                  fieldc_div(rvars1, (double) numSets);
-              }
-            else if (lvarstd)
+            for (int recID = 0; recID < maxRecords; ++recID)
               {
-                if (!rsamp1.empty())
-                  field2_stdvar_func(rvars1, vars2[month][varID][levelID], rsamp1, divisor);
-                else
-                  fieldc_stdvar_func(rvars1, vars2[month][varID][levelID], numSets, divisor);
-              }
-            else if (lrange) { field2_sub(rvars1, vars2[month][varID][levelID]); }
-          }
+                auto [varID, levelID] = recordList[recID].get();
+                if (otsID && varList1.vars[varID].isConstant) continue;
 
-        dtlists[month].stat_taxis_def_timestep(taxisID2);
-        cdo_def_timestep(streamID2, otsID);
-
-        for (int recID = 0; recID < maxrecs; ++recID)
-          {
-            auto [varID, levelID] = recList[recID].get();
-            if (otsID && varList[varID].isConstant) continue;
-
-            auto &rvars1 = vars1[month][varID][levelID];
+                cdo_def_record(streamID2, varID, levelID);
+                cdo_write_record(streamID2, stepStat.var1(stepIndex, varID, levelID));
+              }
 
-            cdo_def_record(streamID2, varID, levelID);
-            cdo_write_record(streamID2, rvars1);
+            otsID++;
           }
-
-        otsID++;
       }
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
diff --git a/src/Yseaspctl.cc b/src/Yseaspctl.cc
index 680d3bcf448d9ba0749abb2295b48e76a2a8bfba..6960fdc4e6826c50d45aa9ae3e49e2d8c5ff6ba1 100644
--- a/src/Yseaspctl.cc
+++ b/src/Yseaspctl.cc
@@ -45,38 +45,26 @@ public:
     .constraints = { 3, 1, NoRestriction },
   };
   inline static RegisterEntry<Yseaspctl> registration = RegisterEntry<Yseaspctl>(module);
+
   CdoStreamID streamID1;
   CdoStreamID streamID2;
   CdoStreamID streamID3;
   CdoStreamID streamID4;
-  FieldVector constFields;
 
+  int vlistID1;
   int taxisID1;
   int taxisID2;
   int taxisID3;
   int taxisID4;
 
-  VarList varList1;
-  Field field1, field2;
-
-  CdiDateTime vDateTimes1[MaxSeasons]{};
-  CdiDateTime vDateTimes2[MaxSeasons]{};
-
-  std::vector<bool> vars1;
-  HistogramSet hsets[MaxSeasons];
-  std::vector<RecordInfo> recList;
-
-  int maxrecs;
-  int ntsteps;
-  int nvars;
   int pn;
 
+  VarList varList1;
+
 public:
   void
-  init()
+  init() override
   {
-    vars1 = std::vector<bool>(MaxSeasons, false);
-
     operator_input_arg("percentile number");
     pn = parameter_to_double(cdo_operator_argv(0));
 
@@ -84,15 +72,19 @@ public:
     streamID2 = cdo_open_read(1);
     streamID3 = cdo_open_read(2);
 
-    auto vlistID1 = cdo_stream_inq_vlist(streamID1);
+    vlistID1 = cdo_stream_inq_vlist(streamID1);
     auto vlistID2 = cdo_stream_inq_vlist(streamID2);
     auto vlistID3 = cdo_stream_inq_vlist(streamID3);
     auto vlistID4 = vlistDuplicate(vlistID1);
 
     vlist_unpack(vlistID4);
 
-    vlist_compare(vlistID1, vlistID2, CmpVlist::All);
-    vlist_compare(vlistID1, vlistID3, CmpVlist::All);
+    varList1 = VarList(vlistID1);
+    VarList varList2(vlistID2);
+    VarList varList3(vlistID3);
+
+    varList_compare(varList1, varList2);
+    varList_compare(varList1, varList3);
 
     taxisID1 = vlistInqTaxis(vlistID1);
     taxisID2 = vlistInqTaxis(vlistID2);
@@ -105,21 +97,24 @@ public:
 
     streamID4 = cdo_open_write(3);
     cdo_def_vlist(streamID4, vlistID4);
-
-    ntsteps = vlistNtsteps(vlistID1);
-    nvars = vlistNvars(vlistID1);
-
-    maxrecs = vlistNrecs(vlistID1);
-    recList = std::vector<RecordInfo>(maxrecs);
-
-    constFields = FieldVector(maxrecs);
-
-    varList_init(varList1, vlistID1);
   }
 
   void
-  run()
+  run() override
   {
+    Field field1, field2;
+    std::vector<bool> seasVars(MaxSeasons, false);
+    CdiDateTime vDateTimes1[MaxSeasons]{};
+    CdiDateTime vDateTimes2[MaxSeasons]{};
+    HistogramSet hsets[MaxSeasons];
+
+    auto numVars = varList1.numVars();
+    auto numSteps = varList1.numSteps();
+    auto maxRecords = varList1.numRecords();
+    std::vector<RecordInfo> recordList(maxRecords);
+
+    FieldVector constFields(maxRecords);
+
     int tsID = 0;
     while (true)
       {
@@ -142,26 +137,21 @@ public:
 
         set_date_time(vDateTimes2[seas], vDateTime);
 
-        if (!vars1[seas])
+        if (!seasVars[seas])
           {
-            vars1[seas] = true;
-            hsets[seas].create(nvars, ntsteps);
-            for (int varID = 0; varID < nvars; ++varID)
-              {
-                const auto &var = varList1[varID];
-                hsets[seas].createVarLevels(varID, var.nlevels, var.gridsize);
-              }
+            seasVars[seas] = true;
+            hsets[seas].create(numVars, numSteps);
+            for (const auto &var : varList1.vars) hsets[seas].createVarLevels(var.ID, var.nlevels, var.gridsize);
           }
 
         for (int recID = 0; recID < nrecs; ++recID)
           {
-            int varID, levelID;
-            cdo_inq_record(streamID2, &varID, &levelID);
-            const auto &var = varList1[varID];
+            auto [varID, levelID] = cdo_inq_record(streamID2);
+            const auto &var = varList1.vars[varID];
             field1.init(var);
             cdo_read_record(streamID2, field1);
 
-            cdo_inq_record(streamID3, &varID, &levelID);
+            (void) cdo_inq_record(streamID3);
             field2.init(var);
             cdo_read_record(streamID3, field2);
 
@@ -186,15 +176,14 @@ public:
 
         set_date_time(vDateTimes1[seas], vDateTime);
 
-        if (!vars1[seas]) cdo_abort("No data for season %d in %s and %s", seas, cdo_get_stream_name(1), cdo_get_stream_name(2));
+        if (!seasVars[seas]) cdo_abort("No data for season %d in %s and %s", seas, cdo_get_stream_name(1), cdo_get_stream_name(2));
 
         for (int recID = 0; recID < nrecs; ++recID)
           {
-            int varID, levelID;
-            cdo_inq_record(streamID1, &varID, &levelID);
-            const auto &var = varList1[varID];
+            auto [varID, levelID] = cdo_inq_record(streamID1);
+            const auto &var = varList1.vars[varID];
 
-            if (tsID == 0) recList[recID].set(varID, levelID);
+            if (tsID == 0) recordList[recID].set(varID, levelID);
 
             if (tsID == 0 && var.isConstant)
               {
@@ -225,17 +214,18 @@ public:
           taxisDefVdatetime(taxisID4, vDateTimes1[seas]);
           cdo_def_timestep(streamID4, otsID);
 
-          for (int recID = 0; recID < maxrecs; ++recID)
+          for (int recID = 0; recID < maxRecords; ++recID)
             {
-              auto [varID, levelID] = recList[recID].get();
-              if (otsID && varList1[varID].isConstant) continue;
+              auto [varID, levelID] = recordList[recID].get();
+              const auto &var = varList1.vars[varID];
+              if (otsID && var.isConstant) continue;
 
               cdo_def_record(streamID4, varID, levelID);
 
-              if (varList1[varID].isConstant) { cdo_write_record(streamID4, constFields[recID]); }
+              if (var.isConstant) { cdo_write_record(streamID4, constFields[recID]); }
               else
                 {
-                  field1.init(varList1[varID]);
+                  field1.init(var);
                   hsets[seas].getVarLevelPercentiles(field1, varID, levelID, pn);
                   cdo_write_record(streamID4, field1);
                 }
@@ -246,7 +236,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID4);
     cdo_stream_close(streamID3);
diff --git a/src/Yseasstat.cc b/src/Yseasstat.cc
index e9dfddffe2b539150dc633415d4b3ff6ff3e7a6e..0ece49912e3f0f594fcd1e901b0808c723f04383 100644
--- a/src/Yseasstat.cc
+++ b/src/Yseasstat.cc
@@ -22,9 +22,11 @@
 
 #include <cdi.h>
 
+#include "cdo_stepstat.h"
 #include "cdo_season.h"
 #include "datetime.h"
 #include "process_int.h"
+#include "progress.h"
 #include "field_functions.h"
 
 class Yseasstat : public Process
@@ -55,46 +57,29 @@ public:
     .constraints = { 1, 1, NoRestriction },
   };
   inline static RegisterEntry<Yseasstat> registration = RegisterEntry<Yseasstat>(module);
-  CdiDateTime vDateTimes[MaxSeasons]{};
-  FieldVector2D vars1[MaxSeasons], vars2[MaxSeasons], samp1[MaxSeasons];
+
+private:
   CdoStreamID streamID1;
   CdoStreamID streamID2;
   int taxisID1;
   int taxisID2;
-  int vlistID1;
-
-  VarList varList;
-  Field field;
 
-  std::vector<RecordInfo> recList;
-
-  int VARS_MEMTYPE = 0;
+  int vlistID1;
+  VarList varList1;
 
-  int operfunc;
-  int maxrecs;
+  int maxRecords;
+  std::vector<RecordInfo> recordList;
 
-  bool lrange;
-  bool lmean;
-  bool lstd;
-  bool lvarstd;
-  bool lvars2;
-  int divisor;
+  cdo::StepStat3D stepStat;
 
 public:
   void
-  init()
+  init() override
   {
-
     auto operatorID = cdo_operator_id();
-    operfunc = cdo_operator_f1(operatorID);
+    auto operfunc = cdo_operator_f1(operatorID);
 
-    auto lminmax = (operfunc == FieldFunc_Min || operfunc == FieldFunc_Max);
-    lrange = (operfunc == FieldFunc_Range);
-    lmean = (operfunc == FieldFunc_Mean || operfunc == FieldFunc_Avg);
-    lstd = (operfunc == FieldFunc_Std || operfunc == FieldFunc_Std1);
-    lvarstd = (lstd || operfunc == FieldFunc_Var || operfunc == FieldFunc_Var1);
-    lvars2 = (lvarstd || lrange);
-    divisor = (operfunc == FieldFunc_Std1 || operfunc == FieldFunc_Var1);
+    stepStat.init(operfunc);
 
     operator_check_argc(0);
 
@@ -103,7 +88,9 @@ public:
     vlistID1 = cdo_stream_inq_vlist(streamID1);
     auto vlistID2 = vlistDuplicate(vlistID1);
 
-    if (!lminmax) vlist_unpack(vlistID2);
+    varList1 = VarList(vlistID1);
+
+    if (!stepStat.lminmax) vlist_unpack(vlistID2);
 
     taxisID1 = vlistInqTaxis(vlistID1);
     taxisID2 = taxisDuplicate(taxisID1);
@@ -113,19 +100,22 @@ public:
     streamID2 = cdo_open_write(1);
     cdo_def_vlist(streamID2, vlistID2);
 
-    maxrecs = vlistNrecs(vlistID1);
-    recList = std::vector<RecordInfo>(maxrecs);
-
-    varList_init(varList, vlistID1);
-
-    if ((operfunc == FieldFunc_Min) || (operfunc == FieldFunc_Max)) VARS_MEMTYPE = FIELD_NAT;
+    maxRecords = varList1.numRecords();
+    recordList = std::vector<RecordInfo>(maxRecords);
   }
 
   void
-  run()
+  run() override
   {
-    auto field2_stdvar_func = lstd ? field2_std : field2_var;
-    auto fieldc_stdvar_func = lstd ? fieldc_std : fieldc_var;
+    Field field;
+    CdiDateTime vDateTimes[MaxSeasons]{};
+    FieldVector2D varsData1[MaxSeasons], varsData2[MaxSeasons], samp1[MaxSeasons];
+
+    int VARS_MEMTYPE = stepStat.lminmax ? FIELD_NAT : 0;
+    stepStat.set_dimlen0(MaxSeasons);
+
+    auto numSteps = varList1.numSteps();
+    cdo::Progress progress;
 
     int tsID = 0;
     int otsID = 0;
@@ -134,116 +124,47 @@ public:
         auto nrecs = cdo_stream_inq_timestep(streamID1, tsID);
         if (nrecs == 0) break;
 
+        if (numSteps > 1) progress.update((tsID + 1.0) / numSteps);
+
         auto vDateTime = taxisInqVdatetime(taxisID1);
 
-        auto seas = month_to_season(decode_month(vDateTime.date));
+        auto season = month_to_season(decode_month(vDateTime.date));
 
-        set_date_time(vDateTimes[seas], vDateTime);
+        set_date_time(vDateTimes[season], vDateTime);
 
-        if (!vars1[seas].size())
-          {
-            fields_from_vlist(vlistID1, samp1[seas]);
-            fields_from_vlist(vlistID1, vars1[seas], FIELD_VEC | VARS_MEMTYPE);
-            if (lvars2) fields_from_vlist(vlistID1, vars2[seas], FIELD_VEC);
-          }
+        if (!stepStat.var1(season).size()) { stepStat.alloc(season, varList1, VARS_MEMTYPE); }
 
+        auto numSets = seas_numSets[season];
         for (int recID = 0; recID < nrecs; ++recID)
           {
-            int varID, levelID;
-            cdo_inq_record(streamID1, &varID, &levelID);
-            const auto &var = varList[varID];
-
-            if (tsID == 0) recList[recID].set(varID, levelID);
-
-            auto &rsamp1 = samp1[seas][varID][levelID];
-            auto &rvars1 = vars1[seas][varID][levelID];
-
-            auto numSets = seas_numSets[seas];
-
-            if (numSets == 0)
-              {
-                cdo_read_record(streamID1, rvars1);
-                if (lrange) field_copy(rvars1, vars2[seas][varID][levelID]);
-
-                if (rvars1.numMissVals || !rsamp1.empty())
-                  {
-                    if (rsamp1.empty()) rsamp1.resize(rvars1.size);
-                    field2_vinit(rsamp1, rvars1);
-                  }
-              }
-            else
-              {
-                field.init(var);
-                cdo_read_record(streamID1, field);
-
-                if (field.numMissVals || !rsamp1.empty())
-                  {
-                    if (rsamp1.empty()) rsamp1.resize(rvars1.size, numSets);
-                    field2_vincr(rsamp1, field);
-                  }
-
-                // clang-format off
-                if      (lvarstd) field2_sumsumq(rvars1, vars2[seas][varID][levelID], field);
-                else if (lrange)  field2_maxmin(rvars1, vars2[seas][varID][levelID], field);
-                else              field2_function(rvars1, field, operfunc);
-                // clang-format on
-              }
+            auto [varID, levelID] = cdo_inq_record(streamID1);
+            if (tsID == 0) recordList[recID].set(varID, levelID);
+            field.init(varList1.vars[varID]);
+            cdo_read_record(streamID1, field);
+            stepStat.add_field(field, season, varID, levelID, numSets);
           }
 
-        if (seas_numSets[seas] == 0 && lvarstd)
-          for (int recID = 0; recID < maxrecs; ++recID)
-            {
-              auto [varID, levelID] = recList[recID].get();
-              if (varList[varID].isConstant) continue;
-
-              field2_moq(vars2[seas][varID][levelID], vars1[seas][varID][levelID]);
-            }
-
-        seas_numSets[seas]++;
+        seas_numSets[season]++;
         tsID++;
       }
 
-    for (int seas = 0; seas < MaxSeasons; ++seas)
-      if (seas_numSets[seas])
+    for (int season = 0; season < MaxSeasons; ++season)
+      if (seas_numSets[season])
         {
-          auto numSets = seas_numSets[seas];
-          for (int recID = 0; recID < maxrecs; ++recID)
-            {
-              auto [varID, levelID] = recList[recID].get();
-              if (varList[varID].isConstant) continue;
-
-              const auto &rsamp1 = samp1[seas][varID][levelID];
-              auto &rvars1 = vars1[seas][varID][levelID];
-
-              if (lmean)
-                {
-                  if (!rsamp1.empty())
-                    field2_div(rvars1, rsamp1);
-                  else
-                    fieldc_div(rvars1, (double) numSets);
-                }
-              else if (lvarstd)
-                {
-                  if (!rsamp1.empty())
-                    field2_stdvar_func(rvars1, vars2[seas][varID][levelID], rsamp1, divisor);
-                  else
-                    fieldc_stdvar_func(rvars1, vars2[seas][varID][levelID], numSets, divisor);
-                }
-              else if (lrange) { field2_sub(rvars1, vars2[seas][varID][levelID]); }
-            }
+          auto numSets = seas_numSets[season];
 
-          taxisDefVdatetime(taxisID2, vDateTimes[seas]);
+          cdo::records_process_3D(season, recordList, varList1, stepStat, numSets);
+
+          taxisDefVdatetime(taxisID2, vDateTimes[season]);
           cdo_def_timestep(streamID2, otsID);
 
-          for (int recID = 0; recID < maxrecs; ++recID)
+          for (int recID = 0; recID < maxRecords; ++recID)
             {
-              auto [varID, levelID] = recList[recID].get();
-              if (otsID && varList[varID].isConstant) continue;
-
-              auto &rvars1 = vars1[seas][varID][levelID];
+              auto [varID, levelID] = recordList[recID].get();
+              if (otsID && varList1.vars[varID].isConstant) continue;
 
               cdo_def_record(streamID2, varID, levelID);
-              cdo_write_record(streamID2, rvars1);
+              cdo_write_record(streamID2, stepStat.var1(season, varID, levelID));
             }
 
           otsID++;
@@ -251,7 +172,7 @@ public:
   }
 
   void
-  close()
+  close() override
   {
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
diff --git a/src/Zonstat.cc b/src/Zonstat.cc
index 1afafd1175d36ed96bb0fce4bd86936cc1e62ee0..efec8d4a019a2d15a4900002f760a1ca332153e8 100644
--- a/src/Zonstat.cc
+++ b/src/Zonstat.cc
@@ -23,8 +23,6 @@
 
 #include <cdi.h>
 
-#include <utility>
-
 #include "cdo_options.h"
 #include "cdo_vlist.h"
 #include "process_int.h"
@@ -120,9 +118,8 @@ public:
 
 public:
   void
-  init()
+  init() override
   {
-
     auto operatorID = cdo_operator_id();
     operfunc = cdo_operator_f1(operatorID);
 
@@ -149,13 +146,15 @@ public:
     auto vlistID1 = cdo_stream_inq_vlist(streamID1);
     auto vlistID2 = vlistDuplicate(vlistID1);
 
+    varList1 = VarList(vlistID1);
+
     if (!lminmax) vlist_unpack(vlistID2);
 
     taxisID1 = vlistInqTaxis(vlistID1);
     taxisID2 = taxisDuplicate(taxisID1);
     vlistDefTaxis(vlistID2, taxisID2);
 
-    auto ngrids = vlistNgrids(vlistID1);
+    auto ngrids = vlistNumGrids(vlistID1);
     for (int index = 0; index < ngrids; ++index)
       {
         auto gridID = vlistGrid(vlistID1, index);
@@ -221,18 +220,15 @@ public:
 
     if (Options::cdoChunkType == CDI_UNDEFID)
       {
-        auto nvars = vlistNvars(vlistID2);
-        for (int varID = 0; varID < nvars; ++varID) cdiDefKeyInt(vlistID2, varID, CDI_KEY_CHUNKTYPE, CDI_CHUNK_AUTO);
+        for (const auto &var : varList1.vars) cdiDefKeyInt(vlistID2, var.ID, CDI_KEY_CHUNKTYPE, CDI_CHUNK_AUTO);
       }
 
     streamID2 = cdo_open_write(1);
     cdo_def_vlist(streamID2, vlistID2);
 
-    varList_init(varList1, vlistID1);
-
     nlatmax = gridInqYsize(gridID2);
 
-    auto zonVar = varList1[0];
+    auto zonVar = varList1.vars[0];
     zonVar.gridID = gridID2;
     zonVar.gridsize = nlatmax;
     zonVar.nlevels = 1;
@@ -257,9 +253,9 @@ public:
 
     for (int recID = 0; recID < nrecs; ++recID)
       {
-        int varID, levelID;
-        cdo_inq_record(streamID1, &varID, &levelID);
-        field1.init(varList1[varID]);
+        auto [varID, levelID] = cdo_inq_record(streamID1);
+        const auto &var1 = varList1.vars[varID];
+        field1.init(var1);
         cdo_read_record(streamID1, field1);
         field1.grid = gridID1;
 
@@ -268,7 +264,7 @@ public:
         if (zongridID != -1 && zongridID == field1.grid) { field_ncopy(nlatmax, field1, field2); }
         else if (sourceGridIsRegular)
           {
-            if (is_healpix_grid(varList1[varID].gridID)) reorder_field(field1, hpRingIndices);
+            if (is_healpix_grid(var1.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); }
@@ -279,7 +275,7 @@ public:
   }
 
   void
-  run()
+  run() override
   {
     int tsID = 0;
     while (true)
@@ -291,9 +287,8 @@ public:
   }
 
   void
-  close()
+  close() override
   {
-
     cdo_stream_close(streamID2);
     cdo_stream_close(streamID1);
 
diff --git a/src/after_dvtrans.cc b/src/after_dvtrans.cc
index d0752c484bc679eb7cbcf667e08372195232df2d..a918e20ec4468cf855dc40d565139580cb784c88 100644
--- a/src/after_dvtrans.cc
+++ b/src/after_dvtrans.cc
@@ -4,12 +4,13 @@
   Author: Uwe Schulzweida
 
 */
-#include <cmath>
 
 #ifdef HAVE_CONFIG_H
 #include "config.h"
 #endif
 
+#include <cmath>
+
 #include "constants.h"
 
 #define SQUARE_RADIUS (-PlanetRadiusDefault * PlanetRadiusDefault)
diff --git a/src/after_fctrans.cc b/src/after_fctrans.cc
index 98c884d767309a664afd7df7b5cae68b96fe8c5d..867454e9c32c7516dddc6aa8d1cdd4704234b4be 100644
--- a/src/after_fctrans.cc
+++ b/src/after_fctrans.cc
@@ -4,12 +4,8 @@
   Author: Uwe Schulzweida
 
 */
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
 
 #include "varray.h"
-#include "process_int.h"
 #include "cdo_options.h"
 #include "cimdOmp.h"
 
diff --git a/src/after_sptrans.cc b/src/after_sptrans.cc
index fa2d5c4af3f528a8ecc9213a5c746eabe8726c60..69508cd196d316edd3abfcad51dfa17ab3a93949 100644
--- a/src/after_sptrans.cc
+++ b/src/after_sptrans.cc
@@ -7,10 +7,6 @@
 
 #include <cmath>
 
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
 #include "cdo_options.h"
 #include "cimdOmp.h"
 #include "constants.h"
diff --git a/src/afterburner.h b/src/afterburner.h
index 57fef16751861460bf27f61ac9e7eddaebba4427..83031634ef020b952909910f105c6f6d93de6563 100644
--- a/src/afterburner.h
+++ b/src/afterburner.h
@@ -88,14 +88,14 @@ struct AfterControl
   long DimFC = 0, DimGP = 0, DimSP = 0;
   long DimSP_half = 0;
 
-  Varray <double> poli;
-  Varray <double> pold;
-  Varray <double> pdev;
-  Varray <double> pol2;
-  Varray <double> pol3;
+  Varray<double> poli;
+  Varray<double> pold;
+  Varray<double> pdev;
+  Varray<double> pol2;
+  Varray<double> pol3;
 
-  Varray <double> dv2uv_f1;
-  Varray <double> dv2uv_f2;
+  Varray<double> dv2uv_f1;
+  Varray<double> dv2uv_f2;
 
   int NumCodesRequest = 0;
 
@@ -220,12 +220,13 @@ void after_EchamDependencies(struct Variable *vars, int ncodes, int type, int so
 
 void after_legini_setup(AfterControl &globs, struct Variable *vars);
 
+static cdo::Task *afterReadTask = nullptr;
+static bool afterReadAsync = true;
+
 template <typename... Args>
 void
 afterAbort(const std::string &format, Args const &...args)
 {
-  extern cdo::Task *afterReadTask;
-  extern bool afterReadAsync;
   if (afterReadAsync && afterReadTask) afterReadTask->wait();
   cdo_abort(format, args...);
 }
diff --git a/src/bitinformation.cc b/src/bitinformation.cc
index 8cec677d798ea885734111eaa99f71d9cd5feb81..c815a07099015d0689331f3472ae8a24ec45907c 100644
--- a/src/bitinformation.cc
+++ b/src/bitinformation.cc
@@ -6,7 +6,6 @@
 #include <cstdint>
 #include <cstdbool>
 #include <cmath>
-#include <cfloat>
 #include <vector>
 
 #ifdef CDO
@@ -42,8 +41,8 @@ struct BitpairCounters
 static double
 binom_confidence(size_t n, double c)
 {
-  const double v = 1.0 - (1.0 - c) * 0.5;
-  const double p = 0.5 + cdo::normal_inv(v) / (2.0 * std::sqrt(n));
+  double v = 1.0 - (1.0 - c) * 0.5;
+  double p = 0.5 + cdo::normal_inv(v) / (2.0 * std::sqrt(n));
   // printf("p = %g %g %g \n", p, cdo::normal_inv(v), v);
   return std::min(1.0, p);  // cap probability at 1 (only important for n small)
 }
@@ -83,7 +82,7 @@ entropy2(double p1, double p2)
 static double
 binom_free_entropy(size_t n, double c)
 {
-  const double p = binom_confidence(n, c);
+  double p = binom_confidence(n, c);
   return 1.0 - entropy2(p, 1.0 - p);
 }
 
@@ -113,10 +112,10 @@ bitpair_count_kernel(uint32_t a, uint32_t b, BitpairCounters &BC)
   uint32_t mask = 1;                    // start with least significant bit
   for (uint32_t i = 0; i < NBITS; ++i)  // loop from least to most significant bit
     {
-      const uint32_t j = ((a & mask) >> i);  // isolate that bit in a,b
-      const uint32_t k = ((b & mask) >> i);  // and move to 0x0 or 0x1s
-      BC.C[NBITS - i - 1][j][k] += 1;        // to be used as index j,k to increase counter C
-      mask <<= 1;                            // shift mask to get the next significant bit
+      uint32_t j = ((a & mask) >> i);  // isolate that bit in a,b
+      uint32_t k = ((b & mask) >> i);  // and move to 0x0 or 0x1s
+      BC.C[NBITS - i - 1][j][k] += 1;  // to be used as index j,k to increase counter C
+      mask <<= 1;                      // shift mask to get the next significant bit
     }
 }
 
@@ -150,9 +149,9 @@ bitpair_count(float *A, float *B, size_t n)
   for (size_t i = 0; i < n; ++i)
     {
 #ifdef CDO
-      const auto ompthID = cdo_omp_get_thread_num();
+      auto ompthID = cdo_omp_get_thread_num();
 #else
-      const auto ompthID = 0;
+      auto ompthID = 0;
 #endif
       bitpair_count_kernel(Auint[i], Buint[i], BC[ompthID]);  // count the bits and update counter array C
     }
@@ -182,8 +181,8 @@ mutual_information_kernel(double p[2][2])
   //@assert sum(p) ≈ one(T)     "Entries in p have to sum to 1"
   //@assert all(p .>= zero(T))  "Entries in p have to be >= 0"
 
-  const double py[2] = { p[0][0] + p[1][0], p[0][1] + p[1][1] };  // marginal probabilities of y
-  const double px[2] = { p[0][0] + p[0][1], p[1][0] + p[1][1] };  // marginal probabilities of x
+  double py[2] = { p[0][0] + p[1][0], p[0][1] + p[1][1] };  // marginal probabilities of y
+  double px[2] = { p[0][0] + p[0][1], p[1][0] + p[1][1] };  // marginal probabilities of x
 
   double M = 0.0;              // mutual information M
   for (int j = 0; j < 2; ++j)  // loop over all entries in p
diff --git a/src/cdi_uuid.h b/src/cdi_uuid.h
index e55d3a8959c4abd9c4393d1367ee78eddd3b4dc3..6b554f1faa5f5cd4d486dcc4e4b062aea6998947 100644
--- a/src/cdi_uuid.h
+++ b/src/cdi_uuid.h
@@ -1,10 +1,6 @@
 #ifndef CDI_UUID_H
 #define CDI_UUID_H
 
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
 #include "cdi.h"
 
 // clang-format off
diff --git a/src/cdo.cc b/src/cdo.cc
index 09cf0149452afd0f936947d159f62cb78a923946..325744caf080fc4b958e566f4ecf5e809a5912e2 100644
--- a/src/cdo.cc
+++ b/src/cdo.cc
@@ -16,7 +16,6 @@
 #include <iostream>
 #include <algorithm>
 #include <vector>
-#include <limits>
 
 #ifdef HAVE_EXECINFO_H
 #include <execinfo.h>
@@ -30,13 +29,14 @@
 #include <cfenv>
 #include <sys/stat.h>
 #include <sys/resource.h>  // getrlimit
-#include <unistd.h> /* sysconf */
+#include <unistd.h>        /* sysconf */
 #include <cstring>
 #include <cstdlib>
 #include <csignal>
 
 #include <cdi.h>
 
+#include "cpp_lib.h"
 #include "cdo_getopt.h"
 #include "cdo_rlimit.h"
 #include <mpim_grid.h>
@@ -44,7 +44,6 @@
 #include "cdo_default_values.h"
 #include "param_conversion.h"
 #include "progress.h"
-
 #include "module_info.h"
 #include "percentiles.h"
 #include "util_wildcards.h"
@@ -58,7 +57,6 @@
 #include "cdo_output.h"
 #include "cdo_features.h"
 #include "cdo_zaxis.h"
-#include "compare.h"
 #include "table.h"
 #include "datetime.h"
 #include "remap_grid_cell_search.h"
@@ -70,8 +68,9 @@
 static ProcessManager g_processManager;
 
 void
-cdo_exit()
+cdo_exit(std::string msg = "")
 {
+  (void) msg;
   g_processManager.kill_processes();
   exit(EXIT_FAILURE);
 }
@@ -178,6 +177,17 @@ cdo_version()
 #ifdef CXX_VERSION
   fprintf(fp, "CXX version : %s\n", CXX_VERSION);
 #endif
+  fprintf(fp, "CXX library :");
+#ifdef HAVE_LIB_RANGES
+  fprintf(fp, " ranges");
+#endif
+#ifdef HAVE_LIB_RANGES_ZIP
+  fprintf(fp, " ranges_zip");
+#endif
+#ifdef HAVE_LIB_MDSPAN
+  fprintf(fp, " mdspan");
+#endif
+  fprintf(fp, "\n");
 #endif
 #ifdef C_COMPILER
   fprintf(fp, "C Compiler: %s\n", C_COMPILER);
@@ -241,8 +251,8 @@ print_category(const std::string &p_category, FILE *p_target)
 static void
 cdo_usage()
 {
-  FILE *target = stderr;
-  std::string pad = CLIOptions::pad_size_terminal('-');
+  auto target = stderr;
+  auto pad = CLIOptions::pad_size_terminal('-');
   fprintf(target, "%s", pad.c_str());
   fprintf(target, "  Usage : cdo  [Options]  Operator1  [-Operator2  [-OperatorN]]\n");
   pad = CLIOptions::pad_size_terminal('-');
@@ -293,11 +303,7 @@ cdo_init_is_tty()
   fstat(0, &statbuf);
   if (S_ISCHR(statbuf.st_mode)) cdo::stdinIsTerminal = true;
   fstat(1, &statbuf);
-  if (S_ISCHR(statbuf.st_mode))
-    {
-      cdo::stdoutIsTerminal = true;
-      progress::stdoutIsTerminal = true;
-    }
+  if (S_ISCHR(statbuf.st_mode)) { cdo::stdoutIsTerminal = true; }
   fstat(2, &statbuf);
   if (S_ISCHR(statbuf.st_mode)) cdo::stderrIsTerminal = true;
 }
@@ -972,7 +978,7 @@ predefined_tables(int p_padding)
   constexpr int id_padding = 4;
   int padding = p_padding + id_padding;
   int numTables = tableInqNumber();
-  std::string tables{"Predefined tables: "};
+  std::string tables{ "Predefined tables: " };
   for (int id = 0; id < numTables; id++)
     {
       if (id % 7 == 6) tables += std::string("\n") + std::string(padding, ' ');
@@ -1246,19 +1252,19 @@ setup_cli_options()
 
   CLIOptions::option("help", "h")
       ->describe_argument("operator")
-      ->add_effect([&](const std::string &operator_name) { cdo_print_help(Factory::get_help(operator_name)); })
+      ->add_effect([&](const std::string &operator_name) { cdo_print_help(operator_name); })
       ->on_empty_argument([]() { cdo_usage(); })
       ->aborts_program(true)
       ->set_category("Help")
       ->add_help("Shows either help information for the given operator or the usage of CDO.");
 
   CLIOptions::option("history")
-      ->add_effect([&]() { Options::CDO_Append_History = 1; })
+      ->add_effect([&]() { Options::CDO_Append_History = true; })
       ->set_category("History")
       ->add_help("Do append to NetCDF \"history\" global attribute.");
 
   CLIOptions::option("no_history")
-      ->add_effect([&]() { Options::CDO_Append_History = 0; })
+      ->add_effect([&]() { Options::CDO_Append_History = false; })
       ->set_category("History")
       ->add_help("Do not append to NetCDF \"history\" global attribute.");
 
@@ -1402,7 +1408,6 @@ setup_cli_options()
       ->add_effect([&]() {
         Options::silentMode = true;
         MpMO::enable_silent_mode(Options::silentMode);
-        progress::silentMode = true;
       })
       ->set_category("Output")
       ->add_help("Silent mode.");
diff --git a/src/cdo_benchmark.h b/src/cdo_benchmark.h
index ab80c4541c818eddd28b633ec5db1f6121003d89..bcfa64d9c73ab209159842b77eca86d72786198e 100644
--- a/src/cdo_benchmark.h
+++ b/src/cdo_benchmark.h
@@ -5,6 +5,7 @@
 
 #include <iostream>
 #include <chrono>
+
 template <typename func>
 void
 cdo_benchmark(func lambda)
@@ -16,6 +17,7 @@ cdo_benchmark(func lambda)
   // time end
   std::cout << "time: " << elapsedm << " ns" << std::endl;
 }
+
 #define CDO_BENCH(x) cdo_benchmark(x)
 
 #else
diff --git a/src/cdo_cdi_wrapper.cc b/src/cdo_cdi_wrapper.cc
index 697f22fd472e1355eb14d241d33f4aee7bcf9bde..bcc99a3ce73c5044b1ad5f3cc1a85215f6144427 100644
--- a/src/cdo_cdi_wrapper.cc
+++ b/src/cdo_cdi_wrapper.cc
@@ -1,9 +1,15 @@
 #include <cdi.h>
-#include <cstring>
+
 #include <cstdlib>
 #include <vector>
 
-#include "cdi_int.h"
+//#include "cdi_int.h"
+extern "C" {
+void cdiDefTableID(int tableID);
+void gridGenXvals(int xsize, double xfirst, double xlast, double xinc, double *xvals);
+void gridGenYvals(int gridtype, int ysize, double yfirst, double ylast, double yinc, double *yvals);
+}
+
 #include "cdo_default_values.h"
 #include "cdo_cdi_wrapper.h"
 #include "cdo_output.h"
@@ -16,7 +22,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 +35,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
     }
 }
 
diff --git a/src/cdo_cmor.h b/src/cdo_cmor.h
index cc332a3b81fbd901f73caefb841dbf782896c53f..2ab094a4f4cc1ceaec2905a3e51587866fab384b 100644
--- a/src/cdo_cmor.h
+++ b/src/cdo_cmor.h
@@ -11,7 +11,7 @@ struct CmorVar
   bool remove = false;
   // missing value
   bool changemissval = false;
-  double missval_old = 0.0;
+  double missvalOld = 0.0;
   //
   bool lfactor = false;
   double factor = 0.0;
diff --git a/src/cdo_data.cc b/src/cdo_data.cc
new file mode 100644
index 0000000000000000000000000000000000000000..8ffd7d35a2575fbfa3340f8e345ec93d352cb6fa
--- /dev/null
+++ b/src/cdo_data.cc
@@ -0,0 +1,111 @@
+#include <cstddef>
+
+#include "cdo_data.h"
+#include "constants.h"
+#include "mpim_grid/grid_convert.h"
+
+static constexpr double etopoScale = 3.0;
+static constexpr double etopoOffset = 11000.0;
+static constexpr unsigned short etopo[] = {
+#include "etopo.dat"
+};
+
+static constexpr double tempScale = 500.0;
+static constexpr double tempOffset = -220.0;
+static constexpr unsigned short temp[] = {
+#include "temp.dat"
+};
+
+static constexpr double maskScale = 1.0;
+static constexpr double maskOffset = 0.0;
+static constexpr unsigned short mask[] = {
+#include "mask.dat"
+};
+
+struct PackedData
+{
+  double scale{};
+  double offset{};
+  const unsigned short *pdata = nullptr;
+  size_t size{ 0 };
+  PackedData(double _scale, double _offset, const unsigned short *_pdata, size_t _size)
+      : scale(_scale), offset(_offset), pdata(_pdata), size(_size)
+  {
+  }
+};
+
+namespace cdo
+{
+const PackedData topoData(etopoScale, etopoOffset, etopo, sizeof(etopo) / sizeof(unsigned short));
+const PackedData tempData(tempScale, tempOffset, temp, sizeof(temp) / sizeof(unsigned short));
+const PackedData maskData(maskScale, maskOffset, mask, sizeof(mask) / sizeof(unsigned short));
+
+Varray<float>
+unpack_data(const PackedData &packedData)
+{
+  auto datasize = packedData.size;
+  Varray<float> data(datasize);
+  for (size_t i = 0; i < datasize; ++i) data[i] = packedData.pdata[i] / packedData.scale - packedData.offset;
+  return data;
+}
+
+void
+fill_random(Varray<float> &varray)
+{
+  for (auto &v : varray) v = ((double) std::rand()) / ((double) RAND_MAX);
+}
+
+void
+fill_sincos(Varray<float> &varray, const Varray<double> &xvals, const Varray<double> &yvals)
+{
+  auto len = varray.size();
+  for (size_t i = 0; i < len; ++i) varray[i] = std::cos(1.0 * xvals[i]) * std::sin(2.0 * yvals[i]);
+}
+
+void
+fill_coshill(Varray<float> &varray, const Varray<double> &xvals, const Varray<double> &yvals)
+{
+  auto len = varray.size();
+  for (size_t i = 0; i < len; ++i) varray[i] = 2.0 - std::cos(std::acos(std::cos(xvals[i]) * std::cos(yvals[i])) / 1.2);
+}
+
+void
+fill_testfield(Varray<float> &varray, const Varray<double> &xvals, const Varray<double> &yvals)
+{
+  auto len = varray.size();
+  double xyz[3];
+  for (size_t i = 0; i < len; ++i)
+    {
+      gcLLtoXYZ(xvals[i], yvals[i], xyz);
+      auto x = xyz[0];
+      auto y = xyz[1];
+      auto z = xyz[2];
+      varray[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;
+    }
+}
+
+// Some Constants for creating temperatur and pressure for the standard atmosphere
+constexpr double T_ZERO = 213.0;
+constexpr double T_DELTA = 75.0;
+constexpr double SCALEHEIGHT = 10000.0;  // [m]
+
+double
+std_atm_temperatur(double height)
+{
+  // Compute the temperatur for the given height (in meters) according to the solution of the hydrostatic atmosphere
+  return (T_ZERO + T_DELTA * std::exp((-1) * (height / SCALEHEIGHT)));
+}
+
+double
+std_atm_pressure(double height)
+{
+  constexpr double P_ZERO = 1013.25;  // surface pressure [hPa]
+  constexpr double CC_R = 287.05;     // specific gas constant for air
+  constexpr double TMP4PRESSURE = (C_EARTH_GRAV * SCALEHEIGHT) / (CC_R * T_ZERO);
+
+  // Compute the pressure for the given height (in meters) according to the solution of the hydrostatic atmosphere
+  return (P_ZERO
+          * std::exp((-1) * TMP4PRESSURE * std::log((std::exp(height / SCALEHEIGHT) * T_ZERO + T_DELTA) / (T_ZERO + T_DELTA))));
+}
+
+}  // namespace cdo
diff --git a/src/cdo_data.h b/src/cdo_data.h
new file mode 100644
index 0000000000000000000000000000000000000000..278d113e3d1f5b3955de0722d9c3d78c182ed7fb
--- /dev/null
+++ b/src/cdo_data.h
@@ -0,0 +1,27 @@
+#ifndef CDO_DATA_H
+#define CDO_DATA_H
+
+#include "varray.h"
+
+struct PackedData;
+
+namespace cdo
+{
+
+extern const PackedData topoData;
+extern const PackedData tempData;
+extern const PackedData maskData;
+
+Varray<float> unpack_data(const PackedData &packedData);
+
+void fill_random(Varray<float> &varray);
+void fill_sincos(Varray<float> &varray, const Varray<double> &xvals, const Varray<double> &yvals);
+void fill_coshill(Varray<float> &varray, const Varray<double> &xvals, const Varray<double> &yvals);
+void fill_testfield(Varray<float> &varray, const Varray<double> &xvals, const Varray<double> &yvals);
+
+double std_atm_temperatur(double height);
+double std_atm_pressure(double height);
+
+}  // namespace cdo
+
+#endif
diff --git a/src/cdo_default_values.h b/src/cdo_default_values.h
index 85565a217b3a4a83cfe7fb5d3bdc5b455b33993d..4a334ca2dfef1caf761ae3b90803cce03269009f 100644
--- a/src/cdo_default_values.h
+++ b/src/cdo_default_values.h
@@ -1,8 +1,6 @@
 #ifndef CDO_DEFAULT_VALUES_H
 #define CDO_DEFAULT_VALUES_H
 
-#include "cdi.h"
-
 namespace CdoDefault
 {
 extern int FileType;
diff --git a/src/cdo_features.cc b/src/cdo_features.cc
index e8d4c1a014318eae868cc105a95274b0f31736a5..c6d7dcc72b27d931c1849f0b792451df264f04ab 100644
--- a/src/cdo_features.cc
+++ b/src/cdo_features.cc
@@ -14,6 +14,8 @@
 
 #include <cdi.h>
 
+#include <map>
+
 #ifdef HAVE_NETCDF_META_H
 #include <netcdf_meta.h>
 #endif
@@ -45,9 +47,10 @@ extern "C"
 }
 #endif
 
-#include <stdio.h>
+#include <cstdio>
+#include <cstring>
+#include <iostream>
 
-#include "process_int.h"
 #include "cdo_features.h"
 #include "cdo_rlimit.h"
 #include "cimdOmp.h"
@@ -136,6 +139,7 @@ static std::map<std::string, std::pair<std::string, bool>> configMap
         { "has-threads", { "PTHREADS", have_threads } },
         { "has-wordexp", { "WORDEXP", have_wordexp } },
         { "has-hirlam_extensions", { "HIRLAM_EXTENSIONS", has_hirlam_extensions } } };
+
 void
 cdo_print_features(void)
 {
@@ -152,10 +156,23 @@ cdo_print_features(void)
   if (numProcs < concurrentThreads) fprintf(fp, "%u/", numProcs);
 #endif
   fprintf(fp, "%uthreads", concurrentThreads);
+#ifdef HAVE_OPENMP4
+  auto numDevices = omp_get_num_devices();
+  if (numDevices > 0) fprintf(fp, " %ddevices", numDevices);
+#endif
   fprintf(fp, " c++%d", (int) ((__cplusplus - 200000) / 100));
+#ifdef __FAST_MATH__
+  fprintf(fp, "/fastmath");
+#endif
 #ifdef _OPENMP
   fprintf(fp, " OpenMP");
-#if defined(HAVE_OPENMP45)
+#if defined(HAVE_OPENMP52)
+  fprintf(fp, "52");
+#elif defined(HAVE_OPENMP51)
+  fprintf(fp, "51");
+#elif defined(HAVE_OPENMP5)
+  fprintf(fp, "5");
+#elif defined(HAVE_OPENMP45)
   fprintf(fp, "45");
 #elif defined(HAVE_OPENMP4)
   fprintf(fp, "4");
diff --git a/src/cdo_features.h b/src/cdo_features.h
index 05c0322574951b2bf5e41ad68b0b7eb024518277..b2f58f82944d3065778cfe4ecc1552c58c8b5d86 100644
--- a/src/cdo_features.h
+++ b/src/cdo_features.h
@@ -1,6 +1,6 @@
 #ifndef CDO_FEATURES_H
 #define CDO_FEATURES_H
-#include <map>
+
 #include <string>
 
 namespace CdoFeatures
diff --git a/src/cdo_fft.cc b/src/cdo_fft.cc
index effe454617191bd0666221e8ba4a2b38b4252dbc..7e17ae6d34d648ac759ffc9281008eae395341ec 100644
--- a/src/cdo_fft.cc
+++ b/src/cdo_fft.cc
@@ -11,11 +11,9 @@ fft(double *real, double *imag, int n, int sign)
 {
   // n must be a power of 2
   // sign should be 1 (FT) or -1 (reverse FT)
-  int j, j1, j2;
-  int bit;
 
   // Bit reversal part
-  for (int i = j = 0; i < n; ++i)  // The bit pattern of i and j are reverse
+  for (int i = 0, j = 0, bit; i < n; ++i)  // The bit pattern of i and j are reverse
     {
       if (i > j) std::swap(real[i], real[j]);
       if (i > j) std::swap(imag[i], imag[j]);
@@ -27,29 +25,28 @@ fft(double *real, double *imag, int n, int sign)
   // Danielson-Lanczos Part
   for (int step = 1; step < n; step <<= 1)
     {
-      const auto w_r = std::cos(M_PI / step);
-      const auto w_i = std::sin(M_PI / step) * sign;
+      auto w_r = std::cos(M_PI / step);
+      auto w_i = std::sin(M_PI / step) * sign;
       double ww_r = 1.0;
       double ww_i = 0.0;
       for (int i = 0; i < step; ++i)
         {
-          double temp_r, temp_i;
-          for (j1 = i, j2 = i + step; j2 < n; j1 += 2 * step, j2 += 2 * step)
+          for (int j1 = i, j2 = i + step; j2 < n; j1 += 2 * step, j2 += 2 * step)
             {
-              temp_r = ww_r * real[j2] - ww_i * imag[j2];
-              temp_i = ww_r * imag[j2] + ww_i * real[j2];
+              auto temp_r = ww_r * real[j2] - ww_i * imag[j2];
+              auto temp_i = ww_r * imag[j2] + ww_i * real[j2];
               real[j2] = real[j1] - temp_r;
               imag[j2] = imag[j1] - temp_i;
               real[j1] += temp_r;
               imag[j1] += temp_i;
             }
-          temp_r = ww_r;
+          auto temp_r = ww_r;
           ww_r = ww_r * w_r - ww_i * w_i;
           ww_i = temp_r * w_i + ww_i * w_r;
         }
     }
 
-  const auto norm = 1.0 / std::sqrt(n);
+  auto norm = 1.0 / std::sqrt(n);
   for (int i = 0; i < n; ++i) real[i] *= norm;
   for (int i = 0; i < n; ++i) imag[i] *= norm;
 }
@@ -74,8 +71,8 @@ ft(double *real, double *imag, int n, int sign)
 
   for (int k = 0; k < n; ++k)
     {
-      const auto w_r = std::cos(2 * M_PI * k / n);
-      const auto w_i = std::sin(2 * M_PI * k / n) * sign;
+      auto w_r = std::cos(2 * M_PI * k / n);
+      auto w_i = std::sin(2 * M_PI * k / n) * sign;
       double ww_r = 1.0;
       double ww_i = 0.0;
       double sum_r = 0.0;
@@ -92,7 +89,7 @@ ft(double *real, double *imag, int n, int sign)
       work_i[k] = sum_i;
     }
 
-  const auto norm = 1. / std::sqrt(n);
+  auto norm = 1. / std::sqrt(n);
   for (int k = 0; k < n; ++k) real[k] = work_r[k] * norm;
   for (int k = 0; k < n; ++k) imag[k] = work_i[k] * norm;
 }
@@ -106,8 +103,8 @@ ft_r(double *real, double *imag, int n, int sign, double *work_r, double *work_i
 
   for (int k = 0; k < n; ++k)
     {
-      const auto w_r = std::cos(2 * M_PI * k / n);
-      const auto w_i = std::sin(2 * M_PI * k / n) * sign;
+      auto w_r = std::cos(2 * M_PI * k / n);
+      auto w_i = std::sin(2 * M_PI * k / n) * sign;
       double ww_r = 1.0;
       double ww_i = 0.0;
       double sum_r = 0.0;
@@ -124,7 +121,7 @@ ft_r(double *real, double *imag, int n, int sign, double *work_r, double *work_i
       work_i[k] = sum_i;
     }
 
-  const auto norm = 1.0 / std::sqrt(n);
+  auto norm = 1.0 / std::sqrt(n);
   for (int k = 0; k < n; ++k) real[k] = work_r[k] * norm;
   for (int k = 0; k < n; ++k) imag[k] = work_i[k] * norm;
 }
diff --git a/src/cdo_fill.cc b/src/cdo_fill.cc
index e3db202d91edfea8814486ae6b4a6d34deb91cc8..c4eb232f22c561c40476719564e9002b70b94ed0 100644
--- a/src/cdo_fill.cc
+++ b/src/cdo_fill.cc
@@ -5,34 +5,27 @@
 
 */
 
+#include "cdi.h"
 #include "cdo_fill.h"
-#include "cdo_vlist.h"
-#include "process_int.h"
+#include "cdo_varlist.h"
 
 void
 cdo_fill_ts(int p_vlistID, Varray2D<double> &p_varData)
 {
-  const auto nvars = vlistNvars(p_vlistID);
-  p_varData.resize(nvars);
-  for (int varID = 0; varID < nvars; ++varID)
-    {
-      const auto gridsize = gridInqSize(vlistInqVarGrid(p_vlistID, varID));
-      const auto nlev = zaxisInqSize(vlistInqVarZaxis(p_vlistID, varID));
-      p_varData[varID].resize(nlev * gridsize);
-    }
+  VarList varList(p_vlistID);
+  p_varData.resize(varList.numVars());
+  for (const auto &var : varList.vars) { p_varData[var.ID].resize(var.nlevels * var.gridsize); }
 }
 
 void
 cdo_fill_ts(int p_vlistID, Varray2D<double> &p_varData, Varray2D<size_t> &p_varNmiss)
 {
-  const auto nvars = vlistNvars(p_vlistID);
-  p_varData.resize(nvars);
-  p_varNmiss.resize(nvars);
-  for (int varID = 0; varID < nvars; ++varID)
+  VarList varList(p_vlistID);
+  p_varData.resize(varList.numVars());
+  p_varNmiss.resize(varList.numVars());
+  for (const auto &var : varList.vars)
     {
-      const auto gridsize = gridInqSize(vlistInqVarGrid(p_vlistID, varID));
-      const auto nlev = zaxisInqSize(vlistInqVarZaxis(p_vlistID, varID));
-      p_varData[varID].resize(nlev * gridsize);
-      p_varNmiss[varID].resize(nlev);
+      p_varData[var.ID].resize(var.nlevels * var.gridsize);
+      p_varNmiss[var.ID].resize(var.nlevels);
     }
 }
diff --git a/src/cdo_getopt.cc b/src/cdo_getopt.cc
index 83f3ad3016d0439bea19429a2f95e080a5b952a3..bcdf0e42cc78501cd532b52d24e9d57ff796e403 100644
--- a/src/cdo_getopt.cc
+++ b/src/cdo_getopt.cc
@@ -5,16 +5,14 @@
 
 */
 
-#include <string.h>
-#include <assert.h>
 #include "cdo_getopt.h"
 #include "util_string.h"
+
 #include <cstdio>
 #include <sstream>
 #include <cstdlib>
 
 #include <vector>
-#include <set>
 #include <map>
 
 #define CLIOP_DBG false
@@ -232,10 +230,11 @@ CLIOptions::print_envvar_help()
     {
       if (!it.second->isInternal)
         {
+          auto len0 = helpString.str().size();
           helpString << std::string(4, ' ');
           helpString << it.second->name;
           if (it.second->hasArgument) helpString << " <" + it.second->argument.description + "> ";
-          int spaceLeft = padding - helpString.str().size();
+          int spaceLeft = padding - helpString.str().size() + len0;
           if (spaceLeft < 0) spaceLeft = 0;
           for (auto &line : it.second->description) { helpString << std::string(spaceLeft, ' ') + line + "\n"; }
         }
@@ -279,6 +278,7 @@ CLIOptions::print_options_help(const std::string &p_category)
     }
   return help.str();
 }
+
 #include <algorithm>
 
 cdo_option_2 *
diff --git a/src/cdo_getopt.h b/src/cdo_getopt.h
index e5e26d5c4d89b84499c354cc183b8e7b5cfaac35..5d8be9c0a9736199faf33225bd0f7cf056bf79cf 100644
--- a/src/cdo_getopt.h
+++ b/src/cdo_getopt.h
@@ -111,7 +111,6 @@ struct cdo_option_2
   cdo_option_2 *
   add_help(std::string desc...)
   {
-
     description.push_back(desc);
     return this;
   }
diff --git a/src/cdo_magics_mapper.cc b/src/cdo_magics_mapper.cc
index 843d2a1fd31f700edf642a3fb5177912038c8a5c..ddea97be0f91209c499804063f469a1ede0ee0e1 100644
--- a/src/cdo_magics_mapper.cc
+++ b/src/cdo_magics_mapper.cc
@@ -1,7 +1,3 @@
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
 #include <cstdio>
 #include <cstdlib>
 #include <cstring>
diff --git a/src/cdo_module.cc b/src/cdo_module.cc
index dc408d9638fdd6b8a9fab5f3cf9d3cf1d16332ad..0e47953d78196d08d5d2274a7a5e9c938e1993da 100644
--- a/src/cdo_module.cc
+++ b/src/cdo_module.cc
@@ -1,4 +1,7 @@
 #include "cdo_module.h"
+#include "cdo_output.h"
+
+#include <algorithm>
 
 oper_t::oper_t() : help(default_help) {}
 
@@ -13,10 +16,12 @@ oper_t::oper_t(const char *_name, int _f1, int _f2, const char *_enter, const Cd
     : 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) {}
@@ -91,7 +96,6 @@ CdoModule::is_alias(const std::string &subject) const
 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;
diff --git a/src/cdo_module.h b/src/cdo_module.h
index 4fce514a429d8feb42dd98cab2ebc2b5524249f7..e1c9ad3e10217bfac9a46dc77be13859c0180333 100644
--- a/src/cdo_module.h
+++ b/src/cdo_module.h
@@ -11,8 +11,8 @@
 #include <string>
 #include <vector>
 #include <map>
+#include <any>
 
-#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
@@ -65,6 +65,8 @@ public:
   oper_t(int _f1, int _f2, const char *_name, const char *_enter);
 };
 
+
+#include "oper_args.h"
 struct CdoModule
 {
 
@@ -75,6 +77,7 @@ public:
   short mode;    // Module mode: 0:intern 1:extern
   short number;  // Allowed number type
   module_constraints constraints;
+  Arguments arguments = {};
 
   std::string toString() const;
 
diff --git a/src/cdo_options.cc b/src/cdo_options.cc
index 465f1f9768bdc41676c9a0fd984da905defb76ff..e77371ce8585eac8a510db5841c349463470a928 100644
--- a/src/cdo_options.cc
+++ b/src/cdo_options.cc
@@ -9,9 +9,11 @@
 
 #include <cdi.h>
 #include "cdo_options.h"
+#include "util_string.h"
 #include "cdo_output.h"
 
 #include <cstring>
+#include <algorithm>
 
 namespace cdo
 {
@@ -74,6 +76,7 @@ bool cdoOverwriteMode = false;
 bool cdoParIO = false;
 bool cdoRegulargrid = false;
 std::vector<std::string> cdoVarnames;
+
 size_t
 cdo_num_varnames()
 {
@@ -167,3 +170,57 @@ cdo_get_search_radius(void)
   searchRadius = std::clamp(searchRadius, 0.0, 180.0);
   return searchRadius;
 }
+
+void
+cdoPrintAttributes(FILE *fp, int cdiID, int varID, int nblanks)
+{
+  int natts;
+  cdiInqNatts(cdiID, varID, &natts);
+
+  for (int ia = 0; ia < natts; ++ia)
+    {
+      char attname[CDI_MAX_NAME];
+      int atttype, attlen;
+      cdiInqAtt(cdiID, varID, ia, attname, &atttype, &attlen);
+
+      if (atttype == CDI_DATATYPE_INT8 || atttype == CDI_DATATYPE_UINT8 || atttype == CDI_DATATYPE_INT16
+          || atttype == CDI_DATATYPE_UINT16 || atttype == CDI_DATATYPE_INT32 || atttype == CDI_DATATYPE_UINT32)
+        {
+          std::vector<int> attint(attlen);
+          cdiInqAttInt(cdiID, varID, attname, attlen, attint.data());
+          fprintf(fp, "%*s", nblanks, "");
+          fprintf(fp, "%s = ", attname);
+          for (int i = 0; i < attlen; ++i)
+            {
+              if (i) fprintf(fp, ", ");
+              fprintf(fp, "%d", attint[i]);
+            }
+          fprintf(fp, "\n");
+        }
+      else if (atttype == CDI_DATATYPE_FLT32 || atttype == CDI_DATATYPE_FLT64)
+        {
+          char fltstr[128];
+          std::vector<double> attflt(attlen);
+          cdiInqAttFlt(cdiID, varID, attname, attlen, attflt.data());
+          fprintf(fp, "%*s", nblanks, "");
+          fprintf(fp, "%s = ", attname);
+          for (int i = 0; i < attlen; ++i)
+            {
+              if (i) fprintf(fp, ", ");
+              if (atttype == CDI_DATATYPE_FLT32)
+                fprintf(fp, "%sf", double_to_att_str(Options::CDO_flt_digits, fltstr, sizeof(fltstr), attflt[i]));
+              else
+                fprintf(fp, "%s", double_to_att_str(Options::CDO_dbl_digits, fltstr, sizeof(fltstr), attflt[i]));
+            }
+          fprintf(fp, "\n");
+        }
+      else if (atttype == CDI_DATATYPE_TXT)
+        {
+          std::vector<char> atttxt(attlen + 1);
+          cdiInqAttTxt(cdiID, varID, attname, attlen, atttxt.data());
+          atttxt[attlen] = 0;
+          fprintf(fp, "%*s", nblanks, "");
+          fprintf(fp, "%s = \"%s\"\n", attname, atttxt.data());
+        }
+    }
+}
diff --git a/src/cdo_options.h b/src/cdo_options.h
index 4972f03094c77ee6fb52283f8f464661aadbdee4..13419b0ac74b2305da37fd0de645f554cb7072ba 100644
--- a/src/cdo_options.h
+++ b/src/cdo_options.h
@@ -98,5 +98,6 @@ void set_compression(int fileID, int filetype);
 
 void cdo_set_search_radius(double searchRadius);
 double cdo_get_search_radius(void);
+void cdoPrintAttributes(FILE *fp, int cdiID, int varID, int nblanks);
 
 #endif
diff --git a/src/cdo_output.cc b/src/cdo_output.cc
index da3b94c7bf1908cf45e4f8633cfb86dba1d5c08c..4939bb3e2421638a1793762fa6f522bf380719fb 100644
--- a/src/cdo_output.cc
+++ b/src/cdo_output.cc
@@ -9,8 +9,6 @@
 #include "config.h"
 #endif
 
-#include <stdarg.h>
-#include <errno.h>
 #include <cdi.h>
 #include <pthread.h>
 #include <string>
@@ -36,7 +34,6 @@ unsigned FACTORY = 0;
 unsigned KVLIST = 0;
 unsigned MODULE_INFO = 0;
 
-
 std::string debug_option_string = "DebugLevels:\n"
                                   "     0: off \n"
                                   "     1: all debugs messages enabled\n"
@@ -162,7 +159,7 @@ dbg()
 }
 
 void
-default_exit()
+default_exit(std::string msg = "")
 {
   exit(EXIT_FAILURE);
 }
@@ -171,11 +168,11 @@ default_context()
 {
   return "cdo init";
 }
-void (*exitProgram)(void) = default_exit;
+void (*exitProgram)(std::string) = default_exit;
 const char *(*getContext)(void) = default_context;
 
 void
-set_exit_function(void (*func)(void))
+set_exit_function(void (*func)(std::string str))
 {
   exitProgram = func;
 }
@@ -269,7 +266,7 @@ cdi_open_error(int cdiErrno, const std::string &format, const char *path)
         }
     }
 
-  if (MpMO::exitOnError) cdo::exitProgram();
+  if (MpMO::exitOnError) cdo::exitProgram("cdi_open_error");
 }
 
 void
diff --git a/src/cdo_output.h b/src/cdo_output.h
index 761848ed204449344ebabe8cae070664502c4a88..ee9d0f22ad1703eba062cbd8a6da3cdf37ecc714 100644
--- a/src/cdo_output.h
+++ b/src/cdo_output.h
@@ -30,14 +30,13 @@ void query_user_exit(const char *argument);
 
 namespace cdo
 {
-
 void parse_debug_arguments(const std::vector<std::string> &tokens, unsigned &cdoDebugLevel, unsigned &cdiDebugLevel);
 void print_debug_levels(unsigned cdoDebugLevel, unsigned cdiDebugLevel);
 void set_debug(unsigned p_debug_level);
 bool dbg();
-extern void (*exitProgram)(void);
+extern void (*exitProgram)(std::string);
 extern const char *(*getContext)(void);
-void set_exit_function(void (*func)(void));
+void set_exit_function(void (*func)(std::string msg));
 void set_context_function(const char *(*func)(void) );
 }  // namespace cdo
 
@@ -46,11 +45,11 @@ std::string cdo_argv_to_string(const std::vector<std::string> &argv);
 
 template <typename... Args>
 void
-cdo_abort(const std::string &format, Args const &...args) noexcept
+cdo_abort(const std::string &format, Args const &...args)
 {
   fflush(stdout);
-  MpMO::PrintCerr(Red("\n%s (Abort): ") + format, cdo::getContext(), args...);
-  if (MpMO::exitOnError) cdo::exitProgram();
+  std::string errmsg = MpMO::PrintCerr(Red("\n%s (Abort): ") + format, cdo::getContext(), args...);
+  if (MpMO::exitOnError) cdo::exitProgram(errmsg);
 }
 
 template <typename... Args>
@@ -71,7 +70,7 @@ cdo_warning(const std::string &format, Args const &...args) noexcept
       if (MpMO::pedantic)
         {
           MpMO::PrintCerr(Red("%s (Warning): ") + format, cdo::getContext(), args...);
-          if (MpMO::exitOnError) cdo::exitProgram();
+          if (MpMO::exitOnError) cdo::exitProgram("cdo_warning (pedantic)");
         }
       else { MpMO::PrintCerr(Yellow("%s (Warning): ") + format, cdo::getContext(), args...); }
     }
diff --git a/src/cdo_read.cc b/src/cdo_read.cc
index 8c57716fb32b362cbd64b1dd1818787c436ef224..8ca39b4ce741403861b9534385c30e63aabebd6e 100644
--- a/src/cdo_read.cc
+++ b/src/cdo_read.cc
@@ -9,6 +9,7 @@
 
 #include "cdo_options.h"
 #include "cdo_output.h"
+#include "cdo_varlist.h"
 #include "compare.h"
 #include "cdi_lockedIO.h"
 #include "varray.h"
@@ -17,18 +18,15 @@ int
 cdo_read_timestepmask(const char *maskfile, std::vector<bool> &imask)
 {
   auto streamID = stream_open_read_locked(maskfile);
-  const auto vlistID = streamInqVlist(streamID);
+  auto vlistID = streamInqVlist(streamID);
+  VarList varList(vlistID);
+  const auto &var0 = varList.vars[0];
 
-  const auto nvars = vlistNvars(vlistID);
-  if (nvars > 1) cdo_abort("timestepmask %s contains more than one variable!", maskfile);
+  if (varList.numVars() > 1) cdo_abort("timestepmask %s contains more than one variable!", maskfile);
+  if (var0.nlevels > 1) cdo_abort("timestepmask %s has more than one level!", maskfile);
+  if (var0.gridsize > 1) cdo_abort("timestepmask %s has more than one gridpoint!", maskfile);
 
-  const auto nlev = zaxisInqSize(vlistInqVarZaxis(vlistID, 0));
-  if (nlev > 1) cdo_abort("timestepmask %s has more than one level!", maskfile);
-
-  const auto gridsize = gridInqSize(vlistInqVarGrid(vlistID, 0));
-  if (gridsize > 1) cdo_abort("timestepmask %s has more than one gridpoint!", maskfile);
-
-  auto nts = vlistNtsteps(vlistID);
+  auto nts = varList.numSteps();
   if (nts == -1)
     {
       nts = 0;
@@ -71,20 +69,16 @@ cdo_read_timestepmask(const char *maskfile, std::vector<bool> &imask)
 static void
 read_one_field(const char *text, const char *filename, Varray<double> &array)
 {
-  const auto streamID = stream_open_read_locked(filename);
-  const auto vlistID = streamInqVlist(streamID);
-
-  const auto nvars = vlistNvars(vlistID);
-  if (nvars > 1) cdo_abort("%s file %s contains more than one variable!", text, filename);
-
-  const auto nlev = zaxisInqSize(vlistInqVarZaxis(vlistID, 0));
-  if (nlev > 1) cdo_abort("%s file %s has more than one level!", text, filename);
+  auto streamID = stream_open_read_locked(filename);
+  auto vlistID = streamInqVlist(streamID);
+  VarList varList(vlistID);
+  const auto &var0 = varList.vars[0];
 
-  const auto nrecs = streamInqTimestep(streamID, 0);
-  if (nrecs != 1) cdo_abort("%s file %s contains more than one field!", text, filename);
+  if (varList.numVars() > 1) cdo_abort("%s file %s contains more than one variable!", text, filename);
+  if (var0.nlevels > 1) cdo_abort("%s file %s has more than one level!", text, filename);
+  if (varList.numRecords() != 1) cdo_abort("%s file %s contains more than one field!", text, filename);
 
-  const auto gridsize = gridInqSize(vlistInqVarGrid(vlistID, 0));
-  array.resize(gridsize);
+  array.resize(var0.gridsize);
 
   int varID, levelID;
   size_t numMissVals;
@@ -99,7 +93,7 @@ cdo_read_mask(const char *maskfile, std::vector<bool> &imask)
   Varray<double> array;
   read_one_field("Mask", maskfile, array);
 
-  const auto gridsize = array.size();
+  auto gridsize = array.size();
   imask.resize(gridsize);
 
   for (size_t i = 0; i < gridsize; ++i) imask[i] = is_not_equal(array[i], 0);
@@ -113,7 +107,7 @@ cdo_read_index(const char *indexfile, std::vector<int> &index)
   Varray<double> array;
   read_one_field("Index", indexfile, array);
 
-  const auto gridsize = array.size();
+  auto gridsize = array.size();
   index.resize(gridsize);
 
   for (size_t i = 0; i < gridsize; ++i) index[i] = (int) std::lround(array[i]) - 1;
diff --git a/src/cdo_stepstat.h b/src/cdo_stepstat.h
new file mode 100644
index 0000000000000000000000000000000000000000..93e2a59b22aae11fe60d199fc55eeaebfe213342
--- /dev/null
+++ b/src/cdo_stepstat.h
@@ -0,0 +1,419 @@
+#ifndef CDO_STEPSTAT_H
+#define CDO_STEPSTAT_H
+
+#include "process_int.h"
+#include "field.h"
+#include "field_functions.h"
+
+namespace cdo
+{
+class StepStatBase
+{
+public:
+  int operfunc{};
+  bool lminmax{ false };
+  bool lminidx{ false };
+  bool lmaxidx{ false };
+  bool lrange{ false };
+  bool lmean{ false };
+  bool lmeanavg{ false };
+  bool lstd{ false };
+  bool lvarstd{ false };
+  double divisor{};
+
+  void
+  init(int _operfunc)
+  {
+    operfunc = _operfunc;
+    lminmax = (operfunc == FieldFunc_Min || operfunc == FieldFunc_Max);
+    lminidx = (operfunc == FieldFunc_Minidx);
+    lmaxidx = (operfunc == FieldFunc_Maxidx);
+    lrange = (operfunc == FieldFunc_Range);
+    lmean = (operfunc == FieldFunc_Mean);
+    lmeanavg = (operfunc == FieldFunc_Mean || operfunc == FieldFunc_Avg);
+    lstd = (operfunc == FieldFunc_Std || operfunc == FieldFunc_Std1);
+    lvarstd = (lstd || operfunc == FieldFunc_Var || operfunc == FieldFunc_Var1);
+    divisor = (operfunc == FieldFunc_Std1 || operfunc == FieldFunc_Var1);
+  }
+
+  void
+  add_field_kernel(const Field &field, Field &samp, Field &var1, Field &var2, int numSets)
+  {
+    if (numSets == 0)
+      {
+        if (lminidx || lmaxidx)
+          field_fill(var1, 0.0);
+        else
+          field_copy(field, var1);
+
+        if (lrange || lminidx || lmaxidx) field_copy(field, var2);
+
+        if (lvarstd) field2_moq(var2, var1);
+
+        if (field.numMissVals || !samp.empty())
+          {
+            if (samp.empty()) samp.resize(var1.size);
+            field2_vinit(samp, field);
+          }
+      }
+    else
+      {
+        if (field.numMissVals || !samp.empty())
+          {
+            if (samp.empty()) samp.resize(var1.size, numSets);
+            field2_vincr(samp, field);
+          }
+
+        // clang-format off
+        if      (lvarstd) field2_sumsumq(var1, var2, field);
+        else if (lrange)  field2_maxmin(var1, var2, field);
+        else if (lminidx) field2_minidx(var1, var2, field, numSets);
+        else if (lmaxidx) field2_maxidx(var1, var2, field, numSets);
+        else              field2_function(var1, field, operfunc);
+        // clang-format on
+      }
+  }
+
+  void
+  process_kernel(const Field &samp, Field &var1, const Field &var2, int numSets)
+  {
+    auto field2_stdvar_func = lstd ? field2_std : field2_var;
+    auto fieldc_stdvar_func = lstd ? fieldc_std : fieldc_var;
+
+    if (lmeanavg)
+      {
+        if (!samp.empty())
+          field2_div(var1, samp);
+        else
+          fieldc_div(var1, (double) numSets);
+      }
+    else if (lvarstd)
+      {
+        if (!samp.empty())
+          field2_stdvar_func(var1, var2, samp, divisor);
+        else
+          fieldc_stdvar_func(var1, var2, numSets, divisor);
+      }
+    else if (lrange) { field2_sub(var1, var2); }
+  }
+};
+
+class StepStat1Dvars : public StepStatBase
+{
+private:
+  FieldVector sampData;
+  FieldVector varsData1;
+  FieldVector varsData2;
+
+public:
+  void
+  alloc(const VarList &varList, int VARS_MEMTYPE)
+  {
+    auto var2needed = (lvarstd || lrange || lminidx || lmaxidx);
+    field1Dvars_init(sampData, varList);
+    field1Dvars_init(varsData1, varList, FIELD_VEC | VARS_MEMTYPE);
+    field1Dvars_init(varsData2, varList, var2needed ? FIELD_VEC : 0);
+  }
+
+  Field &
+  var1(int varID)
+  {
+    return varsData1[varID];
+  }
+
+  Field &
+  var2(int varID)
+  {
+    return varsData2[varID];
+  }
+
+  Field &
+  samp(int varID)
+  {
+    return sampData[varID];
+  }
+
+  void
+  process(int varID, int numSets)
+  {
+    process_kernel(sampData[varID], varsData1[varID], varsData2[varID], numSets);
+  }
+};
+
+class StepStat1Dlevels : public StepStatBase
+{
+private:
+  FieldVector sampData;
+  FieldVector varsData1;
+  FieldVector varsData2;
+
+public:
+  void
+  alloc(const VarList &varList, int VARS_MEMTYPE)
+  {
+    auto var2needed = (lvarstd || lrange || lminidx || lmaxidx);
+    field1Dlevels_init(sampData, varList);
+    field1Dlevels_init(varsData1, varList, FIELD_VEC | VARS_MEMTYPE);
+    field1Dlevels_init(varsData2, varList, var2needed ? FIELD_VEC : 0);
+  }
+
+  Field &
+  var1(int levelID)
+  {
+    return varsData1[levelID];
+  }
+
+  void
+  add_field(const Field &field, int levelID, int numSets)
+  {
+    auto &samp = sampData[levelID];
+    auto &var1 = varsData1[levelID];
+    auto &var2 = varsData2[levelID];
+
+    var1.nsamp++;
+    if (lrange) var2.nsamp++;
+    add_field_kernel(field, samp, var1, var2, numSets);
+  }
+
+  void
+  moq(int levelID)
+  {
+    field2_moq(varsData2[levelID], varsData1[levelID]);
+  }
+
+  void
+  process(int levelID, int numSets)
+  {
+    process_kernel(sampData[levelID], varsData1[levelID], varsData2[levelID], numSets);
+  }
+};
+
+class StepStat2D : public StepStatBase
+{
+private:
+  Varray<double> vsamp;
+  FieldVector2D sampData;
+  FieldVector2D varsData1;
+  FieldVector2D varsData2;
+
+  static void
+  set_missval(Field &field, const Field &samp, int numSets, double vfraction)
+  {
+    auto fieldsize = field.size;
+    auto missval = field.missval;
+
+    size_t irun = 0;
+    for (size_t i = 0; i < fieldsize; ++i)
+      {
+        if ((samp.vec_d[i] / numSets) < vfraction)
+          {
+            field.vec_d[i] = missval;
+            irun++;
+          }
+      }
+
+    if (irun) field.numMissVals = field_num_miss(field);
+  }
+
+public:
+  void
+  alloc(const VarList &varList, int VARS_MEMTYPE)
+  {
+    auto var2needed = (lvarstd || lrange || lminidx || lmaxidx);
+    field2D_init(sampData, varList);
+    field2D_init(varsData1, varList, FIELD_VEC | VARS_MEMTYPE);
+    field2D_init(varsData2, varList, var2needed ? FIELD_VEC : 0);
+  }
+
+  Field &
+  var1(int varID, int levelID)
+  {
+    return varsData1[varID][levelID];
+  }
+
+  Varray<double> &
+  samp(int varID, int levelID, int numSets)
+  {
+    auto &samp = sampData[varID][levelID];
+    auto &var1 = varsData1[varID][levelID];
+
+    vsamp.resize(var1.size);
+    if (!samp.empty())
+      vsamp = samp.vec_d;
+    else
+      ranges::fill(vsamp, (double) numSets);
+
+    return vsamp;
+  }
+
+  void
+  add_field(const Field &field, int varID, int levelID, int numSets)
+  {
+    auto &samp = sampData[varID][levelID];
+    auto &var1 = varsData1[varID][levelID];
+    auto &var2 = varsData2[varID][levelID];
+
+    add_field_kernel(field, samp, var1, var2, numSets);
+  }
+
+  void
+  set_missval(int varID, int levelID, int numSets, double vfraction)
+  {
+    const auto &samp = sampData[varID][levelID];
+    auto &var2 = varsData2[varID][levelID];
+    if (!samp.empty()) set_missval(var2, samp, numSets, vfraction);
+  }
+
+  void
+  process(int varID, int levelID, int numSets)
+  {
+    process_kernel(sampData[varID][levelID], varsData1[varID][levelID], varsData2[varID][levelID], numSets);
+  }
+};
+
+class StepStat3D : public StepStatBase
+{
+private:
+  FieldVector3D sampData;
+  FieldVector3D varsData1;
+  FieldVector3D varsData2;
+  int m_dimlen0{ 0 };
+
+public:
+  void
+  set_dimlen0(int dimlen0)
+  {
+    m_dimlen0 = dimlen0;
+    sampData.resize(dimlen0);
+    varsData1.resize(dimlen0);
+    varsData2.resize(dimlen0);
+  }
+
+  void
+  alloc(int dim0, const VarList &varList, int VARS_MEMTYPE)
+  {
+    auto var2needed = (lvarstd || lrange || lminidx || lmaxidx);
+    field2D_init(sampData[dim0], varList);
+    field2D_init(varsData1[dim0], varList, FIELD_VEC | VARS_MEMTYPE);
+    field2D_init(varsData2[dim0], varList, var2needed ? FIELD_VEC : 0);
+  }
+
+  FieldVector2D &
+  samp(int dim0)
+  {
+    return sampData[dim0];
+  }
+
+  Field &
+  samp(int dim0, int varID, int levelID)
+  {
+    return sampData[dim0][varID][levelID];
+  }
+
+  FieldVector2D &
+  var1(int dim0)
+  {
+    return varsData1[dim0];
+  }
+
+  Field &
+  var1(int dim0, int varID, int levelID)
+  {
+    return varsData1[dim0][varID][levelID];
+  }
+
+  FieldVector2D &
+  var2(int dim0)
+  {
+    return varsData2[dim0];
+  }
+
+  Field &
+  var2(int dim0, int varID, int levelID)
+  {
+    return varsData2[dim0][varID][levelID];
+  }
+
+  void
+  add_field(const Field &field, int dim0, int varID, int levelID, int numSets)
+  {
+    auto &samp = sampData[dim0][varID][levelID];
+    auto &var1 = varsData1[dim0][varID][levelID];
+    auto &var2 = varsData2[dim0][varID][levelID];
+
+    add_field_kernel(field, samp, var1, var2, numSets);
+  }
+
+  void
+  process(int dim0, int varID, int levelID, int numSets)
+  {
+    process_kernel(sampData[dim0][varID][levelID], varsData1[dim0][varID][levelID], varsData2[dim0][varID][levelID], numSets);
+  }
+};
+
+const auto write_out_stream = [](CdoStreamID streamID2, const std::vector<RecordInfo> &recordList, const VarList &varList1,
+                                 cdo::StepStat2D &stepStat, int otsID) noexcept {
+  cdo_def_timestep(streamID2, otsID);
+
+  for (const auto &record : recordList)
+    {
+      auto [varID, levelID] = record.get();
+      if (otsID && varList1.vars[varID].isConstant) continue;
+
+      cdo_def_record(streamID2, varID, levelID);
+      cdo_write_record(streamID2, stepStat.var1(varID, levelID));
+    }
+};
+
+const auto write_diag_stream = [](CdoStreamID streamID3, const std::vector<RecordInfo> &recordList, const VarList &varList1,
+                                  cdo::StepStat2D &stepStat, int otsID, int numSets) noexcept {
+  cdo_def_timestep(streamID3, otsID);
+
+  for (const auto &record : recordList)
+    {
+      auto [varID, levelID] = record.get();
+      if (otsID && varList1.vars[varID].isConstant) continue;
+
+      auto &vsamp = stepStat.samp(varID, levelID, numSets);
+
+      cdo_def_record(streamID3, varID, levelID);
+      cdo_write_record(streamID3, vsamp.data(), 0);
+    }
+};
+
+const auto records_process
+    = [](const std::vector<RecordInfo> &recordList, const VarList &varList1, cdo::StepStat2D &stepStat, int numSets) noexcept {
+        for (const auto &record : recordList)
+          {
+            auto [varID, levelID] = record.get();
+            if (varList1.vars[varID].isConstant) continue;
+
+            stepStat.process(varID, levelID, numSets);
+          }
+      };
+
+const auto records_set_missval = [](const std::vector<RecordInfo> &recordList, const VarList &varList1, cdo::StepStat2D &stepStat,
+                                    int numSets, double vfraction) noexcept {
+  for (const auto &record : recordList)
+    {
+      auto [varID, levelID] = record.get();
+      if (varList1.vars[varID].isConstant) continue;
+
+      stepStat.set_missval(varID, levelID, numSets, vfraction);
+    }
+};
+
+const auto records_process_3D = [](int dim0, const std::vector<RecordInfo> &recordList, const VarList &varList1,
+                                   cdo::StepStat3D &stepStat, int numSets) noexcept {
+  for (const auto &record : recordList)
+    {
+      auto [varID, levelID] = record.get();
+      if (varList1.vars[varID].isConstant) continue;
+
+      stepStat.process(dim0, varID, levelID, numSets);
+    }
+};
+
+};  // namespace cdo
+
+#endif
diff --git a/src/cdo_task.cc b/src/cdo_task.cc
index 06010737804229de5364acbd807f93bdbfaf8531..9a8d640ea1f2ef3e4b4b2f2d6af7801a0f4d7656 100644
--- a/src/cdo_task.cc
+++ b/src/cdo_task.cc
@@ -5,25 +5,29 @@
 
 */
 
-#include "cdo_task.h"
+#if defined(_OPENMP)
+#include <omp.h>
+#include "cdo_options.h"
+#endif
 
-#include <cstdio>
+#include "cdo_task.h"
 
 namespace cdo
 {
 
-static void
-task(cdo::Task *taskInfo)
+void
+Task::task(cdo::Task *taskInfo)
 {
+#if defined(_OPENMP)
+  omp_set_num_threads(Threading::ompNumThreads);  // Has to be called for every thread!
+#endif
+
   // cond.wait mutex must be locked before we can wait
   std::unique_lock<std::mutex> workLock(taskInfo->workMutex);
-
   // ensure boss is waiting
   taskInfo->bossMutex.lock();
-
   // signal to boss that setup is complete
-  taskInfo->state = TaskState::IDLE;
-
+  taskInfo->state = State::IDLE;
   // wake-up signal
   taskInfo->bossCond.notify_one();
   taskInfo->bossMutex.unlock();
@@ -32,21 +36,20 @@ task(cdo::Task *taskInfo)
     {
       taskInfo->workCond.wait(workLock);
 
-      if (TaskState::DIE == taskInfo->state) break;  // kill thread
-
-      if (TaskState::IDLE == taskInfo->state) continue;  // accidental wake-up
+      if (State::DIE == taskInfo->state) break;      // kill thread
+      if (State::IDLE == taskInfo->state) continue;  // accidental wake-up
 
       // do blocking task
       // printf("<worker> JOB start\n");
-      taskInfo->result = taskInfo->routine(taskInfo->arg);
+      if (taskInfo->useFunction)
+        taskInfo->function();
+      else
+        taskInfo->result = taskInfo->routine(taskInfo->arg);
       // printf("<worker> JOB end\n");
-
       // ensure boss is waiting
       taskInfo->bossMutex.lock();
-
       // indicate that job is done
-      taskInfo->state = TaskState::IDLE;
-
+      taskInfo->state = State::IDLE;
       // wake-up signal
       taskInfo->bossCond.notify_one();
       taskInfo->bossMutex.unlock();
@@ -54,19 +57,29 @@ task(cdo::Task *taskInfo)
 }
 
 void
-Task::start(void *(*taskRoutine)(void *), void *taskArg)
+Task::doAsync(const std::function<void()> &_function)
 {
   // ensure worker is waiting
-  workMutex.lock();
+  std::lock_guard<std::mutex> _(workMutex);
+  // set job information & state
+  this->function = _function;
+  this->useFunction = true;
+  this->state = State::JOB;
+  // wake-up signal
+  workCond.notify_one();
+}
 
+void
+Task::start(void *(*taskRoutine)(void *), void *taskArg)
+{
+  // ensure worker is waiting
+  std::lock_guard<std::mutex> _(workMutex);
   // set job information & state
   this->routine = taskRoutine;
   this->arg = taskArg;
-  this->state = TaskState::JOB;
-
+  this->state = State::JOB;
   // wake-up signal
   workCond.notify_one();
-  workMutex.unlock();
 }
 
 void *
@@ -74,8 +87,7 @@ Task::wait()
 {
   while (1)
     {
-      if (TaskState::IDLE == this->state) break;
-
+      if (State::IDLE == this->state) break;
       bossCond.wait(bossMutex);
     }
 
@@ -85,9 +97,7 @@ Task::wait()
 Task::Task()
 {
   bossMutex.lock();
-
-  this->thread = std::thread(task, this);
-
+  this->thread = std::thread(this->task, this);
   this->wait();
 }
 
@@ -95,17 +105,13 @@ Task::~Task()
 {
   // ensure the worker is waiting
   workMutex.lock();
-
   // printf("Task::delete: send DIE to <worker>\n");
-  this->state = TaskState::DIE;
-
+  this->state = State::DIE;
   // wake-up signal
   workCond.notify_one();
   workMutex.unlock();
-
   // wait for thread to exit
   this->thread.join();
-
   bossMutex.unlock();
 }
 
@@ -115,27 +121,46 @@ Task::~Task()
 // g++ -g -Wall -O2 -DTEST_CDO_TASK cdo_task.cc
 
 void *
-mytask(void *arg)
+myfunc(void *arg)
 {
-  printf("run mytask\n");
+  printf("run myfunc\n");
   return nullptr;
 }
 
-int
-main(int argc, char **argv)
+void
+mytask1(void)
 {
   cdo::Task task;
 
   void *myarg = nullptr;
   void *myresult;
 
-  task.start(mytask, myarg);
-
+  task.start(myfunc, myarg);
   myresult = task.wait();
+}
 
-  task.start(mytask, myarg);
+void
+mytask2(void)
+{
+  bool useTask = true;
+  auto task = useTask ? std::make_unique<cdo::Task>() : nullptr;
 
-  myresult = task.wait();
+  void *myarg = nullptr;
+  void *myresult;
+
+  if (useTask)
+    task->start(myfunc, myarg);
+  else
+    myfunc(myarg);
+
+  if (useTask) myresult = task->wait();
+}
+
+int
+main(int argc, char **argv)
+{
+  mytask1();
+  mytask2();
 
   return 0;
 }
diff --git a/src/cdo_task.h b/src/cdo_task.h
index d98f4f9f179e8db96f94892aeb67456edca5be80..edcb27ebf51ab599e11decb3d0095df4fa1b86e3 100644
--- a/src/cdo_task.h
+++ b/src/cdo_task.h
@@ -11,33 +11,41 @@
 #include <thread>
 #include <mutex>
 #include <condition_variable>
+#include <functional>
 
 namespace cdo
 {
 
-enum class TaskState
-{
-  SETUP,
-  IDLE,
-  JOB,
-  DIE
-};
-
 class Task
 {
-public:
+private:
+  enum class State
+  {
+    SETUP,
+    IDLE,
+    JOB,
+    DIE
+  };
+
+  std::function<void()> function;
+  bool useFunction{ false };
+
   void *(*routine)(void *) = nullptr;
   void *arg = nullptr;
   void *result = nullptr;
-  TaskState state{ TaskState::SETUP };
+
+  State state{ State::SETUP };
   std::thread thread;
   std::mutex workMutex;
   std::mutex bossMutex;
   std::condition_variable workCond;
   std::condition_variable_any bossCond;
+  static void task(Task *taskInfo);
 
+public:
   Task();
   ~Task();
+  void doAsync(const std::function<void()> &_function);
   void start(void *(*task_routine)(void *), void *task_arg);
   void *wait();
 };
diff --git a/src/cdo_varlist.cc b/src/cdo_varlist.cc
index e59854ba4319cb8f7585168f95aa0d2cb8e430fb..cf9bdbc4ba75fede39c61a3fbef350c91c37ae57 100644
--- a/src/cdo_varlist.cc
+++ b/src/cdo_varlist.cc
@@ -9,48 +9,48 @@
 #include "cdo_cdi_wrapper.h"
 #include "cdo_output.h"
 #include "util_string.h"
-#include "compare.h"
 #include "stdnametable.h"
 #include "cdo_vlist.h"
 
 static bool
-isIntType(int dataType)
+is_int_type(int dataType)
 {
   return (dataType == CDI_DATATYPE_UINT8 || dataType == CDI_DATATYPE_UINT16 || dataType == CDI_DATATYPE_INT16);
 }
 
 static bool
-isFloatType(int dataType)
+is_float_type(int dataType)
 {
   return (dataType == CDI_DATATYPE_FLT32 || dataType == CDI_DATATYPE_CPX32);
 }
 
 void
-varList_init(VarList &varList, int vlistID)
+cdoVars_init(CdoVars &cdoVars, int vlistID)
 {
   auto numVars = vlistNvars(vlistID);
-  varList.resize(numVars);
+  cdoVars.resize(numVars);
 
   for (int varID = 0; varID < numVars; ++varID)
     {
-      auto &var = varList[varID];
+      auto &var = cdoVars[varID];
+      var.ID = varID;
       var.name = cdo::inq_var_name(vlistID, varID);
       var.longname = cdo::inq_var_longname(vlistID, varID);
       var.units = cdo::inq_var_units(vlistID, varID);
       var.gridID = vlistInqVarGrid(vlistID, varID);
       var.zaxisID = vlistInqVarZaxis(vlistID, varID);
-      var.timetype = vlistInqVarTimetype(vlistID, varID);
-      var.tsteptype = vlistInqVarTsteptype(vlistID, varID);
+      var.timeType = vlistInqVarTimetype(vlistID, varID);
+      var.stepType = vlistInqVarTsteptype(vlistID, varID);
       var.gridType = gridInqType(var.gridID);
       var.gridsize = gridInqSize(var.gridID);
       var.zaxisType = zaxisInqType(var.zaxisID);
       var.nlevels = zaxisInqSize(var.zaxisID);
-      var.datatype = vlistInqVarDatatype(vlistID, varID);
+      var.dataType = vlistInqVarDatatype(vlistID, varID);
       var.missval = vlistInqVarMissval(vlistID, varID);
       var.code = vlistInqVarCode(vlistID, varID);
       var.param = vlistInqVarParam(vlistID, varID);
       var.nwpv = vlistInqVarNumber(vlistID, varID);
-      var.isConstant = (var.timetype == TIME_CONSTANT);
+      var.isConstant = (var.timeType == TIME_CONSTANT);
 
       if (Options::CDO_Memtype == MemType::Native)
         {
@@ -58,7 +58,8 @@ 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 = (var.datatype == CDI_UNDEFID) || isFloatType(var.datatype) || (isIntType(var.datatype) && !isPacked);
+          auto useFloatType
+              = (var.dataType == CDI_UNDEFID) || is_float_type(var.dataType) || (is_int_type(var.dataType) && !isPacked);
           var.memType = useFloatType ? MemType::Float : MemType::Double;
         }
       else { var.memType = Options::CDO_Memtype; }
@@ -66,64 +67,60 @@ varList_init(VarList &varList, int vlistID)
 }
 
 void
-varListSetMemtype(VarList &varList, MemType memType)
+varList_set_memtype(VarList &varList, MemType memType)
 {
-  for (auto &var : varList) var.memType = memType;
+  for (auto &var : varList.vars) var.memType = memType;
 }
 
 void
-varListSetUniqueMemtype(VarList &varList)
+varList_set_unique_memtype(VarList &varList)
 {
-  int numVars = varList.size();
+  auto numVars = varList.numVars();
   if (numVars)
     {
-      auto memtype = varList[0].memType;
+      auto memtype = varList.vars[0].memType;
       int varID;
       for (varID = 1; varID < numVars; ++varID)
         {
-          if (varList[varID].memType != memtype) break;
+          if (varList.vars[varID].memType != memtype) break;
         }
-      if (varID < numVars) varListSetMemtype(varList, MemType::Double);
+      if (varID < numVars) varList_set_memtype(varList, MemType::Double);
     }
 }
 
 int
-varList_numConstVars(const VarList &varList)
+VarList::num_const_vars(const CdoVars &cdoVars)
 {
   int numConstVars = 0;
-  int numVars = varList.size();
-  for (int varID = 0; varID < numVars; ++varID)
+  for (const auto &var : cdoVars)
     {
-      const auto &var = varList[varID];
-      if (var.timetype == TIME_CONSTANT) numConstVars++;
+      if (var.timeType == TIME_CONSTANT) numConstVars++;
     }
   return numConstVars;
 }
 
 int
-varList_numVaryingVars(const VarList &varList)
+VarList::num_varying_vars(const CdoVars &cdoVars)
 {
   int numVaryingVars = 0;
-  int numVars = varList.size();
-  for (int varID = 0; varID < numVars; ++varID)
+  for (const auto &var : cdoVars)
     {
-      const auto &var = varList[varID];
-      if (var.timetype == TIME_VARYING) numVaryingVars++;
+      if (var.timeType == TIME_VARYING) numVaryingVars++;
     }
   return numVaryingVars;
 }
 
 VarIDs
-search_varIDs(const VarList &varList, int vlistID, int numFullLevels)
+varList_search_varIDs(const VarList &varList, int numFullLevels)
 {
   VarIDs varIDs;
 
-  auto numVars = vlistNvars(vlistID);
+  auto numVars = varList.numVars();
 
   auto useTable = false;
   for (int varID = 0; varID < numVars; ++varID)
     {
-      auto tableNum = tableInqNum(vlistInqVarTable(vlistID, varID));
+      auto tableNum = tableInqNum(vlistInqVarTable(varList.vlistID, varID));
       if (tableNum > 0 && tableNum < 255)
         {
           useTable = true;
@@ -138,10 +135,10 @@ search_varIDs(const VarList &varList, int vlistID, int numFullLevels)
 
   for (int varID = 0; varID < numVars; ++varID)
     {
-      auto &var = varList[varID];
-      auto nlevels = var.nlevels;
-      auto instNum = institutInqCenter(vlistInqVarInstitut(vlistID, varID));
-      auto tableNum = tableInqNum(vlistInqVarTable(vlistID, varID));
+      auto &var = varList.vars[varID];
+      auto numLevels = var.nlevels;
+      auto instNum = institutInqCenter(vlistInqVarInstitut(varList.vlistID, varID));
+      auto tableNum = tableInqNum(vlistInqVarTable(varList.vlistID, varID));
 
       auto code = var.code;
 
@@ -167,8 +164,8 @@ search_varIDs(const VarList &varList, int vlistID, int numFullLevels)
 
       if (code <= 0 || code == 255)
         {
-          auto varname = string_to_lower(cdo::inq_var_name(vlistID, varID));
-          auto stdname = string_to_lower(cdo::inq_key_string(vlistID, varID, CDI_KEY_STDNAME));
+          auto varname = string_to_lower(cdo::inq_var_name(varList.vlistID, varID));
+          auto stdname = string_to_lower(cdo::inq_key_string(varList.vlistID, varID, CDI_KEY_STDNAME));
 
           code = stdname_to_echamcode(stdname);
           if (code == -1)
@@ -191,15 +188,15 @@ search_varIDs(const VarList &varList, int vlistID, int numFullLevels)
         }
 
       // clang-format off
-      if      (code == gribcodes.geopot  && nlevels == 1)             varIDs.sgeopotID = varID;
-      else if (code == gribcodes.geopot  && nlevels == numFullLevels) varIDs.geopotID = varID;
-      else if (code == gribcodes.temp    && nlevels == numFullLevels) varIDs.tempID = varID;
-      else if (code == gribcodes.ps      && nlevels == 1)             varIDs.psID = varID;
-      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;
+      if      (code == gribcodes.geopot  && numLevels == 1)                 varIDs.sgeopotID = varID;
+      else if (code == gribcodes.geopot  && numLevels == numFullLevels)     varIDs.geopotID = varID;
+      else if (code == gribcodes.temp    && numLevels == numFullLevels)     varIDs.tempID = varID;
+      else if (code == gribcodes.ps      && numLevels == 1)                 varIDs.psID = varID;
+      else if (code == gribcodes.lsp     && numLevels == 1)                 varIDs.lnpsID = varID;
+      else if (code == 777               && numLevels == 1)                 varIDs.lnpsID2 = varID;
+      else if (code == gribcodes.gheight && numLevels == numFullLevels)     varIDs.gheightID = varID;
+      else if (code == gribcodes.gheight && numLevels == numFullLevels + 1) varIDs.gheightID = varID;
+      else if (code == gribcodes.hum     && numLevels == numFullLevels)     varIDs.humID = varID;
       // else if (code == 246 && nlevels == numFullLevels) varIDs.clwcID = varID;
       // else if (code == 247 && nlevels == numFullLevels) varIDs.ciwcID = varID;
       // clang-format on
@@ -209,51 +206,52 @@ search_varIDs(const VarList &varList, int vlistID, int numFullLevels)
 }
 
 void
-varList_map(const VarList &varList1, const VarList &varList2, CmpVlist cmpFlag, int mapFlag, std::map<int, int> &mapOfVarIDs)
+varList_map(const VarList &varList1, const VarList &varList2, MapFlag mapFlag, std::map<int, int> &mapOfVarIDs)
 {
-  auto flag = static_cast<int>(cmpFlag);
-  int nvars1 = varList1.size();
-  int nvars2 = varList2.size();
+  auto cmpFlag{ CmpVarList::All };
+  auto numVars1 = varList1.numVars();
+  auto numVars2 = varList2.numVars();
 
-  if (mapFlag == 2)
+  if (mapFlag == MapFlag::Right)
     {
-      for (int varID2 = 0; varID2 < nvars2; ++varID2)
+      for (const auto &var2 : varList2.vars)
         {
           int varID1;
-          for (varID1 = 0; varID1 < nvars1; ++varID1)
+          for (varID1 = 0; varID1 < numVars1; ++varID1)
             {
-              if (varList1[varID1].name == varList2[varID2].name) break;
+              if (varList1.vars[varID1].name == var2.name) break;
             }
-          if (varID1 == nvars1) { cdo_abort("Variable %s not found in first input stream!", varList2[varID2].name); }
-          else { mapOfVarIDs[varID1] = varID2; }
+          if (varID1 == numVars1) { cdo_abort("Variable %s not found in first input stream!", var2.name); }
+          else { mapOfVarIDs[varID1] = var2.ID; }
         }
     }
   else
     {
-      for (int varID1 = 0; varID1 < nvars1; ++varID1)
+      for (const auto &var1 : varList1.vars)
         {
           int varID2;
-          for (varID2 = 0; varID2 < nvars2; ++varID2)
+          for (varID2 = 0; varID2 < numVars2; ++varID2)
             {
-              if (varList1[varID1].name == varList2[varID2].name) break;
+              if (var1.name == varList2.vars[varID2].name) break;
             }
-          if (varID2 == nvars2)
+          if (varID2 == numVars2)
             {
-              if (mapFlag == 3) continue;
-              cdo_abort("Variable %s not found in second input stream!", varList1[varID1].name);
+              if (mapFlag == MapFlag::Intersect) continue;
+              cdo_abort("Variable %s not found in second input stream!", var1.name);
             }
-          else { mapOfVarIDs[varID1] = varID2; }
+          else { mapOfVarIDs[var1.ID] = varID2; }
         }
     }
 
   if (mapOfVarIDs.empty()) cdo_abort("No variable found that occurs in both streams!");
 
   if (Options::cdoVerbose)
-    for (int varID1 = 0; varID1 < nvars1; ++varID1)
+    for (int varID1 = 0; varID1 < numVars1; ++varID1)
       {
+        const auto &var1 = varList1.vars[varID1];
         const auto &it = mapOfVarIDs.find(varID1);
         if (it != mapOfVarIDs.end())
-          cdo_print("Variable %d:%s mapped to %d:%s", varID1, varList1[varID1].name, it->second, varList2[it->second].name);
+          cdo_print("Variable %d:%s mapped to %d:%s", varID1, var1.name, it->second, varList2.vars[it->second].name);
       }
 
   if (mapOfVarIDs.size() > 1)
@@ -272,22 +270,22 @@ varList_map(const VarList &varList1, const VarList &varList2, CmpVlist cmpFlag,
     {
       auto varID1 = it->first;
       auto varID2 = it->second;
+      const auto &var1 = varList1.vars[varID1];
+      const auto &var2 = varList2.vars[varID2];
 
-      if (flag & static_cast<int>(CmpVlist::GridSize))
+      if (cmpFlag & CmpVarList::GridSize)
         {
-          if (varList1[varID1].gridsize != varList2[varID2].gridsize) cdo_abort("Grid size of the input fields do not match!");
+          if (var1.gridsize != var2.gridsize) cdo_abort("Grid size of the input fields do not match!");
         }
 
-      if (flag & static_cast<int>(CmpVlist::NumLevels))
+      if (cmpFlag & CmpVarList::NumLevels)
         {
-          if (zaxis_check_levels(varList1[varID1].zaxisID, varList2[varID2].zaxisID) != 0) break;
+          if (zaxis_check_levels(var1.zaxisID, var2.zaxisID) != 0) break;
         }
 
-      if (flag & static_cast<int>(CmpVlist::Grid) && varID1 == mapOfVarIDs.begin()->first)
+      if ((cmpFlag & CmpVarList::Grid) && (varID1 == mapOfVarIDs.begin()->first))
         {
-          auto gridID1 = varList1[varID1].gridID;
-          auto gridID2 = varList2[varID2].gridID;
-          if (gridID1 != gridID2) cdo_compare_grids(gridID1, gridID2);
+          if (var1.gridID != var2.gridID) cdo_compare_grids(var1.gridID, var2.gridID);
         }
     }
 }
@@ -298,9 +296,9 @@ varList_get_psvarid(const VarList &varList, int zaxisID)
   auto psname = cdo::inq_key_string(zaxisID, CDI_GLOBAL, CDI_KEY_PSNAME);
   if (psname.size())
     {
-      for (int varID = 0, numVars = varList.size(); varID < numVars; ++varID)
+      for (const auto &var : varList.vars)
         {
-          if (varList[varID].name == psname) return varID;
+          if (var.name == psname) return var.ID;
         }
       if (Options::cdoVerbose) cdo_warning("Surface pressure variable not found - %s", psname);
     }
@@ -309,30 +307,26 @@ varList_get_psvarid(const VarList &varList, int zaxisID)
 }
 
 static void
-varList_check_names(const VarList &varList1, const VarList &varList2)
+cdoVars_check_names(const CdoVars &cdoVars1, const CdoVars &cdoVars2)
 {
-  int numVars = varList1.size();
+  int numVars = cdoVars1.size();
 
   std::vector<std::string> names1(numVars);
   std::vector<std::string> names2(numVars);
-  for (int varID = 0; varID < numVars; ++varID) names1[varID] = varList1[varID].name;
-  for (int varID = 0; varID < numVars; ++varID) names2[varID] = varList2[varID].name;
+  for (int varID = 0; varID < numVars; ++varID) names1[varID] = cdoVars1[varID].name;
+  for (int varID = 0; varID < numVars; ++varID) names2[varID] = cdoVars2[varID].name;
 
   ranges::sort(names1);
   ranges::sort(names2);
 
-  int varID;
-  for (varID = 0; varID < numVars; ++varID)
-    if (names1[varID] != names2[varID]) break;
-
-  if (varID == numVars) cdo_print("Use CDO option --sortname to sort the parameter by name (NetCDF only)!");
+  if (names1 == names2) cdo_print("Use CDO option --sortname to sort the parameter by name (NetCDF only)!");
 }
 
 static void
-varList_print_missing_vars(const VarList &varList1, const VarList &varList2)
+cdoVars_print_missing_vars(const CdoVars &cdoVars1, const CdoVars &cdoVars2)
 {
-  int numVars1 = varList1.size();
-  int numVars2 = varList2.size();
+  int numVars1 = cdoVars1.size();
+  int numVars2 = cdoVars2.size();
 
   if (numVars1 > numVars2)
     {
@@ -341,9 +335,9 @@ varList_print_missing_vars(const VarList &varList1, const VarList &varList2)
           int varID2;
           for (varID2 = 0; varID2 < numVars2; ++varID2)
             {
-              if (varList1[varID1].name == varList2[varID2].name) break;
+              if (cdoVars1[varID1].name == cdoVars2[varID2].name) break;
             }
-          if (varID2 == numVars2) cdo_print("Variable %s not found in second input stream!", varList1[varID1].name);
+          if (varID2 == numVars2) cdo_print("Variable %s not found in second input stream!", cdoVars1[varID1].name);
         }
     }
   else
@@ -353,81 +347,76 @@ varList_print_missing_vars(const VarList &varList1, const VarList &varList2)
           int varID1;
           for (varID1 = 0; varID1 < numVars1; ++varID1)
             {
-              if (varList1[varID1].name == varList2[varID2].name) break;
+              if (cdoVars1[varID1].name == cdoVars2[varID2].name) break;
             }
-          if (varID1 == numVars1) cdo_print("Variable %s not found in first input stream!", varList2[varID2].name);
+          if (varID1 == numVars1) cdo_print("Variable %s not found in first input stream!", cdoVars2[varID2].name);
         }
     }
 }
 
 static int
-varList_numRecs(const VarList &varList)
+cdoVars_numRecs(const CdoVars &cdoVars)
 {
   int numRecs = 0;
-  for (int varID = 0, numVars = varList.size(); varID < numVars; ++varID) numRecs += varList[varID].nlevels;
+  for (int varID = 0, numVars = cdoVars.size(); varID < numVars; ++varID) numRecs += cdoVars[varID].nlevels;
   return numRecs;
 }
 
 void
-varList_compare(const VarList &varList1, const VarList &varList2, CmpVlist cmpFlag)
+varList_compare(const VarList &varList1, const VarList &varList2, int cmpFlag)
 {
-  auto flag = static_cast<int>(cmpFlag);
   auto doCheckNames = false;
 
-  int numVars = varList1.size();
-
-  if (numVars != (int) varList2.size())
+  auto numVars = varList1.numVars();
+  if (numVars != varList2.numVars())
     {
-      varList_print_missing_vars(varList1, varList2);
+      cdoVars_print_missing_vars(varList1.vars, varList2.vars);
       cdo_abort("Input streams have different number of variables per timestep!");
     }
 
-  if (varList_numRecs(varList1) != varList_numRecs(varList2))
+  if (cdoVars_numRecs(varList1.vars) != cdoVars_numRecs(varList2.vars))
     cdo_abort("Input streams have different number of %s per timestep!", (numVars == 1) ? "layers" : "records");
 
   for (int varID = 0; varID < numVars; ++varID)
     {
+      const auto &var1 = varList1.vars[varID];
+      const auto &var2 = varList2.vars[varID];
       if (numVars > 1)
         {
-          if (flag & static_cast<int>(CmpVlist::Name))
+          if (cmpFlag & CmpVarList::Name)
             {
-              if (string_to_lower(varList1[varID].name) != string_to_lower(varList2[varID].name))
+              if (string_to_lower(var1.name) != string_to_lower(var2.name))
                 {
                   cdo_warning("Input streams have different parameter names!");
                   doCheckNames = true;
-                  flag -= static_cast<int>(CmpVlist::Name);
+                  cmpFlag = cmpFlag ^ CmpVarList::Name;
                 }
             }
         }
 
-      if (flag & static_cast<int>(CmpVlist::GridSize))
+      if (cmpFlag & CmpVarList::GridSize)
         {
-          if (varList1[varID].gridsize != varList2[varID].gridsize)
-            {
-              cdo_abort("Grid size of the input field '%s' do not match!", varList1[varID].name);
-            }
+          if (var1.gridsize != var2.gridsize) { cdo_abort("Grid size of the input field '%s' do not match!", var1.name); }
         }
 
-      if (flag & static_cast<int>(CmpVlist::NumLevels))
+      if (cmpFlag & CmpVarList::NumLevels)
         {
-          if (zaxis_check_levels(varList1[varID].zaxisID, varList2[varID].zaxisID) != 0) break;
+          if (zaxis_check_levels(var1.zaxisID, var2.zaxisID) != 0) break;
         }
     }
 
-  if (flag & static_cast<int>(CmpVlist::Grid))
+  if (cmpFlag & CmpVarList::Grid)
     {
-      if (varList1[0].gridID != varList2[0].gridID) cdo_compare_grids(varList1[0].gridID, varList2[0].gridID);
+      if (varList1.vars[0].gridID != varList2.vars[0].gridID) cdo_compare_grids(varList1.vars[0].gridID, varList2.vars[0].gridID);
     }
 
-  if (doCheckNames) varList_check_names(varList1, varList2);
+  if (doCheckNames) cdoVars_check_names(varList1.vars, varList2.vars);
 }
 
 void
-vlist_compare(int vlistID1, int vlistID2, CmpVlist cmpFlag)
+vlist_compare(int vlistID1, int vlistID2, int cmpFlag)
 {
-  VarList varList1;
-  VarList varList2;
-  varList_init(varList1, vlistID1);
-  varList_init(varList2, vlistID2);
+  VarList varList1(vlistID1);
+  VarList varList2(vlistID2);
   varList_compare(varList1, varList2, cmpFlag);
 }
diff --git a/src/cdo_varlist.h b/src/cdo_varlist.h
index a345de25bb7626a3c84263ad8d9f36e9d3da583f..efa7acc97405424a0ce79587860306c443d09eb9 100644
--- a/src/cdo_varlist.h
+++ b/src/cdo_varlist.h
@@ -1,79 +1,119 @@
 #ifndef CDO_VARLIST_H
 #define CDO_VARLIST_H
 
+#include <cassert>
 #include <string>
 #include <vector>
 #include <map>
 
 #include <cdi.h>
 
-#include "cdo_varlist.h"
 #include "cdo_options.h"
 
-enum class CmpVlist
+enum class MapFlag
 {
-  Name = 1,
-  Grid = 2,
-  NumLevels = 4,
-  GridSize = 8,
-  Dim = GridSize | NumLevels | Grid,
-  All = Name | Dim
+  Undefined = 0,
+  Left = 1,
+  Right = 2,
+  Intersect = 3
 };
 
-inline CmpVlist
-operator|(CmpVlist lhs, CmpVlist rhs)
+namespace CmpVarList
 {
-  return (CmpVlist) (static_cast<int>(lhs) | static_cast<int>(rhs));
-}
+constexpr int Name = 1;
+constexpr int Grid = 2;
+constexpr int NumLevels = 4;
+constexpr int GridSize = 8;
+constexpr int Dim = GridSize | NumLevels | Grid;
+constexpr int All = Name | Dim;
+};  // namespace CmpVarList
 
 struct CdoVar
 {
   std::string name;
   std::string longname;
   std::string units;
-  MemType memType = MemType::Native;
-  int gridID = -1;
-  int zaxisID = -1;
-  int gridType = -1;
-  int zaxisType = -1;
-  int timetype = -1;
-  int tsteptype = -1;
-  size_t gridsize = 0;
-  int nlevels = 0;
-  int datatype = -1;
-  double missval = 0;
-  int code = 0;
-  int param = 0;
-  int nwpv = 1;  // number of words per value; real:1  complex:2
-  bool isConstant = false;
+  MemType memType{ MemType::Native };
+  int gridID{ -1 };
+  int zaxisID{ -1 };
+  int gridType{ -1 };
+  int zaxisType{ -1 };
+  int timeType{ -1 };
+  int stepType{ -1 };
+  size_t gridsize{ 0 };
+  int nlevels{ 0 };
+  int dataType{ -1 };
+  double missval{ 0 };
+  int code{ 0 };
+  int param{ 0 };
+  int nwpv{ 1 };  // number of words per value; real:1  complex:2
+  bool isConstant{ false };
+  int ID{ 0 };
 };
 
-using VarList = std::vector<CdoVar>;
-void varList_init(VarList &varList, int vlistID);
-void varListSetUniqueMemtype(VarList &varList);
-void varListSetMemtype(VarList &varList, MemType memType);
-int varList_numConstVars(const VarList &varList);
-int varList_numVaryingVars(const VarList &varList);
-void varList_map(const VarList &varList1, const VarList &varList2, CmpVlist cmpFlag, int mapFlag, std::map<int, int> &mapOfVarIDs);
-int varList_get_psvarid(const VarList &varList, int zaxisID);
+using CdoVars = std::vector<CdoVar>;
+
+void cdoVars_init(CdoVars &cdoVars, int vlistID);
+
+class VarList
+{
+public:
+  CdoVars vars;
+  int vlistID{ CDI_UNDEFID };
+
+  VarList() {}
+  VarList(int _vlistID) : vlistID(_vlistID)
+  {
+    cdoVars_init(vars, _vlistID);
+    m_numRecords = vlistNumRecords(_vlistID);
+    m_numSteps = vlistNtsteps(_vlistID);
+    m_numConstVars = num_const_vars(vars);
+    m_numVaryingVars = num_varying_vars(vars);
+  }
 
-void varList_compare(const VarList &varList1, const VarList &varList2, CmpVlist cmpFlag);
-void vlist_compare(int vlistID1, int vlistID2, CmpVlist cmpFlag);
+  // clang-format off
+  void isInit() const{ assert(vlistID != CDI_UNDEFID); }
+  int numVars() const noexcept { isInit(); return static_cast<int>(vars.size()); }
+  int numRecords() const noexcept { isInit(); return m_numRecords; }
+  int numSteps() const noexcept { isInit(); return m_numSteps; }
+  int numConstVars() const noexcept { isInit(); return m_numConstVars; }
+  int numVaryingVars() const noexcept { isInit(); return m_numVaryingVars; }
+  // clang-format on
+
+private:
+  // clang-format off
+  int m_numRecords{ 0 };
+  int m_numSteps{ 0 };
+  int m_numConstVars{ 0 };
+  int m_numVaryingVars{ 0 };
+  int num_const_vars(const CdoVars &cdoVars);
+  int num_varying_vars(const CdoVars &cdoVars);
+  // clang-format on
+};
 
 struct VarIDs
 {
-  int sgeopotID = CDI_UNDEFID;
-  int geopotID = CDI_UNDEFID;
-  int tempID = CDI_UNDEFID;
-  int psID = CDI_UNDEFID;
-  int lnpsID = CDI_UNDEFID;
-  int lnpsID2 = CDI_UNDEFID;
-  int gheightID = CDI_UNDEFID;
-  int humID = CDI_UNDEFID;
-  int clwcID = CDI_UNDEFID;
-  int ciwcID = CDI_UNDEFID;
+  int sgeopotID{ CDI_UNDEFID };
+  int geopotID{ CDI_UNDEFID };
+  int tempID{ CDI_UNDEFID };
+  int psID{ CDI_UNDEFID };
+  int lnpsID{ CDI_UNDEFID };
+  int lnpsID2{ CDI_UNDEFID };
+  int gheightID{ CDI_UNDEFID };
+  int humID{ CDI_UNDEFID };
+  int clwcID{ CDI_UNDEFID };
+  int ciwcID{ CDI_UNDEFID };
 };
 
-VarIDs search_varIDs(const VarList &varList, int vlistID, int numFullLevels);
+VarIDs varList_search_varIDs(const VarList &varList, int numFullLevels);
+
+void vlist_compare(int vlistID1, int vlistID2, int cmpFlag);
+
+void varList_compare(const VarList &varList1, const VarList &varList2, int cmpFlag = CmpVarList::All);
+void varList_map(const VarList &varList1, const VarList &varList2, MapFlag mapFlag, std::map<int, int> &mapOfVarIDs);
+
+void varList_set_memtype(VarList &varList, MemType memType);
+void varList_set_unique_memtype(VarList &varList);
+int varList_get_psvarid(const VarList &varList, int zaxisID);
 
 #endif
diff --git a/src/cdo_vlist.cc b/src/cdo_vlist.cc
index 87666c43d28fe59d62935595eddd464946d915ec..9071550856532a32c7cde700f2196a981c64f1d2 100644
--- a/src/cdo_vlist.cc
+++ b/src/cdo_vlist.cc
@@ -5,13 +5,8 @@
 
 */
 
-#include <array>
-#include <algorithm>
-
 #include <cdi.h>
-#include "cdo_cdi_wrapper.h"
 #include "cdo_options.h"
-#include "util_string.h"
 #include "cdo_vlist.h"
 #include "compare.h"
 #include "cdo_output.h"
@@ -218,34 +213,33 @@ zaxis_check_levels(int zaxisID1, int zaxisID2)
 }
 
 int
-vlist_compare_x(int vlistID1, int vlistID2, CmpVlist cmpFlag)
+vlist_compare_x(int vlistID1, int vlistID2, int cmpFlag)
 {
-  auto flag = static_cast<int>(cmpFlag);
-  auto nvars = vlistNvars(vlistID1);
-  auto nvars2 = vlistNvars(vlistID2);
-  auto nlevels2 = zaxisInqSize(vlistInqVarZaxis(vlistID2, 0));
+  VarList varList1(vlistID1);
+  VarList varList2(vlistID2);
 
-  if (nvars2 != 1) cdo_abort("Internal problem, vlist_compare_x() called with unexpected vlistID2 argument!");
+  auto nlevels2 = varList2.vars[0].nlevels;
 
-  for (int varID = 0; varID < nvars; ++varID)
+  if (varList2.numVars() != 1) cdo_abort("Internal problem, vlist_compare_x() called with unexpected vlistID2 argument!");
+
+  for (int varID = 0; varID < varList1.numVars(); ++varID)
     {
-      if (flag & static_cast<int>(CmpVlist::GridSize))
+      if (cmpFlag & CmpVarList::GridSize)
         {
-          if (gridInqSize(vlistInqVarGrid(vlistID1, varID)) != gridInqSize(vlistInqVarGrid(vlistID2, 0)))
-            cdo_abort("Grid size of the input fields do not match!");
+          if (varList1.vars[varID].gridsize != varList2.vars[0].gridsize) cdo_abort("Grid size of the input fields do not match!");
         }
 
-      if (flag & static_cast<int>(CmpVlist::NumLevels))
+      if (cmpFlag & CmpVarList::NumLevels)
         {
-          if ((zaxisInqSize(vlistInqVarZaxis(vlistID1, varID)) != nlevels2) && nlevels2 > 1)
+          if ((varList1.vars[varID].nlevels != nlevels2) && nlevels2 > 1)
             cdo_abort("Number of levels of the input fields do not match!");
         }
     }
 
-  if (flag & static_cast<int>(CmpVlist::Grid))
+  if (cmpFlag & CmpVarList::Grid)
     {
-      auto gridID1 = vlistInqVarGrid(vlistID1, 0);
-      auto gridID2 = vlistInqVarGrid(vlistID2, 0);
+      auto gridID1 = varList1.vars[0].gridID;
+      auto gridID2 = varList2.vars[0].gridID;
       if (gridID1 != gridID2) cdo_compare_grids(gridID1, gridID2);
     }
 
@@ -265,16 +259,6 @@ vlist_is_szipped(int vlistID)
   return false;
 }
 
-int
-vlist_inq_nwpv(int vlistID, int varID)
-{
-  auto datatype = vlistInqVarDatatype(vlistID, varID);
-  // number of words per value; real:1  complex:2
-  auto nwpv = (datatype == CDI_DATATYPE_CPX32 || datatype == CDI_DATATYPE_CPX64) ? 2 : 1;
-
-  return nwpv;
-}
-
 size_t
 vlist_check_gridsize(int vlistID)
 {
@@ -282,7 +266,7 @@ vlist_check_gridsize(int vlistID)
   auto ngp = gridInqSize(vlistGrid(vlistID, 0));
 
   // check gridsize
-  auto ngrids = vlistNgrids(vlistID);
+  auto ngrids = vlistNumGrids(vlistID);
   for (int index = 0; index < ngrids; ++index)
     {
       auto gridID = vlistGrid(vlistID, index);
@@ -319,7 +303,7 @@ vlist_read_vct(int vlistID, int &zaxisID_ML, int &numHybridLevels, int &numFullL
   numHalfLevels = 0;
 
   auto haveVCT = false;
-  auto nzaxis = vlistNzaxis(vlistID);
+  auto nzaxis = vlistNumZaxis(vlistID);
   for (int iz = 0; iz < nzaxis; ++iz)
     {
       // auto monoLevel = false;
@@ -438,7 +422,7 @@ vlist_change_hybrid_zaxis(int vlistID1, int vlistID2, int zaxisID1, int zaxisID2
   int vctSize0 = 0;
   Varray<double> vct;
 
-  auto nzaxis = vlistNzaxis(vlistID1);
+  auto nzaxis = vlistNumZaxis(vlistID1);
   for (int i = 0; i < nzaxis; ++i)
     {
       auto zaxisID = vlistZaxis(vlistID1, i);
@@ -468,7 +452,7 @@ int
 vlist_get_first_spectral_grid(int vlistID)
 {
   // find first spectral grid
-  auto ngrids = vlistNgrids(vlistID);
+  auto ngrids = vlistNumGrids(vlistID);
   for (int index = 0; index < ngrids; ++index)
     {
       auto gridID = vlistGrid(vlistID, index);
@@ -482,7 +466,7 @@ int
 vlist_get_first_gaussian_grid(int vlistID)
 {
   // find first gaussian grid
-  auto ngrids = vlistNgrids(vlistID);
+  auto ngrids = vlistNumGrids(vlistID);
   for (int index = 0; index < ngrids; ++index)
     {
       auto gridID = vlistGrid(vlistID, index);
@@ -496,7 +480,7 @@ int
 vlist_get_first_fourier_grid(int vlistID)
 {
   // find first fourier grid
-  auto ngrids = vlistNgrids(vlistID);
+  auto ngrids = vlistNumGrids(vlistID);
   for (int index = 0; index < ngrids; ++index)
     {
       auto gridID = vlistGrid(vlistID, index);
diff --git a/src/cdo_vlist.h b/src/cdo_vlist.h
index 386d941a7322fd8aa410973bc631c0950ea7cb5b..792282d361b4a13de971ad2ee32ed50d4180433a 100644
--- a/src/cdo_vlist.h
+++ b/src/cdo_vlist.h
@@ -4,16 +4,15 @@
 #include <string>
 #include <cdi.h>
 
-#include "cdo_varlist.h"
 #include "cdo_options.h"
 #include "varray.h"
+#include "cdo_varlist.h"
 
 void vlist_define_timestep_type(int vlistID, int operfunc);
 
-int vlist_compare_x(int vlistID1, int vlistID2, CmpVlist cmpFlag);
+int vlist_compare_x(int vlistID1, int vlistID2, int cmpFlag);
 bool vlist_is_szipped(int vlistID);
 
-int vlist_inq_nwpv(int vlistID, int varID);
 size_t vlist_check_gridsize(int vlistID);
 Varray<double> vlist_read_vct(int vlistID, int &zaxisID_ML, int &numHybridLevels, int &numFullLevels, int &numHalfLevels);
 void vlist_change_hybrid_zaxis(int vlistID1, int vlistID2, int zaxisID1, int zaxisID2);
diff --git a/src/cdo_zaxis.cc b/src/cdo_zaxis.cc
index 7a71ee4cdb659b983f6b231ba989b3b5e25c4248..65da0cfc9469c81238eaba3602ddeb5ee62ce1aa 100644
--- a/src/cdo_zaxis.cc
+++ b/src/cdo_zaxis.cc
@@ -17,7 +17,6 @@
 #include "parse_literals.h"
 #include "pmlist.h"
 #include "cdo_output.h"
-#include "compare.h"
 #include "varray.h"
 
 struct ZaxisDesciption
diff --git a/src/cdotest.cc b/src/cdotest.cc
index e0c5b1afed1f8397b56cfffd6d3178a26d0038d4..dd1114c1c2e07e510f383a0c42027b072ee49d07 100644
--- a/src/cdotest.cc
+++ b/src/cdotest.cc
@@ -122,9 +122,6 @@ write_file(const char path[], const double array[], int length)
   streamClose(streamID);
 
   vlistDestroy(vlistID);
-  taxisDestroy(taxisID);
-  zaxisDestroy(zaxisID);
-  gridDestroy(gridID);
 }
 
 // gets the path of the CDO binary executable
diff --git a/src/cimdOmp.h b/src/cimdOmp.h
index 1c60b6a23f684013d1ed3319a8e4521b918e8d0a..f8b5b5ec24d0a89082b3beaeaed2525831ea2c92 100644
--- a/src/cimdOmp.h
+++ b/src/cimdOmp.h
@@ -5,6 +5,9 @@
 #define OPENMP3 200805
 #define OPENMP4 201307
 #define OPENMP45 201511
+#define OPENMP5 201905
+#define OPENMP51 202011
+#define OPENMP52 202111
 
 #if _OPENMP >= OPENMP3
 #define HAVE_OPENMP3 1
@@ -17,6 +20,18 @@
 #if _OPENMP >= OPENMP45
 #define HAVE_OPENMP45 1
 #endif
+
+#if _OPENMP >= OPENMP5
+#define HAVE_OPENMP5 1
+#endif
+
+#if _OPENMP >= OPENMP51
+#define HAVE_OPENMP51 1
+#endif
+
+#if _OPENMP >= OPENMP52
+#define HAVE_OPENMP52 1
+#endif
 #endif
 
 int cdo_omp_get_thread_num(void);
diff --git a/src/compare.h b/src/compare.h
index 465967936c7a240aaeaaa4c8aff492dd31376e91..f00f3b70b89e0125efc0089e7e5e4eb4caa41013 100644
--- a/src/compare.h
+++ b/src/compare.h
@@ -9,15 +9,16 @@
 // clang-format off
 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); };
-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)); };
+const auto dbl_is_not_equal = [](auto a, auto b) noexcept { return ((std::isnan(a) || std::isnan(b)) ? (std::isnan(a) && std::isnan(b)) : is_not_equal(a, b)); };
+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) ((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));
+  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))
diff --git a/src/cpp_lib.h b/src/cpp_lib.h
new file mode 100644
index 0000000000000000000000000000000000000000..7a0613e9675f18511cd75fe694288814d10615b4
--- /dev/null
+++ b/src/cpp_lib.h
@@ -0,0 +1,26 @@
+/*
+  This file is part of CDO. CDO is a collection of Operators to manipulate and analyse Climate model Data.
+
+  Author: Uwe Schulzweida
+
+*/
+#ifndef CPP_LIB_H
+#define CPP_LIB_H
+
+#include <version>
+
+#if __cpp_lib_ranges >= 201911L
+#define HAVE_LIB_RANGES 1
+#else
+#error C++20 Ranges library is Not Available!
+#endif
+
+#if __cpp_lib_ranges_zip >= 202110L
+#define HAVE_LIB_RANGES_ZIP 1
+#endif
+
+#if __cpp_lib_mdspan >= 202207L
+#define HAVE_LIB_MDSPAN 1
+#endif
+
+#endif
diff --git a/src/datetime.cc b/src/datetime.cc
index e1b41d9116448edc73ff69c209ee7c02b8e91f87..361daf41148881a782df161318eeee3363086df2 100644
--- a/src/datetime.cc
+++ b/src/datetime.cc
@@ -13,6 +13,7 @@
 #include "process_int.h"
 #include "datetime.h"
 #include "param_conversion.h"
+#include "printinfo.h"
 #include "util_string.h"
 
 TimeStat CDO_Timestat_Date = TimeStat::UNDEF;
@@ -53,25 +54,25 @@ set_timestat_date(const std::string &p_optarg)
 }
 
 static void
-get_timestat_date(TimeStat *tstat_date)
+get_timestat_date(TimeStat &tstatDate)
 {
   char *envstr = getenv("CDO_TIMESTAT_DATE");
   if (envstr == nullptr) envstr = getenv("RUNSTAT_DATE");
   if (envstr)
     {
-      TimeStat env_date = TimeStat::UNDEF;
+      TimeStat envDate = TimeStat::UNDEF;
       auto envstrl = string_to_lower(envstr);
 
       // clang-format off
-      if      (envstrl == "first")   env_date = TimeStat::FIRST;
-      else if (envstrl == "last")    env_date = TimeStat::LAST;
-      else if (envstrl == "middle")  env_date = TimeStat::MEAN;
-      else if (envstrl == "midhigh") env_date = TimeStat::MIDHIGH;
+      if      (envstrl == "first")   envDate = TimeStat::FIRST;
+      else if (envstrl == "last")    envDate = TimeStat::LAST;
+      else if (envstrl == "middle")  envDate = TimeStat::MEAN;
+      else if (envstrl == "midhigh") envDate = TimeStat::MIDHIGH;
       // clang-format on
 
-      if (env_date != TimeStat::UNDEF)
+      if (envDate != TimeStat::UNDEF)
         {
-          *tstat_date = env_date;
+          tstatDate = envDate;
           if (Options::cdoVerbose) cdo_print("Set CDO_TIMESTAT_DATE to %s", envstr);
         }
     }
@@ -80,7 +81,7 @@ get_timestat_date(TimeStat *tstat_date)
 void
 DateTimeList::init()
 {
-  if (!dateTimeInit) get_timestat_date(&CDO_Timestat_Date);
+  if (!dateTimeInit) get_timestat_date(CDO_Timestat_Date);
   dateTimeInit = true;
 }
 
@@ -101,13 +102,8 @@ DateTimeList::shift()
 void
 DateTimeList::taxis_inq_timestep(int taxisID, int tsID)
 {
-  constexpr size_t NALLOC = 128;
-
-  if ((size_t) tsID >= this->nalloc)
-    {
-      this->nalloc += NALLOC;
-      this->dtInfo.resize(this->nalloc);
-    }
+  auto nalloc = this->dtInfo.size();
+  if ((size_t) tsID >= nalloc) { this->dtInfo.resize((nalloc >= 1024) ? nalloc + 512 : nalloc * 2); }
 
   if ((size_t) tsID >= this->size) this->size = (size_t) tsID + 1;
 
@@ -161,66 +157,68 @@ DateTimeList::taxis_def_timestep(int taxisID, int tsID)
 }
 
 void
-DateTimeList::mean(int nsteps)
+DateTimeList::mean(int numSteps)
 {
-  if (nsteps % 2 == 0)
+  if (numSteps % 2 == 0)
     {
 #ifdef TEST_DTLIST_MEAN
       auto julianDate0 = julianDate_encode(this->calendar, this->dtInfo[0].v);
 
       double seconds = 0.0;
-      for (int i = 1; i < nsteps; ++i)
+      for (int i = 1; i < numSteps; ++i)
         {
           auto julianDate = julianDate_encode(this->calendar, this->dtInfo[i].v);
           seconds += julianDate_to_seconds(julianDate_sub(julianDate, julianDate0));
         }
 
-      auto julianDate = julianDate_add_seconds(julianDate0, std::lround(seconds / nsteps));
+      auto julianDate = julianDate_add_seconds(julianDate0, std::lround(seconds / numSteps));
       this->timestat.v = julianDate_decode(this->calendar, julianDate);
 #else
-      auto julianDate1 = julianDate_encode(this->calendar, this->dtInfo[nsteps / 2 - 1].v);
-      auto julianDate2 = julianDate_encode(this->calendar, this->dtInfo[nsteps / 2].v);
+      auto julianDate1 = julianDate_encode(this->calendar, this->dtInfo[numSteps / 2 - 1].v);
+      auto julianDate2 = julianDate_encode(this->calendar, this->dtInfo[numSteps / 2].v);
 
       auto seconds = julianDate_to_seconds(julianDate_sub(julianDate2, julianDate1)) / 2;
       auto julianDatem = julianDate_add_seconds(julianDate1, std::lround(seconds));
       this->timestat.v = julianDate_decode(this->calendar, julianDatem);
 #endif
     }
-  else { this->timestat.v = this->dtInfo[nsteps / 2].v; }
+  else { this->timestat.v = this->dtInfo[numSteps / 2].v; }
 }
 
 void
-DateTimeList::midhigh(int nsteps)
+DateTimeList::midhigh(int numSteps)
 {
-  this->timestat.v = this->dtInfo[nsteps / 2].v;
+  this->timestat.v = this->dtInfo[numSteps / 2].v;
 }
 
 void
-DateTimeList::stat_taxis_def_timestep(int taxisID, int nsteps)
+DateTimeList::stat_taxis_def_timestep(int taxisID, int numSteps)
 {
-  if ((size_t) nsteps > this->size) cdo_abort("Internal error; unexpected nsteps=%d (limit=%ld)!", nsteps, this->size);
+  if ((size_t) numSteps > this->size) cdo_abort("Internal error; unexpected numSteps=%d (limit=%ld)!", numSteps, this->size);
 
   if (CDO_Timestat_Date != TimeStat::UNDEF) this->stat = CDO_Timestat_Date;
 
   // clang-format off
-  if      (this->stat == TimeStat::MEAN)    this->mean(nsteps);
-  else if (this->stat == TimeStat::MIDHIGH) this->midhigh(nsteps);
+  if      (this->stat == TimeStat::MEAN)    this->mean(numSteps);
+  else if (this->stat == TimeStat::MIDHIGH) this->midhigh(numSteps);
   else if (this->stat == TimeStat::FIRST)   this->timestat.v = this->dtInfo[0].v;
-  else if (this->stat == TimeStat::LAST)    this->timestat.v = this->dtInfo[nsteps - 1].v;
+  else if (this->stat == TimeStat::LAST)    this->timestat.v = this->dtInfo[numSteps - 1].v;
   else cdo_abort("Internal error; implementation missing for timestat=%d", (int)this->stat);
   // clang-format on
 
   if (this->hasBounds)
     {
       this->timestat.b[0] = this->dtInfo[0].b[0];
-      this->timestat.b[1] = this->dtInfo[nsteps - 1].b[1];
+      this->timestat.b[1] = this->dtInfo[numSteps - 1].b[1];
     }
   else
     {
       this->timestat.b[0] = this->dtInfo[0].v;
-      this->timestat.b[1] = this->dtInfo[nsteps - 1].v;
+      this->timestat.b[1] = this->dtInfo[numSteps - 1].v;
     }
 
+  if (this->year) { this->timestat.v.date.year = this->year; }
+
   taxisDefVdatetime(taxisID, this->timestat.v);
   // if (this->hasBounds)
   {
@@ -231,8 +229,8 @@ DateTimeList::stat_taxis_def_timestep(int taxisID, int nsteps)
 void
 DateTimeList::stat_taxis_def_timestep(int taxisID)
 {
-  int nsteps = this->size;
-  this->stat_taxis_def_timestep(taxisID, nsteps);
+  int numSteps = this->size;
+  this->stat_taxis_def_timestep(taxisID, numSteps);
 }
 
 CdiDateTime
@@ -376,32 +374,58 @@ decode_day_of_year(const CdiDate &date)
 }
 
 int
-decode_hour_of_year(const CdiDateTime &cdiDateTime)
+decode_hour_of_year(const CdiDateTime &cdiDateTime, int maxHours)
 {
   int year, month, day;
   int hour, minute, second, ms;
   cdiDate_decode(cdiDateTime.date, &year, &month, &day);
   cdiTime_decode(cdiDateTime.time, &hour, &minute, &second, &ms);
 
-  int houroy = 0;
+  int hourOfDay = 0;
   if (month >= 1 && month <= 12 && day >= 1 && day <= 31 && hour >= 0 && hour < 24)
-    houroy = ((month - 1) * 31 + day - 1) * 25 + hour + 1;
+    hourOfDay = ((month - 1) * 31 + day - 1) * 25 + hour + 1;
+
+  if (hourOfDay < 0 || hourOfDay >= maxHours)
+    cdo_abort("Hour of year %d out of range (%s)!", hourOfDay, datetime_to_string(cdiDateTime));
+
+  return hourOfDay;
+}
+
+int
+decode_hour_of_day(const CdiDateTime &cdiDateTime, int maxHours)
+{
+  int year, month, day;
+  int hour, minute, second, ms;
+  cdiDate_decode(cdiDateTime.date, &year, &month, &day);
+  cdiTime_decode(cdiDateTime.time, &hour, &minute, &second, &ms);
+
+  int hourOfDay = 0;
+  if (month >= 1 && month <= 12 && day >= 1 && day <= 31 && hour >= 0 && hour < 24) hourOfDay = hour + 1;
 
-  return houroy;
+  if (hourOfDay < 0 || hourOfDay >= maxHours)
+    cdo_abort("Hour of day %d out of range (%s)!", hourOfDay, datetime_to_string(cdiDateTime));
+
+  return hourOfDay;
 }
 
 int
-decode_hour_of_day(const CdiDateTime &cdiDateTime)
+decode_minute_of_day(const CdiDateTime &cdiDateTime, int maxMinutes)
 {
   int year, month, day;
   int hour, minute, second, ms;
   cdiDate_decode(cdiDateTime.date, &year, &month, &day);
   cdiTime_decode(cdiDateTime.time, &hour, &minute, &second, &ms);
 
-  int hourod = 0;
-  if (month >= 1 && month <= 12 && day >= 1 && day <= 31 && hour >= 0 && hour < 24) hourod = hour + 1;
+  int minuteOfDay = 0;
+  if (month >= 1 && month <= 12 && day >= 1 && day <= 31 && hour >= 0 && hour < 24)
+    {
+      if (minute >= 0 && minute < 60) minuteOfDay = hour * 60 + minute + 1;
+    }
+
+  if (minuteOfDay < 0 || minuteOfDay >= maxMinutes)
+    cdo_abort("Minute of day %d out of range (%s)!", minuteOfDay, datetime_to_string(cdiDateTime));
 
-  return hourod;
+  return minuteOfDay;
 }
 
 void
@@ -421,9 +445,6 @@ get_timeunits(const char *unitsStr, int &incrPeriod, int &incrUnits, int &timeUn
   if      (memcmp(unitsStr, "seconds", len) == 0) { incrUnits = 1;     timeUnits = TUNIT_SECOND; }
   else if (memcmp(unitsStr, "minutes", len) == 0) { incrUnits = 60;    timeUnits = TUNIT_MINUTE; }
   else if (memcmp(unitsStr, "hours", len) == 0)   { incrUnits = 3600;  timeUnits = TUNIT_HOUR; }
-  else if (memcmp(unitsStr, "3hours", len) == 0)  { incrUnits = 10800; timeUnits = TUNIT_3HOURS; }
-  else if (memcmp(unitsStr, "6hours", len) == 0)  { incrUnits = 21600; timeUnits = TUNIT_6HOURS; }
-  else if (memcmp(unitsStr, "12hours", len) == 0) { incrUnits = 43200; timeUnits = TUNIT_12HOURS; }
   else if (memcmp(unitsStr, "days", len) == 0)    { incrUnits = 86400; timeUnits = TUNIT_DAY; }
   else if (memcmp(unitsStr, "months", len) == 0)  { incrUnits = 1;     timeUnits = TUNIT_MONTH; }
   else if (memcmp(unitsStr, "years", len) == 0)   { incrUnits = 12;    timeUnits = TUNIT_YEAR; }
@@ -457,11 +478,11 @@ decode_timestring(const std::string &timeString)
 {
   if (strchr(timeString.c_str(), ':'))
     {
-      int hour = 0, minute = 0, second = 0, ms = 0;
+      int hour = 0, minute = 0;
       double fseconds = 0.0;
       std::sscanf(timeString.c_str(), "%d:%d:%lf", &hour, &minute, &fseconds);
-      second = (int) fseconds;
-      ms = (fseconds - second) * 1000;
+      int second = (int) fseconds;
+      int ms = (fseconds - second) * 1000;
       return cdiTime_encode(hour, minute, second, ms);
     }
   else { return cdiTime_set(parameter_to_int(timeString)); }
diff --git a/src/datetime.h b/src/datetime.h
index c4b18e50cbf7267f421658292faff78ab944f612..ec510c7de685c88b182fe909582224e23221292b 100644
--- a/src/datetime.h
+++ b/src/datetime.h
@@ -79,10 +79,13 @@ DateTimeList
 // clang-format on
 {
 public:
-  DateTimeList() { init(); }
   // clang-format off
+  DateTimeList() { init(); dtInfo.resize(2); }
+  void set_year(int _year) { this->year = _year; }
   void set_stat(TimeStat _stat) { this->stat = _stat; }
   void set_calendar(int _calendar) { this->calendar = _calendar; }
+  size_t get_size(){ return this->size; }
+  const std::vector<DateTimeInfo>& get_info() { return this->dtInfo; }
   // clang-format on
   CdiDateTime get_vDateTime(int tsID);
   void shift();
@@ -90,15 +93,15 @@ public:
   void taxis_set_next_timestep(int taxisID);
   void taxis_inq_timestep(int taxisID, int tsID);
   void taxis_def_timestep(int taxisID, int tsID);
-  void stat_taxis_def_timestep(int taxisID, int nsteps);
+  void stat_taxis_def_timestep(int taxisID, int numSteps);
   void stat_taxis_def_timestep(int taxisID);
 
 private:
-  size_t nalloc = 0;
-  size_t size = 0;
-  int hasBounds = -1;
-  int calendar = -1;
-  TimeStat stat = TimeStat::LAST;
+  size_t size{ 0 };
+  int year{ 0 };
+  int hasBounds{ -1 };
+  int calendar{ -1 };
+  TimeStat stat{ TimeStat::LAST };
   DateTimeInfo timestat;
   std::vector<DateTimeInfo> dtInfo;
 
@@ -122,8 +125,9 @@ int decode_month(const CdiDate &date);
 int decode_month_and_day(const CdiDate &date);
 
 int decode_day_of_year(const CdiDate &date);
-int decode_hour_of_year(const CdiDateTime &cdiDateTime);
-int decode_hour_of_day(const CdiDateTime &cdiDateTime);
+int decode_hour_of_year(const CdiDateTime &cdiDateTime, int maxHours);
+int decode_hour_of_day(const CdiDateTime &cdiDateTime, int maxHours);
+int decode_minute_of_day(const CdiDateTime &cdiDateTime, int maxMinutes);
 
 void set_date_time(CdiDateTime &datetime1, CdiDateTime datetime2);
 
diff --git a/src/dmemory.h b/src/dmemory.h
deleted file mode 100644
index 2582c4087a31c572118143b07b052c8f2ec58d30..0000000000000000000000000000000000000000
--- a/src/dmemory.h
+++ /dev/null
@@ -1,68 +0,0 @@
-#ifndef _DMEMORY_H
-#define _DMEMORY_H
-
-// clang-format off
-
-#include <stdio.h>
-
-// if DEBUG_MEMORY is defined setenv MEMORY_DEBUG to debug memory
-#define  DEBUG_MEMORY
-
-#ifndef  WITH_FUNCTION_NAME
-#define  WITH_FUNCTION_NAME
-#endif
-
-#ifdef  __cplusplus
-extern "C" {
-#endif
-
-extern size_t  memTotal(void);
-extern void    memDebug(int debug);
-extern void    memExitOnError(void);
-
-extern void   *memRealloc(void *ptr, size_t size, const char *file, const char *functionname, int line);
-extern void   *memCalloc(size_t nobjs, size_t size, const char *file, const char *functionname, int line);
-extern void   *memMalloc(size_t size, const char *file, const char *functionname, int line);
-extern void    memFree(void *ptr, const char *file, const char *functionname, int line);
-
-#ifdef  __cplusplus
-}
-#endif
-
-#ifdef  DEBUG_MEMORY
-
-#ifdef  WITH_FUNCTION_NAME
-#define  Realloc(p, s)  memRealloc((p), (s), __FILE__, __func__, __LINE__)
-#define   Calloc(n, s)   memCalloc((n), (s), __FILE__, __func__, __LINE__)
-#define   Malloc(s)      memMalloc((s), __FILE__, __func__, __LINE__)
-#define     Free(p)        memFree((p), __FILE__, __func__, __LINE__)
-#else
-#define  Realloc(p, s)  memRealloc((p), (s), __FILE__, (void *) NULL, __LINE__)
-#define   Calloc(n, s)   memCalloc((n), (s), __FILE__, (void *) NULL, __LINE__)
-#define   Malloc(s)      memMalloc((s), __FILE__, (void *) NULL, __LINE__)
-#define     Free(p)        memFree((p), __FILE__, (void *) NULL, __LINE__)
-#endif
-
-#else
-
-#include <stdlib.h>
-
-#define  Realloc(p, s)  realloc((p), (s))
-#define   Calloc(n, s)   calloc((n), (s))
-#define   Malloc(s)      malloc((s))
-#define     Free(p)        free((p))
-
-#endif /* DEBUG_MEMORY */
-
-// clang-format on
-
-#endif /* _DMEMORY_H */
-/*
- * Local Variables:
- * c-file-style: "Java"
- * c-basic-offset: 2
- * indent-tabs-mode: nil
- * show-trailing-whitespace: t
- * require-trailing-newline: t
- * End:
- */
diff --git a/src/ecacore.cc b/src/ecacore.cc
index f00e7f1e521b1dc6ef53114cdefffa5de7b45dd8..d4f62ede545a859cc814e008ac79d46581024d14 100644
--- a/src/ecacore.cc
+++ b/src/ecacore.cc
@@ -8,17 +8,14 @@
 
 */
 
-#include <assert.h>
-#include <string.h>
+#include <cstring>
 
 #include <cdi.h>
 
-#include "cdo_vlist.h"
 #include <mpim_grid.h>
 #include "process_int.h"
 #include "ecacore.h"
 #include "ecautil.h"
-#include "util_files.h"
 #include "util_date.h"
 #include "datetime.h"
 #include "field_functions.h"
@@ -66,9 +63,11 @@ eca1(const ECA_REQUEST_1 &request)
   auto ivlistID = cdo_stream_inq_vlist(istreamID);
   auto ovlistID = vlistCreate();
 
-  auto gridID = vlistInqVarGrid(ivlistID, FIRST_VAR_ID);
-  auto zaxisID = vlistInqVarZaxis(ivlistID, FIRST_VAR_ID);
-  auto missval = vlistInqVarMissval(ivlistID, FIRST_VAR_ID);
+  VarList varList1(ivlistID);
+  const auto varFirst = varList1.vars[FIRST_VAR_ID];
+  auto gridID = varFirst.gridID;
+  auto zaxisID = varFirst.zaxisID;
+  auto missval = varFirst.missval;
 
   auto varID = vlistDefVar(ovlistID, gridID, zaxisID, TIME_VARYING);
 
@@ -257,14 +256,14 @@ eca1(const ECA_REQUEST_1 &request)
                         {
                           for (size_t i = 0; i < len; ++i)
                             {
-                              if (!dbl_is_equal(array2[i], missval2) && !dbl_is_equal(array2[i], 0.0)) array1[i] = 0.0;
+                              if (dbl_is_not_equal(array2[i], missval2) && dbl_is_not_equal(array2[i], 0.0)) array1[i] = 0.0;
                             }
                         }
                       else
                         {
                           for (size_t i = 0; i < len; ++i)
                             {
-                              if (!dbl_is_equal(array2[i], 0.0)) array1[i] = 0.0;
+                              if (dbl_is_not_equal(array2[i], 0.0)) array1[i] = 0.0;
                             }
                         }
                       request.var1.f3(var13[levelID], field3);
@@ -327,7 +326,7 @@ eca1(const ECA_REQUEST_1 &request)
           cdo_def_timestep(ostreamID, otsID);
         }
 
-      if (otsID && vlistInqVarTimetype(ivlistID, FIRST_VAR_ID) == TIME_CONSTANT) continue;
+      if (otsID && varFirst.timeType == TIME_CONSTANT) continue;
 
       varID = 0;
       for (int levelID = 0; levelID < nlevels; ++levelID)
@@ -380,12 +379,17 @@ eca2(const ECA_REQUEST_2 &request)
   auto ivlistID2 = cdo_stream_inq_vlist(istreamID2);
   auto ovlistID = vlistCreate();
 
-  vlist_compare(ivlistID1, ivlistID2, CmpVlist::All);
+  VarList varList1(ivlistID1);
+  VarList varList2(ivlistID2);
+  const auto var1First = varList1.vars[FIRST_VAR_ID];
+  const auto var2First = varList2.vars[FIRST_VAR_ID];
 
-  auto gridID = vlistInqVarGrid(ivlistID1, FIRST_VAR_ID);
-  auto zaxisID = vlistInqVarZaxis(ivlistID1, FIRST_VAR_ID);
-  auto missval1 = vlistInqVarMissval(ivlistID1, FIRST_VAR_ID);
-  auto missval2 = vlistInqVarMissval(ivlistID2, FIRST_VAR_ID);
+  varList_compare(varList1, varList2);
+
+  auto gridID = var1First.gridID;
+  auto zaxisID = var1First.zaxisID;
+  auto missval1 = var1First.missval;
+  auto missval2 = var2First.missval;
 
   varID = vlistDefVar(ovlistID, gridID, zaxisID, TIME_VARYING);
 
@@ -428,7 +432,7 @@ eca2(const ECA_REQUEST_2 &request)
   field2.resize(gridsize);
   field3.resize(gridsize);
   constexpr int MaxDays = 373;
-  FieldVector2D vars2[MaxDays];
+  FieldVector2D varsData2[MaxDays];
 
   auto nlevels = zaxisInqSize(zaxisID);
 
@@ -461,14 +465,14 @@ eca2(const ECA_REQUEST_2 &request)
       auto dayOfYear = decode_day_of_year(ivDateTime.date);
       if (dayOfYear < 0 || dayOfYear >= MaxDays) cdo_abort("Day %d out of range!", dayOfYear);
 
-      if (!vars2[dayOfYear].size()) fields_from_vlist(ivlistID2, vars2[dayOfYear], FIELD_VEC);
+      if (!varsData2[dayOfYear].size()) field2D_init(varsData2[dayOfYear], varList2, FIELD_VEC);
 
       for (int recID = 0; recID < nrecs; ++recID)
         {
           int levelID;
           cdo_inq_record(istreamID2, &varID, &levelID);
           if (varID != FIRST_VAR_ID) continue;
-          auto &rvar = vars2[dayOfYear][0][levelID];
+          auto &rvar = varsData2[dayOfYear][0][levelID];
           cdo_read_record(istreamID2, rvar.vec_d.data(), &rvar.numMissVals);
         }
 
@@ -490,7 +494,7 @@ eca2(const ECA_REQUEST_2 &request)
           auto ivDateTime = taxisInqVdatetime(itaxisID1);
 
           auto dayOfYear = decode_day_of_year(ivDateTime.date);
-          if (!vars2[dayOfYear].size()) cdo_abort("Input streams have different time values!");
+          if (!varsData2[dayOfYear].size()) cdo_abort("Input streams have different time values!");
 
           if (numSets == 0) inDateTime21 = ivDateTime;
 
@@ -531,10 +535,10 @@ eca2(const ECA_REQUEST_2 &request)
               cdo_read_record(istreamID1, field1.vec_d.data(), &field1.numMissVals);
               field1.grid = gridID;
               field1.missval = missval1;
-              field2.grid = vars2[dayOfYear][0][levelID].grid;
-              field2.numMissVals = vars2[dayOfYear][0][levelID].numMissVals;
+              field2.grid = varsData2[dayOfYear][0][levelID].grid;
+              field2.numMissVals = varsData2[dayOfYear][0][levelID].numMissVals;
               field2.missval = missval2;
-              field_copy(vars2[dayOfYear][0][levelID], field2);
+              field_copy(varsData2[dayOfYear][0][levelID], field2);
 
               vfarnum(samp1[levelID], field1);
               vfarnum(samp2[levelID], field2);
@@ -596,14 +600,14 @@ eca2(const ECA_REQUEST_2 &request)
                         {
                           for (size_t i = 0; i < len; ++i)
                             {
-                              if (!dbl_is_equal(array2[i], missvaltemp) && !dbl_is_equal(array2[i], 0.0)) array1[i] = 0.0;
+                              if (dbl_is_not_equal(array2[i], missvaltemp) && dbl_is_not_equal(array2[i], 0.0)) array1[i] = 0.0;
                             }
                         }
                       else
                         {
                           for (size_t i = 0; i < len; ++i)
                             {
-                              if (!dbl_is_equal(array2[i], 0.0)) array1[i] = 0.0;
+                              if (dbl_is_not_equal(array2[i], 0.0)) array1[i] = 0.0;
                             }
                         }
                       request.var1.f5(var15[levelID], field3, request.var1.f5arg);
@@ -673,7 +677,7 @@ eca2(const ECA_REQUEST_2 &request)
           cdo_def_timestep(ostreamID, otsID);
         }
 
-      if (otsID && vlistInqVarTimetype(ivlistID1, FIRST_VAR_ID) == TIME_CONSTANT) continue;
+      if (otsID && var1First.timeType == TIME_CONSTANT) continue;
 
       varID = 0;
       for (int levelID = 0; levelID < nlevels; ++levelID)
@@ -730,11 +734,15 @@ eca3(const ECA_REQUEST_3 &request)
   auto ivlistID2 = cdo_stream_inq_vlist(istreamID2);
   auto ovlistID = vlistCreate();
 
-  vlist_compare(ivlistID1, ivlistID2, CmpVlist::All);
+  VarList varList1(ivlistID1);
+  VarList varList2(ivlistID2);
+  const auto var1First = varList1.vars[FIRST_VAR_ID];
+
+  varList_compare(varList1, varList2);
 
-  auto gridID = vlistInqVarGrid(ivlistID1, FIRST_VAR_ID);
-  auto zaxisID = vlistInqVarZaxis(ivlistID1, FIRST_VAR_ID);
-  auto missval = vlistInqVarMissval(ivlistID1, FIRST_VAR_ID);
+  auto gridID = var1First.gridID;
+  auto zaxisID = var1First.zaxisID;
+  auto missval = var1First.missval;
 
   varID = vlistDefVar(ovlistID, gridID, zaxisID, TIME_VARYING);
 
@@ -861,7 +869,7 @@ eca3(const ECA_REQUEST_3 &request)
           cdo_def_timestep(ostreamID, otsID);
         }
 
-      if (otsID && vlistInqVarTimetype(ivlistID1, FIRST_VAR_ID) == TIME_CONSTANT) continue;
+      if (otsID && var1First.timeType == TIME_CONSTANT) continue;
 
       varID = 0;
       for (int levelID = 0; levelID < nlevels; ++levelID)
@@ -911,12 +919,16 @@ eca4(const ECA_REQUEST_4 &request)
   auto ivlistID2 = cdo_stream_inq_vlist(istreamID2);
   auto ovlistID = vlistCreate();
 
-  int gridID = vlistInqVarGrid(ivlistID1, FIRST_VAR_ID);
-  if (gridInqSize(gridID) != gridInqSize(vlistInqVarGrid(ivlistID2, FIRST_VAR_ID)))
-    cdo_abort("Grid sizes of the input fields do not match!");
+  VarList varList1(ivlistID1);
+  VarList varList2(ivlistID2);
+  const auto var1First = varList1.vars[FIRST_VAR_ID];
+  const auto var2First = varList2.vars[FIRST_VAR_ID];
+
+  auto gridID = var1First.gridID;
+  if (var1First.gridsize != var2First.gridsize) cdo_abort("Grid sizes of the input fields do not match!");
 
-  auto zaxisID = vlistInqVarZaxis(ivlistID1, FIRST_VAR_ID);
-  auto missval = vlistInqVarMissval(ivlistID1, FIRST_VAR_ID);
+  auto zaxisID = var1First.zaxisID;
+  auto missval = var1First.missval;
 
   auto ovarID1 = vlistDefVar(ovlistID, gridID, zaxisID, TIME_VARYING);
 
@@ -1010,7 +1022,7 @@ eca4(const ECA_REQUEST_4 &request)
       cdo_inq_record(istreamID2, &varID, &levelID);
       cdo_read_record(istreamID2, mask.vec_d.data(), &mask.numMissVals);
       mask.grid = gridID;
-      mask.missval = vlistInqVarMissval(ivlistID2, 0);
+      mask.missval = var2First.missval;
 
       request.s3(mask, request.s3arg);
     }
@@ -1088,7 +1100,7 @@ eca4(const ECA_REQUEST_4 &request)
                   for (size_t i = 0; i < gridsize; ++i)
                     {
                       if (yvals[i] >= 0.0)
-                        if (!dbl_is_equal(startCount[levelID].vec_d[i], missval))
+                        if (dbl_is_not_equal(startCount[levelID].vec_d[i], missval))
                           {
                             startCount[levelID].vec_d[i] = missval;
                             startCount[levelID].numMissVals++;
@@ -1098,7 +1110,7 @@ eca4(const ECA_REQUEST_4 &request)
                   for (size_t i = 0; i < gridsize; ++i)
                     {
                       if (yvals[i] < 0.0)
-                        if (!dbl_is_equal(endCount[levelID].vec_d[i], missval))
+                        if (dbl_is_not_equal(endCount[levelID].vec_d[i], missval))
                           {
                             endCount[levelID].vec_d[i] = missval;
                             endCount[levelID].numMissVals++;
@@ -1122,7 +1134,7 @@ eca4(const ECA_REQUEST_4 &request)
                         {
                           if (yvals[i] >= 0.0)
                             {
-                              if (!dbl_is_equal(endCount[levelID].vec_d[i], missval))
+                              if (dbl_is_not_equal(endCount[levelID].vec_d[i], missval))
                                 {
                                   endCount[levelID].vec_d[i] = missval;
                                   endCount[levelID].numMissVals++;
@@ -1139,7 +1151,7 @@ eca4(const ECA_REQUEST_4 &request)
                         {
                           if (yvals[i] < 0.0)
                             {
-                              if (!dbl_is_equal(startCount[levelID].vec_d[i], missval))
+                              if (dbl_is_not_equal(startCount[levelID].vec_d[i], missval))
                                 {
                                   startCount[levelID].vec_d[i] = missval;
                                   startCount[levelID].numMissVals++;
diff --git a/src/ecautil.cc b/src/ecautil.cc
index f2970b504616978d4aca569897094151a38631c9..d2f26da91f1810cd547ca02ff0b8394a4fd67405 100644
--- a/src/ecautil.cc
+++ b/src/ecautil.cc
@@ -63,7 +63,7 @@ count(Field &field1, const Field &field2, double mode)
               continue;
             }
 
-          if (!dbl_is_equal(array1[i], missval1))
+          if (dbl_is_not_equal(array1[i], missval1))
             {
               if (is_equal(mode, 0.0) || is_equal(mode, 1.0))
                 array1[i] += 1.0;
@@ -227,7 +227,7 @@ eq(double a, double b)
 static int
 ne(double a, double b)
 {
-  return !dbl_is_equal(a, b);
+  return dbl_is_not_equal(a, b);
 }
 
 void
diff --git a/src/expr.cc b/src/expr.cc
index f5a376fb74353d8f28eae97f95d6ca55d588c4e5..4085f63cd522d9b1ba0adc3b9135cfebcde70d6e 100644
--- a/src/expr.cc
+++ b/src/expr.cc
@@ -2028,7 +2028,7 @@ expr_run_var(nodeType *p, ParseParamType &parseArg)
   if (varID == -1) { cdo_abort("Variable >%s< not found!", vnm); }
   else if (init)
     {
-      if (varID < parseArg.nvars1 && !parseArg.needed[varID]) parseArg.needed[varID] = true;
+      if (varID < parseArg.numVars1 && !parseArg.needed[varID]) parseArg.needed[varID] = true;
     }
 
   param_meta_copy(p->param, params[varID]);
@@ -2173,7 +2173,7 @@ expr_run_opr(nodeType *p, ParseParamType &parseArg)
             if (varID >= 0)
               {
                 // printf("  found %s\n", varname2);
-                if (varID < parseArg.nvars1)
+                if (varID < parseArg.numVars1)
                   {
                     if (rnode->param.nlev > 0 && params[varID].nlev != rnode->param.nlev)
                       cdo_abort("The number of layers must not change (name=%s layers: in=%zu out=%zu)!", params[varID].name,
@@ -2254,3 +2254,16 @@ expr_run(nodeType *p, ParseParamType &parseArg)
 
   return nullptr;
 }
+int
+params_get_coord_ID(const ParseParamType &parseArg, int coord, int cdiID)
+{
+  auto ncoords = parseArg.numCoords;
+  for (int coordID = 0; coordID < ncoords; ++coordID)
+    {
+      if (parseArg.coords[coordID].coord == coord && parseArg.coords[coordID].cdiID == cdiID) return coordID;
+    }
+
+  cdo_abort("%s: coordinate %c not found!", __func__, coord);
+
+  return -1;
+}
diff --git a/src/expr.h b/src/expr.h
index ce354ac123e977dc8ca78373c660e96aa63e4fbb..84f64f85fce99db08989f6154a3ba860036afa20 100644
--- a/src/expr.h
+++ b/src/expr.h
@@ -6,6 +6,7 @@
 */
 #ifndef CDO_EXPR_H
 #define CDO_EXPR_H
+
 #include <cstdio>
 #include <cstdarg>
 #include <cassert>
@@ -48,6 +49,7 @@ struct conNodeType
 {
   double value;  // value of constant
 
+  conNodeType() : value(0.0) {}  // Default ctor is needed to initialize variant object
   conNodeType(double _value) : value(_value) {}
 };
 
@@ -135,8 +137,7 @@ struct nodeType
   ParamEntry param;
   NodeEnum type{ NodeEnum::typeUndef };  // type of node
   bool isTmpObj = false;
-  // int variant is a dummy for default constructor
-  std::variant<int, conNodeType, varNodeType, cmdNodeType, funNodeType, oprNodeType> v;
+  std::variant<conNodeType, varNodeType, cmdNodeType, funNodeType, oprNodeType> v;
 
   auto &con() const { assert(std::holds_alternative<conNodeType>(v)); return std::get<conNodeType>(v); }
   auto &var()       { assert(std::holds_alternative<varNodeType>(v)); return std::get<varNodeType>(v); }
@@ -166,8 +167,8 @@ struct ParseParamType
   int maxparams;
   int nparams;
   int cnparams;  // current number of valid params
-  int nvars1;
-  int ncoords;
+  int numVars1;
+  int numCoords;
   int maxCoords;
   int tsID;
   int pointID;
diff --git a/src/expr_fun.cc b/src/expr_fun.cc
index 22364ddca79a48499a86ab84a13ea2d79e4fcbd6..97974229a098bb3d77eade27ca99585146a1adc7 100644
--- a/src/expr_fun.cc
+++ b/src/expr_fun.cc
@@ -5,7 +5,7 @@
 
 */
 
-#include "process_int.h"
+#include "cdo_output.h"
 #include "cdo_zaxis.h"
 #include "cimdOmp.h"
 #include <mpim_grid.h>
diff --git a/src/factory.cc b/src/factory.cc
index ca4706ae070a8883da84005ffadb0d0fa5bc57f2..63f133953f86fcf4849f0fe7a7e5281409629486 100644
--- a/src/factory.cc
+++ b/src/factory.cc
@@ -10,12 +10,55 @@
 #include <algorithm>
 #include <functional>
 
-#include "process.h"
 #include "factory.h"
 #include "util_string.h"
 
 namespace Factory
 {
+std::string
+err_msg_oper_not_found(const std::string &operatorName)
+{
+  // 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
+}
+
 OperatorMap &
 get()
 {
@@ -38,6 +81,7 @@ find(const std::string &p_operName, std::function<void()> p_onError)
   return it;
 }
 
+
 const CdoModule &
 get_module(const std::string &p_operName)
 {
@@ -66,7 +110,7 @@ get_constructor(const OperatorMap::iterator it)
 const CdoHelp &
 get_help(const std::string &p_operName)
 {
-  auto it = find(p_operName);
+  auto it = find(p_operName, [&p_operName]() { cdo_abort("%s", err_msg_oper_not_found(p_operName)); });
   return get_help(it);
 };
 
diff --git a/src/factory.h b/src/factory.h
index 7cf4a5375a25d464d46832a211ab5d644ec54a41..5666e56f173f3bf5340c9a46f678faa1b8eb11b1 100644
--- a/src/factory.h
+++ b/src/factory.h
@@ -7,6 +7,7 @@
 */
 #ifndef FACTORY_H
 #define FACTORY_H
+
 #include <vector>
 #include <iostream>
 #include <map>
@@ -27,11 +28,18 @@ struct FactoryEntry
 {
   const CdoModule &module;
   Factory::ModuleConstructor constructor;
+  ArgumentHandler argHandlers;
 
-  FactoryEntry(const CdoModule &mod, Factory::ModuleConstructor con) : module(mod), constructor(con) {}
+  FactoryEntry(const CdoModule &mod, Factory::ModuleConstructor con, ArgumentHandler &p_argHandlers)
+      : module(mod), constructor(con), argHandlers(p_argHandlers)
+  {
+  }
 };
 typedef std::map<std::string, FactoryEntry> OperatorMap;
 
+std::string
+err_msg_oper_not_found(const std::string &operatorname);
+
 std::string find_similar_operators(const std::string &operatorName);
 
 std::vector<std::string> get_module_operator_names(const std::string &module_name);
@@ -64,23 +72,22 @@ struct RegisterEntry
   {
     return
         [&mod](int p_ID, const std::string &p_operName, const std::vector<std::string> &p_operatorArguments) -> std::shared_ptr<T> {
-
-          Debug(FACTORY,"Creating process via factory function, %d = ID, %s = name, %s = mod_name", p_ID, p_operName, mod.name);
+          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)
+  register_operator(const CdoModule &mod, std::string &p_oper_name, ArgumentHandler &arghandler)
   {
-    Factory::get().insert(std::make_pair(p_oper_name, Factory::FactoryEntry(mod, create_constructor(mod))));
+    Factory::get().insert(std::make_pair(p_oper_name, Factory::FactoryEntry(mod, create_constructor(mod),arghandler)));
   }
 
 public:
-  RegisterEntry(CdoModule &module)
+  RegisterEntry(CdoModule &module, ArgumentHandler arghandler = ArgumentHandler())
   {
-    for (auto &oper : module.operators) { register_operator(module, oper.name); }
-    for (auto &alias : module.aliases) { register_operator(module, alias.alias); }
+    for (auto &oper : module.operators) { register_operator(module, oper.name, arghandler); }
+    for (auto &alias : module.aliases) { register_operator(module, alias.alias, arghandler); }
   };
 };
 #endif
diff --git a/src/field.cc b/src/field.cc
index 1e240fceb9ad23657766fa99e245f776cb00840a..bca4937064b1f7c0c85647608f26c1ae82bf7909 100644
--- a/src/field.cc
+++ b/src/field.cc
@@ -129,14 +129,7 @@ field_ncopy(size_t n, const Field &fieldIn, Field &fieldOut)
 void
 field_copy(const Field &fieldIn, Field &fieldOut)
 {
-  if (fieldIn.size > fieldOut.size) cdo_abort("Target field to small (%s)", __func__);
-
-  fieldOut.numMissVals = fieldIn.numMissVals;
-
-  if (fieldIn.memType == MemType::Float)
-    fieldOut.vec_f = fieldIn.vec_f;
-  else
-    fieldOut.vec_d = fieldIn.vec_d;
+  field_ncopy(fieldIn.size, fieldIn, fieldOut);
 }
 
 void
@@ -258,7 +251,8 @@ double
 field_range(const Field &field)
 {
   if (field.memType == MemType::Float)
-    return field.numMissVals ? 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.numMissVals ? varray_range_mv(field.size, field.vec_d, field.missval) : varray_range(field.size, field.vec_d);
 }
@@ -276,7 +270,8 @@ double
 field_mean(const Field &field)
 {
   if (field.memType == MemType::Float)
-    return field.numMissVals ? 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.numMissVals ? varray_mean_mv(field.size, field.vec_d, field.missval) : varray_mean(field.size, field.vec_d);
 }
@@ -286,10 +281,10 @@ field_meanw(const Field &field)
 {
   if (field.memType == MemType::Float)
     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);
+                             : varray_weighted_mean(field.size, field.vec_f, field.weightv, (float) field.missval);
   else
     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);
+                             : varray_weighted_mean(field.size, field.vec_d, field.weightv, field.missval);
 }
 
 double
@@ -306,10 +301,10 @@ field_avgw(const Field &field)
 {
   if (field.memType == MemType::Float)
     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);
+                             : varray_weighted_mean(field.size, field.vec_f, field.weightv, (float) field.missval);
   else
     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);
+                             : varray_weighted_mean(field.size, field.vec_d, field.weightv, field.missval);
 }
 
 double
@@ -430,6 +425,7 @@ field_rms(const Field &field, const Field &field2, Field &field3)
   auto missval2 = field2.missval;
   const auto &w = field.weightv;
   auto rsum = 0.0, rsumw = 0.0;
+  auto is_NE = dbl_is_not_equal;
   auto is_EQ = dbl_is_equal;
 
   auto len = gridInqSize(grid1);
@@ -438,7 +434,7 @@ field_rms(const Field &field, const Field &field2, Field &field3)
   // if ( numMissVals1 )
   {
     for (size_t i = 0; i < len; ++i)
-      if (!is_EQ(w[i], missval1))
+      if (is_NE(w[i], missval1))
         {
           rsum = ADDM(rsum, MULM(w[i], MULM(SUBM(array2[i], array1[i]), SUBM(array2[i], array1[i]))));
           rsumw = ADDM(rsumw, w[i]);
@@ -477,11 +473,11 @@ array_pctl(size_t len, T *array, size_t numMissVals, T missval, double pn)
 
           size_t j = 0;
           for (size_t i = 0; i < len; ++i)
-            if (!dbl_is_equal(array[i], missval)) v[j++] = array[i];
+            if (dbl_is_not_equal(array[i], missval)) v[j++] = array[i];
 
           if (numMissVals != len - j)
-            cdo_warning("Internal problem, inconsistent number of missing values (numMissVals: exprected=%zu found=%zu!)", 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);
         }
diff --git a/src/field.h b/src/field.h
index 870a5b43c52f169f235ec0ea23946bb0209db650..0b5bd3931f6671df8372fe95b75527df5ccaa2f7 100644
--- a/src/field.h
+++ b/src/field.h
@@ -11,6 +11,7 @@
 #include <utility>
 #include "varray.h"
 #include "cdo_options.h"
+#include "cdo_varlist.h"
 #include "cdo_vlist.h"
 #include "compare.h"
 
diff --git a/src/field2.cc b/src/field2.cc
index ee80cd65ed4b07ad70f1616c51e497757ac6f559..bd0a72b0a583120e1676f6efe10bd5f934e14cf1 100644
--- a/src/field2.cc
+++ b/src/field2.cc
@@ -5,12 +5,12 @@
 
 */
 
-#include <algorithm>
 #include <cdi.h>
 
 #include "arithmetic.h"
 #include "cdo_options.h"
-#include "process_int.h"
+#include "cdo_output.h"
+#include "cimdOmp.h"
 #include "field_functions.h"
 
 // clang-format off
@@ -52,45 +52,45 @@ const auto arith_vincr_mv = [](auto &a, auto mv_a, auto b, auto mv_b, auto is_EQ
 
 template <typename T1, typename T2>
 static void
-varray2_div(size_t len, Varray<T1> &v1, const Varray<T2> &v2, double missval1)
+varray2_div(size_t n, Varray<T1> &v1, const Varray<T2> &v2, double missval1)
 {
-  assert(len > 0);
+  assert(n > 0);
   assert(v1.size() > 0);
   assert(v2.size() > 0);
-  assert(len <= v1.size());
-  assert(len <= v2.size());
+  assert(n <= v1.size());
+  assert(n <= v2.size());
 
 #ifdef _OPENMP
-#pragma omp parallel for default(shared) schedule(static)
+#pragma omp parallel for if (n > cdoMinLoopSize) default(shared) schedule(static)
 #endif
-  for (size_t i = 0; i < len; ++i) { v1[i] = is_equal(v2[i], 0.0) ? missval1 : arith_div(v1[i], v2[i]); }
+  for (size_t i = 0; i < n; ++i) { v1[i] = is_equal(v2[i], 0.0) ? missval1 : arith_div(v1[i], v2[i]); }
 }
 
 template <typename T1, typename T2, typename FUNC>
 static void
-varray2_arith(size_t len, Varray<T1> &v1, const Varray<T2> &v2, FUNC func)
+varray2_arith(size_t n, Varray<T1> &v1, const Varray<T2> &v2, FUNC func)
 {
-  assert(len > 0);
+  assert(n > 0);
   assert(v1.size() > 0);
   assert(v2.size() > 0);
-  assert(len <= v1.size());
-  assert(len <= v2.size());
+  assert(n <= v1.size());
+  assert(n <= v2.size());
 
-#ifdef _OPENMP
-#pragma omp parallel for default(shared) schedule(static)
+#ifdef HAVE_OPENMP4
+#pragma omp parallel for simd if (n > cdoMinLoopSize) default(shared) schedule(static)
 #endif
-  for (size_t i = 0; i < len; ++i) { v1[i] = func(v1[i], v2[i]); }
+  for (size_t i = 0; i < n; ++i) { v1[i] = func(v1[i], v2[i]); }
 }
 
 template <typename T1, typename T2, typename FUNC>
 static void
-varray2_arith_mv(size_t len, Varray<T1> &v1, const Varray<T2> &v2, double missval1, double missval2, FUNC func)
+varray2_arith_mv(size_t n, Varray<T1> &v1, const Varray<T2> &v2, double missval1, double missval2, FUNC func)
 {
-  assert(len > 0);
+  assert(n > 0);
   assert(v1.size() > 0);
   assert(v2.size() > 0);
-  assert(len <= v1.size());
-  assert(len <= v2.size());
+  assert(n <= v1.size());
+  assert(n <= v2.size());
 
   T1 mv1 = missval1;
   T2 mv2 = missval2;
@@ -98,16 +98,16 @@ varray2_arith_mv(size_t len, Varray<T1> &v1, const Varray<T2> &v2, double missva
   if (std::isnan(missval1) || std::isnan(missval2))
     {
 #ifdef _OPENMP
-#pragma omp parallel for default(shared) schedule(static)
+#pragma omp parallel for if (n > cdoMinLoopSize) default(shared) schedule(static)
 #endif
-      for (size_t i = 0; i < len; ++i) func(v1[i], mv1, v2[i], mv2, dbl_is_equal);
+      for (size_t i = 0; i < n; ++i) { func(v1[i], mv1, v2[i], mv2, dbl_is_equal); }
     }
   else
     {
 #ifdef _OPENMP
-#pragma omp parallel for default(shared) schedule(static)
+#pragma omp parallel for if (n > cdoMinLoopSize) default(shared) schedule(static)
 #endif
-      for (size_t i = 0; i < len; ++i) func(v1[i], mv1, v2[i], mv2, is_equal);
+      for (size_t i = 0; i < n; ++i) { func(v1[i], mv1, v2[i], mv2, is_equal); }
     }
 }
 
@@ -178,7 +178,7 @@ static void
 field2_vincr(size_t len, Varray<T1> &v1, const Varray<T2> &v2, T2 missval, int vincr)
 {
   for (size_t i = 0; i < len; ++i)
-    if (!dbl_is_equal(v2[i], missval)) v1[i] += vincr;
+    if (dbl_is_not_equal(v2[i], missval)) v1[i] += vincr;
 }
 
 void
@@ -274,12 +274,12 @@ field2_sumw(Field &field1, const Field &field2, double w)
   if (field1.numMissVals || field2.numMissVals)
     {
 #ifdef _OPENMP
-#pragma omp parallel for default(shared) schedule(static)
+#pragma omp parallel for if (len > cdoMinLoopSize) default(shared) schedule(static)
 #endif
       for (size_t i = 0; i < len; ++i)
-        if (!dbl_is_equal(array2[i], missval2))
+        if (dbl_is_not_equal(array2[i], missval2))
           {
-            if (!dbl_is_equal(array1[i], missval1))
+            if (dbl_is_not_equal(array1[i], missval1))
               array1[i] += w * array2[i];
             else
               array1[i] = w * array2[i];
@@ -290,7 +290,7 @@ field2_sumw(Field &field1, const Field &field2, double w)
   else
     {
 #ifdef _OPENMP
-#pragma omp parallel for default(shared) schedule(static)
+#pragma omp parallel for if (len > cdoMinLoopSize) default(shared) schedule(static)
 #endif
       for (size_t i = 0; i < len; ++i) array1[i] += w * array2[i];
     }
@@ -317,19 +317,19 @@ field2_sumtr(Field &occur, const Field &field, double refval)
   if (occur.numMissVals || field.numMissVals)
     {
 #ifdef _OPENMP
-#pragma omp parallel for default(shared) schedule(static)
+#pragma omp parallel for if (len > cdoMinLoopSize) default(shared) schedule(static)
 #endif
       for (size_t i = 0; i < len; ++i)
-        if (!dbl_is_equal(farray[i], fmissval))
+        if (dbl_is_not_equal(farray[i], fmissval))
           {
-            if (!dbl_is_equal(oarray[i], omissval))
+            if (dbl_is_not_equal(oarray[i], omissval))
               oarray[i] = (dbl_is_equal(farray[i], refval)) ? 0.0 : oarray[i] + 1.0;
             else
               oarray[i] = (dbl_is_equal(farray[i], refval)) ? 0.0 : 1.0;
           }
         else
           {
-            if (!dbl_is_equal(oarray[i], omissval)) oarray[i] = 0.0;
+            if (dbl_is_not_equal(oarray[i], omissval)) oarray[i] = 0.0;
           }
 
       occur.numMissVals = field_num_miss(occur);
@@ -337,7 +337,7 @@ field2_sumtr(Field &occur, const Field &field, double refval)
   else
     {
 #ifdef _OPENMP
-#pragma omp parallel for default(shared) schedule(static)
+#pragma omp parallel for if (len > cdoMinLoopSize) default(shared) schedule(static)
 #endif
       for (size_t i = 0; i < len; ++i) oarray[i] = (dbl_is_equal(farray[i], refval)) ? 0.0 : oarray[i] + 1.0;
     }
@@ -395,12 +395,12 @@ field2_sumqw(Field &field1, const Field &field2, double w)
   if (field1.numMissVals || field2.numMissVals)
     {
 #ifdef _OPENMP
-#pragma omp parallel for default(shared) schedule(static)
+#pragma omp parallel for if (len > cdoMinLoopSize) default(shared) schedule(static)
 #endif
       for (size_t i = 0; i < len; ++i)
-        if (!dbl_is_equal(array2[i], missval2))
+        if (dbl_is_not_equal(array2[i], missval2))
           {
-            if (!dbl_is_equal(array1[i], missval1))
+            if (dbl_is_not_equal(array1[i], missval1))
               array1[i] += w * array2[i] * array2[i];
             else
               array1[i] = w * array2[i] * array2[i];
@@ -411,7 +411,7 @@ field2_sumqw(Field &field1, const Field &field2, double w)
   else
     {
 #ifdef _OPENMP
-#pragma omp parallel for default(shared) schedule(static)
+#pragma omp parallel for if (len > cdoMinLoopSize) default(shared) schedule(static)
 #endif
       for (size_t i = 0; i < len; ++i) array1[i] += w * array2[i] * array2[i];
     }
@@ -869,7 +869,7 @@ field2_moq(Field &field1, const Field &field2)
   if (field2.numMissVals)
     {
       for (size_t i = 0; i < len; ++i)
-        if (!dbl_is_equal(array2[i], missval2))
+        if (dbl_is_not_equal(array2[i], missval2))
           array1[i] = array2[i] * array2[i];
         else
           array1[i] = missval1;
@@ -896,7 +896,7 @@ field2_moqw(Field &field1, const Field &field2, double w)
   if (field2.numMissVals)
     {
       for (size_t i = 0; i < len; ++i)
-        if (!dbl_is_equal(array2[i], missval2))
+        if (dbl_is_not_equal(array2[i], missval2))
           array1[i] = w * array2[i] * array2[i];
         else
           array1[i] = missval1;
@@ -932,9 +932,9 @@ varray2_count_mv(size_t len, Varray<T1> &v1, const Varray<T2> &v2, double mv1, d
   assert(len <= v2.size());
 
   for (size_t i = 0; i < len; ++i)
-    if (!dbl_is_equal(v2[i], mv2))
+    if (dbl_is_not_equal(v2[i], mv2))
       {
-        if (!dbl_is_equal(v1[i], mv1))
+        if (dbl_is_not_equal(v1[i], mv1))
           v1[i] += 1.0;
         else
           v1[i] = 1.0;
diff --git a/src/field2_complex.cc b/src/field2_complex.cc
index 5e2880376e36517dc8e079c33847db65c4afedda..f7a5c8cb650aee03f596f28d9ee93f471c492a6f 100644
--- a/src/field2_complex.cc
+++ b/src/field2_complex.cc
@@ -8,8 +8,8 @@
 #include <cdi.h>
 
 #include "arithmetic.h"
-#include "process_int.h"
 #include "field_functions.h"
+#include "cdo_output.h"
 
 static void
 field2_add_complex(Field &field1, const Field &field2)
diff --git a/src/field_functions.h b/src/field_functions.h
index ed23b74f23b495bd1fa0833146b5a548d9fc4d35..a4db6fd1853f94d592e4f6522dc2dee25c899772 100644
--- a/src/field_functions.h
+++ b/src/field_functions.h
@@ -11,6 +11,13 @@
 
 double var_to_std(double rvar, double missval);
 
+enum Func
+{
+  Func_Name,
+  Func_Code,
+  Func_Param
+};
+
 enum FieldFunc
 {
   FieldFunc_Min = 100,
@@ -58,10 +65,14 @@ enum FieldFunc
   FieldFunc_Setmiss,
 };
 
-// fieldmem.cc
-void fields_from_vlist(int vlistID, FieldVector2D &field2D);
-void fields_from_vlist(int vlistID, FieldVector2D &field2D, int ptype);
-void fields_from_vlist(int vlistID, FieldVector2D &field2D, int ptype, double fillValue);
+// field_memory.cc
+void field2D_init(FieldVector2D &field2D, const VarList &varList);
+void field2D_init(FieldVector2D &field2D, const VarList &varList, int ptype);
+void field2D_init(FieldVector2D &field2D, const VarList &varList, int ptype, double fillValue);
+void field1Dvars_init(FieldVector &field1D, const VarList &varList);
+void field1Dvars_init(FieldVector &field1D, const VarList &varList, int ptype);
+void field1Dlevels_init(FieldVector &field1D, const VarList &varList);
+void field1Dlevels_init(FieldVector &field1D, const VarList &varList, int ptype);
 
 // field.cc
 double field_function(const Field &field, int function);
diff --git a/src/field_memory.cc b/src/field_memory.cc
index c507a3a0f6d1bfeb38c1468796c9b58a0a885c8f..6de4f594fb87154df7b59818a6c12145f6af1579 100644
--- a/src/field_memory.cc
+++ b/src/field_memory.cc
@@ -8,45 +8,40 @@
 #include <cdi.h>
 
 #include "field.h"
-#include "cdo_vlist.h"
 #include "cdo_options.h"
 
 static void
-fields_from_vlist_kernel(int vlistID, FieldVector2D &field2D, int ptype, bool lfill, double fillValue)
+field2D_init_kernel(FieldVector2D &field2D, const VarList &varList, int ptype, bool lfill, double fillValue)
 {
   auto allocateData = (ptype & FIELD_VEC);
-  auto nvars = vlistNvars(vlistID);
-  field2D.resize(nvars);
+  auto numVars = varList.numVars();
+  field2D.resize(numVars);
 
-  for (int varID = 0; varID < nvars; ++varID)
+  for (const auto &var : varList.vars)
     {
-      auto gridID = vlistInqVarGrid(vlistID, varID);
-      auto gridsize = gridInqSize(gridID);
-      size_t nwpv = vlist_inq_nwpv(vlistID, varID);  // number of words per value; real:1  complex:2
-      auto size = gridsize * nwpv;
-      auto zaxisID = vlistInqVarZaxis(vlistID, varID);
-      auto nlevels = zaxisInqSize(zaxisID);
-      auto missval = vlistInqVarMissval(vlistID, varID);
-      auto datatype = vlistInqVarDatatype(vlistID, varID);
+      auto gridSize = var.gridsize;
+      auto size = gridSize * var.nwpv;
+      auto numLevels = var.nlevels;
+      auto dataType = var.dataType;
       auto memType = (ptype & FIELD_FLT) ? MemType::Float : MemType::Double;
       if (ptype & FIELD_NAT)
         {
           if (Options::CDO_Memtype == MemType::Native)
-            memType = (datatype == CDI_DATATYPE_FLT32 || datatype == CDI_DATATYPE_CPX32) ? MemType::Float : MemType::Double;
+            memType = (dataType == CDI_DATATYPE_FLT32 || dataType == CDI_DATATYPE_CPX32) ? MemType::Float : MemType::Double;
           else
             memType = Options::CDO_Memtype;
         }
 
-      field2D[varID].resize(nlevels);
+      field2D[var.ID].resize(numLevels);
 
-      for (int levelID = 0; levelID < nlevels; ++levelID)
+      for (int levelID = 0; levelID < numLevels; ++levelID)
         {
-          auto &field = field2D[varID][levelID];
-          field.nwpv = nwpv;
-          field.grid = gridID;
+          auto &field = field2D[var.ID][levelID];
+          field.nwpv = var.nwpv;
+          field.grid = var.gridID;
           field.size = size;
           field.memType = memType;
-          field.missval = missval;
+          field.missval = var.missval;
 
           if (allocateData)
             {
@@ -70,19 +65,143 @@ fields_from_vlist_kernel(int vlistID, FieldVector2D &field2D, int ptype, bool lf
 }
 
 void
-fields_from_vlist(int vlistID, FieldVector2D &field2D)
+field2D_init(FieldVector2D &field2D, const VarList &varList)
 {
-  fields_from_vlist_kernel(vlistID, field2D, 0, false, 0);
+  field2D_init_kernel(field2D, varList, 0, false, 0);
 }
 
 void
-fields_from_vlist(int vlistID, FieldVector2D &field2D, int ptype)
+field2D_init(FieldVector2D &field2D, const VarList &varList, int ptype)
 {
-  fields_from_vlist_kernel(vlistID, field2D, ptype, false, 0);
+  field2D_init_kernel(field2D, varList, ptype, false, 0);
 }
 
 void
-fields_from_vlist(int vlistID, FieldVector2D &field2D, int ptype, double fillValue)
+field2D_init(FieldVector2D &field2D, const VarList &varList, int ptype, double fillValue)
 {
-  fields_from_vlist_kernel(vlistID, field2D, ptype, true, fillValue);
+  field2D_init_kernel(field2D, varList, ptype, true, fillValue);
+}
+
+static void
+field1Dvars_init_kernel(FieldVector &field1D, const VarList &varList, int ptype, bool lfill, double fillValue)
+{
+  auto allocateData = (ptype & FIELD_VEC);
+  auto numVars = varList.numVars();
+  field1D.resize(numVars);
+
+  for (const auto &var : varList.vars)
+    {
+      auto gridSize = var.gridsize;
+      auto size = gridSize * var.nwpv;
+      auto dataType = var.dataType;
+      auto memType = (ptype & FIELD_FLT) ? MemType::Float : MemType::Double;
+      if (ptype & FIELD_NAT)
+        {
+          if (Options::CDO_Memtype == MemType::Native)
+            memType = (dataType == CDI_DATATYPE_FLT32 || dataType == CDI_DATATYPE_CPX32) ? MemType::Float : MemType::Double;
+          else
+            memType = Options::CDO_Memtype;
+        }
+
+      auto &field = field1D[var.ID];
+
+      field.nwpv = var.nwpv;
+      field.grid = var.gridID;
+      field.size = size;
+      field.memType = memType;
+      field.missval = var.missval;
+
+      if (allocateData)
+        {
+          if (memType == MemType::Float)
+            {
+              if (lfill)
+                field.resizef(size, (float) fillValue);
+              else
+                field.resizef(size);
+            }
+          else
+            {
+              if (lfill)
+                field.resize(size, fillValue);
+              else
+                field.resize(size);
+            }
+        }
+    }
+}
+
+void
+field1Dvars_init(FieldVector &field1D, const VarList &varList)
+{
+  field1Dvars_init_kernel(field1D, varList, 0, false, 0);
+}
+
+void
+field1Dvars_init(FieldVector &field1D, const VarList &varList, int ptype)
+{
+  field1Dvars_init_kernel(field1D, varList, ptype, false, 0);
+}
+
+static void
+field1Dlevels_init_kernel(FieldVector &field1D, const VarList &varList, int ptype, bool lfill, double fillValue)
+{
+  auto allocateData = (ptype & FIELD_VEC);
+
+  const auto &var = varList.vars[0];
+  auto gridSize = var.gridsize;
+  auto size = gridSize * var.nwpv;
+  auto numLevels = var.nlevels;
+  auto dataType = var.dataType;
+  auto memType = (ptype & FIELD_FLT) ? MemType::Float : MemType::Double;
+  if (ptype & FIELD_NAT)
+    {
+      if (Options::CDO_Memtype == MemType::Native)
+        memType = (dataType == CDI_DATATYPE_FLT32 || dataType == CDI_DATATYPE_CPX32) ? MemType::Float : MemType::Double;
+      else
+        memType = Options::CDO_Memtype;
+    }
+
+  field1D.resize(numLevels);
+
+  for (int levelID = 0; levelID < numLevels; ++levelID)
+    {
+      auto &field = field1D[levelID];
+
+      field.nwpv = var.nwpv;
+      field.grid = var.gridID;
+      field.size = size;
+      field.memType = memType;
+      field.missval = var.missval;
+
+      if (allocateData)
+        {
+          if (memType == MemType::Float)
+            {
+              if (lfill)
+                field.resizef(size, (float) fillValue);
+              else
+                field.resizef(size);
+            }
+          else
+            {
+              if (lfill)
+                field.resize(size, fillValue);
+              else
+                field.resize(size);
+            }
+        }
+    }
+}
+
+void
+field1Dlevels_init(FieldVector &field1D, const VarList &varList)
+{
+  field1Dlevels_init_kernel(field1D, varList, 0, false, 0);
+}
+
+void
+field1Dlevels_init(FieldVector &field1D, const VarList &varList, int ptype)
+{
+  field1Dlevels_init_kernel(field1D, varList, ptype, false, 0);
 }
diff --git a/src/field_meridional.cc b/src/field_meridional.cc
index afac0b9dee7539b5922a967216012d289b09f695..96034ebdb0da2a5fe69582e41ef0211a3c60c570 100644
--- a/src/field_meridional.cc
+++ b/src/field_meridional.cc
@@ -7,9 +7,9 @@
 
 #include <cdi.h>
 
-#include "process_int.h"
 #include "percentiles.h"
 #include "field_functions.h"
+#include "cdo_output.h"
 
 using funcType1 = double(size_t, const Varray<double> &);
 using funcTypeMV1 = double(size_t, const Varray<double> &, double);
@@ -257,12 +257,12 @@ meridional_pctl(const Field &field1, Field &field2, double pn)
           if (field1.memType == MemType::Float)
             {
               for (size_t j = 0; j < ny; ++j)
-                if (!dbl_is_equal(field1.vec_f[j * nx + i], missval)) v[k++] = field1.vec_f[j * nx + i];
+                if (dbl_is_not_equal(field1.vec_f[j * nx + i], missval)) v[k++] = field1.vec_f[j * nx + i];
             }
           else
             {
               for (size_t j = 0; j < ny; ++j)
-                if (!dbl_is_equal(field1.vec_d[j * nx + i], missval)) v[k++] = field1.vec_d[j * nx + i];
+                if (dbl_is_not_equal(field1.vec_d[j * nx + i], missval)) v[k++] = field1.vec_d[j * nx + i];
             }
 
           if (k > 0) { field2.vec_d[i] = percentile(v.data(), k, pn); }
diff --git a/src/field_trend.cc b/src/field_trend.cc
new file mode 100644
index 0000000000000000000000000000000000000000..8012e3b26ddf9f7da3a87971755c95245f4e004b
--- /dev/null
+++ b/src/field_trend.cc
@@ -0,0 +1,121 @@
+#include "field_trend.h"
+#include "compare.h"
+#include "cimdOmp.h"
+#include "arithmetic.h"
+
+template <typename T>
+static void
+calc_trend_sum(FieldVector3D &work, bool hasMissvals, size_t len, const Varray<T> &varray, T missval, double zj, int varID,
+               int levelID)
+{
+  auto &sumj = work[0][varID][levelID].vec_d;
+  auto &sumjj = work[1][varID][levelID].vec_d;
+  auto &sumjx = work[2][varID][levelID].vec_d;
+  auto &sumx = work[3][varID][levelID].vec_d;
+  auto &zn = work[4][varID][levelID].vec_d;
+
+  auto trend_sum = [&](auto i, double value) {
+    sumj[i] += zj;
+    sumjj[i] += zj * zj;
+    sumjx[i] += zj * value;
+    sumx[i] += value;
+    zn[i]++;
+  };
+
+  auto trend_sum_mv = [&](auto i, T value, auto is_NE) {
+    if (is_NE(value, missval)) trend_sum(i, value);
+  };
+
+  if (hasMissvals)
+    {
+      if (std::isnan(missval))
+#ifdef _OPENMP
+#pragma omp parallel for if (len > cdoMinLoopSize) default(shared) schedule(static)
+#endif
+        for (size_t i = 0; i < len; ++i) { trend_sum_mv(i, varray[i], dbl_is_not_equal); }
+      else
+#ifdef _OPENMP
+#pragma omp parallel for if (len > cdoMinLoopSize) default(shared) schedule(static)
+#endif
+        for (size_t i = 0; i < len; ++i) { trend_sum_mv(i, varray[i], is_not_equal); }
+    }
+  else
+    {
+#ifdef HAVE_OPENMP4
+#pragma omp parallel for simd if (len > cdoMinLoopSize) default(shared) schedule(static)
+#endif
+      for (size_t i = 0; i < len; ++i) { trend_sum(i, varray[i]); }
+    }
+}
+
+void
+calc_trend_sum(FieldVector3D &work, const Field &field, double zj, int varID, int levelID)
+{
+  auto hasMissvals = (field.numMissVals > 0);
+  if (field.memType == MemType::Float)
+    calc_trend_sum(work, hasMissvals, field.size, field.vec_f, static_cast<float>(field.missval), zj, varID, levelID);
+  else
+    calc_trend_sum(work, hasMissvals, field.size, field.vec_d, field.missval, zj, varID, levelID);
+}
+
+template <typename T>
+static void
+sub_trend(double zj, Varray<T> &v1, const Varray<double> &v2, const Varray<double> &v3, size_t len, double mv)
+{
+  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))); };
+
+  if (std::isnan(missval1))
+    {
+#ifdef _OPENMP
+#pragma omp parallel for if (len > cdoMinLoopSize) default(shared) schedule(static)
+#endif
+      for (size_t i = 0; i < len; ++i) { v1[i] = sub_kernel(i, dbl_is_equal); }
+    }
+  else
+    {
+#ifdef _OPENMP
+#pragma omp parallel for if (len > cdoMinLoopSize) default(shared) schedule(static)
+#endif
+      for (size_t i = 0; i < len; ++i) { v1[i] = sub_kernel(i, is_equal); }
+    }
+}
+
+void
+sub_trend(double zj, Field &field1, const Field &field2, const Field &field3)
+{
+  if (field1.memType == MemType::Float)
+    sub_trend(zj, field1.vec_f, field2.vec_d, field3.vec_d, field1.size, field1.missval);
+  else
+    sub_trend(zj, field1.vec_d, field2.vec_d, field3.vec_d, field1.size, field1.missval);
+}
+
+void
+calc_trend_param(const FieldVector3D &work, Field &paramA, Field &paramB, int varID, int levelID)
+{
+  auto gridsize = paramA.size;
+  auto missval1 = paramA.missval;
+  auto missval2 = paramA.missval;
+
+  const auto &sumj = work[0][varID][levelID].vec_d;
+  const auto &sumjj = work[1][varID][levelID].vec_d;
+  const auto &sumjx = work[2][varID][levelID].vec_d;
+  const auto &sumx = work[3][varID][levelID].vec_d;
+  const auto &zn = work[4][varID][levelID].vec_d;
+
+  auto trend_kernel = [&](auto i, auto is_EQ) {
+    auto temp1 = SUBM(sumjx[i], DIVM(MULM(sumj[i], sumx[i]), zn[i]));
+    auto temp2 = SUBM(sumjj[i], DIVM(MULM(sumj[i], sumj[i]), zn[i]));
+    auto temp3 = DIVM(temp1, temp2);
+
+    paramA.vec_d[i] = SUBM(DIVM(sumx[i], zn[i]), MULM(DIVM(sumj[i], zn[i]), temp3));
+    paramB.vec_d[i] = temp3;
+  };
+
+  if (std::isnan(missval1))
+    for (size_t i = 0; i < gridsize; ++i) trend_kernel(i, dbl_is_equal);
+  else
+    for (size_t i = 0; i < gridsize; ++i) trend_kernel(i, is_equal);
+}
diff --git a/src/field_trend.h b/src/field_trend.h
new file mode 100644
index 0000000000000000000000000000000000000000..72cea72f9533d8a7156de4165692f48452e32cfc
--- /dev/null
+++ b/src/field_trend.h
@@ -0,0 +1,16 @@
+/*
+  This file is part of CDO. CDO is a collection of Operators to manipulate and analyse Climate model Data.
+
+  Author: Uwe Schulzweida
+
+*/
+#ifndef FIELD_TREND_H
+#define FIELD_TREND_H
+
+#include "field.h"
+
+void calc_trend_sum(FieldVector3D &work, const Field &field, double zj, int varID, int levelID);
+void sub_trend(double zj, Field &field1, const Field &field2, const Field &field3);
+void calc_trend_param(const FieldVector3D &work, Field &field2, Field &field3, int varID, int levelID);
+
+#endif
diff --git a/src/field_zonal.cc b/src/field_zonal.cc
index 7ba5239a7cf01156c15ba4e1bc3e8b421b93630a..e50738945b9b5bbb4b3764d590873ec62af6922e 100644
--- a/src/field_zonal.cc
+++ b/src/field_zonal.cc
@@ -10,9 +10,9 @@
 #include <vector>
 #include <algorithm>
 
-#include "process_int.h"
 #include "percentiles.h"
 #include "field_functions.h"
+#include "cdo_output.h"
 
 template <typename T>
 static void
@@ -232,7 +232,7 @@ zonal_pctl(const Field &field1, Field &field2, double pn)
 
           size_t k = 0;
           for (size_t i = 0; i < nx; ++i)
-            if (!dbl_is_equal(v[i], missval)) v[k++] = v[i];
+            if (dbl_is_not_equal(v[i], missval)) v[k++] = v[i];
 
           if (k > 0) { field2.vec_d[j] = percentile(v.data(), k, pn); }
           else
diff --git a/src/fieldc.cc b/src/fieldc.cc
index eece907208488ba479bc05f0a8e64877052d1499..cd8373604e73df83ece2407b4a05035a136dd501 100644
--- a/src/fieldc.cc
+++ b/src/fieldc.cc
@@ -104,13 +104,13 @@ template <typename T>
 static void
 fieldc_min(size_t len, size_t numMissVals, Varray<T> &v, T missval, T rconst)
 {
-  auto is_EQ = dbl_is_equal;
+  auto is_NE = dbl_is_not_equal;
   auto missval1 = missval;
 
   if (numMissVals)
     {
       for (size_t i = 0; i < len; ++i)
-        if (is_EQ(v[i], missval1) || v[i] > rconst) v[i] = rconst;
+        if (is_NE(v[i], missval1) && v[i] > rconst) v[i] = rconst;
     }
   else
     {
@@ -132,13 +132,13 @@ template <typename T>
 static void
 fieldc_max(size_t len, size_t numMissVals, Varray<T> &v, T missval, T rconst)
 {
-  auto is_EQ = dbl_is_equal;
+  auto is_NE = dbl_is_not_equal;
   auto missval1 = missval;
 
   if (numMissVals)
     {
       for (size_t i = 0; i < len; ++i)
-        if (is_EQ(v[i], missval1) || v[i] < rconst) v[i] = rconst;
+        if (is_NE(v[i], missval1) && v[i] < rconst) v[i] = rconst;
     }
   else
     {
diff --git a/src/fieldc_complex.cc b/src/fieldc_complex.cc
index d27581e1bf589549ce6ed9c310ab4950b7459a87..e4adedee210416791edd775b83f9fa67c5b5721d 100644
--- a/src/fieldc_complex.cc
+++ b/src/fieldc_complex.cc
@@ -8,7 +8,7 @@
 #include <cdi.h>
 
 #include "arithmetic.h"
-#include "process_int.h"
+#include "cdo_output.h"
 #include "field_functions.h"
 
 template <typename T>
diff --git a/src/fileStream.cc b/src/fileStream.cc
index 03cde10d0d3f4d531facab949c8c8ff9e88c6534..c1158b537a04a98ccae01f6be3e6cf975a5617a0 100644
--- a/src/fileStream.cc
+++ b/src/fileStream.cc
@@ -180,7 +180,7 @@ FileStream::def_vlist(int p_vlistID)
     {
       auto nvars = vlistNvars(p_vlistID);
       for (int varID = 0; varID < nvars; ++varID) cdiDefKeyInt(p_vlistID, varID, CDI_KEY_CHUNKTYPE, Options::cdoChunkType);
-      auto ngrids = vlistNgrids(p_vlistID);
+      auto ngrids = vlistNumGrids(p_vlistID);
       for (int index = 0; index < ngrids; ++index)
         cdiDefKeyInt(vlistGrid(p_vlistID, index), CDI_GLOBAL, CDI_KEY_CHUNKTYPE, Options::cdoChunkType);
     }
@@ -189,7 +189,7 @@ FileStream::def_vlist(int p_vlistID)
     {
       auto nvars = vlistNvars(p_vlistID);
       for (int varID = 0; varID < nvars; ++varID) cdiDefKeyInt(p_vlistID, varID, CDI_KEY_CHUNKSIZE, Options::cdoChunkSize);
-      auto ngrids = vlistNgrids(p_vlistID);
+      auto ngrids = vlistNumGrids(p_vlistID);
       for (int index = 0; index < ngrids; ++index)
         cdiDefKeyInt(vlistGrid(p_vlistID, index), CDI_GLOBAL, CDI_KEY_CHUNKSIZE, Options::cdoChunkSize);
     }
diff --git a/src/fill_1d.cc b/src/fill_1d.cc
index a1a711c517a2390cd50c47b99b9f33cf58bd174b..8a807c0fe18b0f892472b992c026b6acc73c521c 100644
--- a/src/fill_1d.cc
+++ b/src/fill_1d.cc
@@ -54,7 +54,7 @@ fill_1d_nearest(int numValues, const Varray<double> &timeValues, Varray<double>
           // search for next non missing value
           for (int k = i + 1; k < numValues; ++k)
             {
-              if (!dbl_is_equal(dataValues[k], missval))
+              if (dbl_is_not_equal(dataValues[k], missval))
                 {
                   lastIndex = k;
                   break;
@@ -121,7 +121,7 @@ fill_1d_linear(int numValues, const Varray<double> &timeValues, Varray<double> &
           // search for next non missing value
           for (int k = i + 1; k < numValues; ++k)
             {
-              if (!dbl_is_equal(dataValues[k], missval))
+              if (dbl_is_not_equal(dataValues[k], missval))
                 {
                   lastIndex = k;
                   break;
@@ -163,7 +163,7 @@ fill_1d_forward(int numValues, Varray<double> &dataValues, double missval, int l
           // search for next non missing value
           for (int k = i + 1; k < numValues; ++k)
             {
-              if (!dbl_is_equal(dataValues[k], missval))
+              if (dbl_is_not_equal(dataValues[k], missval))
                 {
                   lastIndex = k;
                   break;
@@ -217,7 +217,7 @@ fill_1d_backward(int numValues, Varray<double> &dataValues, double missval, int
           // search for next non missing value
           for (int k = i + 1; k < numValues; ++k)
             {
-              if (!dbl_is_equal(dataValues[k], missval))
+              if (dbl_is_not_equal(dataValues[k], missval))
                 {
                   lastIndex = k;
                   break;
diff --git a/src/grid_area.cc b/src/grid_area.cc
index 6e7ee2d1ac52899a5471c5e7c8fdbb592bdc1a32..512b15a76095d59307c6ade3f76e3564bc012476 100644
--- a/src/grid_area.cc
+++ b/src/grid_area.cc
@@ -356,7 +356,7 @@ gen_gridcellarea_reg2d(int gridID, double *area, bool lweights)
   constexpr int nv = 4;
   std::atomic<size_t> atomicCount{ 0 };
 
-  progress::init();
+  cdo::Progress progress;
 
 #ifdef _OPENMP
 #pragma omp parallel for default(shared) schedule(static)
@@ -364,15 +364,13 @@ gen_gridcellarea_reg2d(int gridID, double *area, bool lweights)
   for (size_t i = 0; i < gridsize; ++i)
     {
       atomicCount++;
-      if (cdo_omp_get_thread_num() == 0) progress::update(0, 1, (double) atomicCount / gridsize);
+      if (cdo_omp_get_thread_num() == 0) progress.update((double) atomicCount / gridsize);
 
       double lons[4], lats[4];
       getLonLatCorner(nlon, i, grid_corner_lon.data(), grid_corner_lat.data(), lons, lats);
       area[i] = mod_huiliers_area(nv, lons, lats);
     }
 
-  progress::update(0, 1, 1);
-
   return status;
 }
 
@@ -458,7 +456,7 @@ gen_gridcellarea_unstruct(int gridID, double *area)
 
   std::atomic<size_t> atomicCount{ 0 };
 
-  progress::init();
+  cdo::Progress progress;
 
 #ifdef _OPENMP
 #pragma omp parallel for default(shared) schedule(static)
@@ -466,7 +464,7 @@ gen_gridcellarea_unstruct(int gridID, double *area)
   for (size_t i = 0; i < gridsize; ++i)
     {
       atomicCount++;
-      if (cdo_omp_get_thread_num() == 0) progress::update(0, 1, (double) atomicCount / gridsize);
+      if (cdo_omp_get_thread_num() == 0) progress.update((double) atomicCount / gridsize);
 
       if (nv <= 4)
         area[i] = mod_huiliers_area(nv, &grid_corner_lon[i * nv], &grid_corner_lat[i * nv]);
@@ -475,8 +473,6 @@ gen_gridcellarea_unstruct(int gridID, double *area)
             = mod_huiliers_area2(nv, &grid_corner_lon[i * nv], &grid_corner_lat[i * nv], grid_center_lon[i], grid_center_lat[i]);
     }
 
-  progress::update(0, 1, 1);
-
   return status;
 }
 
diff --git a/src/grid_from_name.cc b/src/grid_from_name.cc
index 72a435e414898e4cf2fcda5543747efaba16cd38..a72f661bbde90e90d7cc4f0b7a7a28a5b8617ebd 100644
--- a/src/grid_from_name.cc
+++ b/src/grid_from_name.cc
@@ -15,8 +15,8 @@
 #include "util_string.h"
 #include "dcw_reader.h"
 
-size_t genIcosphereCoords(int subdivisions, bool withBounds, std::vector<double> &xvals, std::vector<double> &yvals,
-                          std::vector<double> &xbounds, std::vector<double> &ybounds);
+size_t gen_icosphere_coords(int subdivisions, bool withBounds, std::vector<double> &xvals, std::vector<double> &yvals,
+                            std::vector<double> &xbounds, std::vector<double> &ybounds);
 
 static void
 generate_grid_icosphere(GridDesciption &grid, const std::string &gridname)
@@ -37,7 +37,7 @@ generate_grid_icosphere(GridDesciption &grid, const std::string &gridname)
   grid.type = gridtype;
   if (withBounds) grid.nvertex = 3;
 
-  auto ncells = genIcosphereCoords(bVal + 1, withBounds, grid.xvals, grid.yvals, grid.xbounds, grid.ybounds);
+  auto ncells = gen_icosphere_coords(bVal + 1, withBounds, grid.xvals, grid.yvals, grid.xbounds, grid.ybounds);
   grid.xsize = ncells;
   grid.ysize = ncells;
   grid.xname = "clon";
diff --git a/src/grid_icosphere.cc b/src/grid_icosphere.cc
index f608110e0dbe3fe4b1bb89dcf6dce34c3205c980..d9deb4c35b0b0871cc4f565f2381c609a188c5ac 100644
--- a/src/grid_icosphere.cc
+++ b/src/grid_icosphere.cc
@@ -41,20 +41,14 @@ init(void)
   vertices[11] = Vertex{ 0.0, 0.0, -1.0 };
   // now set the vertices on the two latitude rings
   int i_mdist[10];
-  for (int j = 1; j < 11; ++j)
-    {
-      if (j % 2 == 0)
-        i_mdist[j / 2 + 4] = -1 + (j - 1) - 10 * ((j - 1) / 7);
-      else
-        i_mdist[(j + 1) / 2 - 1] = -1 + (j - 1) - 10 * ((j - 1) / 7);
-    }
+  for (int j = 1; j < 11; ++j) { i_mdist[(j % 2 == 0) ? (j / 2 + 4) : ((j + 1) / 2 - 1)] = -1 + (j - 1) - 10 * ((j - 1) / 7); }
 
   for (int j = 1; j < 11; ++j)
     {
       // toggle the hemisphere
-      const auto i_msgn = (j >= 6) ? -1.0 : 1.0;
+      auto i_msgn = (j >= 6) ? -1.0 : 1.0;
       // compute the meridian angle for the base vertex.
-      const auto z_rlon = (1.0 + i_mdist[j - 1]) * pi_5;
+      auto z_rlon = (1.0 + i_mdist[j - 1]) * pi_5;
       // now initialize the coordinates
       vertices[j] = Vertex{ std::sin(z_w) * std::cos(z_rlon), std::sin(z_w) * std::sin(z_rlon), std::cos(z_w) * i_msgn };
     }
@@ -70,12 +64,12 @@ static const TriangleList triangles
 }  // namespace icosahedron
 
 static Index
-vertexForEdge(Lookup &lookup, VertexList &vertices, Index first, Index second)
+vertex_for_edge(Lookup &lookup, VertexList &vertices, Index first, Index second)
 {
   Lookup::key_type key(first, second);
   if (key.first > key.second) std::swap(key.first, key.second);
 
-  const auto inserted = lookup.insert({ key, vertices.size() });
+  auto inserted = lookup.insert({ key, vertices.size() });
   if (inserted.second) vertices.push_back((vertices[first] + vertices[second]).normalised());
 
   return inserted.first->second;
@@ -87,12 +81,12 @@ subdivide(VertexList &vertices, const TriangleList &triangles)
   Lookup lookup;
   Triangle mid;
 
-  const auto n = triangles.size();
+  auto n = triangles.size();
   TriangleList result(4 * n);
   for (size_t i = 0; i < n; ++i)
     {
       const auto &each = triangles[i];
-      for (int edge = 0; edge < 3; edge++) { mid[edge] = vertexForEdge(lookup, vertices, each[edge], each[(edge + 1) % 3]); }
+      for (int edge = 0; edge < 3; edge++) { mid[edge] = vertex_for_edge(lookup, vertices, each[edge], each[(edge + 1) % 3]); }
 
       result[i * 4 + 0] = { each[0], mid[0], mid[2] };
       result[i * 4 + 1] = { each[1], mid[1], mid[0] };
@@ -106,7 +100,7 @@ subdivide(VertexList &vertices, const TriangleList &triangles)
 using IndexedMesh = std::pair<VertexList, TriangleList>;
 
 static IndexedMesh
-makeIcosphere(int subdivisions)
+make_icosphere(int subdivisions)
 {
   icosahedron::init();
   auto vertices = icosahedron::vertices;
@@ -120,12 +114,12 @@ makeIcosphere(int subdivisions)
 static inline void
 d_normalize(Vertex &v)
 {
-  const auto dnorm = std::sqrt(v * v);
+  auto dnorm = std::sqrt(v * v);
   v /= dnorm;
 }
 
 static Vertex
-circumCenterMean(const Vertex &v0, const Vertex &v1, const Vertex &v2)
+circum_center_mean(const Vertex &v0, const Vertex &v1, const Vertex &v2)
 {
   /*
     v0, v1, v2: the coordinates of the three triangle vertices (_dmo,nit vectors) in
@@ -159,14 +153,14 @@ circumCenterMean(const Vertex &v0, const Vertex &v1, const Vertex &v2)
 }
 
 size_t
-genIcosphereCoords(int subdivisions, bool withBounds, std::vector<double> &xvals, std::vector<double> &yvals,
-                   std::vector<double> &xbounds, std::vector<double> &ybounds)
+gen_icosphere_coords(int subdivisions, bool withBounds, std::vector<double> &xvals, std::vector<double> &yvals,
+                     std::vector<double> &xbounds, std::vector<double> &ybounds)
 {
-  const auto mesh = makeIcosphere(subdivisions);
+  const auto mesh = make_icosphere(subdivisions);
   const auto &vertices = mesh.first;
   const auto &triangles = mesh.second;
 
-  const auto ncells = triangles.size();
+  auto ncells = triangles.size();
   xvals.resize(ncells);
   yvals.resize(ncells);
   if (withBounds)
@@ -178,7 +172,7 @@ genIcosphereCoords(int subdivisions, bool withBounds, std::vector<double> &xvals
   size_t i = 0;
   for (const Triangle &t : triangles)
     {
-      const auto center = circumCenterMean(vertices[t[0]], vertices[t[1]], vertices[t[2]]);
+      auto center = circum_center_mean(vertices[t[0]], vertices[t[1]], vertices[t[2]]);
       xvals[i] = center.longitude();
       yvals[i] = center.latitude();
       if (withBounds)
@@ -215,8 +209,8 @@ subDivdeAndVertexForEdgeTest(int subdivisions)
   auto end = std::chrono::steady_clock::now();
   auto elapsedM = std::chrono::duration_cast<std::chrono::nanoseconds>(end - start).count();
 
-  const size_t expectedNumTriangles = std::pow(4, subdivisions) * 20;
-  const size_t expectedNumVerticies = std::pow(4, subdivisions) * 10 + 2;
+  size_t expectedNumTriangles = std::pow(4, subdivisions) * 20;
+  size_t expectedNumVerticies = std::pow(4, subdivisions) * 10 + 2;
   if (triangles.size() != expectedNumTriangles)
     {
       std::cout << "ERROR: unexpected number of triangles for T: std::map, expected " << expectedNumTriangles << " got "
diff --git a/src/griddes.cc b/src/griddes.cc
index 66f6f0b79da7f5ef4c4fd1f61a3a3553a5e0806d..10f293555fe036c863861f747d340d54ce7e9b62 100644
--- a/src/griddes.cc
+++ b/src/griddes.cc
@@ -315,19 +315,20 @@ gridFromMPAS(const char *filename)
   if (streamID >= 0)
     {
       auto vlistID = streamInqVlist(streamID);
-      auto nvars = vlistNvars(vlistID);
+      VarList varList(vlistID);
+
       int latCellID = -1, lonCellID = -1, areaCellID = -1;
       int latVertexID = -1, lonVertexID = -1, verticesOnCellID = -1;
-      for (int varID = 0; varID < nvars; ++varID)
+      for (int varID = 0; varID < varList.numVars(); ++varID)
         {
-          auto varname = cdo::inq_var_name(vlistID, varID);
+          const auto &var = varList.vars[varID];
           // clang-format off
-          if      (varname == "latCell")   latCellID = varID;
-          else if (varname == "lonCell")   lonCellID = varID;
-          else if (varname == "latVertex") latVertexID = varID;
-          else if (varname == "lonVertex") lonVertexID = varID;
-          else if (varname == "areaCell")  areaCellID = varID;
-          else if (varname == "verticesOnCell") verticesOnCellID = varID;
+          if      (var.name == "latCell")   latCellID = varID;
+          else if (var.name == "lonCell")   lonCellID = varID;
+          else if (var.name == "latVertex") latVertexID = varID;
+          else if (var.name == "lonVertex") lonVertexID = varID;
+          else if (var.name == "areaCell")  areaCellID = varID;
+          else if (var.name == "verticesOnCell") verticesOnCellID = varID;
           // clang-format on
         }
 
@@ -339,10 +340,10 @@ gridFromMPAS(const char *filename)
           return -1;
         }
 
-      auto maxEdges = gridInqXsize(vlistInqVarGrid(vlistID, verticesOnCellID));
-      auto ncells = gridInqSize(vlistInqVarGrid(vlistID, latCellID));
+      auto maxEdges = gridInqXsize(varList.vars[verticesOnCellID].gridID);
+      auto ncells = varList.vars[latCellID].gridsize;
       std::vector<double> latCell(ncells), lonCell(ncells), areaCell(ncells);
-      auto nvertices = gridInqSize(vlistInqVarGrid(vlistID, latVertexID));
+      auto nvertices = varList.vars[latVertexID].gridsize;
       std::vector<double> latVertex(nvertices), lonVertex(nvertices);
       std::vector<double> verticesOnCell(maxEdges * ncells);
       std::vector<double> xbounds(maxEdges * ncells), ybounds(maxEdges * ncells);
@@ -512,7 +513,7 @@ cdo_define_grid(const char *pgridfile)
           if (streamID >= 0)
             {
               auto vlistID = streamInqVlist(streamID);
-              auto ngrids = vlistNgrids(vlistID);
+              auto ngrids = vlistNumGrids(vlistID);
               if (gridno < 1 || gridno > ngrids) cdo_abort("Grid number %d not available in %s!", gridno, filename);
               gridID = vlistGrid(vlistID, gridno - 1);
               streamClose(streamID);
diff --git a/src/hetaeta.cc b/src/hetaeta.cc
index 90302290020457f7d3c9a95ac648524bc1796e8d..98c108f65fee50e6cf31b712195aaff7a822a550 100644
--- a/src/hetaeta.cc
+++ b/src/hetaeta.cc
@@ -12,7 +12,6 @@
 #endif
 
 #include "varray.h"
-#include "process_int.h"
 #include "hetaeta.h"
 #include "cdo_options.h"
 #include "cimdOmp.h"
@@ -74,9 +73,9 @@ int_index(long n, const Varray<double> &x1, const double x2)
 static double
 esat(double temperature)
 {
-  const auto tc = temperature - 273.16;
-  const auto zes = (tc < 0.0) ? 21.8745584 * tc / (temperature - 7.66) : 17.2693882 * tc / (temperature - 35.86);
-  const auto es = 610.78 * std::exp(zes);
+  auto tc = temperature - 273.16;
+  auto zes = (tc < 0.0) ? 21.8745584 * tc / (temperature - 7.66) : 17.2693882 * tc / (temperature - 35.86);
+  auto es = 610.78 * std::exp(zes);
 
   return es;
 }
@@ -104,8 +103,8 @@ hetaeta_sc(bool ltq, int lpsmod, long ij, long ngp, long nlev1, long nlev2, long
 
   constexpr double rair_d_cpair = rair / cpair;
 
-  const auto nlev1p1 = nlev1 + 1;
-  const auto nlev2p1 = nlev2 + 1;
+  auto nlev1p1 = nlev1 + 1;
+  auto nlev2p1 = nlev2 + 1;
 
   // ****** initialise atmospheric fields in old system
 
@@ -128,9 +127,9 @@ hetaeta_sc(bool ltq, int lpsmod, long ij, long ngp, long nlev1, long nlev2, long
   if (ltq)
     for (int k = 0; k < nlev1; ++k)
       {
-        const auto ijk = k * ngp + ij;
-        const auto zq1 = q1[ijk];
-        const auto zt1 = t1[ijk];
+        auto ijk = k * ngp + ij;
+        auto zq1 = q1[ijk];
+        auto zt1 = t1[ijk];
         tv1[k] = (1.0 + epsm1i * zq1) * zt1;
         rh1[k] = zq1 * pf1[k] / (epsilon * esat(zt1));
         theta1[k] = zt1 * std::pow(apr / pf1[k], rair_d_cpair);
@@ -147,7 +146,7 @@ hetaeta_sc(bool ltq, int lpsmod, long ij, long ngp, long nlev1, long nlev2, long
   if (ij == OPOINT)
     for (int k = nlev1 - 1; k >= 0; --k)
       {
-        const auto ijk = k * ngp + ij;
+        auto ijk = k * ngp + ij;
         double t = ltq ? t1[ijk] : 0;
         double q = ltq ? q1[ijk] : 0;
         double fi = ltq ? fi1[k] : 0;
@@ -277,8 +276,8 @@ hetaeta_sc(bool ltq, int lpsmod, long ij, long ngp, long nlev1, long nlev2, long
   for (int iv = 0; iv < nvars; ++iv)
     for (int k = jjblt; k < nlev2; ++k)
       {
-        const auto ijk1 = jl1[k] * ngp + ij;
-        const auto ijk2 = jl2[k] * ngp + ij;
+        auto ijk1 = jl1[k] * ngp + ij;
+        auto ijk2 = jl2[k] * ngp + ij;
         vars_pbl[iv][k] = w1[k] * vars1[iv][ijk1] + w2[k] * vars1[iv][ijk2];
       }
 
@@ -293,13 +292,13 @@ hetaeta_sc(bool ltq, int lpsmod, long ij, long ngp, long nlev1, long nlev2, long
     {
       for (int k = 0; k < nlev1; ++k)
         {
-          const auto ijk = k * ngp + ij;
+          auto ijk = k * ngp + ij;
           zvar[k] = t1[ijk];
         }
 
       for (int k = 0; k <= jjblt; ++k)
         {
-          const auto klo = idx[k];
+          auto klo = idx[k];
           zt2[k] = wgt[k] * zvar[klo] + (1 - wgt[k]) * zvar[klo + 1];
           rh2[k] = wgt[k] * rh1[klo] + (1 - wgt[k]) * rh1[klo + 1];
         }
@@ -309,13 +308,13 @@ hetaeta_sc(bool ltq, int lpsmod, long ij, long ngp, long nlev1, long nlev2, long
     {
       for (int k = 0; k < nlev1; ++k)
         {
-          const auto ijk = k * ngp + ij;
+          auto ijk = k * ngp + ij;
           zvar[k] = vars1[iv][ijk];
         }
       for (int k = 0; k <= jjblt; ++k)
         {
-          const auto ijk = k * ngp + ij;
-          const auto klo = idx[k];
+          auto ijk = k * ngp + ij;
+          auto klo = idx[k];
           vars2[iv][ijk] = wgt[k] * zvar[klo] + (1 - wgt[k]) * zvar[klo + 1];
         }
     }
@@ -332,7 +331,7 @@ hetaeta_sc(bool ltq, int lpsmod, long ij, long ngp, long nlev1, long nlev2, long
     }
 
   {
-    const auto ijk = jjblt * ngp + ij;
+    auto ijk = jjblt * ngp + ij;
     for (int iv = 0; iv < nvars; ++iv) vars2[iv][ijk] = 0.5 * (vars2[iv][ijk] + vars_pbl[iv][jjblt]);
   }
 
@@ -347,7 +346,7 @@ hetaeta_sc(bool ltq, int lpsmod, long ij, long ngp, long nlev1, long nlev2, long
   for (int iv = 0; iv < nvars; ++iv)
     for (int k = jjblt + 1; k < nlev2; ++k)
       {
-        const auto ijk = k * ngp + ij;
+        auto ijk = k * ngp + ij;
         vars2[iv][ijk] = vars_pbl[iv][k];
       }
 
@@ -393,7 +392,7 @@ hetaeta_sc(bool ltq, int lpsmod, long ij, long ngp, long nlev1, long nlev2, long
   if (ij == OPOINT)
     for (int k = nlev2 - 1; k >= 0; --k)
       {
-        const auto ijk = k * ngp + ij;
+        auto ijk = k * ngp + ij;
         double t = ltq ? t1[ijk] : 0;
         double q = ltq ? q1[ijk] : 0;
         double fi = ltq ? fi1[k] : 0;
@@ -417,7 +416,7 @@ hetaeta_sc(bool ltq, int lpsmod, long ij, long ngp, long nlev1, long nlev2, long
   if (ltq)
     for (int k = 0; k < nlev2; ++k)
       {
-        const auto ijk = k * ngp + ij;
+        auto ijk = k * ngp + ij;
         t2[ijk] = zt2[k];
         q2[ijk] = zq2[k];
       }
@@ -441,7 +440,7 @@ hetaeta(bool ltq, int ngp, const int *imiss, int nlev1, const double *ah1, const
   long nlev1p1 = nlev1 + 1;
   long nlev2p1 = nlev2 + 1;
 
-  const auto nthreads = Threading::ompNumThreads;
+  auto nthreads = Threading::ompNumThreads;
   Varray2D<double> ph1(nthreads, Varray<double>(nlev1p1));
   Varray2D<double> lnph1(nthreads, Varray<double>(nlev1p1));
   Varray2D<double> fi1(nthreads, Varray<double>(nlev1p1));
@@ -559,17 +558,17 @@ hetaeta(bool ltq, int ngp, const int *imiss, int nlev1, const double *ah1, const
 
   double epsm1i = 1.0 / epsilon - 1.0;
 
-#ifdef _OPENMP
+#ifdef _OPENMPs
 #pragma omp parallel for default(none) firstprivate(lpsmod) schedule(dynamic, 1)                                                  \
     shared(ngp, ph1, lnph1, fi1, pf1, lnpf1, tv1, theta1, rh1, zvar, ph2, lnph2, fi2, pf2, rh_pbl, zt2, zq2, theta_pbl, rh2, wgt, \
-           idx, vars_pbl, af1, bf1, etah2, af2, bf2, w1, w2, jl1, jl2, ltq, nvars, imiss, ah1, bh1, ps1, nlev1, epsm1i, q1, t1,   \
-           fis1, fis2, ps2, ah2, bh2, nlev2, vars1, vars2, t2, q2, tscor, pscor, secor, jblt)
+               idx, vars_pbl, af1, bf1, etah2, af2, bf2, w1, w2, jl1, jl2, ltq, nvars, imiss, ah1, bh1, ps1, nlev1, epsm1i, q1,   \
+               t1, fis1, fis2, ps2, ah2, bh2, nlev2, vars1, vars2, t2, q2, tscor, pscor, secor, jblt)
 #endif
   for (int ij = 0; ij < ngp; ++ij)
     {
       if (imiss && imiss[ij]) continue;
 
-      const auto ompthID = cdo_omp_get_thread_num();
+      auto ompthID = cdo_omp_get_thread_num();
       hetaeta_sc(ltq, lpsmod, ij, ngp, nlev1, nlev2, nvars, af1, bf1, etah2, af2, bf2, w1, w2, jl1, jl2, ah1, bh1, ps1, epsm1i, q1,
                  t1, fis1, fis2, ps2, ah2, bh2, vars1, vars2, t2, q2, tscor, pscor, secor, jblt, ph1[ompthID], lnph1[ompthID],
                  fi1[ompthID], pf1[ompthID], lnpf1[ompthID], tv1[ompthID], theta1[ompthID], rh1[ompthID], zvar[ompthID],
diff --git a/src/interpol.cc b/src/interpol.cc
index 3ed2a32d9f2eb91df6ee5408e9c094d7b611657e..ea5f6db76fa7992757ed2fd48894559e8bce81e0 100644
--- a/src/interpol.cc
+++ b/src/interpol.cc
@@ -216,7 +216,7 @@ intlinarr2(T missval, int lonIsCircular, size_t nxm, size_t nym, const Varray<T>
   for (size_t jj = 0; jj < nym; ++jj)
     for (size_t ii = 0; ii < nlon1; ++ii) grid1_mask[jj * nlon1 + ii] = !DBL_IS_EQUAL(varray1[jj * nlon1 + ii], missval);
 
-  progress::init();
+  cdo::Progress progress;
 
 #ifdef _OPENMP
 #pragma omp parallel for default(shared)
@@ -228,7 +228,7 @@ intlinarr2(T missval, int lonIsCircular, size_t nxm, size_t nym, const Varray<T>
       varray2[i] = missval;
 
       atomicCount++;
-      if (cdo_omp_get_thread_num() == 0) progress::update(0, 1, (double) atomicCount / gridsize2);
+      if (cdo_omp_get_thread_num() == 0) progress.update((double) atomicCount / gridsize2);
 
       size_t ii, jj;
       auto lfound = rect_grid_search(ii, jj, x[i], y[i], nxm, nym, xm, ym);
@@ -263,8 +263,6 @@ intlinarr2(T missval, int lonIsCircular, size_t nxm, size_t nym, const Varray<T>
           varray2[i] = fwsum;
         }
     }
-
-  progress::update(0, 1, 1);
 }
 
 void
diff --git a/src/lib/yac/CMakeLists.txt b/src/lib/yac/CMakeLists.txt
index 7d373f3c7899feb33df08023c1208da3aee5e0ac..d579319b87e9013761124011f0e7781b24d42bf6 100644
--- a/src/lib/yac/CMakeLists.txt
+++ b/src/lib/yac/CMakeLists.txt
@@ -11,7 +11,6 @@ list( APPEND yac_src_files
                ensure_array_size.h
                field_data.h
                geometry.h
-               grid.h
                grid_cell.c
                grid_cell.h
                intersection.c
diff --git a/src/mapping.cc b/src/mapping.cc
index 32cb2fe31f3a622d6da904f274f161f5dbb3bd8e..195e73cd286ce030d8c2489457441b94e01dcdcf 100644
--- a/src/mapping.cc
+++ b/src/mapping.cc
@@ -5,13 +5,11 @@
 
 */
 
-#include <cstdlib>
 #include <string>
 
 #include "cdo_options.h"
 #include "cdo_output.h"
 #include "pmlist.h"
-#include "convert_units.h"
 #include "param_conversion.h"
 #include "parse_literals.h"
 #include "cdo_cmor.h"
@@ -108,7 +106,7 @@ mapvar(int vlistID, int varID, const KeyValues &kv, const std::string &key, Cmor
             {
               if (Options::cdoVerbose) cdo_print("%s - change missval from %g to %g", var->name, missval_old, missval);
               var->changemissval = true;
-              var->missval_old = missval_old;
+              var->missvalOld = missval_old;
               vlistDefVarMissval(vlistID, varID, missval);
             }
         }
diff --git a/src/merge_axis.cc b/src/merge_axis.cc
index a120b7ede0253f9f38b7cc6575b5166ce6a3b8d5..1b4f2c594ac45595f6cef801f52d567489332de5 100644
--- a/src/merge_axis.cc
+++ b/src/merge_axis.cc
@@ -8,18 +8,13 @@
 #include <cdi.h>
 
 #include "process_int.h"
-#include "param_conversion.h"
-#include "util_files.h"
-#include "cdo_options.h"
 #include "varray.h"
 
-#include <cassert>
 #include <unistd.h>
 #include <mpim_grid.h>
 
 #include "pmlist.h"
 #include "merge_axis.h"
-#include "listbuffer.h"
 
 /** All Vars in the input file will be merged.
 *** Therefore, they need to have the same structure i.e. axis sizes
diff --git a/src/module_definitions.h b/src/module_definitions.h
deleted file mode 100644
index 345b92a351bb1941569866ad655f2ee28662618d..0000000000000000000000000000000000000000
--- a/src/module_definitions.h
+++ /dev/null
@@ -1,809 +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 MODULE_DEFINITIONS_H
-#define MODULE_DEFINITIONS_H /* \cond */
-
-// clang-format off
-
-void   *Adisit(void *argument);
-#define AdisitOperators                   {"adisit", "adipot"}
-
-void   *Afterburner(void *argument);
-#define AfterburnerOperators              {"after"}
-
-void   *Arith(void *argument);
-#define ArithOperators                    {"add", "sub", "mul", "div", "min", "max", "atan2", "setmiss"}
-
-void   *Arithc(void *argument);
-#define ArithcOperators                   {"addc", "subc", "mulc", "divc", "minc", "maxc", "mod"}
-
-void   *Arithdays(void *argument);
-#define ArithdaysOperators                {"muldpm", "divdpm", "muldpy", "divdpy", "muldoy"}
-
-void   *Arithlat(void *argument);
-#define ArithlatOperators                 {"mulcoslat", "divcoslat"}
-
-void   *Bitrounding(void *argument);
-#define BitroundingOperators              {"bitrounding"}
-
-void   *Cat(void *argument);
-#define CatOperators                      {"cat"}
-
-void   *CDItest(void *argument);
-#define CDItestOperators                  {"ncopy"}
-
-void   *CDIread(void *argument);
-#define CDIreadOperators                  {"cdiread"}
-
-void   *CDIwrite(void *argument);
-#define CDIwriteOperators                 {"cdiwrite"}
-
-void   *Change(void *argument);
-#define ChangeOperators                   {"chcode", "chtabnum", "chparam", "chname", "chunit", "chlevel", "chlevelc", "chlevelv", "chltype"}
-
-void   *Change_e5slm(void *argument);
-#define Change_e5slmOperators             {"change_e5slm", "change_e5lsm", "change_e5mask"}
-
-void   *Cloudlayer(void *argument);
-#define CloudlayerOperators               {"cloudlayer"}
-
-void   *CMOR(void *argument);
-#define CMOROperators                     {"cmor"}
-
-void   *CMOR_lite(void *argument);
-#define CMORliteOperators                 {"cmorlite"}
-
-void   *CMOR_table(void *argument);
-#define CMORtableOperators                {"dump_cmor_table", "conv_cmor_table"}
-
-void   *Collgrid(void *argument);
-#define CollgridOperators                 {"collgrid"}
-
-void   *Command(void *argument);
-#define CommandOperators                  {"command", "com", "cmd"}
-
-void   *Comp(void *argument);
-#define CompOperators                     {"eq", "ne", "le", "lt", "ge", "gt"}
-
-void   *Compc(void *argument);
-#define CompcOperators                    {"eqc", "nec", "lec", "ltc", "gec", "gtc"}
-
-void   *Complextorect(void *argument);
-#define ComplextorectOperators            {"complextorect", "complextopol"}
-
-void   *Cond(void *argument);
-#define CondOperators                     {"ifthen", "ifnotthen"}
-
-void   *Cond2(void *argument);
-#define Cond2Operators                    {"ifthenelse"}
-
-void   *Condc(void *argument);
-#define CondcOperators                    {"ifthenc", "ifnotthenc"}
-
-void   *Consecstat(void *argument);
-#define ConsecstatOperators               {"consects", "consecsum"}
-
-void   *Copy(void *argument);
-#define CopyOperators                     {"copy", "clone", "szip"}
-
-void   *DCW_util(void *argument);
-#define DCW_utilOperators                 {"dcw"}
-
-void   *Dayarith(void *argument);
-#define DayarithOperators                 {"dayadd", "daysub", "daymul", "daydiv"}
-
-void   *Deltat(void *argument);
-#define DeltatOperators                   {"deltat", "timederivative"}
-
-void   *Deltime(void *argument);
-#define DeltimeOperators                  {"delday", "del29feb"}
-
-void   *Depth(void *argument);
-#define DepthOperators                    {"zsdepth"}
-
-void   *Derivepar(void *argument);
-#define DeriveparOperators                {"gheight", "gheighthalf", "sealevelpressure"}
-
-void   *Detrend(void *argument);
-#define DetrendOperators                  {"detrend"}
-
-void   *Diff(void *argument);
-#define DiffOperators                     {"diff", "diffp", "diffn", "diffc"}
-
-void   *Distgrid(void *argument);
-#define DistgridOperators                 {"distgrid"}
-
-void   *Duplicate(void *argument);
-#define DuplicateOperators                {"duplicate"}
-
-
-void   *Echam5ini(void *argument);
-#define Echam5iniOperators                {"import_e5ml", "export_e5ml"}
-
-void   *Enlarge(void *argument);
-#define EnlargeOperators                  {"enlarge"}
-
-void   *Enlargegrid(void *argument);
-#define EnlargegridOperators              {"enlargegrid"}
-
-void   *Ensstat(void *argument);
-#define EnsstatOperators                  {"ensrange", "ensmin", "ensmax", "enssum", "ensmean", "ensavg", "ensvar", "ensvar1", "ensstd", "ensstd1", "ensskew", "enskurt", "ensmedian", "enspctl"}
-
-void   *Ensstat3(void *argument);
-#define Ensstat3Operators                 {"ensrkhistspace", "ensrkhisttime", "ensroc"}
-
-void   *Ensval(void *argument);
-#define EnsvalOperators                   {"enscrps", "ensbrs"}
-
-void   *Eofcoeff(void *argument);
-#define EofcoeffOperators                 {"eofcoeff"}
-
-void   *Eofcoeff3d(void *argument);
-#define Eofcoeff3dOperators               {"eofcoeff3d"}
-
-void   *EOFs(void *argument);
-#define EOFsOperators                     {"eof", "eofspatial", "eoftime"}
-
-void   *EOF3d(void *argument);
-#define EOF3dOperators                    {"eof3d","eof3dspatial","eof3dtime"}
-
-void   *EstFreq(void *argument);
-#define EstFreqOperators                  {"estfreq"}
-
-void   *Exprf(void *argument);
-#define ExprfOperators                     {"expr", "exprf", "aexpr", "aexprf"}
-
-void   *FC(void *argument);
-#define FCOperators                       {"fc2sp", "sp2fc", "fc2gp", "gp2fc", "fourier2grid", "grid2fourier"}
-
-void   *Filedes(void *argument);
-#define FiledesOperators                  {"filedes", "griddes", "griddes2", "zaxisdes", "vct", "vct2", "codetab", "vlist", "partab", "partab2", "spartab"}
-
-void   *Fillmiss(void *argument);
-#define SetmisstonnOperators              {"setmisstonn", "setmisstodis"}
-#define FillmissOperators                 {"fillmiss", "fillmiss2"}
-
-void   *Filter(void *argument);
-#define FilterOperators                   {"bandpass", "highpass", "lowpass"}
-
-void   *Fldrms(void *argument);
-#define FldrmsOperators                   {"fldrms"}
-
-void   *Fldstat(void *argument);
-#define FldstatOperators                  {"fldrange", "fldmin", "fldmax", "fldsum", "fldint", "fldmean", "fldavg", "fldstd", "fldstd1", "fldvar", "fldvar1", "fldskew", "fldkurt", "fldmedian", "fldcount", "fldpctl"}
-
-void   *Fldstat2(void *argument);
-#define FldcorOperators                   {"fldcor"}
-#define FldcovarOperators                 {"fldcovar"}
-
-void   *Fourier(void *argument);
-#define FourierOperators                  {"fourier"}
-
-void   *Gengrid(void *argument);
-#define GengridOperators                  {"gengrid"}
-
-void   *Getgridcell(void *argument);
-#define GetgridcellOperators              {"gridcellindex"}
-
-void   *Gradsdes(void *argument);
-#define GradsdesOperators                 {"gradsdes", "dumpmap"}
-
-void   *Gridboxstat(void *argument);
-#define GridboxstatOperators              {"gridboxrange", "gridboxmin", "gridboxmax", "gridboxsum", "gridboxmean", "gridboxavg", "gridboxstd", "gridboxstd1", "gridboxvar", "gridboxvar1", "gridboxskew", "gridboxkurt", "gridboxmedian"}
-
-void   *Gridcell(void *argument);
-#define GridcellOperators                 {"gridarea", "gridweights", "gridmask", "griddx", "griddy", "gridcellidx"}
-
-void   *Gridsearch(void *argument);
-#define GridsearchOperators               {"testpointsearch", "testcellsearch"}
-
-void   *Harmonic(void *argument);
-#define HarmonicOperators                 {"harmonic"}
-
-void   *Healpix(void *argument);
-#define HealpixOperators                  {"hpupgrade", "hpdegrade"}
-
-void   *Histogram(void *argument);
-#define HistogramOperators                {"histcount", "histsum", "histmean", "histfreq"}
-
-void   *Importamsr(void *argument);
-#define ImportamsrOperators               {"import_amsr"}
-
-void   *Importbinary(void *argument);
-#define ImportbinaryOperators             {"import_binary"}
-
-void   *Importcmsaf(void *argument);
-#define ImportcmsafOperators              {"import_cmsaf"}
-
-void   *Importobs(void *argument);
-#define ImportobsOperators                {"import_obs"}
-
-void   *Importfv3grid(void *argument);
-#define Importfv3gridOperators            {"import_fv3grid"}
-
-void   *Info(void *argument);
-#define InfoOperators                     {"info", "infop", "infon", "infoc", "vinfon", "xinfon", "map"}
-
-void   *Input(void *argument);
-#define InputOperators                    {"input", "inputsrv", "inputext"}
-
-void   *Intgrid(void *argument);
-#define IntgridOperators                  {"intgridbil", "intgriddis", "intgridnn", "interpolate", "boxavg", "thinout"}
-
-void   *Intgridtraj(void *argument);
-#define IntgridtrajOperators              {"intgridtraj"}
-
-void   *Intlevel(void *argument);
-#define IntlevelOperators                 {"intlevel", "intlevelx"}
-
-void   *Intlevel3d(void *argument);
-#define Intlevel3dOperators               {"intlevel3d", "intlevelx3d"}
-
-void   *Inttime(void *argument);
-#define InttimeOperators                  {"inttime"}
-
-void   *Intntime(void *argument);
-#define IntntimeOperators                 {"intntime"}
-
-void   *Intyear(void *argument);
-#define IntyearOperators                  {"intyear"}
-
-void   *Invert(void *argument);
-#define InvertOperators                   {"invertlat", "invertlon", "invertlatdes", "invertlondes", "invertlatdata", "invertlondata"}
-
-void   *Invertlev(void *argument);
-#define InvertlevOperators                {"invertlev"}
-
-void   *Lic(void *argument);
-#define LicOperators                      {"lic"}
-
-void   *Longinfo(void *argument);
-#define LonginfoOperators                 {"linfo"}
-
-void   *MapReduce(void *argument);
-#define MapReduceOperators                {"reducegrid"}
-
-void   *Maskbox(void *argument);
-#define MaskboxOperators                  {"masklonlatbox", "maskindexbox"}
-#define MaskregionOperators               {"maskregion", "maskcircle"}
-
-void   *Mastrfu(void *argument);
-#define MastrfuOperators                  {"mastrfu"}
-
-void   *Math(void *argument);
-#define MathOperators                     {"abs", "int", "nint", "sqr", "sqrt", "exp", "ln", "log10", "sin", "cos", "tan", "asin", "acos", "atan", "pow", "rand", "reci", "not", "conj", "re", "im", "arg"}
-
-void   *Merge(void *argument);
-#define MergeOperators                    {"merge"}
-
-void   *Mergegrid(void *argument);
-#define MergegridOperators                {"mergegrid"}
-
-void   *Mergetime(void *argument);
-#define MergetimeOperators                {"mergetime"}
-
-void   *Merstat(void *argument);
-#define MerstatOperators                  {"merrange", "mermin", "mermax", "mersum", "mermean", "meravg", "merstd", "merstd1", "mervar", "mervar1", "merskew", "merkurt", "mermedian", "merpctl"}
-
-void   *Monarith(void *argument);
-#define MonarithOperators                 {"monadd", "monsub", "monmul", "mondiv"}
-
-void   *Mrotuv(void *argument);
-#define MrotuvOperators                   {"mrotuv"}
-
-void   *Mrotuvb(void *argument);
-#define MrotuvbOperators                  {"mrotuvb"}
-
-void   *NCL_wind(void *argument);
-#define NCL_windOperators                 {"uv2dv_cfd", "uv2vr_cfd"}
-
-void   *Ninfo(void *argument);
-#define NinfoOperators                    {"nyear", "nmon", "ndate", "ntime", "ncode", "npar", "nlevel", "ngridpoints", "ngrids"}
-
-void   *Nmldump(void *argument);
-#define NmldumpOperators                  {"nmldump", "kvldump"}
-
-void   *Output(void *argument);
-#define OutputOperators                   {"output", "outputint", "outputsrv", "outputext", "outputf", "outputts", "outputfld", "outputarr", "outputxyz"}
-#define OutputtabOperators                {"outputtab"}
-
-void   *Outputgmt(void *argument);
-#define OutputgmtOperators                {"gmtxyz", "gmtcells", "outputcenter2", "outputcentercpt", "outputboundscpt", "outputvector", "outputtri", "outputvrml", "outputkml"}
-
-void   *Pack(void *argument);
-#define PackOperators                     {"pack"}
-
-void   *Pardup(void *argument);
-#define PardupOperators                   {"pardup", "parmul"}
-
-void   *Pinfo(void *argument);
-#define PinfoOperators                    {"pinfo", "pinfov"}
-
-void   *Query(void *argument);
-#define QueryOperators                    {"query"}
-
-void   *Pressure(void *argument);
-#define PressureOperators                 {"pressure_fl", "pressure_hl", "deltap"}
-
-void   *Recttocomplex(void *argument);
-#define RecttocomplexOperators            {"recttocomplex"}
-
-void   *Regres(void *argument);
-#define RegresOperators                   {"regres"}
-
-void   *Remapweights(void *argument);
-#define GenbilOperators                   {"genbil"}
-#define GenbicOperators                   {"genbic"}
-#define GennnOperators                    {"gennn"}
-#define GendisOperators                   {"gendis"}
-#define GenconOperators                   {"gencon"}
-#define Genycon2Operators                 {"genycon2test"}
-#define GensconOperators                  {"genscon"}
-#define Genscon2Operators                 {"genscon2"}
-#define GenlafOperators                   {"genlaf"}
-
-void   *Remapgrid(void *argument);
-#define RemapOperators                    {"remap"}
-#define RemapbilOperators                 {"remapbil"}
-#define RemapbicOperators                 {"remapbic"}
-#define RemapnnOperators                  {"remapnn"}
-#define RemapdisOperators                 {"remapdis"}
-#define RemapconOperators                 {"remapcon"}
-#define Remapycon2Operators               {"remapycon2test"}
-#define RemapsconOperators                {"remapscon"}
-#define Remapscon2Operators               {"remapscon2"}
-#define RemaplafOperators                 {"remaplaf"}
-#define RemapavgOperators                 {"remapavgtest"}
-
-void   *Remapeta(void *argument);
-#define RemapetaOperators                 {"remapeta", "remapeta_s", "remapeta_z"}
-
-void   *Remapstat(void *argument);
-#define RemapstatOperators                {"remaprange", "remapmin", "remapmax", "remapsum", "remapmean", "remapavg", "remapstd", "remapstd1", "remapvar", "remapvar1", "remapskew", "remapkurt", "remapmedian"}
-
-void   *Replace(void *argument);
-#define ReplaceOperators                  {"replace"}
-
-void   *Replacevalues(void *argument);
-#define ReplacevaluesOperators            {"setvals", "setrtoc", "setrtoc2"}
-
-void   *Rotuv(void *argument);
-#define RotuvOperators                    {"rotuvb"}
-
-void   *Rhopot(void *argument);
-#define RhopotOperators                   {"rhopot"}
-
-void   *Runpctl(void *argument);
-#define RunpctlOperators                  {"runpctl"}
-
-void   *Runstat(void *argument);
-#define RunstatOperators                  {"runrange", "runmin", "runmax", "runsum", "runmean", "runavg", "runstd", "runstd1", "runvar", "runvar1"}
-
-void   *Samplegridicon(void *argument);
-#define SamplegridiconOperators           {"samplegridicon"}
-
-void   *Seascount(void *argument);
-#define SeascountOperators                {"seascount"}
-
-void   *Seaspctl(void *argument);
-#define SeaspctlOperators                 {"seaspctl"}
-
-void   *Seasstat(void *argument);
-#define SeasstatOperators                 {"seasrange", "seasmin", "seasmax", "seassum", "seasmean", "seasavg", "seasstd", "seasstd1", "seasvar", "seasvar1"}
-
-void   *Seasmonstat(void *argument);
-#define SeasmonstatOperators              {"seasmonmean", "seasmonavg"}
-
-void   *Selbox(void *argument);
-#define SelboxOperators                   {"sellonlatbox", "selindexbox"}
-
-void   *Selgridcell(void *argument);
-#define SelgridcellOperators              {"selgridcell", "delgridcell"}
-
-void   *Select(void *argument);
-#define SelectOperators                   {"select", "delete"}
-
-void   *Selvar(void *argument);
-#define SelvarOperators                   {"selparam", "selcode", "selname", "selstdname", "sellevel", "sellevidx", "selgrid", "selzaxis", "selzaxisname", "seltabnum", "delparam", "delcode", "delname", "selltype"}
-
-void   *Seloperator(void *argument);
-#define SeloperatorOperators              {"seloperator"}
-
-void   *Selrec(void *argument);
-#define SelrecOperators                   {"selrec"}
-
-void   *Selregion(void *argument);
-#define SelregionOperators                {"selregion", "selcircle"}
-
-void   *Selsurface(void *argument);
-#define SelsurfaceOperators               {"isosurface", "bottomvalue", "topvalue"}
-
-void   *Seltime(void *argument);
-#define SeltimeOperators                  {"seltimestep", "selyear", "selseason", "selmonth", "selday", "selhour", "seldate", "seltime", "selsmon"}
-
-void   *Selyearidx(void *argument);
-#define SelyearidxOperators               {"selyearidx", "seltimeidx"}
-
-void   *Set(void *argument);
-#define SetOperators                      {"setcode", "setparam", "setname", "setunit", "setlevel", "setltype", "settabnum", "setmaxsteps"}
-
-void   *Setattribute(void *argument);
-#define SetattributeOperators             {"setattribute"}
-
-void   *Setbox(void *argument);
-#define SetboxOperators                   {"setclonlatbox", "setcindexbox"}
-
-void   *Setgrid(void *argument);
-#define SetgridOperators                  {"setgrid", "setgridtype", "setgridarea", "setgridmask", "unsetgridmask", "setgridnumber", "setgriduri", "usegridnumber", "setprojparams"}
-
-void   *Setgridcell(void *argument);
-#define SetgridcellOperators              {"setgridcell"}
-
-void   *Sethalo(void *argument);
-#define SethaloOperators                  {"sethalo", "tpnhalo"}
-
-void   *Setmiss(void *argument);
-#define SetmissOperators                  {"setmissval", "setctomiss", "setmisstoc", "setrtomiss", "setvrange"}
-
-void   *Setpartab(void *argument);
-
-#define SetpartabOperators                {"setpartabc", "setpartabp", "setpartabn"}
-#define SetcodetabOperators               {"setcodetab"}
-
-void   *Setrcaname(void *argument);
-#define SetrcanameOperators               {"setrcaname"}
-
-void   *Settime(void *argument);
-#define SettimeOperators                  {"setyear", "setmon", "setday", "setdate", "settime", "settunits", "settaxis", "settbounds", "setreftime", "setcalendar", "shifttime"}
-
-void   *Setzaxis(void *argument);
-#define SetzaxisOperators                 {"setzaxis", "genlevelbounds"}
-
-void   *Shiftxy(void *argument);
-#define ShiftxyOperators                  {"shiftx", "shifty"}
-
-void   *Showinfo(void *argument);
-#define ShowinfoOperators                 {"showyear", "showmon", "showdate", "showtime", "showtimestamp", "showcode", "showunit", "showparam", "showname", "showstdname", "showlevel", "showltype", "showformat", "showgrid", "showatts", "showattsglob"}
-
-void   *Showattribute(void *argument);
-#define ShowattributeOperators            {"showattribute", "showattsvar"}
-
-void   *Sinfo(void *argument);
-#define SinfoOperators                    {"sinfo", "sinfop", "sinfon", "sinfoc", "seinfo", "seinfop", "seinfon", "seinfoc"}
-#define XSinfoOperators                   {"xsinfo", "xsinfop", "xsinfon", "xsinfoc"}
-
-void   *Smooth(void *argument);
-#define SmoothOperators                   {"smooth", "smooth9"}
-
-void   *Sort(void *argument);
-#define SortOperators                     {"sortcode", "sortparam", "sortname", "sortlevel"}
-
-void   *Sorttimestamp(void *argument);
-#define SorttimestampOperators            {"sorttimestamp", "sorttaxis"}
-
-void   *Specinfo(void *argument);
-#define SpecinfoOperators                 {"specinfo"}
-
-void   *Spectral(void *argument);
-#define SpectralOperators                 {"gp2sp", "gp2spl", "sp2gp", "sp2gpl"}
-#define SpecconvOperators                 {"sp2sp", "spcut"}
-
-void   *Spectrum(void *argument);
-#define SpectrumOperators                 {"spectrum"}
-
-void   *Split(void *argument);
-#define SplitOperators                    {"splitcode", "splitparam", "splitname", "splitlevel", "splitgrid", "splitzaxis", "splittabnum"}
-
-void   *Splitdate(void *argument);
-#define SplitdateOperators                {"splitdate", "splitdatetime"}
-
-void   *Splitrec(void *argument);
-#define SplitrecOperators                 {"splitrec"}
-
-void   *Splitsel(void *argument);
-#define SplitselOperators                 {"splitsel"}
-
-void   *Splittime(void *argument);
-#define SplittimeOperators                {"splithour", "splitday", "splitmon", "splitseas"}
-
-void   *Splityear(void *argument);
-#define SplityearOperators                {"splityear", "splityearmon"}
-
-void   *Tee(void *argument);
-#define TeeOperators                      {"tee"}
-
-void   *Template1(void *argument);
-#define Template1Operators                {"template1"}
-
-void   *Template2(void *argument);
-#define Template2Operators                {"template2"}
-
-void   *Testdata(void *argument);
-#define TestdataOperators                 {"testdata"}
-
-void   *Tests(void *argument);
-#define TestsOperators                    {"normal", "studentt", "chisquare", "beta", "fisher"}
-
-void   *Timfillmiss(void *argument);
-#define TimfillmissOperators              {"timfillmiss"}
-
-void   *Timsort(void *argument);
-#define TimsortOperators                  {"timsort"}
-
-void   *Timcount(void *argument);
-#define TimcountOperators                 {"timcount"}
-#define YearcountOperators                {"yearcount"}
-#define MoncountOperators                 {"moncount"}
-#define DaycountOperators                 {"daycount"}
-#define HourcountOperators                {"hourcount"}
-
-void   *Timcumsum(void *argument);
-#define TimcumsumOperators                {"timcumsum"}
-
-void   *Timpctl(void *argument);
-#define TimpctlOperators                  {"timpctl"}
-#define YearpctlOperators                 {"yearpctl"}
-#define MonpctlOperators                  {"monpctl"}
-#define DaypctlOperators                  {"daypctl"}
-#define HourpctlOperators                 {"hourpctl"}
-
-void   *Timselpctl(void *argument);
-#define TimselpctlOperators               {"timselpctl"}
-
-void   *Timselstat(void *argument);
-#define TimselstatOperators               {"timselrange", "timselmin", "timselmax", "timselsum", "timselmean", "timselavg", "timselvar", "timselvar1", "timselstd", "timselstd1"}
-
-void   *XTimstat(void *argument);
-#define XTimstatOperators                 {"xtimmin", "xtimmax", "xtimsum", "xtimmean", "xtimavg", "xtimvar", "xtimvar1", "xtimstd", "xtimstd1", "xyearmin", "xyearmax", "xyearsum","xyearmean", "xyearavg", "xyearvar", "xyearvar1", "xyearstd", "xyearstd1", "xmonmin", "xmonmax", "xmonsum", "xmonmean", "xmonavg", "xmonvar", "xmonvar1", "xmonstd", "xmonstd1"}
-
-void   *Timstat(void *argument);
-#define TimstatOperators                  {"timrange", "timmin", "timmax", "timsum", "timmean", "timavg", "timvar", "timvar1", "timstd", "timstd1", "timminidx", "timmaxidx"}
-#define YearstatOperators                 {"yearrange", "yearmin", "yearmax", "yearsum", "yearmean", "yearavg", "yearvar", "yearvar1", "yearstd", "yearstd1", "yearminidx", "yearmaxidx"}
-#define MonstatOperators                  {"monrange", "monmin", "monmax", "monsum", "monmean", "monavg", "monvar", "monvar1", "monstd", "monstd1"}
-#define DaystatOperators                  {"dayrange", "daymin", "daymax", "daysum", "daymean", "dayavg", "dayvar", "dayvar1", "daystd", "daystd1"}
-#define HourstatOperators                 {"hourrange", "hourmin", "hourmax", "hoursum", "hourmean", "houravg", "hourvar", "hourvar1", "hourstd", "hourstd1"}
-
-void   *Timstat2(void *argument);
-#define TimcorOperators                   {"timcor"}
-#define TimcovarOperators                 {"timcovar"}
-#define TimrmsdOperators                  {"timrmsd"}
-
-void   *Timstat3(void *argument);
-#define Timstat3Operators                 {"meandiff2test", "varquot2test"}
-
-void   *Tinfo(void *argument);
-#define TinfoOperators                    {"tinfo"}
-
-void   *Tocomplex(void *argument);
-#define TocomplexOperators                {"retocomplex", "imtocomplex"}
-
-void   *Transpose(void *argument);
-#define TransposeOperators                {"transxy"}
-
-void   *Trend(void *argument);
-#define TrendOperators                    {"trend"}
-
-void   *Trendarith(void *argument);
-#define TrendarithOperators               {"addtrend", "subtrend"}
-
-void   *Tstepcount(void *argument);
-#define TstepcountOperators               {"tstepcount"}
-
-void   *Unpack(void *argument);
-#define UnpackOperators                   {"unpack"}
-
-void   *Vargen(void *argument);
-#define VargenOperators                   {"random", "const", "sincos", "coshill", "testfield", "seq", "topo", "temp", "mask", "stdatm"}
-
-void   *Varrms(void *argument);
-#define VarrmsOperators                   {"varrms"}
-
-void   *Varsstat(void *argument);
-#define VarsstatOperators                 {"varsrange", "varsmin", "varsmax", "varssum", "varsmean", "varsavg", "varsstd", "varsstd1", "varsvar", "varsvar1"}
-
-void   *Vertfillmiss(void *argument);
-#define VertfillmissOperators             {"vertfillmiss"}
-
-void   *Vertintap(void *argument);
-#define VertintapOperators                {"ap2pl", "ap2plx", "ap2hl", "ap2hlx"}
-
-void   *Vertintgh(void *argument);
-#define VertintghOperators                {"gh2hl", "gh2hlx"}
-
-void   *Vertintml(void *argument);
-#define VertintmlOperators                {"ml2pl", "ml2hl", "ml2plx", "ml2hlx", "ml2height", "ml2heightx"}
-
-void   *Vertintzs(void *argument);
-#define VertintzsOperators                {"zs2zl", "zs2zlx"}
-
-void   *Vertstat(void *argument);
-#define VertstatOperators                 {"vertrange", "vertmin", "vertmax", "vertsum", "vertint", "vertmean", "vertavg", "vertstd", "vertstd1", "vertvar", "vertvar1"}
-
-void   *Vertcum(void *argument);
-#define VertcumOperators                  {"vertcum", "vertcumhl"}
-
-void   *Vertwind(void *argument);
-#define VertwindOperators                 {"vertwind"}
-
-void   *Verifygrid(void *argument);
-#define VerifygridOperators               {"verifygrid"}
-
-void   *Verifyweights(void *argument);
-#define VerifyweightsOperators            {"verifyweights", "writeremapscrip"}
-
-void   *Wind(void *argument);
-#define WindOperators                     {"uv2dv", "uv2dvl", "dv2uv", "dv2uvl"}
-
-#define Wind2Operators                    {"dv2ps"}
-void   *Writegrid(void *argument);
-
-#define WritegridOperators                {"writegrid"}
-void   *Writerandom(void *argument);
-
-#define WriterandomOperators              {"writerandom"}
-void   *YAR(void *argument);
-
-void   *Yeararith(void *argument);
-#define YeararithOperators                {"yearadd", "yearsub", "yearmul", "yeardiv"}
-
-void   *Yearmonstat(void *argument);
-#define YearmonstatOperators              {"yearmonmean", "yearmonavg"}
-
-void   *Ydayarith(void *argument);
-#define YdayarithOperators                {"ydayadd", "ydaysub", "ydaymul", "ydaydiv"}
-
-void   *Ydaypctl(void *argument);
-#define YdaypctlOperators                 {"ydaypctl"}
-
-void   *Ydaystat(void *argument);
-#define YdaystatOperators                 {"ydayrange", "ydaymin", "ydaymax", "ydaysum", "ydaymean", "ydayavg", "ydaystd", "ydaystd1", "ydayvar", "ydayvar1"}
-
-void   *Ydrunpctl(void *argument);
-#define YdrunpctlOperators                {"ydrunpctl"}
-
-void   *Ydrunstat(void *argument);
-#define YdrunstatOperators                {"ydrunmin", "ydrunmax", "ydrunsum", "ydrunmean", "ydrunavg", "ydrunstd", "ydrunstd1", "ydrunvar", "ydrunvar1"}
-
-void   *Yhourarith(void *argument);
-#define YhourarithOperators               {"yhouradd", "yhoursub", "yhourmul", "yhourdiv"}
-
-void   *Yhourstat(void *argument);
-#define YhourstatOperators                {"yhourrange", "yhourmin", "yhourmax", "yhoursum", "yhourmean", "yhouravg", "yhourstd", "yhourstd1", "yhourvar", "yhourvar1"}
-#define DhourstatOperators                {"dhourrange", "dhourmin", "dhourmax", "dhoursum", "dhourmean", "dhouravg", "dhourstd", "dhourstd1", "dhourvar", "dhourvar1"}
-
-void   *Ymonarith(void *argument);
-#define YmonarithOperators                {"ymonadd", "ymonsub", "ymonmul", "ymondiv"}
-#define YseasarithOperators               {"yseasadd", "yseassub", "yseasmul", "yseasdiv"}
-
-void   *Ymoncomp(void *argument);
-#define YmoncompOperators                 {"ymoneq", "ymonne", "ymonle", "ymonlt", "ymonge", "ymongt"}
-#define YseascompOperators                {"yseaseq", "yseasne", "yseasle", "yseaslt", "yseasge", "yseasgt"}
-
-void   *Ymonpctl(void *argument);
-#define YmonpctlOperators                 {"ymonpctl"}
-
-void   *Ymonstat(void *argument);
-#define YmonstatOperators                 {"ymonrange", "ymonmin", "ymonmax", "ymonsum", "ymonmean", "ymonavg", "ymonstd", "ymonstd1", "ymonvar", "ymonvar1"}
-
-void   *Yseaspctl(void *argument);
-#define YseaspctlOperators                {"yseaspctl"}
-
-void   *Yseasstat(void *argument);
-#define YseasstatOperators                {"yseasrange", "yseasmin", "yseasmax", "yseassum", "yseasmean", "yseasavg", "yseasstd", "yseasstd1", "yseasvar", "yseasvar1"}
-
-void   *Zonstat(void *argument);
-#define ZonstatOperators                  {"zonrange", "zonmin", "zonmax", "zonsum", "zonmean", "zonavg", "zonstd", "zonstd1", "zonvar", "zonvar1", "zonskew", "zonkurt", "zonmedian", "zonpctl"}
-
-//------------------------------
-
-void   *EcaCfd(void *argument);
-#define EcaCfdOperators                   {"eca_cfd"}
-void   *EcaCsu(void *argument);
-#define EcaCsuOperators                   {"eca_csu"}
-void   *EcaCwdi(void *argument);
-#define EcaCwdiOperators                  {"eca_cwdi"}
-void   *EcaCwfi(void *argument);
-#define EcaCwfiOperators                  {"eca_cwfi", "etccdi_csdi"}
-void   *EcaEtr(void *argument);
-#define EcaEtrOperators                   {"eca_etr"}
-void   *EcaFd(void *argument);
-#define EcaFdOperators                    {"eca_fd", "etccdi_fd"}
-void   *EcaGsl(void *argument);
-#define EcaGslOperators                   {"eca_gsl", "etccdi_gsl"}
-void   *EcaHd(void *argument);
-#define EcaHdOperators                    {"eca_hd", "etccdi_hd"}
-void   *EcaHwdi(void *argument);
-#define EcaHwdiOperators                  {"eca_hwdi"}
-void   *EcaHwfi(void *argument);
-#define EcaHwfiOperators                  {"eca_hwfi", "etccdi_wsdi"}
-void   *EcaId(void *argument);
-#define EcaIdOperators                    {"eca_id", "etccdi_id"}
-void   *EcaSu(void *argument);
-#define EcaSuOperators                    {"eca_su", "etccdi_su"}
-void   *EcaTr(void *argument);
-#define EcaTrOperators                    {"eca_tr", "etccdi_tr"}
-void   *EcaTg10p(void *argument);
-#define EcaTg10pOperators                 {"eca_tg10p"}
-void   *EcaTg90p(void *argument);
-#define EcaTg90pOperators                 {"eca_tg90p"}
-void   *EcaTn10p(void *argument);
-#define EcaTn10pOperators                 {"eca_tn10p"}
-void   *EcaTn90p(void *argument);
-#define EcaTn90pOperators                 {"eca_tn90p"}
-void   *EcaTx10p(void *argument);
-#define EcaTx10pOperators                 {"eca_tx10p"}
-void   *EcaTx90p(void *argument);
-#define EcaTx90pOperators                 {"eca_tx90p"}
-void   *EcaCdd(void *argument);
-#define EcaCddOperators                   {"eca_cdd", "etccdi_cdd"}
-void   *EcaCwd(void *argument);
-#define EcaCwdOperators                   {"eca_cwd", "etccdi_cwd"}
-void   *EcaRr1(void *argument);
-#define EcaRr1Operators                   {"eca_rr1"}
-void   *EcaPd(void *argument);
-#define EcaPdOperators                    {"eca_pd", "eca_r10mm", "eca_r20mm", "etccdi_r1mm", "etccdi_r10mm", "etccdi_r20mm"}
-void   *EcaR75p(void *argument);
-#define EcaR75pOperators                  {"eca_r75p"}
-void   *EcaR75ptot(void *argument);
-#define EcaR75ptotOperators               {"eca_r75ptot"}
-void   *EcaR90p(void *argument);
-#define EcaR90pOperators                  {"eca_r90p"}
-void   *EcaR90ptot(void *argument);
-#define EcaR90ptotOperators               {"eca_r90ptot"}
-void   *EcaR95p(void *argument);
-#define EcaR95pOperators                  {"eca_r95p"}
-void   *EcaR95ptot(void *argument);
-#define EcaR95ptotOperators               {"eca_r95ptot"}
-void   *EcaR99p(void *argument);
-#define EcaR99pOperators                  {"eca_r99p"}
-void   *EcaR99ptot(void *argument);
-#define EcaR99ptotOperators               {"eca_r99ptot"}
-void   *EcaRx1day(void *argument);
-#define EcaRx1dayOperators                {"eca_rx1day", "etccdi_rx1day", "etccdi_rx1daymon"}
-void   *EcaRx5day(void *argument);
-#define EcaRx5dayOperators                {"eca_rx5day", "etccdi_rx5day", "etccdi_rx5daymon"}
-void   *EcaSdii(void *argument);
-#define EcaSdiiOperators                  {"eca_sdii", "etccdi_sdii"}
-void   *EcaEtccdi(void *argument);
-#define EcaEtccdiOperators                {"etccdi_tx90p","etccdi_tx10p","etccdi_tn90p","etccdi_tn10p","etccdi_r95p","etccdi_r99p","etccdi"}
-
-void   *Fdns(void *argument);
-#define FdnsOperators                     {"fdns"}
-
-void   *Strwin(void *argument);
-#define StrwinOperators                   {"strwin"}
-void   *Strbre(void *argument);
-#define StrbreOperators                   {"strbre"}
-void   *Strgal(void *argument);
-#define StrgalOperators                   {"strgal"}
-void   *Hurr(void *argument);
-#define HurrOperators                     {"hurr"}
-
-// void   *Hi(void *argument);
-//#define HiOperators                     {"hi"}
-void   *Wct(void *argument);
-#define WctOperators                      {"wct"}
-
-void   *Magplot(void *argument);
-#define MagplotOperators                  {"contour", "shaded", "grfill"}
-void   *Magvector(void *argument);
-#define MagvectorOperators                {"vector"}
-void   *Maggraph(void *argument);
-#define MaggraphOperators                 {"graph"}
-
-//------------------------------
-// HIRLAM_EXTENSIONS
-void   *Selmulti(void *argument); // "selmulti", "delmulti"
-#define SelmultiOperators                 {"selmulti", "delmulti", "changemulti"}
-void   *WindTrans(void *argument); // "uvDestag", "rotuvN", "rotuvNorth", "projuvLatLon"
-#define WindTransOperators                {"uvDestag", "rotuvN", "rotuvNorth", "projuvLatLon"}
-void   *Samplegrid(void *argument); // "samplegrid", "subgrid"
-#define SamplegridOperators               {"samplegrid", "subgrid"}
-
-// clang-format on
-/* \endcond */
-#endif
diff --git a/src/module_info.cc b/src/module_info.cc
index 0423c76d2c325f367cd89b65d1b8297e0e8c1f59..711f3691ea122f3b67c62204333e41d38e5343e1 100644
--- a/src/module_info.cc
+++ b/src/module_info.cc
@@ -249,15 +249,19 @@ operator_print_list(bool print_no_output)
 }
 
 void
-cdo_print_help(const CdoHelp &p_help)
+cdo_print_help(const std::string &p_operator_name)
 {
-  if (p_help.empty())
+
+  auto it
+      = Factory::find(p_operator_name, [&p_operator_name]() { cdo_abort("%s", Factory::err_msg_oper_not_found(p_operator_name)); });
+  const CdoHelp &help = Factory::get_help(it);
+  if (help.empty())
     fprintf(stderr, "No help available for this operator!\n");
   else
     {
-      for (size_t i = 0; i < p_help.size(); ++i)
+      for (size_t i = 0; i < help.size(); ++i)
         {
-          auto doPrint = !(p_help[i][0] == '\0' && p_help[i + 1][0] == ' ');
+          auto doPrint = !(help[i][0] == '\0' && help[i + 1][0] == ' ');
           if (doPrint)
             {
 
@@ -266,7 +270,7 @@ cdo_print_help(const CdoHelp &p_help)
               // 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]);
+              std::string line = std::string(help[i]);
               //===========================================================================
 
               if (color_enabled())
diff --git a/src/module_info.h b/src/module_info.h
index 792df2da214d402fd5f002bc97a887674506da40..81ec5c762b2fce7d02fe859f9f04dfef3ffa000d 100644
--- a/src/module_info.h
+++ b/src/module_info.h
@@ -29,5 +29,6 @@ std::string get_operator_description(const std::string &p_current_op_name, const
 void operator_print_list(ModListOptions &p_modListOpt);
 
 void cdo_print_help(const char **help);
+void cdo_print_help(const std::string &help);
 void cdo_print_help(const CdoHelp &p_help);
 #endif
diff --git a/src/module_list.h b/src/module_list.h
deleted file mode 100644
index 5a9ff4292c357e5da71dd89bf4913ec3dfc6e0d5..0000000000000000000000000000000000000000
--- a/src/module_list.h
+++ /dev/null
@@ -1,324 +0,0 @@
-//#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
diff --git a/src/mpim_grid/grid_healpix.cc b/src/mpim_grid/grid_healpix.cc
index 9c2fd62d198fde141cd925aece791569db9347f1..2fb32112c9db84ae05f686ab1040ee31958aaddf 100644
--- a/src/mpim_grid/grid_healpix.cc
+++ b/src/mpim_grid/grid_healpix.cc
@@ -12,6 +12,7 @@ extern "C"
 #include <cstdio>
 #include <cstring>
 #include <climits>
+#include <cassert>
 
 HpOrder
 hp_get_order(const std::string &orderName)
diff --git a/src/mpim_grid/grid_healpix.h b/src/mpim_grid/grid_healpix.h
index 381b5bbbfe9514fd2bcc634f149064e36e5f176e..101b7cda64a13f636899e8b633adf6155a484ad0 100644
--- a/src/mpim_grid/grid_healpix.h
+++ b/src/mpim_grid/grid_healpix.h
@@ -1,8 +1,6 @@
 #ifndef GRID_HEALPIX_H
 #define GRID_HEALPIX_H
 
-#include <cassert>
-#include <cinttypes>
 #include <string>
 #include <vector>
 
diff --git a/src/mpim_grid/gridreference.cc b/src/mpim_grid/gridreference.cc
index 0da339baf4f4f92ab658dd403a18c384ee8c0afb..307a60ed04fea7b35df476e5b28f74d934f3ee10 100644
--- a/src/mpim_grid/gridreference.cc
+++ b/src/mpim_grid/gridreference.cc
@@ -17,14 +17,12 @@
 #include <curl/curl.h>
 #endif
 
-#include <cerrno>
 #include <cstring>
 
 #include <cdi.h>
 
 #include "cdi_uuid.h"
 #include "gridreference.h"
-#include "process_int.h"
 #include "cdo_output.h"
 #include <mpim_grid.h>
 #include "cdi_lockedIO.h"
@@ -47,7 +45,6 @@ downloadGridfile(const char *uri, const char *basename)
 #ifdef HAVE_LIBCURL
   // As curl_easy_init calls non-thread safe curl_global_init the libcurl
   // developer advice to call curl_global_init first and before potential thread spawning.
-
   int curlflags = CURL_GLOBAL_DEFAULT;
 
 #ifdef CURL_GLOBAL_ACK_EINTR
@@ -189,7 +186,7 @@ grid_from_file(int gridID1, char *gridfilepath)
   open_unlock();
 
   auto vlistID = streamInqVlist(streamID);
-  auto ngrids = vlistNgrids(vlistID);
+  auto ngrids = vlistNumGrids(vlistID);
   if (position > 0 && position <= ngrids)
     {
       auto gridID = vlistGrid(vlistID, position - 1);
diff --git a/src/mpim_grid/mpim_grid.cc b/src/mpim_grid/mpim_grid.cc
index 4c8f082885897b759ba3ba0ab4724915aa814de6..2aaecdfaa372251c622a6fa80d217d6724a00e1f 100644
--- a/src/mpim_grid/mpim_grid.cc
+++ b/src/mpim_grid/mpim_grid.cc
@@ -1246,7 +1246,7 @@ gridToUnstructuredGaussianReduced(int gridID1, int gridID2, size_t gridsize, int
       for (size_t j = 0; j < nlat; ++j)
         {
           size_t nlon = reducedPoints[j];
-          for (size_t i = 0; i < nlon; ++i) xvals[ij++] = i * 360. / nlon;
+          for (size_t i = 0; i < nlon; ++i) xvals[ij++] = i * 360.0 / nlon;
         }
     }
 
@@ -1272,10 +1272,10 @@ gridToUnstructuredGaussianReduced(int gridID1, int gridID2, size_t gridsize, int
               size_t nlon = reducedPoints[j];
               for (size_t i = 0; i < nlon; ++i)
                 {
-                  xbounds2D[ij + 0] = (i + .5) * 360. / nlon;
-                  xbounds2D[ij + 1] = (i + .5) * 360. / nlon;
-                  xbounds2D[ij + 2] = (i - .5) * 360. / nlon;
-                  xbounds2D[ij + 3] = (i - .5) * 360. / nlon;
+                  xbounds2D[ij + 0] = (i + 0.5) * 360.0 / nlon;
+                  xbounds2D[ij + 1] = (i + 0.5) * 360.0 / nlon;
+                  xbounds2D[ij + 2] = (i - 0.5) * 360.0 / nlon;
+                  xbounds2D[ij + 3] = (i - 0.5) * 360.0 / nlon;
                   ybounds2D[ij + 0] = ybounds[j * 2];
                   ybounds2D[ij + 1] = ybounds[j * 2 + 1];
                   ybounds2D[ij + 2] = ybounds[j * 2 + 1];
diff --git a/src/mpmo.h b/src/mpmo.h
index 8ec49cabdb1959d310fe3af4c85c1f527fd953a1..0f45216d002d59764a9bb68467f1b0e99fbccc7d 100644
--- a/src/mpmo.h
+++ b/src/mpmo.h
@@ -63,14 +63,15 @@ Print(const std::string &format, Args const &...args) noexcept
 }
 
 template <typename... Args>
-void
-PrintCerr(const std::string &format, Args const &...args) noexcept
+std::string
+PrintCerr(const std::string &format, Args const &...args)
 {
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wformat-nonliteral"
-#pragma GCC diagnostic ignored "-Wformat-security"
-  fprintf(stderr, (format + "\n").c_str(), Argument(args)...);
-#pragma GCC diagnostic pop
+  int cx;
+  cx = snprintf(nullptr, 0, format.c_str(), Argument(args)...);
+  std::string msg = std::string(cx + 1, '\0');
+  snprintf(&msg[0], cx + 1, format.c_str(), Argument(args)...);
+  fprintf(stderr, "%s\n", msg.c_str());
+  return std::string(msg);
 }
 
 std::string debug_scope_string(const char *p_file, const char *p_func, int p_line, const char *context);
diff --git a/src/mpmo_color.h b/src/mpmo_color.h
index 29d98658b4d0f0af8261849f476ec75d40556ae4..af800352c638bdfb2f973a1516bf55fff81536bb 100644
--- a/src/mpmo_color.h
+++ b/src/mpmo_color.h
@@ -10,6 +10,7 @@
 
 #include <string>
 #include <sstream>
+#include <type_traits>
 
 enum ColorEnabled
 {
diff --git a/src/node.cc b/src/node.cc
index 8873b7ffe20b2aa9723424abaabc75cdc6d2a756..9f77b0bf750c986555de55fcce2a2f3fdae43bbc 100644
--- a/src/node.cc
+++ b/src/node.cc
@@ -50,6 +50,10 @@ void
 Node::append(std::shared_ptr<Node> &node)
 {
 
+  if (!isFile && node->constraints.pos_restriction == PositionRestrictions::OnlyFirst)
+    {
+      throw NodeAttachException(node, errmsg_node_not_in_first_position);
+    }
   Debug(CDO_NODE, "appending  %s to %s", node->oper, oper);
   if (isFile && !isOutFile && node->isFile) { throw NodeAttachException(node, errmsg_node_file_to_file); }
   if (isOutFile && is_done()) { throw NodeAttachException(node, errmsg_node_unassigned); }
diff --git a/src/node.h b/src/node.h
index e3b49aab1d2f26d7b52624a3647dc499ec399a64..31e60d693351e35b8ca3f3afb6d1bd7bea1f2b11 100644
--- a/src/node.h
+++ b/src/node.h
@@ -12,6 +12,7 @@ static std::string errmsg_node_no_output = "Operator has no output, cannot be us
 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";
+static std::string errmsg_node_not_in_first_position = "This operator can't be combined with other operators!";
 
 class Node
 {
@@ -25,6 +26,10 @@ public:
   Node(std::vector<std::string>::const_iterator p_iter, const std::string &p_operName, const std::string &p_args,
        module_constraints p_constraints);
   Node(std::vector<std::string>::const_iterator p_iter, bool p_isOutFile);
+  Node(const std::string &p_operName, const std::string &p_args, module_constraints p_constraints)
+      : oper(p_operName), arguments(p_args), constraints(p_constraints)
+  {
+  }
   explicit Node(Node *p_nodePtr);
   std::shared_ptr<Node> copy();
 
diff --git a/src/oper_args.cc b/src/oper_args.cc
new file mode 100644
index 0000000000000000000000000000000000000000..071823eeead3572502646d4e545e699500c1145d
--- /dev/null
+++ b/src/oper_args.cc
@@ -0,0 +1,99 @@
+#include <string>
+#include <any>
+#include <functional>
+
+#include "oper_args.h"
+
+bool
+ArgumentHandler::check(std::string key)
+{
+  if (arguments.handlers.find(key) == arguments.handlers.end())
+    {
+      cdo_abort("Unkown Option %s", key);
+      return false;
+    }
+  if (keyValuePairs.find(key) == keyValuePairs.end())
+    {
+      if (arguments.handlers[key].required) { cdo_abort("Argument >%s< is required!", key); }
+      return false;
+    };
+  return true;
+}
+
+int
+ArgumentHandler::parse(const std::vector<std::string> &argv)
+{
+  /*  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 (const std::string &arg : argv)
+    {
+      auto current = keyValuePairs.end();
+      equalPos = arg.find('=');
+      if (equalPos != std::string::npos)
+        {
+          auto key = arg.substr(0, equalPos);
+          auto success = found_keys.insert(key);
+          auto current_arg = arguments.handlers[key];
+
+          for (auto &ew : current_arg.exclusive_with)
+            {
+              std::cout << key << " is exclsive with: " << ew << std::endl;
+              if (found_keys.find(ew) != found_keys.end())
+                {
+                  cdo_abort("%s can not be combined with any of %s", key, cdo_argv_to_string(current_arg.exclusive_with));
+                }
+            }
+
+          if (success.second == false) { cdo_abort("Error while creating argument parser: duplicate key <%s>", key); }
+          keyValuePairs[key] = {};
+          current = keyValuePairs.find(key);
+
+          std::string value = Util::String::trim(arg.substr(equalPos + 1));
+          if (value.empty()) { cdo_abort("%s has no value", arg); }
+          {  // value vector gets moved at the end of scope
+
+            if (!value.empty()) { current->second.push_back(value); }
+          }
+        }
+      else { current->second.push_back(arg); }
+    }
+
+  return 0;
+}
+
+OperArg
+optional(const std::string &key, std::function<std::any(const std::string)> p_func, std::string mut_exclusive)
+{
+  return OperArg(key, p_func, { mut_exclusive });
+}
+
+OperArg
+optional(const std::string &key, std::function<std::any(const std::string)> p_func, const std::vector<std::string> &mut_exclusive)
+{
+  return OperArg(key, p_func, mut_exclusive);
+}
+
+OperArg
+required(const std::string &key, std::function<std::any(const std::string)> p_func, std::string mut_exclusive)
+{
+  auto operarg = OperArg(key, p_func, { mut_exclusive });
+  operarg.required = true;
+  return operarg;
+}
+
+OperArg
+required(const std::string &key, std::function<std::any(const std::string)> p_func, const std::vector<std::string> &mut_exclusive)
+{
+  auto operarg = OperArg(key, p_func, mut_exclusive);
+  operarg.required = true;
+  return operarg;
+}
diff --git a/src/oper_args.h b/src/oper_args.h
new file mode 100644
index 0000000000000000000000000000000000000000..0ca2acd2a1257f2ddfd40346bd57675c65c25060
--- /dev/null
+++ b/src/oper_args.h
@@ -0,0 +1,106 @@
+#ifndef OPER_ARGS_H
+#define OPER_ARGS_H
+#include <string>
+#include <map>
+#include <any>
+#include <set>
+#include <functional>
+
+#include "cdo_output.h"
+#include "util_string.h"
+
+class OperArg
+{
+public:
+  OperArg(const std::string &p_key, std::function<std::any(const std::string)> p_func,
+          const std::vector<std::string> &p_exclusive_with)
+      : key(p_key), func([p_func](const std::string l_key) { return p_func(l_key); }), exclusive_with(p_exclusive_with)
+  {
+  }
+
+  const std::string key;
+  bool required = false;
+  std::function<std::any(const std::string)> func;
+  std::vector<std::string> exclusive_with;
+  OperArg(){};
+};
+
+struct Arguments
+{
+  std::map<std::string, OperArg> handlers;
+  template <typename... OperArgTypes>
+  Arguments(std::initializer_list<OperArg> &&a_args)
+  {
+    for (auto &a : a_args) { handlers.emplace(a.key, std::move(a)); }
+  }
+};
+
+struct ArgumentHandler
+{
+private:
+  Arguments arguments;
+
+  std::map<const std::string, std::vector<std::string>> keyValuePairs;
+  std::set<std::string> found_keys;
+
+public:
+  ArgumentHandler(Arguments &args) : arguments(args) {}
+  ArgumentHandler(const Arguments &args) : arguments(args) {}
+  ArgumentHandler() : arguments({}) {}
+
+  bool check(std::string key);
+  int parse(const std::vector<std::string> &argv);
+
+  // Templates
+  template <typename T>
+  bool
+  get(const std::string &key, T &value)
+  {
+
+    Debug(false, "getting arg %s", key);
+    if (not check(key)) return false;
+    std::any return_val = arguments.handlers[key].func(keyValuePairs[key][0]);
+    try
+      {
+        value = std::any_cast<T>(return_val);
+      }
+    catch (std::bad_any_cast &e)
+      {
+        cdo_abort("Mismatch while getting argument for %s: requested type was %s, actual type was %s", key, typeid(T).name(),
+                  return_val.type().name());
+      }
+    return true;
+  }
+
+  template <class T, template <class, class Allocator = std::allocator<T>> class V>
+  bool
+  get(const std::string &key, V<T> &values)
+  {
+
+    Debug(false, "getting arg %s", key);
+    if (not check(key)) return false;
+    for (auto &s : keyValuePairs[key])
+      {
+        T val;
+        std::any return_val = arguments.handlers[key].func(s);
+        try
+          {
+            val = std::any_cast<T>(return_val);
+          }
+        catch (std::bad_any_cast &e)
+          {
+            cdo_abort("Mismatch while getting argument for %s: requested type was %s, actual type was %s", key, typeid(T).name(),
+                      return_val.type().name());
+          }
+        values.push_back(val);
+      }
+    return true;
+  }
+};
+
+OperArg optional(std::string key, std::function<std::any(const std::string)> p_func, std::string mut_exclusive);
+OperArg optional(std::string key, std::function<std::any(const std::string)> p_func, std::vector<std::string> mut_exclusive = {});
+OperArg required(std::string key, std::function<std::any(const std::string)> p_func, std::string mut_exclusive);
+OperArg required(std::string key, std::function<std::any(const std::string)> p_func, std::vector<std::string> mut_exclusive = {});
+
+#endif
diff --git a/src/operator_help.cc b/src/operator_help.cc
index ef680ef478e4e58058f44ab6e0768d8d31b97b5b..51f56a88c4fcd580a876bd1e1e66450f987cfbd7 100644
--- a/src/operator_help.cc
+++ b/src/operator_help.cc
@@ -99,7 +99,7 @@ const CdoHelp DiffHelp = {
     "    diff, diffn - Compare two datasets field by field",
     "",
     "SYNOPSIS",
-    "    <operator>[,options]  infile1 infile2",
+    "    <operator>[,parameter]  infile1 infile2",
     "",
     "DESCRIPTION",
     "    Compares the contents of two datasets field by field. The input datasets need",
@@ -128,7 +128,6 @@ const CdoHelp DiffHelp = {
     "    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 = {
@@ -431,7 +430,8 @@ const CdoHelp MergeHelp = {
     "    merge, mergetime - Merge datasets",
     "",
     "SYNOPSIS",
-    "    <operator>  infiles outfile",
+    "    merge  infiles outfile",
+    "    mergetime[,options]  infiles outfile",
     "",
     "DESCRIPTION",
     "    This module reads datasets from several input files, merges them and writes the resulting dataset to outfile.",
@@ -451,9 +451,9 @@ const CdoHelp MergeHelp = {
     "               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.",
+    "PARAMETER",
+    "    skip_same_time  BOOL    Skips all consecutive timesteps with a double entry of the same timestamp.",
+    "    names           STRING  Fill missing variable names with missing values (union) or use the intersection (intersect).",
     "",
     "NOTE",
     "    Operators of this module need to open all input files simultaneously.",
@@ -980,8 +980,20 @@ const CdoHelp SelyearidxHelp = {
     "    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.",
+    "    Selects field elements from infile2 according to a year index from infile1.",
+    "    The index of the year in infile1 should be the result of corresponding yearminidx or yearmaxidx operations, respectively.",
+};
+
+const CdoHelp SeltimeidxHelp = {
+    "NAME",
+    "    seltimeidx - Select timestep by index",
+    "",
+    "SYNOPSIS",
+    "    seltimeidx  infile1 infile2 outfile",
+    "",
+    "DESCRIPTION",
+    "    Selects field elements from infile2 according to a timestep index from infile1.",
+    "    The index of the timestep in infile1 should be the result of corresponding timminidx or timmaxidx operations, respectively.",
 };
 
 const CdoHelp SelsurfaceHelp = {
@@ -1465,47 +1477,53 @@ const CdoHelp ChangeHelp = {
 
 const CdoHelp SetgridHelp = {
     "NAME",
-    "    setgrid, setgridtype, setgridarea, setgridmask - Set grid information",
+    "    setgrid, setgridtype, setgridarea, setgridmask, setprojparams - ",
+    "    Set grid information",
     "",
     "SYNOPSIS",
     "    setgrid,grid  infile outfile",
     "    setgridtype,gridtype  infile outfile",
     "    setgridarea,gridarea  infile outfile",
     "    setgridmask,gridmask  infile outfile",
+    "    setprojparams,projparams  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",
+    "    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.",
+    "    setprojparams  Set proj params",
+    "                   Sets the proj_params attribute of a projection. This attribute is used to compute",
+    "                   geographic coordinates of a projecton with the proj library.",
+    "",
+    "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",
+    "    projparams  STRING  Proj library parameter (e.g.:+init=EPSG:3413)",
 };
 
 const CdoHelp SetzaxisHelp = {
@@ -1822,24 +1840,24 @@ const CdoHelp ExprHelp = {
     "    ",
     "    The following operators are supported:",
     "    ",
-    "     Operator   & Meaning             & Example   & Result ",
+    "     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 ",
+    "         -      & 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 ",
+    "         ^      & 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 ",
+    "         <=>    & 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 ",
+    "         ?:     & ternary conditional & x ? y : z & y, if x not equal 0, else z",
     "    ",
     "    The following functions are supported:",
     "    ",
@@ -1870,7 +1888,7 @@ const CdoHelp ExprHelp = {
     "    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 ",
+    "    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",
@@ -1878,7 +1896,7 @@ const CdoHelp ExprHelp = {
     "    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 ",
+    "    atan2(x,y)  "    "    Arc tangent function of y/x, using signs to determine quadrants",
     "    ",
     "    Coordinates:",
     "    ",
@@ -1907,17 +1925,17 @@ const CdoHelp ExprHelp = {
     "    size(x)   "    "    Total number of elements (ngp(x)*nlev(x))",
     "    missval(x)"    "    Returns the missing value of variable x",
     "    ",
-    "    Statistical values over a field:",
+    "    Statistics 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:",
+    "    Zonal statistics 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:",
+    "    Vertical statistics:",
     "    ",
     "    vertmin(x), vertmax(x), vertrange(x), vertsum(x), vertmean(x), vertavg(x), vertstd(x), vertstd1(x),",
     "    vertvar(x), vertvar1(x)",
@@ -2612,7 +2630,7 @@ const CdoHelp FldstatHelp = {
 const CdoHelp ZonstatHelp = {
     "NAME",
     "    zonmin, zonmax, zonrange, zonsum, zonmean, zonavg, zonstd, zonstd1, zonvar, ",
-    "    zonvar1, zonskew, zonkurt, zonmedian, zonpctl - Zonal statistical values",
+    "    zonvar1, zonskew, zonkurt, zonmedian, zonpctl - Zonal statistics",
     "",
     "SYNOPSIS",
     "    <operator>  infile outfile",
@@ -2644,7 +2662,7 @@ const CdoHelp ZonstatHelp = {
     "    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). ",
+    "               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)",
@@ -2666,7 +2684,7 @@ const CdoHelp ZonstatHelp = {
 const CdoHelp MerstatHelp = {
     "NAME",
     "    mermin, mermax, merrange, mersum, mermean, meravg, merstd, merstd1, mervar, ",
-    "    mervar1, merskew, merkurt, mermedian, merpctl - Meridional statistical values",
+    "    mervar1, merskew, merkurt, mermedian, merpctl - Meridional statistics",
     "",
     "SYNOPSIS",
     "    <operator>  infile outfile",
@@ -2812,7 +2830,7 @@ const CdoHelp RemapstatHelp = {
 const CdoHelp VertstatHelp = {
     "NAME",
     "    vertmin, vertmax, vertrange, vertsum, vertmean, vertavg, vertstd, vertstd1, ",
-    "    vertvar, vertvar1 - Vertical statistical values",
+    "    vertvar, vertvar1 - Vertical statistics",
     "",
     "SYNOPSIS",
     "    <operator>,weights  infile outfile",
@@ -2851,14 +2869,14 @@ const CdoHelp VertstatHelp = {
 const CdoHelp TimselstatHelp = {
     "NAME",
     "    timselmin, timselmax, timselrange, timselsum, timselmean, timselavg, ",
-    "    timselstd, timselstd1, timselvar, timselvar1 - Time range statistical values",
+    "    timselstd, timselstd1, timselvar, timselvar1 - Time range statistics",
     "",
     "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 ",
+    "    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>.",
@@ -2906,7 +2924,7 @@ const CdoHelp TimselstatHelp = {
     "                 o(t,x) = var1{i(t',x), t1 < t' <= tn}",
     "",
     "PARAMETER",
-    "    nsets    INTEGER  Number of input timesteps for each output timestep ",
+    "    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)",
 };
@@ -2944,15 +2962,15 @@ const CdoHelp TimselpctlHelp = {
 const CdoHelp RunstatHelp = {
     "NAME",
     "    runmin, runmax, runrange, runsum, runmean, runavg, runstd, runstd1, runvar, ",
-    "    runvar1 - Running statistical values",
+    "    runvar1 - Running statistics",
     "",
     "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. ",
+    "    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>.",
     "",
@@ -2970,19 +2988,19 @@ const CdoHelp RunstatHelp = {
     "    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. ",
+    "              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). ",
+    "              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. ",
+    "              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). ",
+    "              Normalize by (n-1).",
     "              ",
     "              o(t+(nts-1)/2,x) = var1{i(t,x), i(t+1,x), ..., i(t+nts-1,x)}",
     "",
@@ -3015,8 +3033,8 @@ const CdoHelp RunpctlHelp = {
 
 const CdoHelp TimstatHelp = {
     "NAME",
-    "    timmin, timmax, timrange, timsum, timmean, timavg, timstd, timstd1, timvar, ",
-    "    timvar1 - Statistical values over all timesteps",
+    "    timmin, timmax, timminidx, timmaxidx, timrange, timsum, timmean, timavg, ",
+    "    timstd, timstd1, timvar, timvar1 - Statistical values over all timesteps",
     "",
     "SYNOPSIS",
     "    <operator>  infile outfile",
@@ -3029,34 +3047,38 @@ const CdoHelp TimstatHelp = {
     "    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}",
+    "    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}",
+    "    timminidx  Index of time minimum",
+    "               o(1,x) = minidx{i(t',x), t_1<t'<=t_n}",
+    "    timmaxidx  Index of time maximum",
+    "               o(1,x) = maxidx{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 = {
@@ -3088,7 +3110,7 @@ const CdoHelp TimpctlHelp = {
 const CdoHelp HourstatHelp = {
     "NAME",
     "    hourmin, hourmax, hourrange, hoursum, hourmean, houravg, hourstd, hourstd1, ",
-    "    hourvar, hourvar1 - Hourly statistical values",
+    "    hourvar, hourvar1 - Hourly statistics",
     "",
     "SYNOPSIS",
     "    <operator>  infile outfile",
@@ -3174,10 +3196,10 @@ const CdoHelp HourpctlHelp = {
 const CdoHelp DaystatHelp = {
     "NAME",
     "    daymin, daymax, dayrange, daysum, daymean, dayavg, daystd, daystd1, dayvar, ",
-    "    dayvar1 - Daily statistical values",
+    "    dayvar1 - Daily statistics",
     "",
     "SYNOPSIS",
-    "    <operator>  infile outfile",
+    "    <operator>[,parameter]  infile outfile",
     "",
     "DESCRIPTION",
     "    This module computes statistical values over timesteps of the same day.",
@@ -3227,6 +3249,9 @@ const CdoHelp DaystatHelp = {
     "              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}",
+    "",
+    "PARAMETER",
+    "    complete_only  BOOL Process only complete years",
 };
 
 const CdoHelp DaypctlHelp = {
@@ -3260,10 +3285,10 @@ const CdoHelp DaypctlHelp = {
 const CdoHelp MonstatHelp = {
     "NAME",
     "    monmin, monmax, monrange, monsum, monmean, monavg, monstd, monstd1, monvar, ",
-    "    monvar1 - Monthly statistical values",
+    "    monvar1 - Monthly statistics",
     "",
     "SYNOPSIS",
-    "    <operator>  infile outfile",
+    "    <operator>[,parameter]  infile outfile",
     "",
     "DESCRIPTION",
     "    This module computes statistical values over timesteps of the same month.",
@@ -3313,6 +3338,9 @@ const CdoHelp MonstatHelp = {
     "              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}",
+    "",
+    "PARAMETER",
+    "    complete_only  BOOL Process only complete years",
 };
 
 const CdoHelp MonpctlHelp = {
@@ -3367,10 +3395,10 @@ const CdoHelp YearmonstatHelp = {
 const CdoHelp YearstatHelp = {
     "NAME",
     "    yearmin, yearmax, yearminidx, yearmaxidx, yearrange, yearsum, yearmean, ",
-    "    yearavg, yearstd, yearstd1, yearvar, yearvar1 - Yearly statistical values",
+    "    yearavg, yearstd, yearstd1, yearvar, yearvar1 - Yearly statistics",
     "",
     "SYNOPSIS",
-    "    <operator>  infile outfile",
+    "    <operator>[,parameter]  infile outfile",
     "",
     "DESCRIPTION",
     "    This module computes statistical values over timesteps of the same year.",
@@ -3388,11 +3416,11 @@ const CdoHelp YearstatHelp = {
     "                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",
+    "    yearminidx  Index of yearly minimum",
     "                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",
+    "    yearmaxidx  Index of yearly maximum",
     "                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}",
@@ -3429,6 +3457,9 @@ const CdoHelp YearstatHelp = {
     "                ",
     "                o(t,x) = var1{i(t',x), t_1 < t' <= t_n}",
     "",
+    "PARAMETER",
+    "    complete_only  BOOL Process only complete years",
+    "",
     "NOTE",
     "    The operators yearmean and yearavg compute only arithmetical means!",
 };
@@ -3463,7 +3494,7 @@ const CdoHelp YearpctlHelp = {
 const CdoHelp SeasstatHelp = {
     "NAME",
     "    seasmin, seasmax, seasrange, seassum, seasmean, seasavg, seasstd, seasstd1, ",
-    "    seasvar, seasvar1 - Seasonal statistical values",
+    "    seasvar, seasvar1 - Seasonal statistics",
     "",
     "SYNOPSIS",
     "    <operator>  infile outfile",
@@ -3474,7 +3505,7 @@ const CdoHelp SeasstatHelp = {
     "    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 ",
+    "    Be careful about the first and the last output timestep, they may be incorrect values",
     "    if the seasons have incomplete timesteps.",
     "",
     "OPERATORS",
@@ -3552,7 +3583,7 @@ const CdoHelp SeaspctlHelp = {
 const CdoHelp YhourstatHelp = {
     "NAME",
     "    yhourmin, yhourmax, yhourrange, yhoursum, yhourmean, yhouravg, yhourstd, ",
-    "    yhourstd1, yhourvar, yhourvar1 - Multi-year hourly statistical values",
+    "    yhourstd1, yhourvar, yhourvar1 - Multi-year hourly statistics",
     "",
     "SYNOPSIS",
     "    <operator>  infile outfile",
@@ -3589,25 +3620,25 @@ const CdoHelp YhourstatHelp = {
     "                                 ...",
     "                o(8784,x) = avg{i(t,x), day(i(t)) = 8784}",
     "    yhourstd    Multi-year hourly standard deviation",
-    "                Normalize by n. ",
+    "                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). ",
+    "                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. ",
+    "                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). ",
+    "                Normalize by (n-1).",
     "                ",
     "                o(0001,x) = var1{i(t,x), day(i(t)) = 0001}",
     "                                 ...",
@@ -3617,7 +3648,7 @@ const CdoHelp YhourstatHelp = {
 const CdoHelp DhourstatHelp = {
     "NAME",
     "    dhourmin, dhourmax, dhourrange, dhoursum, dhourmean, dhouravg, dhourstd, ",
-    "    dhourstd1, dhourvar, dhourvar1 - Multi-day hourly statistical values",
+    "    dhourstd1, dhourvar, dhourvar1 - Multi-day hourly statistics",
     "",
     "SYNOPSIS",
     "    <operator>  infile outfile",
@@ -3654,35 +3685,101 @@ const CdoHelp DhourstatHelp = {
     "                                 ...",
     "                o(24,x) = avg{i(t,x), day(i(t)) = 24}",
     "    dhourstd    Multi-day hourly standard deviation",
-    "                Normalize by n. ",
+    "                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). ",
+    "                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. ",
+    "                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). ",
+    "                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 DminutestatHelp = {
+    "NAME",
+    "    dminutemin, dminutemax, dminuterange, dminutesum, dminutemean, dminuteavg, ",
+    "    dminutestd, dminutestd1, dminutevar, dminutevar1 - ",
+    "    Multi-day by the minute statistics",
+    "",
+    "SYNOPSIS",
+    "    <operator>  infile outfile",
+    "",
+    "DESCRIPTION",
+    "    This module computes statistical values of each minute of day.",
+    "    Depending on the chosen operator the minimum, maximum, range, sum, average, variance",
+    "    or standard deviation of each minute 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",
+    "    dminutemin    Multi-day by the minute minimum",
+    "                  o(01,x) = min{i(t,x), day(i(t)) = 01}",
+    "                                   ...",
+    "                  o(1440,x) = min{i(t,x), day(i(t)) = 1440}",
+    "    dminutemax    Multi-day by the minute maximum",
+    "                  o(01,x) = max{i(t,x), day(i(t)) = 01}",
+    "                                   ...",
+    "                  o(1440,x) = max{i(t,x), day(i(t)) = 1440}",
+    "    dminuterange  Multi-day by the minute range",
+    "                  o(01,x) = range{i(t,x), day(i(t)) = 01}",
+    "                                   ...",
+    "                  o(1440,x) = range{i(t,x), day(i(t)) = 1440}",
+    "    dminutesum    Multi-day by the minute sum",
+    "                  o(01,x) = sum{i(t,x), day(i(t)) = 01}",
+    "                                   ...",
+    "                  o(1440,x) = sum{i(t,x), day(i(t)) = 1440}",
+    "    dminutemean   Multi-day by the minute mean",
+    "                  o(01,x) = mean{i(t,x), day(i(t)) = 01}",
+    "                                   ...",
+    "                  o(1440,x) = mean{i(t,x), day(i(t)) = 1440}",
+    "    dminuteavg    Multi-day by the minute average",
+    "                  o(01,x) = avg{i(t,x), day(i(t)) = 01}",
+    "                                   ...",
+    "                  o(1440,x) = avg{i(t,x), day(i(t)) = 1440}",
+    "    dminutestd    Multi-day by the minute standard deviation",
+    "                  Normalize by n.",
+    "                  ",
+    "                  o(01,x) = std{i(t,x), day(i(t)) = 01}",
+    "                                   ...",
+    "                  o(1440,x) = std{i(t,x), day(i(t)) = 1440}",
+    "    dminutestd1   Multi-day by the minute standard deviation (n-1)",
+    "                  Normalize by (n-1).",
+    "                  ",
+    "                  o(01,x) = std1{i(t,x), day(i(t)) = 01}",
+    "                                   ...",
+    "                  o(1440,x) = std1{i(t,x), day(i(t)) = 1440}",
+    "    dminutevar    Multi-day by the minute variance",
+    "                  Normalize by n.",
+    "                  ",
+    "                  o(01,x) = var{i(t,x), day(i(t)) = 01}",
+    "                                   ...",
+    "                  o(1440,x) = var{i(t,x), day(i(t)) = 1440}",
+    "    dminutevar1   Multi-day by the minute variance (n-1)",
+    "                  Normalize by (n-1).",
+    "                  ",
+    "                  o(01,x) = var1{i(t,x), day(i(t)) = 01}",
+    "                                   ...",
+    "                  o(1440,x) = var1{i(t,x), day(i(t)) = 1440}",
+};
+
 const CdoHelp YdaystatHelp = {
     "NAME",
     "    ydaymin, ydaymax, ydayrange, ydaysum, ydaymean, ydayavg, ydaystd, ydaystd1, ",
-    "    ydayvar, ydayvar1 - Multi-year daily statistical values",
+    "    ydayvar, ydayvar1 - Multi-year daily statistics",
     "",
     "SYNOPSIS",
     "    <operator>  infile outfile",
@@ -3719,25 +3816,25 @@ const CdoHelp YdaystatHelp = {
     "                                ...",
     "               o(366,x) = avg{i(t,x), day(i(t)) = 366}",
     "    ydaystd    Multi-year daily standard deviation",
-    "               Normalize by n. ",
+    "               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). ",
+    "               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. ",
+    "               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). ",
+    "               Normalize by (n-1).",
     "               ",
     "               o(001,x) = var1{i(t,x), day(i(t)) = 001}",
     "                                ...",
@@ -3775,7 +3872,7 @@ const CdoHelp YdaypctlHelp = {
 const CdoHelp YmonstatHelp = {
     "NAME",
     "    ymonmin, ymonmax, ymonrange, ymonsum, ymonmean, ymonavg, ymonstd, ymonstd1, ",
-    "    ymonvar, ymonvar1 - Multi-year monthly statistical values",
+    "    ymonvar, ymonvar1 - Multi-year monthly statistics",
     "",
     "SYNOPSIS",
     "    <operator>  infile outfile",
@@ -3813,25 +3910,25 @@ const CdoHelp YmonstatHelp = {
     "                                ...",
     "               o(12,x) = avg{i(t,x), month(i(t)) = 12}",
     "    ymonstd    Multi-year monthly standard deviation",
-    "               Normalize by n. ",
+    "               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). ",
+    "               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. ",
+    "               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). ",
+    "               Normalize by (n-1).",
     "               ",
     "               o(01,x) = var1{i(t,x), month(i(t)) = 01}",
     "                                ...",
@@ -3871,7 +3968,7 @@ const CdoHelp YmonpctlHelp = {
 const CdoHelp YseasstatHelp = {
     "NAME",
     "    yseasmin, yseasmax, yseasrange, yseassum, yseasmean, yseasavg, yseasstd, ",
-    "    yseasstd1, yseasvar, yseasvar1 - Multi-year seasonal statistical values",
+    "    yseasstd1, yseasvar, yseasvar1 - Multi-year seasonal statistics",
     "",
     "SYNOPSIS",
     "    <operator>  infile outfile",
@@ -3969,22 +4066,22 @@ const CdoHelp YseaspctlHelp = {
 const CdoHelp YdrunstatHelp = {
     "NAME",
     "    ydrunmin, ydrunmax, ydrunsum, ydrunmean, ydrunavg, ydrunstd, ydrunstd1, ",
-    "    ydrunvar, ydrunvar1 - Multi-year daily running statistical values",
+    "    ydrunvar, ydrunvar1 - Multi-year daily running statistics",
     "",
     "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 ",
+    "    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 ",
+    "    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 ",
+    "    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 ",
+    "    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",
@@ -4009,25 +4106,25 @@ const CdoHelp YdrunstatHelp = {
     "                                ...",
     "               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. ",
+    "               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). ",
+    "               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. ",
+    "               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). ",
+    "               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}",
     "                                ...",
@@ -4351,7 +4448,7 @@ const CdoHelp RemapbilHelp = {
     "              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.",
+    "              Set the parameter map3d=true to generate all mapfiles of the first 3D field with varying masks.",
     "              In this case the mapfiles will be named <outfile><xxx>.nc. xxx will have five digits with the number of the mapfile.",
     "",
     "PARAMETER",
@@ -4385,7 +4482,7 @@ const CdoHelp RemapbicHelp = {
     "              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.",
+    "              Set the parameter map3d=true to generate all mapfiles of the first 3D field with varying masks.",
     "              In this case the mapfiles will be named <outfile><xxx>.nc. xxx will have five digits with the number of the mapfile.",
     "",
     "PARAMETER",
@@ -4417,7 +4514,7 @@ const CdoHelp RemapnnHelp = {
     "             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.",
+    "             Set the parameter map3d=true to generate all mapfiles of the first 3D field with varying masks.",
     "             In this case the mapfiles will be named <outfile><xxx>.nc. xxx will have five digits with the number of the mapfile.",
     "",
     "PARAMETER",
@@ -4452,7 +4549,7 @@ const CdoHelp RemapdisHelp = {
     "              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.",
+    "              Set the parameter map3d=true to generate all mapfiles of the first 3D field with varying masks.",
     "              In this case the mapfiles will be named <outfile><xxx>.nc. xxx will have five digits with the number of the mapfile.",
     "",
     "PARAMETER",
@@ -4490,7 +4587,7 @@ const CdoHelp RemapconHelp = {
     "              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.",
+    "              Set the parameter map3d=true to generate all mapfiles of the first 3D field with varying masks.",
     "              In this case the mapfiles will be named <outfile><xxx>.nc. xxx will have five digits with the number of the mapfile.",
     "",
     "PARAMETER",
@@ -4746,9 +4843,9 @@ const CdoHelp IntlevelHelp = {
     "    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",
+    "    level         FLOAT   Comma-separated list of target levels",
+    "    zdescription  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 = {
@@ -5535,7 +5632,7 @@ const CdoHelp VargenHelp = {
     "            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:",
+    "            The formulas 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)",
diff --git a/src/operator_help.h b/src/operator_help.h
index 1815b95ff6ea1601a2be16c0e123ab0251fc9e1f..65bf3fe05ba576b1458c306fa05cae92c1378d36 100644
--- a/src/operator_help.h
+++ b/src/operator_help.h
@@ -41,6 +41,7 @@ extern const CdoHelp SelregionHelp;
 extern const CdoHelp SelgridcellHelp;
 extern const CdoHelp SamplegridHelp;
 extern const CdoHelp SelyearidxHelp;
+extern const CdoHelp SeltimeidxHelp;
 extern const CdoHelp SelsurfaceHelp;
 extern const CdoHelp CondHelp;
 extern const CdoHelp Cond2Help;
@@ -111,6 +112,7 @@ extern const CdoHelp SeasstatHelp;
 extern const CdoHelp SeaspctlHelp;
 extern const CdoHelp YhourstatHelp;
 extern const CdoHelp DhourstatHelp;
+extern const CdoHelp DminutestatHelp;
 extern const CdoHelp YdaystatHelp;
 extern const CdoHelp YdaypctlHelp;
 extern const CdoHelp YmonstatHelp;
diff --git a/src/param_conversion.cc b/src/param_conversion.cc
index c648c90cf2ca832fb51a3633ea88b730c0e3741a..933a7bb92619c41fd97a2fc32686b98855d9e6eb 100644
--- a/src/param_conversion.cc
+++ b/src/param_conversion.cc
@@ -20,6 +20,7 @@
 #include "util_string.h"
 #include "constants.h"
 #include "const.h"
+#include "fill_1d.h"
 
 const char *
 parameter_to_word(const char *cstring)
@@ -260,7 +261,7 @@ param_to_string(int param)
 
   if (len >= maxlen || len < 0) cdo_abort("Internal problem (%s): size of input string is too small!", __func__);
 
-  return std::string{paramstr};
+  return std::string{ paramstr };
 }
 
 /* time/date/season converisons */
@@ -429,3 +430,56 @@ split_intstring(const std::string &intstr, int &first, int &last, int &inc)
 {
   split_intstring(intstr.c_str(), first, last, inc);
 }
+
+template <>
+int
+convert(const std::string &str_value)
+{
+  return parameter_to_int(str_value.c_str());
+}
+
+template <>
+double
+convert(const std::string &str_value)
+{
+  return parameter_to_double(str_value.c_str());
+}
+
+template <>
+bool
+convert(const std::string &str_value)
+{
+  return parameter_to_bool(str_value);
+}
+
+template <>
+long
+convert(const std::string &str_value)
+{
+  return parameter_to_long(str_value.c_str());
+}
+
+template <>
+size_t
+convert(const std::string &str_value)
+{
+  return parameter_to_size_t(str_value.c_str());
+}
+
+template <>
+std::string
+convert(const std::string &str_value)
+{
+  return parameter_to_word(str_value.c_str());
+}
+
+template <>
+FillMethod
+convert(const std::string &str_value)
+{
+  auto fillMethod = string_to_fillmethod(parameter_to_word(str_value));
+
+  if (fillMethod == FillMethod::Undefined) cdo_abort("method=%s unsupported!", fillMethod);
+
+  return fillMethod;
+}
diff --git a/src/param_conversion.h b/src/param_conversion.h
index d4ff737678a93f15c5da1568ceb3e6c7f5eece8c..493ebf30321eab5c9d4bd1baabfd76bfaf930c8a 100644
--- a/src/param_conversion.h
+++ b/src/param_conversion.h
@@ -38,4 +38,7 @@ std::vector<double> cdo_argv_to_flt(const std::vector<std::string> &argv);
 
 void split_intstring(const std::string &intstr, int &first, int &last, int &inc);
 
+template <typename T>
+T convert(const std::string &str_value);
+
 #endif
diff --git a/src/parser.cc b/src/parser.cc
index ed2eb17f247d8d9beaca00d2cbb6b1a2c72631d1..9ac8930d56457c88ff9ea28283d73900801d9fff 100644
--- a/src/parser.cc
+++ b/src/parser.cc
@@ -1,4 +1,3 @@
-#include <string>
 #include <map>
 #include <set>
 #include <iostream>
@@ -97,49 +96,6 @@ append(std::shared_ptr<T> &parent, std::shared_ptr<T> &child)
 
 // Factory Funcs!
 
-std::string
-err_msg_oper_not_found(const std::string &operatorName)
-{
-  // 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>
@@ -153,13 +109,11 @@ create_operator_node(ARGV_ITERATOR &p_curentArgument, bool is_first = false)
   Util::extract_name_and_argument(*p_curentArgument, operatorName, operatorArguments);
 
   auto moduleIterator = Factory::find(
-      operatorName, [&]() { THROW(InternalCdoSyntaxError, p_curentArgument, err_msg_oper_not_found(operatorName)); });
+      operatorName, [&]() {
+      THROW(InternalCdoSyntaxError, p_curentArgument, Factory::err_msg_oper_not_found(operatorName));
+      });
 
   auto mod = Factory::get_module(moduleIterator);
-  if (!is_first && mod.constraints.pos_restriction == PositionRestrictions::OnlyFirst)
-    {
-      THROW(InternalCdoSyntaxError, p_curentArgument, errmsg_not_in_first_position);
-    }
   auto newNode = std::make_shared<Node>(p_curentArgument, operatorName, operatorArguments, mod.constraints);
   return newNode;
 }
@@ -234,6 +188,7 @@ public:
   pop()
   {
     const auto &node = stack.back();
+    debug_parser("poping node: %s", node->oper);
     if (node->constraints.streamInCnt == -1) { cntVariableInputs--; }
     debug_parser("poping node: %s", node->oper);
     stack.pop_back();
@@ -314,6 +269,7 @@ handle_node(Parser &parser, ARGV_ITERATOR &cur_arg)
     }
   parser.push(node);
 
+  debug_parser("removing finished from stack");
   while (!parser.empty() && parser.top()->is_done()) { parser.pop(); }
 }
 /*  triggered on ':' */
@@ -421,8 +377,10 @@ handle_sub_group(ARGV_ITERATOR &p_cur_arg, PARSER_STACK &stack)
 bool
 has_required_inputs(std::shared_ptr<Node> &node)
 {
+  debug_parser("Checking required inputs");
   bool variable_done = (node->constraints.streamInCnt == -1 && node->children.size() >= 1);
   bool node_done = node->is_done() || variable_done;
+
   bool has_required_inputs = !node->isFile && !node_done;
   return has_required_inputs;
 }
@@ -563,8 +521,10 @@ _parse(std::vector<std::string> p_argv)
     }
   catch (InternalCdoSyntaxError &e)
     {
+      Debug("Error");
       throw CdoSyntaxError(e, p_argv);
     }
+  Debug("we got here");
   return res;
 }
 
diff --git a/src/parser.h b/src/parser.h
index 94e4d69f0e8813d89ecbb0ec385b87c18da47aaf..20d215fdeb4e67b59ac301063fd481a229b3674e 100644
--- a/src/parser.h
+++ b/src/parser.h
@@ -53,7 +53,6 @@ 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_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
 static std::string errmsg_mixed_input = "Mixing of normal inputs and subgroups is not allowed";
@@ -70,8 +69,6 @@ static std::string errmsg_apply_requires_bracket = "Apply requires brackets";
 static std::string errmsg_apply_no_inputs = "Apply content has no available free inputs";
 static std::string errmsg_apply_in_first_pos = "Apply can not be in first position";
 
-std::string err_msg_oper_not_found(const std::string &name);
-
 std::vector<std::shared_ptr<Node>> run(std::vector<std::string> &p_argv);
 std::vector<std::shared_ptr<Node>> parse(std::vector<std::string> p_argv, const char *(*context)(void) );
 std::vector<std::shared_ptr<Node>> _parse(std::vector<std::string> p_argv);
diff --git a/src/percentiles.cc b/src/percentiles.cc
index 195d7e21c04d5f162b7bf7f2f548d8be00a54551..12cf365a179c91ede262a892c8391fe82c424490 100644
--- a/src/percentiles.cc
+++ b/src/percentiles.cc
@@ -314,7 +314,7 @@ EOF
 #
 cat > per_cdo.sh << EOR
 #/bin/sh
-CDO=/Users/m214003/cdt/work/cdo/build/clang/src/cdo
+CDO=cdo
 PERS="30 40 50 75 100"
 METS="nrank nist rtype8 numpy numpy_lower numpy_higher numpy_nearest"
 METS="linear lower higher nearest midpoint inverted_cdf averaged_inverted_cdf closest_observation interpolated_inverted_cdf hazen
diff --git a/src/percentiles_hist.cc b/src/percentiles_hist.cc
index 37f2ec6b751ac1d83e0e9bb57e7cb0ed8961b803..2074b37436dac4780964be98c250a3d4da7314d3 100644
--- a/src/percentiles_hist.cc
+++ b/src/percentiles_hist.cc
@@ -341,7 +341,7 @@ histAddVarLevelValues(size_t nhists, std::vector<HistogramEntry> &hists, const V
   if (numMissVals)
     {
       for (size_t i = 0; i < nhists; ++i)
-        if (!dbl_is_equal(v[i], mv)) nign += histAddValue(hists[i], v[i]);
+        if (dbl_is_not_equal(v[i], mv)) nign += histAddValue(hists[i], v[i]);
     }
   else
     {
@@ -361,7 +361,7 @@ histSubVarLevelValues(size_t nhists, std::vector<HistogramEntry> &hists, const V
   if (numMissVals)
     {
       for (size_t i = 0; i < nhists; ++i)
-        if (!dbl_is_equal(v[i], mv)) nign += histSubValue(hists[i], v[i]);
+        if (dbl_is_not_equal(v[i], mv)) nign += histSubValue(hists[i], v[i]);
     }
   else
     {
diff --git a/src/pipe.cc b/src/pipe.cc
index ad78aa35fd950c099a75c233de0466e7439eb491..9534d1829f4b068a9e37a706b808ffbf792c34a8 100644
--- a/src/pipe.cc
+++ b/src/pipe.cc
@@ -143,7 +143,7 @@ pipe_t::pipe_def_timestep(int p_vlistID, int p_tsID)
     if (p_tsID != tsIDw) cdo_abort("unexpected p_tsID %d(%d) for %s", p_tsID, tsIDw, name);
 
     int numrecs = 0;
-    if (p_tsID == 0) { numrecs = vlistNrecs(p_vlistID); }
+    if (p_tsID == 0) { numrecs = vlistNumRecords(p_vlistID); }
     else
       {
         auto vlistID = p_vlistID;
diff --git a/src/pmlist.cc b/src/pmlist.cc
index b31dc5e1aba687506ac64580068db709e4928e16..e969858e49c508d99e7dacd3c367b229e4f7dd65 100644
--- a/src/pmlist.cc
+++ b/src/pmlist.cc
@@ -109,7 +109,7 @@ KVList::append(const char *key, const char *const *values, int nvalues)
 }
 
 void
-KVList::append(std::string &key, std::vector<std::string> values)
+KVList::append(std::string &key, const std::vector<std::string> &values)
 {
   KeyValues kv;
   kv.key = key;
diff --git a/src/pmlist.h b/src/pmlist.h
index d834b1c8e098fe5723e03484a5528cf520bcdf5e..b551b0fa2dad5b82d92ba26cdd2a0452463a2063 100644
--- a/src/pmlist.h
+++ b/src/pmlist.h
@@ -37,7 +37,7 @@ public:
   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, const 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 71267c7b3a4f37b9f80588a3810e74593150935f..3dac3dd9a0d0657a317e33fadedf2f7a949f4dd1 100644
--- a/src/printinfo.cc
+++ b/src/printinfo.cc
@@ -464,7 +464,7 @@ printGridInfoKernel(int gridID, int index, int lproj)
 void
 print_grid_info(int vlistID)
 {
-  auto ngrids = vlistNgrids(vlistID);
+  auto ngrids = vlistNumGrids(vlistID);
   for (int index = 0; index < ngrids; ++index)
     {
       auto gridID = vlistGrid(vlistID, index);
@@ -599,7 +599,7 @@ print_zaxis_info(int vlistID)
   auto dig = Options::CDO_flt_digits;
   char zaxisname[CDI_MAX_NAME];
 
-  auto nzaxis = vlistNzaxis(vlistID);
+  auto nzaxis = vlistNumZaxis(vlistID);
   for (int index = 0; index < nzaxis; ++index)
     {
       auto zaxisID = vlistZaxis(vlistID, index);
diff --git a/src/process.cc b/src/process.cc
index 38eb6960c12db17168e27c0a4c6beece8a0cd47a..2f91d8fb5edd5a84ae8ff0ec7235c621d37d99de 100644
--- a/src/process.cc
+++ b/src/process.cc
@@ -58,7 +58,7 @@ Process::replace_alias(const std::string &p_calledBy, const CdoModule &p_module)
 
 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)
+    : arguments(p_module.arguments), m_ID(p_ID), m_module(p_module), m_oargv(p_arguments)
 {
 #ifdef HAVE_LIBPTHREAD
   threadID = pthread_self();
@@ -306,6 +306,7 @@ Process::get_id()
 {
   return m_ID;
 }
+
 void
 Process::cdo_initialize()
 {
@@ -322,6 +323,7 @@ Process::cdo_initialize()
   Debug(PROCESS_INT, "process %d thread %ld", m_ID, pthread_self());
 #endif
 }
+
 void
 Process::cdo_finish(void)
 {
diff --git a/src/process.h b/src/process.h
index cd6d8fb24af71cad83d9b7b462edb1799cbfcb44..0abe98c026b62a0ec3d8d294544d6e763a405a47 100644
--- a/src/process.h
+++ b/src/process.h
@@ -33,6 +33,8 @@ void *execute(void *process);
 
 class Process
 {
+private:
+  ArgumentHandler arguments;
 
 public:
   /* Member Variables */
@@ -61,7 +63,6 @@ public:
 #endif
 
   /* Member Functions  */
-
   Process(int p_ID, const std::string &p_operatorName, const std::vector<std::string> &p_arguments, const CdoModule &p_module);
   virtual ~Process() {}
 
@@ -70,6 +71,18 @@ public:
   virtual void run() = 0;
   virtual void close() = 0;
 
+  void
+  parse_arguments()
+  {
+    arguments.parse(m_oargv);
+  }
+
+  template <typename T>
+  void
+  get_argument(const std::string &key, T &destination)
+  {
+    arguments.get(key, destination);
+  }
   /**
    * returns the number of in streams this process currently has.
    **/
diff --git a/src/process_int.cc b/src/process_int.cc
index 2b67fa3933fab39c50ffe70094103c9f48370c33..93b047176b37383b54a3beb21f245a497d28ad02 100644
--- a/src/process_int.cc
+++ b/src/process_int.cc
@@ -62,7 +62,7 @@ int
 cdo_stream_inq_timestep(CdoStreamID p_pstreamptr, int tsID)
 {
   Debug(PROCESS_INT, "%s pstreamID %d", p_pstreamptr->m_name, p_pstreamptr->get_id());
-  const auto nrecs = p_pstreamptr->inq_timestep(tsID);
+  auto nrecs = p_pstreamptr->inq_timestep(tsID);
   if (nrecs && tsID == p_pstreamptr->getTsID())
     {
       localProcess->ntimesteps++;
@@ -213,7 +213,7 @@ cdo_open_read(int inStreamIDX)
   if (localProcess->get_stream_cnt_in() < inStreamIDX || inStreamIDX < 0)
     cdo_abort("instream %d of process %d not found", inStreamIDX, localProcess->m_ID);
 
-  const auto inStream = localProcess->inputStreams[inStreamIDX];
+  auto inStream = localProcess->inputStreams[inStreamIDX];
   inStream->open_read();
 
   return inStream;
@@ -365,7 +365,7 @@ cdo_stream_inq_vlist(CdoStreamID p_pstreamPtr)
   if (p_pstreamPtr == nullptr) return -1;
 
   Debug(PROCESS_INT, "Inquiring Vlist from pstream %d", p_pstreamPtr->get_id());
-  const auto vlistID = p_pstreamPtr->inq_vlist();
+  auto vlistID = p_pstreamPtr->inq_vlist();
   if (vlistNumber(vlistID) == CDI_COMP && cdo_stream_number() == CDI_REAL)
     cdo_abort("Fields with complex numbers are not supported by this operator!");
 
@@ -485,6 +485,14 @@ cdo_copy_record(CdoStreamID pstreamPtrDest, CdoStreamID pstreamPtrSrc)
 
 // - - - - - - -
 
+std::pair<int, int>
+cdo_inq_record(CdoStreamID pstreamptr)
+{
+  int varID = -1, levelID = -1;
+  pstreamptr->inq_record(&varID, &levelID);
+  return std::make_pair(varID, levelID);
+}
+
 void
 cdo_inq_record(CdoStreamID pstreamptr, int *varID, int *levelID)
 {
diff --git a/src/process_int.h b/src/process_int.h
index ee4a7ada7649ddebffb4cd3f1cc48030de04500b..861b61a522f0f39d5baa288986d72fc50a223fbe 100644
--- a/src/process_int.h
+++ b/src/process_int.h
@@ -39,6 +39,7 @@ void cdo_inq_grib_info(CdoStreamID streamID, int *intnum, float *fltnum, off_t *
 void cdo_set_nan(double missval, size_t gridsize, double *array);
 // ***********************************************************
 
+std::pair<int, int> cdo_inq_record(CdoStreamID pstreamptr);
 void cdo_inq_record(CdoStreamID streamID, int *varID, int *levelID);
 void cdo_def_record(CdoStreamID streamID, int varID, int levelID);
 
diff --git a/src/progress.cc b/src/progress.cc
index 285ae7a9356668a46896354a840b925296521e10..5024e453852f0998c47287f05a70442f2144f049 100644
--- a/src/progress.cc
+++ b/src/progress.cc
@@ -2,20 +2,12 @@
 #include <algorithm>
 
 #include "progress.h"
+#include "cdo_options.h"
 
 namespace progress
 {
 
-bool stdoutIsTerminal = false;
-bool silentMode = false;
-
-static bool ps_lhead = false;
-static int ps_nch = 0;
-static int ps_cval = -1;
-
-static const char *context = "";
 const char *(*getContext)(void) = nullptr;
-
 /**
  * parameter p_context:
  *          will be displayed in status message and indicates sub process for
@@ -27,58 +19,51 @@ set_context_function(const char *(*func)(void) )
   getContext = func;
 }
 
-void
-init()
+}  // namespace progress
+
+namespace cdo
 {
-  ps_lhead = false;
-  ps_nch = 0;
-  ps_cval = -1;
 
-  if (getContext != nullptr) context = getContext();
-}
+bool ProgressInUse = false;
 
 void
-init(const char *p_context)
+Progress::init()
 {
-  ps_lhead = false;
-  ps_nch = 0;
-  ps_cval = -1;
-
-  context = p_context;
+  if (progress::getContext != nullptr) context = progress::getContext();
 }
 
 void
-update(double offset, double refval, double curval)
+Progress::update(double curval, double offset, double refval)
 {
-  if (silentMode) return;
-  if (!stdoutIsTerminal) return;
+  if (!isActiv) return;
+  if (!cdo::stdoutIsTerminal || Options::silentMode || Options::cdoVerbose) return;
 
+  curval = std::clamp(curval, 0.0, 1.0);
   offset = std::clamp(offset, 0.0, 1.0);
   refval = std::clamp(refval, 0.0, 1.0);
-  curval = std::clamp(curval, 0.0, 1.0);
 
-  int ival = (offset + refval * curval) * 100;
+  int newValue = (offset + refval * curval) * 100;
 
-  if (ps_cval == -1)
+  if (value == -1)
     {
-      ps_nch = fprintf(stdout, "%s: %3d%%", context, 0);
+      contextLen = fprintf(stdout, "%s: %3d%%", context, 0);
       fflush(stdout);
-      ps_lhead = true;
+      contextActive = true;
     }
 
-  if (ival != ps_cval)
+  if (newValue != value)
     {
-      ps_cval = ival;
-      fprintf(stdout, "\b\b\b\b%3d%%", ps_cval);
+      value = newValue;
+      fprintf(stdout, "\b\b\b\b%3d%%", value);
       fflush(stdout);
     }
 
-  if (ps_cval == 100 && ps_lhead)
+  if (value == 100 && contextActive)
     {
-      ps_lhead = false;
-      while (ps_nch--) fprintf(stdout, "\b \b");
+      contextActive = false;
+      while (contextLen--) fprintf(stdout, "\b \b");
       fflush(stdout);
     }
 }
 
-}  // namespace progress
+}  // namespace cdo
diff --git a/src/progress.h b/src/progress.h
index b542019b813ae8286c09e8bba6be1e7cf106ae3d..3060d226bd7b46deb60b229028bb9cc756720b9e 100644
--- a/src/progress.h
+++ b/src/progress.h
@@ -4,14 +4,45 @@
 namespace progress
 {
 
-extern bool stdoutIsTerminal;
-extern bool silentMode;
-
-void init();
-void init(const char *p_context);
-void update(double offset, double refval, double curval);
 void set_context_function(const char *(*func)(void) );
 
 }  // namespace progress
 
+namespace cdo
+{
+
+extern bool ProgressInUse;
+
+class Progress
+{
+private:
+  bool isActiv{ false };
+  bool contextActive{ false };
+  int contextLen{ 0 };
+  int value{ -1 };
+  const char *context = "";
+
+  void init();
+
+public:
+  Progress()
+  {
+    if (!ProgressInUse)
+      {
+        ProgressInUse = true;
+        isActiv = true;
+        init();
+      }
+  }
+  ~Progress()
+  {
+    update(1.0);
+    if (isActiv) ProgressInUse = false;
+  }
+
+  void update(double curval, double offset = 0.0, double refval = 1.0);
+};
+
+}  // namespace cdo
+
 #endif
diff --git a/src/remap.h b/src/remap.h
index 4ca62eea9b3407eccf47646fcfc44273cda71048..6c969cca61028890636cbadf36b9f6272af3cf25 100644
--- a/src/remap.h
+++ b/src/remap.h
@@ -228,7 +228,7 @@ remap_set_mask(size_t gridsize, const Varray<T> &v, size_t numMissVals, T missva
 #ifdef _OPENMP
 #pragma omp parallel for default(shared) schedule(static)
 #endif
-          for (size_t i = 0; i < gridsize; ++i) mask[i] = !dbl_is_equal(v[i], missval);
+          for (size_t i = 0; i < gridsize; ++i) mask[i] = dbl_is_not_equal(v[i], missval);
         }
       else
         {
diff --git a/src/remap_bicubic.cc b/src/remap_bicubic.cc
index bf8826af263a920de6c249abf4ca40c4db4c9778..20f5187855e820c45a6568b42b58bae0238bdc76 100644
--- a/src/remap_bicubic.cc
+++ b/src/remap_bicubic.cc
@@ -7,6 +7,7 @@
 
 #include <atomic>
 
+#include "cpp_lib.h"
 #include "process_int.h"
 #include "cdo_timer.h"
 #include <mpim_grid.h>
@@ -59,14 +60,26 @@ renormalize_weights(const double (&srcLats)[4], double (&weights)[4][4])
   for (int i = 0; i < 4; ++i) weights[i][2] = 0.0;
   for (int i = 0; i < 4; ++i) weights[i][3] = 0.0;
 }
-
+/*
+#ifdef HAVE_LIB_RANGES_ZIP
+#include <ranges>
 static void
-bicubic_sort_weights(size_t (&indices)[4], double (&weights)[4][4])
+bicubic_sort_weights_by_index_zip(size_t (&indices)[4], double (&weights)[4][4])
+{
+  auto r = std::views::zip(indices, weights);
+  std::sort(r.begin(), r.end, [](auto a, auto b) {
+    auto [ai, aw] = a;  // std::tuple
+    auto [bi, bw] = b;
+    return (ai < bi);
+  });
+}
+#endif
+*/
+static void
+bicubic_sort_weights_by_index(size_t (&indices)[4], double (&weights)[4][4])
 {
   constexpr size_t numWeights = 4;
 
-  if (is_sorted_list(numWeights, indices)) return;
-
   struct IndexWeightX
   {
     size_t index;
@@ -90,6 +103,19 @@ bicubic_sort_weights(size_t (&indices)[4], double (&weights)[4][4])
     }
 }
 
+static void
+bicubic_sort_weights(size_t (&indices)[4], double (&weights)[4][4])
+{
+  constexpr size_t numWeights = 4;
+  if (is_sorted_list(numWeights, indices)) return;
+
+  // #ifdef HAVE_LIB_RANGES_ZIP
+  //   bicubic_sort_weights_by_index_zip(indices, weights);
+  // #else
+  bicubic_sort_weights_by_index(indices, weights);
+  // #endif
+}
+
 static void
 bicubic_warning()
 {
@@ -120,8 +146,7 @@ remap_bicubic_weights(RemapSearch &rsearch, RemapVars &rv)
   if (srcGrid->rank != 2) cdo_abort("Can't do bicubic interpolation if the source grid is not a regular 2D grid!");
 
   cdo::timer timer;
-
-  progress::init();
+  cdo::Progress progress;
 
   // Compute mappings from source to target grid
 
@@ -140,7 +165,7 @@ remap_bicubic_weights(RemapSearch &rsearch, RemapVars &rv)
   for (size_t tgtCellIndex = 0; tgtCellIndex < tgtGridSize; ++tgtCellIndex)
     {
       atomicCount++;
-      if (cdo_omp_get_thread_num() == 0) progress::update(0, 1, (double) atomicCount / tgtGridSize);
+      if (cdo_omp_get_thread_num() == 0) progress.update((double) atomicCount / tgtGridSize);
 
       weightLinks[tgtCellIndex].nlinks = 0;
 
@@ -191,8 +216,6 @@ remap_bicubic_weights(RemapSearch &rsearch, RemapVars &rv)
         }
     }
 
-  progress::update(0, 1, 1);
-
   weight_links_4_to_remap_links(tgtGridSize, weightLinks, rv);
 
   rv.linksPerValue = 4;
@@ -236,13 +259,12 @@ remap_bicubic(RemapSearch &rsearch, const Varray<T> &srcArray, Varray<T> &tgtArr
   if (srcGrid->rank != 2) cdo_abort("Can't do bicubic interpolation if the source grid is not a regular 2D grid!");
 
   cdo::timer timer;
-
-  progress::init();
+  cdo::Progress progress;
 
   auto tgtGridSize = tgtGrid->size;
   auto srcGridSize = srcGrid->size;
 
-  Varray<short> srcGridMask;
+  Varray<short> srcGridMask(srcGridSize, 1);
   if (numMissVals) remap_set_mask(srcGridSize, srcArray, numMissVals, missval, srcGridMask);
 
   // Compute mappings from source to target grid
@@ -260,7 +282,7 @@ remap_bicubic(RemapSearch &rsearch, const Varray<T> &srcArray, Varray<T> &tgtArr
   for (size_t tgtCellIndex = 0; tgtCellIndex < tgtGridSize; ++tgtCellIndex)
     {
       atomicCount++;
-      if (cdo_omp_get_thread_num() == 0) progress::update(0, 1, (double) atomicCount / tgtGridSize);
+      if (cdo_omp_get_thread_num() == 0) progress.update((double) atomicCount / tgtGridSize);
 
       tgtArray[tgtCellIndex] = missval;
 
@@ -310,8 +332,6 @@ remap_bicubic(RemapSearch &rsearch, const Varray<T> &srcArray, Varray<T> &tgtArr
         }
     }
 
-  progress::update(0, 1, 1);
-
   if (Options::cdoVerbose) cdo_print("%s: %.2f seconds", __func__, timer.elapsed());
 }  // remap_bicubic
 
diff --git a/src/remap_bilinear.cc b/src/remap_bilinear.cc
index 011ec125f2aab9dc65e8dfff8b65977baee2e0e8..049ba5713db5a335a443c07fc4752c4adb75c21a 100644
--- a/src/remap_bilinear.cc
+++ b/src/remap_bilinear.cc
@@ -7,6 +7,7 @@
 
 #include <atomic>
 
+#include "cpp_lib.h"
 #include "process_int.h"
 #include "cdo_timer.h"
 #include "remap.h"
@@ -120,12 +121,24 @@ renormalize_weights(const double (&srcLats)[4], double (&weights)[4])
   for (int i = 0; i < 4; ++i) weights[i] = std::fabs(srcLats[i]) / sumWeights;
 }
 
+#ifdef HAVE_LIB_RANGES_ZIP
+#include <ranges>
 static void
-bilinear_sort_weights(size_t (&indices)[4], double (&weights)[4])
+bilinear_sort_weights_by_index_zip(size_t (&indices)[4], double (&weights)[4])
 {
-  constexpr size_t numWeights = 4;
+  auto r = std::views::zip(indices, weights);
+  std::sort(r.begin(), r.end(), [](auto a, auto b) {
+    auto [ai, aw] = a;  // std::tuple
+    auto [bi, bw] = b;
+    return (ai < bi);
+  });
+}
+#endif
 
-  if (is_sorted_list(numWeights, indices)) return;
+static void
+bilinear_sort_weights_by_index(size_t (&indices)[4], double (&weights)[4])
+{
+  constexpr size_t numWeights = 4;
 
   struct IndexWeightX
   {
@@ -150,6 +163,19 @@ bilinear_sort_weights(size_t (&indices)[4], double (&weights)[4])
     }
 }
 
+static void
+bilinear_sort_weights(size_t (&indices)[4], double (&weights)[4])
+{
+  constexpr size_t numWeights = 4;
+  if (is_sorted_list(numWeights, indices)) return;
+
+#ifdef HAVE_LIB_RANGES_ZIP
+  bilinear_sort_weights_by_index_zip(indices, weights);
+#else
+  bilinear_sort_weights_by_index(indices, weights);
+#endif
+}
+
 static void
 bilinear_warning()
 {
@@ -248,8 +274,7 @@ remap_bilinear_weights(RemapSearch &rsearch, RemapVars &rv)
     cdo_abort("Can't do bilinear interpolation if the source grid is not a regular 2D grid!");
 
   cdo::timer timer;
-
-  progress::init();
+  cdo::Progress progress;
 
   // Compute mappings from source to target grid
 
@@ -268,7 +293,7 @@ remap_bilinear_weights(RemapSearch &rsearch, RemapVars &rv)
   for (size_t tgtCellIndex = 0; tgtCellIndex < tgtGridSize; ++tgtCellIndex)
     {
       atomicCount++;
-      if (cdo_omp_get_thread_num() == 0) progress::update(0, 1, (double) atomicCount / tgtGridSize);
+      if (cdo_omp_get_thread_num() == 0) progress.update((double) atomicCount / tgtGridSize);
 
       weightLinks[tgtCellIndex].nlinks = 0;
 
@@ -283,8 +308,6 @@ remap_bilinear_weights(RemapSearch &rsearch, RemapVars &rv)
         remap_bilinear_weights_regular(rsearch, srcGrid->mask, llpoint, tgtCellFrac, tgtCellIndex, weightLinks);
     }
 
-  progress::update(0, 1, 1);
-
   weight_links_to_remap_links(0, tgtGridSize, weightLinks, rv);
 
   rv.linksPerValue = 4;
@@ -390,8 +413,7 @@ remap_bilinear(RemapSearch &rsearch, const Varray<T> &srcArray, Varray<T> &tgtAr
     cdo_abort("Can't do bilinear interpolation if the source grid is not a regular 2D grid!");
 
   cdo::timer timer;
-
-  progress::init();
+  cdo::Progress progress;
 
   auto tgtGridSize = tgtGrid->size;
   auto srcGridSize = srcGrid->size;
@@ -411,7 +433,7 @@ remap_bilinear(RemapSearch &rsearch, const Varray<T> &srcArray, Varray<T> &tgtAr
   for (size_t tgtCellIndex = 0; tgtCellIndex < tgtGridSize; ++tgtCellIndex)
     {
       atomicCount++;
-      if (cdo_omp_get_thread_num() == 0) progress::update(0, 1, (double) atomicCount / tgtGridSize);
+      if (cdo_omp_get_thread_num() == 0) progress.update((double) atomicCount / tgtGridSize);
 
       auto &tgtValue = tgtArray[tgtCellIndex];
       tgtValue = missval;
@@ -426,8 +448,6 @@ remap_bilinear(RemapSearch &rsearch, const Varray<T> &srcArray, Varray<T> &tgtAr
         remap_bilinear_regular(rsearch, srcArray, srcGridMask, llpoint, tgtValue);
     }
 
-  progress::update(0, 1, 1);
-
   if (Options::cdoVerbose) cdo_print("%s: %.2f seconds", __func__, timer.elapsed());
 }  // remap_bilinear
 
diff --git a/src/remap_conserv.cc b/src/remap_conserv.cc
index 5c0c0bfc30a9193642ebe9b5a9517a2c74d83de3..15926c83bcf034fa236e1a702db302fa122e93f2 100644
--- a/src/remap_conserv.cc
+++ b/src/remap_conserv.cc
@@ -7,6 +7,7 @@
 
 #include <atomic>
 
+#include "cpp_lib.h"
 #include "process_int.h"
 #include "cdo_timer.h"
 #include "remap.h"
@@ -430,13 +431,31 @@ correct_weights(double cellArea, size_t numWeights, Varray<double> &weights)
   for (size_t i = 0; i < numWeights; ++i) weights[i] *= cellArea;
 }
 
+#ifdef HAVE_LIB_RANGES_ZIP
+#include <ranges>
 static void
-sort_weights(size_t numWeights, Varray<size_t> &indices, Varray<double> &weights)
+sort_weights_by_index_zip(size_t numWeights, Varray<size_t> &indices, Varray<double> &weights)
 {
-  if (numWeights <= 1) return;
-
-  if (is_sorted_list(numWeights, indices.data())) return;
+  /*
+  static bool doPrint = true;
+  if (doPrint)
+    {
+      doPrint = false;
+      printf("using sort_weights_by_index_zip()\n");
+    }
+  */
+  auto r = std::views::zip(indices, weights);
+  std::sort(r.begin(), r.begin() + numWeights, [](auto a, auto b) {
+    auto [ai, aw] = a;  // std::tuple
+    auto [bi, bw] = b;
+    return (ai < bi);
+  });
+}
+#endif
 
+static void
+sort_weights_by_index(size_t numWeights, Varray<size_t> &indices, Varray<double> &weights)
+{
   struct IndexWeightX
   {
     size_t index;
@@ -459,6 +478,20 @@ sort_weights(size_t numWeights, Varray<size_t> &indices, Varray<double> &weights
       weights[i] = indexWeights[i].weight;
     }
 }
+
+static void
+sort_weights(size_t numWeights, Varray<size_t> &indices, Varray<double> &weights)
+{
+  if (numWeights <= 1) return;
+
+  if (is_sorted_list(numWeights, indices.data())) return;
+
+#ifdef HAVE_LIB_RANGES_ZIP
+  sort_weights_by_index_zip(numWeights, indices, weights);
+#else
+  sort_weights_by_index(numWeights, indices, weights);
+#endif
+}
 /*
 static void
 reg2d_bound_box(RemapGrid *remapGrid, double *gridBoundBox)
@@ -574,8 +607,7 @@ remap_conserv_weights(RemapSearch &remapSearch, RemapVars &rv)
 
   if (Options::cdoVerbose) cdo_print("Called %s()", __func__);
 
-  progress::init();
-
+  cdo::Progress progress;
   cdo::timer timer;
 
   auto srcGridSize = srcGrid->size;
@@ -650,7 +682,7 @@ remap_conserv_weights(RemapSearch &remapSearch, RemapVars &rv)
       auto &tgtGridCell = tgtGridCell2[ompthID];
 
       atomicCount++;
-      if (ompthID == 0) progress::update(0, 1, (double) atomicCount / tgtGridSize);
+      if (ompthID == 0) progress.update((double) atomicCount / tgtGridSize);
 
       weightLinks[tgtCellIndex].nlinks = 0;
 
@@ -729,8 +761,6 @@ remap_conserv_weights(RemapSearch &remapSearch, RemapVars &rv)
         }
     }
 
-  progress::update(0, 1, 1);
-
   if (linksPerValue > 0) rv.linksPerValue = linksPerValue;
 
   if (1 && Options::cdoVerbose)
@@ -780,7 +810,8 @@ 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 numMissVals)
+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;
@@ -793,8 +824,7 @@ remap_conserv(NormOpt normOpt, RemapSearch &remapSearch, const Varray<T> &srcArr
 
   if (Options::cdoVerbose) cdo_print("Called %s()", __func__);
 
-  progress::init();
-
+  cdo::Progress progress;
   cdo::timer timer;
 
   auto srcGridSize = srcGrid->size;
@@ -869,7 +899,7 @@ remap_conserv(NormOpt normOpt, RemapSearch &remapSearch, const Varray<T> &srcArr
       auto &tgtGridCell = tgtGridCell2[ompthID];
 
       atomicCount++;
-      if (ompthID == 0) progress::update(0, 1, (double) atomicCount / tgtGridSize);
+      if (ompthID == 0) progress.update((double) atomicCount / tgtGridSize);
 
       tgtArray[tgtCellIndex] = missval;
 
@@ -941,8 +971,6 @@ remap_conserv(NormOpt normOpt, RemapSearch &remapSearch, const Varray<T> &srcArr
         }
     }
 
-  progress::update(0, 1, 1);
-
   if (1 && Options::cdoVerbose)
     {
       cdo_print("Num search cells min,mean,max :  %zu  %3.1f  %zu", numSearchCellsStat[1],
@@ -985,7 +1013,7 @@ remove_missing_weights(const Varray<T> &srcArray, T missval, size_t numWeights,
   for (size_t i = 0; i < numWeights; ++i)
     {
       auto cellIndex = indices[i];
-      if (!dbl_is_equal(srcArray[cellIndex], missval))
+      if (dbl_is_not_equal(srcArray[cellIndex], missval))
         {
           partialWeights[n] = partialWeights[i];
           indices[n] = cellIndex;
diff --git a/src/remap_conserv_scrip.cc b/src/remap_conserv_scrip.cc
index e1f04913b40b8d51dfe5b5d6c5b5e4d2db66fb44..37b31a3d41157ef6ceb6a6982aa2dabc5bbd02ff 100644
--- a/src/remap_conserv_scrip.cc
+++ b/src/remap_conserv_scrip.cc
@@ -10,7 +10,6 @@
 #include "remap.h"
 #include "remap_store_link_cnsrv.h"
 #include "cdo_options.h"
-#include "progress.h"
 #include "cimdOmp.h"
 
 #define ZERO 0.0
@@ -1221,8 +1220,6 @@ remap_conserv_weights_scrip(RemapSearch &rsearch, RemapVars &rv)
 
   if (Options::cdoVerbose) cdo_print("Called %s()", __func__);
 
-  progress::init();
-
   long numWeights = rv.numWeights;
 
   GridStore grid_store;
@@ -1280,9 +1277,10 @@ 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,   \
-                                              Options::cdoVerbose, max_subseg, srch_corner_lat, srch_corner_lon, maxSearchCells, \
-                                              srcNumCorners, srch_corners, srcGrid, tgtGrid, tgtGridSize, srcGridSize, srch_add)
+#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
   for (long srcCellIndex = 0; srcCellIndex < srcGridSize; ++srcCellIndex)
     {
@@ -1292,7 +1290,6 @@ remap_conserv_weights_scrip(RemapSearch &rsearch, RemapVars &rv)
 #pragma omp atomic
 #endif
       findex++;
-      if (ompthID == 0) progress::update(0, 0.5, findex / srcGridSize);
 
       bool lcoinc;  // flag for coincident segments
       bool lthresh = false;
@@ -1467,9 +1464,10 @@ 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,   \
-                                              Options::cdoVerbose, max_subseg, srch_corner_lat, srch_corner_lon, maxSearchCells, \
-                                              tgtNumCorners, srch_corners, srcGrid, tgtGrid, tgtGridSize, srcGridSize, srch_add)
+#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
   for (long tgtCellIndex = 0; tgtCellIndex < tgtGridSize; ++tgtCellIndex)
     {
@@ -1479,7 +1477,6 @@ remap_conserv_weights_scrip(RemapSearch &rsearch, RemapVars &rv)
 #pragma omp atomic
 #endif
       findex++;
-      if (ompthID == 0) progress::update(0.5, 0.5, findex / tgtGridSize);
 
       bool lcoinc;  // flag for coincident segments
       bool lthresh = false;
@@ -1637,8 +1634,6 @@ remap_conserv_weights_scrip(RemapSearch &rsearch, RemapVars &rv)
         }
     }
 
-  progress::update(0, 1, 1);
-
   /* Finished with all cells: deallocate search arrays */
 
   for (int i = 0; i < Threading::ompNumThreads; ++i) free(srch_corner_lon[i]);
diff --git a/src/remap_distwgt.cc b/src/remap_distwgt.cc
index c826fc6d9e1eec8d9e73d9ec33766ea978925e6c..12a745093d98b5700df288423fbd4a3185073064 100644
--- a/src/remap_distwgt.cc
+++ b/src/remap_distwgt.cc
@@ -26,7 +26,7 @@ remap_distwgt_weights(size_t numNeighbors, RemapSearch &rsearch, RemapVars &rv)
 
   if (Options::cdoVerbose) cdo_print("Called %s()", __func__);
 
-  progress::init();
+  cdo::Progress progress;
 
   // Compute mappings from source to target grid
 
@@ -54,7 +54,7 @@ remap_distwgt_weights(size_t numNeighbors, RemapSearch &rsearch, RemapVars &rv)
       auto ompthID = cdo_omp_get_thread_num();
 
       atomicCount++;
-      if (ompthID == 0) progress::update(0, 1, (double) atomicCount / tgtGridSize);
+      if (ompthID == 0) progress.update((double) atomicCount / tgtGridSize);
 
       weightLinks[tgtCellIndex].nlinks = 0;
 
@@ -84,8 +84,6 @@ remap_distwgt_weights(size_t numNeighbors, RemapSearch &rsearch, RemapVars &rv)
         }
     }
 
-  progress::update(0, 1, 1);
-
   grid_point_search_delete(rsearch.gps);
 
   weight_links_to_remap_links(0, tgtGridSize, weightLinks, rv);
@@ -100,14 +98,15 @@ remap_distwgt_weights(size_t numNeighbors, RemapSearch &rsearch, RemapVars &rv)
 
 template <typename T>
 static void
-remap_distwgt(size_t numNeighbors, RemapSearch &rsearch, const Varray<T> &srcArray, Varray<T> &tgtArray, T missval, size_t numMissVals)
+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;
 
   if (Options::cdoVerbose) cdo_print("Called %s()", __func__);
 
-  progress::init();
+  cdo::Progress progress;
 
   // Compute mappings from source to target grid
 
@@ -135,7 +134,7 @@ remap_distwgt(size_t numNeighbors, RemapSearch &rsearch, const Varray<T> &srcArr
       auto ompthID = cdo_omp_get_thread_num();
 
       atomicCount++;
-      if (ompthID == 0) progress::update(0, 1, (double) atomicCount / tgtGridSize);
+      if (ompthID == 0) progress.update((double) atomicCount / tgtGridSize);
 
       auto &tgtValue = tgtArray[tgtCellIndex];
       tgtValue = missval;
@@ -153,8 +152,6 @@ remap_distwgt(size_t numNeighbors, RemapSearch &rsearch, const Varray<T> &srcArr
       if (nadds) tgtValue = knnWgt.arrayWeightsSum(srcArray);
     }
 
-  progress::update(0, 1, 1);
-
   if (Options::cdoVerbose) cdo_print("Point search nearest: %.2f seconds", timer.elapsed());
 }  // remap_distwgt
 
@@ -182,7 +179,7 @@ intgriddis(const Field &field1, Field &field2, size_t numNeighbors)
 
   if (Options::cdoVerbose) cdo_print("Called %s()", __func__);
 
-  progress::init();
+  cdo::Progress progress;
 
   // Interpolate from source to target grid
 
@@ -219,7 +216,7 @@ intgriddis(const Field &field1, Field &field2, size_t numNeighbors)
       auto ompthID = cdo_omp_get_thread_num();
 
       atomicCount++;
-      if (ompthID == 0) progress::update(0, 1, (double) atomicCount / tgtGridSize);
+      if (ompthID == 0) progress.update((double) atomicCount / tgtGridSize);
 
       auto &tgtValue = tgtArray[tgtCellIndex];
       tgtValue = tgtMissval;
@@ -240,8 +237,6 @@ intgriddis(const Field &field1, Field &field2, size_t numNeighbors)
         numMissVals++;
     }
 
-  progress::update(0, 1, 1);
-
   field2.numMissVals = numMissVals;
 
   remap_grid_free(remap.srcGrid);
diff --git a/src/remap_utils.cc b/src/remap_utils.cc
index 186f426a87828b50a15b12ed7f9cf6d155ccaddf..c8c82b54486bd8c23ba385cdf12ba6c06391b1c4 100644
--- a/src/remap_utils.cc
+++ b/src/remap_utils.cc
@@ -353,7 +353,7 @@ remap_gen_numbins(int ysize)
 std::vector<bool>
 remap_set_grids(int vlistID, const VarList &varList)
 {
-  auto numGrids = vlistNgrids(vlistID);
+  auto numGrids = vlistNumGrids(vlistID);
   std::vector<bool> remapGrids(numGrids, true);
   for (int index = 0; index < numGrids; ++index)
     {
@@ -368,11 +368,11 @@ remap_set_grids(int vlistID, const VarList &varList)
           if (gridtype == GRID_GENERIC && gridInqSize(gridID) <= 2) { remapGrids[index] = false; }
           else
             {
-              auto numVars = vlistNvars(vlistID);
+              auto numVars = varList.numVars();
               for (int varID = 0; varID < numVars; ++varID)
-                if (gridID == varList[varID].gridID)
+                if (gridID == varList.vars[varID].gridID)
                   {
-                    cdo_abort("Unsupported %s coordinates (Variable: %s)!", gridNamePtr(gridtype), varList[varID].name);
+                    cdo_abort("Unsupported %s coordinates (Variable: %s)!", gridNamePtr(gridtype), varList.vars[varID].name);
                     break;
                   }
             }
@@ -387,7 +387,7 @@ remap_get_max_maps(int vlistID)
 {
   int maxRemaps = 0;
 
-  auto numZaxis = vlistNzaxis(vlistID);
+  auto numZaxis = vlistNumZaxis(vlistID);
   for (int index = 0; index < numZaxis; ++index)
     {
       auto zaxisID = vlistZaxis(vlistID, index);
diff --git a/src/remap_vars.cc b/src/remap_vars.cc
index 52feb2a2acac9075b0f7b71c17cd124d30d33bc7..7f401f8cafd54d3e2bf99f4bbc0bfc31e798b00e 100644
--- a/src/remap_vars.cc
+++ b/src/remap_vars.cc
@@ -467,7 +467,7 @@ remap_avg(Varray<T2> &tgtArray, T2 missval, size_t tgtSize, const RemapVars &rv,
         for (n = minIndex; n < maxIndex; ++n)
           {
             auto value = srcArray[srcIndices[n]];
-            if (weights[numWeights * n] > lim && !dbl_is_equal(value, missval))
+            if (weights[numWeights * n] > lim && dbl_is_not_equal(value, missval))
               {
                 sum += value;
                 // weights += weights[numWeights * n];
diff --git a/src/remaplib.cc b/src/remaplib.cc
index c5753074aa6b393f07ea44957b618c0210c7dcaa..d72b87f72ec90a9aa57df1b8ab3433863524fe2b 100644
--- a/src/remaplib.cc
+++ b/src/remaplib.cc
@@ -869,12 +869,12 @@ remap_stat(int remapOrder, RemapGrid &srcGrid, RemapGrid &tgtGrid, RemapVars &rv
       cdo_print("  Conservation:");
       double sum = 0.0;
       for (size_t i = 0; i < srcGrid.size; ++i)
-        if (!dbl_is_equal(array1[i], mv1)) sum += array1[i] * srcGrid.cell_area[i] * srcGrid.cell_frac[i];
+        if (dbl_is_not_equal(array1[i], mv1)) sum += array1[i] * srcGrid.cell_area[i] * srcGrid.cell_frac[i];
       cdo_print("  Grid1 Integral = %g", sum);
 
       sum = 0;
       for (size_t i = 0; i < tgtGrid.size; ++i)
-        if (!dbl_is_equal(array2[i], mv2)) sum += array2[i] * tgtGrid.cell_area[i] * tgtGrid.cell_frac[i];
+        if (dbl_is_not_equal(array2[i], mv2)) sum += array2[i] * tgtGrid.cell_area[i] * tgtGrid.cell_frac[i];
       cdo_print("  Grid2 Integral = %g", sum);
     }
 
diff --git a/src/specspace.cc b/src/specspace.cc
index ea33b9ac7e45364ad3d8bca0e94fca9b42cc12c7..790d7f3ceb67abc953c29130d78609abcbd6e3b2 100644
--- a/src/specspace.cc
+++ b/src/specspace.cc
@@ -130,8 +130,8 @@ speccut(int gridIDin, const Varray<double> &arrayIn, Varray<double> &arrayOut, c
 }
 
 void
-trans_uv2dv(const SP_Transformation &spTrans, long nlev, int gridID1, const double *gu, const double *gv, int gridID2, double *sd,
-            double *svo)
+trans_uv2dv(const SP_Transformation &spTrans, long nlev, int gridID1, const Varray<double> &gu, const Varray<double> &gv,
+            int gridID2, Varray<double> &sd, Varray<double> &svo)
 {
   if (gridInqType(gridID1) != GRID_GAUSSIAN)
     cdo_abort("unexpected grid1 type: %s instead of Gaussian", gridNamePtr(gridInqType(gridID1)));
@@ -151,24 +151,24 @@ trans_uv2dv(const SP_Transformation &spTrans, long nlev, int gridID1, const doub
 
   if (fcTrans.use_fftw)
     {
-      gp2fc(gu, fpwork1.data(), nlat, nlon, nlev, nfc);
-      gp2fc(gv, fpwork2.data(), nlat, nlon, nlev, nfc);
+      gp2fc(gu.data(), fpwork1.data(), nlat, nlon, nlev, nfc);
+      gp2fc(gv.data(), fpwork2.data(), nlat, nlon, nlev, nfc);
     }
   else
     {
-      gp2fc(fcTrans.vtrig.data(), fcTrans.ifax, gu, fpwork1.data(), nlat, nlon, nlev, nfc);
-      gp2fc(fcTrans.vtrig.data(), fcTrans.ifax, gv, fpwork2.data(), nlat, nlon, nlev, nfc);
+      gp2fc(fcTrans.vtrig.data(), fcTrans.ifax, gu.data(), fpwork1.data(), nlat, nlon, nlev, nfc);
+      gp2fc(fcTrans.vtrig.data(), fcTrans.ifax, gv.data(), fpwork2.data(), nlat, nlon, nlev, nfc);
     }
 
   scaluv(fpwork1.data(), spTrans.coslat.data(), nlat, nfc * nlev);
   scaluv(fpwork2.data(), spTrans.coslat.data(), nlat, nfc * nlev);
 
-  uv2dv(fpwork1.data(), fpwork2.data(), sd, svo, spTrans.pol2.data(), spTrans.pol3.data(), nlev, nlat, ntr);
+  uv2dv(fpwork1.data(), fpwork2.data(), sd.data(), svo.data(), spTrans.pol2.data(), spTrans.pol3.data(), nlev, nlat, ntr);
 }
 
 void
-trans_dv2uv(const SP_Transformation &spTrans, const DV_Transformation &dvTrans, long nlev, int gridID1, const double *sd,
-            const double *svo, int gridID2, double *gu, double *gv)
+trans_dv2uv(const SP_Transformation &spTrans, const DV_Transformation &dvTrans, long nlev, int gridID1, const Varray<double> &sd,
+            const Varray<double> &svo, int gridID2, Varray<double> &gu, Varray<double> &gv)
 {
   if (gridInqType(gridID1) != GRID_SPECTRAL)
     cdo_warning("unexpected grid1 type: %s instead of spectral", gridNamePtr(gridInqType(gridID1)));
@@ -183,10 +183,10 @@ trans_dv2uv(const SP_Transformation &spTrans, const DV_Transformation &dvTrans,
   long nfc = waves * 2;
   long dimsp = (ntr + 1) * (ntr + 2);
 
-  double *su = gu;
-  double *sv = gv;
+  double *su = gu.data();
+  double *sv = gv.data();
 
-  dv2uv(sd, svo, su, sv, dvTrans.f1.data(), dvTrans.f2.data(), ntr, dimsp, nlev);
+  dv2uv(sd.data(), svo.data(), su, sv, dvTrans.f1.data(), dvTrans.f2.data(), ntr, dimsp, nlev);
 
   Varray<double> fpwork(nlat * nfc * nlev);
 
@@ -194,15 +194,15 @@ trans_dv2uv(const SP_Transformation &spTrans, const DV_Transformation &dvTrans,
   scaluv(fpwork.data(), spTrans.rcoslat.data(), nlat, nfc * nlev);
 
   if (fcTrans.use_fftw)
-    fc2gp(fpwork.data(), gu, nlat, nlon, nlev, nfc);
+    fc2gp(fpwork.data(), gu.data(), nlat, nlon, nlev, nfc);
   else
-    fc2gp(fcTrans.vtrig.data(), fcTrans.ifax, fpwork.data(), gu, nlat, nlon, nlev, nfc);
+    fc2gp(fcTrans.vtrig.data(), fcTrans.ifax, fpwork.data(), gu.data(), nlat, nlon, nlev, nfc);
 
   sp2fc(sv, fpwork.data(), spTrans.poli.data(), nlev, nlat, nfc, ntr);
   scaluv(fpwork.data(), spTrans.rcoslat.data(), nlat, nfc * nlev);
 
   if (fcTrans.use_fftw)
-    fc2gp(fpwork.data(), gv, nlat, nlon, nlev, nfc);
+    fc2gp(fpwork.data(), gv.data(), nlat, nlon, nlev, nfc);
   else
-    fc2gp(fcTrans.vtrig.data(), fcTrans.ifax, fpwork.data(), gv, nlat, nlon, nlev, nfc);
+    fc2gp(fcTrans.vtrig.data(), fcTrans.ifax, fpwork.data(), gv.data(), nlat, nlon, nlev, nfc);
 }
diff --git a/src/specspace.h b/src/specspace.h
index 87162d0b351411bcbbe8c52b13dce8e4fe10e2e5..1d6dd339cb5f198f1a47bf839f93fc27a98a3785 100644
--- a/src/specspace.h
+++ b/src/specspace.h
@@ -74,7 +74,16 @@ public:
       {
         vtrig.resize(nlon);
         auto status = fft_set(vtrig.data(), ifax, nlon);
-        if (status < 0) cdo_abort("FFT error!");
+        if (status < 0)
+          {
+            cdo_print("Retry it with the fftw3 library!");
+#ifdef HAVE_LIBFFTW3
+            cdo_warning("Using fftw3 lib is disabled!");
+#else
+            cdo_warning("LIBFFTW3 support not compiled in!");
+#endif
+            cdo_abort("FFT error!");
+          }
       }
   }
 };
@@ -168,11 +177,11 @@ public:
 
 void dv2ps(const double *div, double *pot, long nlev, long ntr);
 
-void trans_uv2dv(const SP_Transformation &spTrans, long nlev, int gridID1, const double *gu, const double *gv, int gridID2,
-                 double *sd, double *svo);
+void trans_uv2dv(const SP_Transformation &spTrans, long nlev, int gridID1, const Varray<double> &gu, const Varray<double> &gv,
+                 int gridID2, Varray<double> &sd, Varray<double> &svo);
 
-void trans_dv2uv(const SP_Transformation &spTrans, const DV_Transformation &dvTrans, long nlev, int gridID1, const double *sd,
-                 const double *svo, int gridID2, double *gu, double *gv);
+void trans_dv2uv(const SP_Transformation &spTrans, const DV_Transformation &dvTrans, long nlev, int gridID1,
+                 const Varray<double> &sd, const Varray<double> &svo, int gridID2, Varray<double> &gu, Varray<double> &gv);
 
 void grid2spec(const SP_Transformation &spTrans, int gridIDin, const Varray<double> &arrayIn, int gridIDout,
                Varray<double> &arrayOut);
diff --git a/src/varray.cc b/src/varray.cc
index 651d3e722c5e54f4fa321bef325356c4064cf649..00d5e0f1a8f63f176af83713e71d82e6cb7d252a 100644
--- a/src/varray.cc
+++ b/src/varray.cc
@@ -5,8 +5,6 @@
 
 */
 
-#include <cfloat>
-#include <cfenv>
 #include <cassert>
 #include <limits>
 
@@ -33,8 +31,8 @@ template <typename T>
 MinMax
 varray_min_max_mv(size_t len, const T *array, T missval)
 {
-  auto f_minmax_mv = [](auto a, auto mv_a, auto &vmin, auto &vmax, auto &nvals, auto is_EQ) {
-    if (!is_EQ(a, mv_a))
+  auto f_minmax_mv = [](auto a, auto mv_a, auto &vmin, auto &vmax, auto &nvals, auto is_NE) {
+    if (is_NE(a, mv_a))
       {
         vmin = min_value(vmin, a);
         vmax = max_value(vmax, a);
@@ -47,9 +45,9 @@ varray_min_max_mv(size_t len, const T *array, T missval)
 
   size_t nvals = 0;
   if (std::isnan(missval))
-    for (size_t i = 0; i < len; ++i) f_minmax_mv(array[i], missval, vmin, vmax, nvals, dbl_is_equal);
+    for (size_t i = 0; i < len; ++i) f_minmax_mv(array[i], missval, vmin, vmax, nvals, dbl_is_not_equal);
   else
-    for (size_t i = 0; i < len; ++i) f_minmax_mv(array[i], missval, vmin, vmax, nvals, is_equal);
+    for (size_t i = 0; i < len; ++i) f_minmax_mv(array[i], missval, vmin, vmax, nvals, is_not_equal);
 
   return MinMax(vmin, vmax, nvals);
 }
@@ -113,8 +111,8 @@ template <typename T>
 MinMaxSum
 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))
+  auto f_minmaxsum_mv = [](auto val, auto mv, auto &vmin, auto &vmax, auto &vsum, auto &nvals, auto is_NE) {
+    if (is_NE(val, mv))
       {
         vmin = min_value(vmin, val);
         vmax = max_value(vmax, val);
@@ -136,7 +134,7 @@ varray_min_max_sum_mv(size_t len, const Varray<T> &v, T missval, const MinMaxSum
     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);
+      for (size_t i = 0; i < len; ++i) f_minmaxsum_mv((double) v[i], missval, vmin, vmax, vsum, nvals, dbl_is_not_equal);
     }
   else
     {
@@ -146,7 +144,7 @@ varray_min_max_sum_mv(size_t len, const Varray<T> &v, T missval, const MinMaxSum
     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);
+      for (size_t i = 0; i < len; ++i) f_minmaxsum_mv((double) v[i], missval, vmin, vmax, vsum, nvals, is_not_equal);
     }
 
   if (nvals == 0 && is_equal(vmin, std::numeric_limits<double>::max())) vmin = missval;
@@ -234,7 +232,7 @@ array_add_array_mv(size_t len, double *array1, const double *array2, double miss
   if (std::isnan(missval))
     {
       for (size_t i = 0; i < len; ++i)
-        if (!dbl_is_equal(array2[i], missval)) array1[i] = dbl_is_equal(array1[i], missval) ? array2[i] : array1[i] + array2[i];
+        if (dbl_is_not_equal(array2[i], missval)) array1[i] = dbl_is_equal(array1[i], missval) ? array2[i] : array1[i] + array2[i];
     }
   else
     {
@@ -463,8 +461,8 @@ template <typename T>
 T
 varray_min_mv(size_t len, const Varray<T> &v, T missval)
 {
-  auto f_min_mv = [](auto a, auto mv_a, auto &vmin, auto is_EQ) {
-    if (!is_EQ(a, mv_a)) vmin = min_value(vmin, a);
+  auto f_min_mv = [](auto a, auto mv_a, auto &vmin, auto is_NE) {
+    if (is_NE(a, mv_a)) vmin = min_value(vmin, a);
   };
 
   assert(len > 0);
@@ -482,7 +480,7 @@ varray_min_mv(size_t len, const Varray<T> &v, T missval)
 #pragma omp parallel for simd default(shared) schedule(static) reduction(min : vmin)
 #endif
 #endif
-          for (size_t i = 0; i < len; ++i) f_min_mv(v[i], missval, vmin, dbl_is_equal);
+          for (size_t i = 0; i < len; ++i) f_min_mv(v[i], missval, vmin, dbl_is_not_equal);
         }
       else
         {
@@ -491,7 +489,7 @@ varray_min_mv(size_t len, const Varray<T> &v, T missval)
 #pragma omp simd reduction(min : vmin)
 #endif
 #endif
-          for (size_t i = 0; i < len; ++i) f_min_mv(v[i], missval, vmin, dbl_is_equal);
+          for (size_t i = 0; i < len; ++i) f_min_mv(v[i], missval, vmin, dbl_is_not_equal);
         }
     }
   else
@@ -503,7 +501,7 @@ varray_min_mv(size_t len, const Varray<T> &v, T missval)
 #pragma omp parallel for simd default(shared) schedule(static) reduction(min : vmin)
 #endif
 #endif
-          for (size_t i = 0; i < len; ++i) f_min_mv(v[i], missval, vmin, is_equal);
+          for (size_t i = 0; i < len; ++i) f_min_mv(v[i], missval, vmin, is_not_equal);
         }
       else
         {
@@ -512,7 +510,7 @@ varray_min_mv(size_t len, const Varray<T> &v, T missval)
 #pragma omp simd reduction(min : vmin)
 #endif
 #endif
-          for (size_t i = 0; i < len; ++i) f_min_mv(v[i], missval, vmin, is_equal);
+          for (size_t i = 0; i < len; ++i) f_min_mv(v[i], missval, vmin, is_not_equal);
         }
     }
 
@@ -529,8 +527,8 @@ template <typename T>
 T
 varray_max_mv(size_t len, const Varray<T> &v, T missval)
 {
-  auto f_max_mv = [](auto a, auto mv_a, auto &vmax, auto is_EQ) {
-    if (!is_EQ(a, mv_a)) vmax = max_value(vmax, a);
+  auto f_max_mv = [](auto a, auto mv_a, auto &vmax, auto is_NE) {
+    if (is_NE(a, mv_a)) vmax = max_value(vmax, a);
   };
 
   assert(len > 0);
@@ -548,7 +546,7 @@ varray_max_mv(size_t len, const Varray<T> &v, T missval)
 #pragma omp parallel for simd default(shared) schedule(static) reduction(max : vmax)
 #endif
 #endif
-          for (size_t i = 0; i < len; ++i) f_max_mv(v[i], missval, vmax, dbl_is_equal);
+          for (size_t i = 0; i < len; ++i) f_max_mv(v[i], missval, vmax, dbl_is_not_equal);
         }
       else
         {
@@ -557,7 +555,7 @@ varray_max_mv(size_t len, const Varray<T> &v, T missval)
 #pragma omp simd reduction(max : vmax)
 #endif
 #endif
-          for (size_t i = 0; i < len; ++i) f_max_mv(v[i], missval, vmax, dbl_is_equal);
+          for (size_t i = 0; i < len; ++i) f_max_mv(v[i], missval, vmax, dbl_is_not_equal);
         }
     }
   else
@@ -569,7 +567,7 @@ varray_max_mv(size_t len, const Varray<T> &v, T missval)
 #pragma omp parallel for simd default(shared) schedule(static) reduction(max : vmax)
 #endif
 #endif
-          for (size_t i = 0; i < len; ++i) f_max_mv(v[i], missval, vmax, is_equal);
+          for (size_t i = 0; i < len; ++i) f_max_mv(v[i], missval, vmax, is_not_equal);
         }
       else
         {
@@ -578,7 +576,7 @@ varray_max_mv(size_t len, const Varray<T> &v, T missval)
 #pragma omp simd reduction(max : vmax)
 #endif
 #endif
-          for (size_t i = 0; i < len; ++i) f_max_mv(v[i], missval, vmax, is_equal);
+          for (size_t i = 0; i < len; ++i) f_max_mv(v[i], missval, vmax, is_not_equal);
         }
     }
 
@@ -595,8 +593,8 @@ template <typename T>
 T
 varray_range_mv(size_t len, const Varray<T> &v, T missval)
 {
-  auto f_minmax_mv = [](auto a, auto mv_a, auto &vmin, auto &vmax, auto is_EQ) {
-    if (!is_EQ(a, mv_a))
+  auto f_minmax_mv = [](auto a, auto mv_a, auto &vmin, auto &vmax, auto is_NE) {
+    if (is_NE(a, mv_a))
       {
         vmin = min_value(vmin, a);
         vmax = max_value(vmax, a);
@@ -619,7 +617,7 @@ varray_range_mv(size_t len, const Varray<T> &v, T missval)
 #pragma omp parallel for simd default(shared) schedule(static) reduction(min : vmin) reduction(max : vmax)
 #endif
 #endif
-          for (size_t i = 0; i < len; ++i) f_minmax_mv(v[i], missval, vmin, vmax, dbl_is_equal);
+          for (size_t i = 0; i < len; ++i) f_minmax_mv(v[i], missval, vmin, vmax, dbl_is_not_equal);
         }
       else
         {
@@ -628,7 +626,7 @@ varray_range_mv(size_t len, const Varray<T> &v, T missval)
 #pragma omp simd reduction(min : vmin) reduction(max : vmax)
 #endif
 #endif
-          for (size_t i = 0; i < len; ++i) f_minmax_mv(v[i], missval, vmin, vmax, dbl_is_equal);
+          for (size_t i = 0; i < len; ++i) f_minmax_mv(v[i], missval, vmin, vmax, dbl_is_not_equal);
         }
     }
   else
@@ -640,7 +638,7 @@ varray_range_mv(size_t len, const Varray<T> &v, T missval)
 #pragma omp parallel for simd default(shared) schedule(static) reduction(min : vmin) reduction(max : vmax)
 #endif
 #endif
-          for (size_t i = 0; i < len; ++i) f_minmax_mv(v[i], missval, vmin, vmax, is_equal);
+          for (size_t i = 0; i < len; ++i) f_minmax_mv(v[i], missval, vmin, vmax, is_not_equal);
         }
       else
         {
@@ -649,7 +647,7 @@ varray_range_mv(size_t len, const Varray<T> &v, T missval)
 #pragma omp simd reduction(min : vmin) reduction(max : vmax)
 #endif
 #endif
-          for (size_t i = 0; i < len; ++i) f_minmax_mv(v[i], missval, vmin, vmax, is_equal);
+          for (size_t i = 0; i < len; ++i) f_minmax_mv(v[i], missval, vmin, vmax, is_not_equal);
         }
     }
 
@@ -704,8 +702,8 @@ template <typename T>
 double
 varray_sum_mv(size_t len, const Varray<T> &v, T missval)
 {
-  auto f_sum_mv = [](auto a, auto mv_a, auto &sum, auto &nvals, auto is_EQ) {
-    if (!is_EQ(a, mv_a))
+  auto f_sum_mv = [](auto a, auto mv_a, auto &sum, auto &nvals, auto is_NE) {
+    if (is_NE(a, mv_a))
       {
         sum += a;
         nvals++;
@@ -728,7 +726,7 @@ varray_sum_mv(size_t len, const Varray<T> &v, T missval)
 #pragma omp parallel for simd default(shared) schedule(static) reduction(+ : sum, nvals)
 #endif
 #endif
-          for (size_t i = 0; i < len; ++i) f_sum_mv(v[i], missval, sum, nvals, dbl_is_equal);
+          for (size_t i = 0; i < len; ++i) f_sum_mv(v[i], missval, sum, nvals, dbl_is_not_equal);
         }
       else
         {
@@ -737,7 +735,7 @@ varray_sum_mv(size_t len, const Varray<T> &v, T missval)
 #pragma omp simd reduction(+ : sum, nvals)
 #endif
 #endif
-          for (size_t i = 0; i < len; ++i) f_sum_mv(v[i], missval, sum, nvals, dbl_is_equal);
+          for (size_t i = 0; i < len; ++i) f_sum_mv(v[i], missval, sum, nvals, dbl_is_not_equal);
         }
     }
   else
@@ -749,7 +747,7 @@ varray_sum_mv(size_t len, const Varray<T> &v, T missval)
 #pragma omp parallel for simd default(shared) schedule(static) reduction(+ : sum, nvals)
 #endif
 #endif
-          for (size_t i = 0; i < len; ++i) f_sum_mv(v[i], missval, sum, nvals, is_equal);
+          for (size_t i = 0; i < len; ++i) f_sum_mv(v[i], missval, sum, nvals, is_not_equal);
         }
       else
         {
@@ -758,7 +756,7 @@ varray_sum_mv(size_t len, const Varray<T> &v, T missval)
 #pragma omp simd reduction(+ : sum, nvals)
 #endif
 #endif
-          for (size_t i = 0; i < len; ++i) f_sum_mv(v[i], missval, sum, nvals, is_equal);
+          for (size_t i = 0; i < len; ++i) f_sum_mv(v[i], missval, sum, nvals, is_not_equal);
         }
     }
 
@@ -796,11 +794,12 @@ varray_mean_mv(size_t len, const Varray<T> &v, T missval)
   assert(v.size() > 0);
   assert(len <= v.size());
 
+  auto is_NE = dbl_is_not_equal;
   auto is_EQ = dbl_is_equal;
   double sum = 0.0, sumw = 0.0;
 
   for (size_t i = 0; i < len; ++i)
-    if (!is_EQ(v[i], missval))
+    if (is_NE(v[i], missval))
       {
         sum += v[i];
         sumw += 1;
@@ -859,8 +858,8 @@ template <typename T>
 double
 varray_weighted_mean_mv(size_t len, const Varray<T> &v, const Varray<double> &w, T missval)
 {
-  auto f_weighted_mean_mv = [](auto aw, auto a, auto mv_a, auto &sum, auto &sumw, auto is_EQ) {
-    if (!is_EQ(a, mv_a) && !is_EQ(aw, mv_a))
+  auto f_weighted_mean_mv = [](auto aw, auto a, auto mv_a, auto &sum, auto &sumw, auto is_NE) {
+    if (is_NE(a, mv_a) && is_NE(aw, mv_a))
       {
         sum += aw * a;
         sumw += aw;
@@ -878,6 +877,7 @@ varray_weighted_mean_mv(size_t len, const Varray<T> &v, const Varray<double> &w,
 
   if (std::isnan(missval))
     {
+      auto is_NE = dbl_is_not_equal;
       auto is_EQ = dbl_is_equal;
       if (len > cdoMinLoopSize)
         {
@@ -886,7 +886,7 @@ varray_weighted_mean_mv(size_t len, const Varray<T> &v, const Varray<double> &w,
 #pragma omp parallel for default(shared) schedule(static) reduction(+ : sum, sumw)
 #endif
 #endif
-          for (size_t i = 0; i < len; ++i) f_weighted_mean_mv(w[i], v[i], missval1, sum, sumw, is_EQ);
+          for (size_t i = 0; i < len; ++i) f_weighted_mean_mv(w[i], v[i], missval1, sum, sumw, is_NE);
         }
       else
         {
@@ -895,12 +895,13 @@ varray_weighted_mean_mv(size_t len, const Varray<T> &v, const Varray<double> &w,
 #pragma omp simd reduction(+ : sum, sumw)
 #endif
 #endif
-          for (size_t i = 0; i < len; ++i) f_weighted_mean_mv(w[i], v[i], missval1, sum, sumw, is_EQ);
+          for (size_t i = 0; i < len; ++i) f_weighted_mean_mv(w[i], v[i], missval1, sum, sumw, is_NE);
         }
       wmean = DIVM(sum, sumw);
     }
   else
     {
+      auto is_NE = is_not_equal;
       auto is_EQ = is_equal;
       if (len > cdoMinLoopSize)
         {
@@ -909,7 +910,7 @@ varray_weighted_mean_mv(size_t len, const Varray<T> &v, const Varray<double> &w,
 #pragma omp parallel for simd default(shared) schedule(static) reduction(+ : sum, sumw)
 #endif
 #endif
-          for (size_t i = 0; i < len; ++i) f_weighted_mean_mv(w[i], v[i], missval1, sum, sumw, is_EQ);
+          for (size_t i = 0; i < len; ++i) f_weighted_mean_mv(w[i], v[i], missval1, sum, sumw, is_NE);
         }
       else
         {
@@ -918,7 +919,7 @@ varray_weighted_mean_mv(size_t len, const Varray<T> &v, const Varray<double> &w,
 #pragma omp simd reduction(+ : sum, sumw)
 #endif
 #endif
-          for (size_t i = 0; i < len; ++i) f_weighted_mean_mv(w[i], v[i], missval1, sum, sumw, is_EQ);
+          for (size_t i = 0; i < len; ++i) f_weighted_mean_mv(w[i], v[i], missval1, sum, sumw, is_NE);
         }
       wmean = DIVM(sum, sumw);
     }
@@ -969,7 +970,7 @@ varray_weighted_avg_mv(size_t len, const Varray<T> &v, const Varray<double> &w,
   double sum = 0.0, sumw = 0.0;
 
   for (size_t i = 0; i < len; ++i)
-    if (!dbl_is_equal(w[i], missval))
+    if (dbl_is_not_equal(w[i], missval))
       {
         sum = ADDM(sum, MULM(w[i], v[i]));
         sumw = ADDM(sumw, w[i]);
@@ -1009,7 +1010,7 @@ static void
 varray_prevarsum0_mv(size_t len, const Varray<T> &v, double missval, double &rsum, double &rsumw)
 {
   auto f_prevarsum0_mv = [](auto a, auto mv_a, auto &sum, auto &sumw) {
-    if (!dbl_is_equal(a, mv_a))
+    if (dbl_is_not_equal(a, mv_a))
       {
         sum += a;
         sumw += 1.0;
@@ -1077,7 +1078,7 @@ static void
 varray_prevarsum_mv(size_t len, const Varray<T> &v, T missval, double &rsum, double &rsumw, double &rsumq, double &rsumwq)
 {
   auto f_prevarsum = [](auto a, auto mv_a, auto &sum, auto &sumq, auto &sumw, auto &sumwq) {
-    if (!dbl_is_equal(a, mv_a))
+    if (dbl_is_not_equal(a, mv_a))
       {
         double ad = (double) a;
         sum += ad;
@@ -1177,7 +1178,7 @@ varray_weighted_prevarsum_mv(size_t len, const Varray<T> &v, const Varray<double
                              double &rsumq, double &rsumwq)
 {
   auto f_weighted_prevarsum_mv = [](auto aw, auto a, auto mv_a, auto &sum, auto &sumq, auto &sumw, auto &sumwq) {
-    if (!dbl_is_equal(a, mv_a) && !dbl_is_equal(aw, mv_a))
+    if (dbl_is_not_equal(a, mv_a) && dbl_is_not_equal(aw, mv_a))
       {
         sum += aw * a;
         sumq += aw * a * a;
@@ -1226,7 +1227,8 @@ 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 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 double varray_weighted_var(size_t len, const Varray<double> &v, const Varray<double> &w, size_t numMissVals,
+                                    double missval);
 
 template <typename T>
 double
@@ -1245,8 +1247,10 @@ 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 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 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
@@ -1286,7 +1290,7 @@ static void
 varray_prekurtsum_mv(size_t len, const Varray<T> &v, T missval, double mean, double &rsum3w, double &rsum2diff, double &rsum4diff)
 {
   auto f_prekurtsum_mv = [](auto a, auto mv_a, auto meanval, auto &sum2diff, auto &sum4diff, auto &sum3w) {
-    if (!dbl_is_equal(a, mv_a))
+    if (dbl_is_not_equal(a, mv_a))
       {
         double vdiff = a - meanval;
         sum2diff += vdiff * vdiff;
@@ -1387,7 +1391,7 @@ static void
 varray_preskewsum_mv(size_t len, const Varray<T> &v, T missval, double mean, double &rsum3w, double &rsum3diff, double &rsum2diff)
 {
   auto f_preskewsum_mv = [](auto a, auto mv_a, auto meanval, auto &sum3diff, auto &sum2diff, auto &sum3w) {
-    if (!dbl_is_equal(a, mv_a))
+    if (dbl_is_not_equal(a, mv_a))
       {
         double vdiff = a - meanval;
         sum3diff += vdiff * vdiff * vdiff;
@@ -1497,7 +1501,7 @@ varray_median(size_t len, const Varray<T> &v, size_t numMissVals, 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];
+        if (dbl_is_not_equal(v[i], missval)) v2[count++] = v[i];
       if (count > 0 && count < len) median = f_median(count, v2);
     }
 
@@ -1523,7 +1527,7 @@ varray_count(size_t len, const Varray<T> &v, size_t numMissVals, T missval)
       count = 0;
       for (size_t i = 0; i < len; ++i)
         {
-          if (!dbl_is_equal(v[i], missval)) count++;
+          if (dbl_is_not_equal(v[i], missval)) count++;
         }
     }
 
diff --git a/src/varray.h b/src/varray.h
index d3c7bf82a9e146b3a8b1eb21c2b106f66f7d8083..5027c60864671404e80f3569dcb139c056489cda 100644
--- a/src/varray.h
+++ b/src/varray.h
@@ -13,6 +13,7 @@
 #include <cstddef>
 #include <cfloat>
 #include <cassert>
+#include "cpp_lib.h"
 #include "compare.h"
 
 namespace ranges = std::ranges;
@@ -78,7 +79,7 @@ CheckVector
   T * end() noexcept { return &ptr[0] + 1; }
   const T * begin() const noexcept { return &ptr[0]; }
   const T * end() const noexcept { return &ptr[0] + 1; }
-  
+
   bool empty() const { return true; }
   size_t size() const { return m_count; }
   void clear() { }
diff --git a/src/vertical_interp.cc b/src/vertical_interp.cc
index c2c96de248e6cd530b463a425298ba83121b077b..1b0789a7e1342e8ebd9d466c439406521854bfdf 100644
--- a/src/vertical_interp.cc
+++ b/src/vertical_interp.cc
@@ -14,7 +14,6 @@
 #include <cmath>
 #include <cassert>
 
-#include "compare.h"
 #include "varray.h"
 #include "constants.h"
 #include "vertical_interp.h"
@@ -337,7 +336,8 @@ vertical_interp_X(const T *restrict arrayIn3D, T *restrict arrayOut3D, const T *
 template void vertical_interp_X(const float *restrict arrayIn3D, float *arrayOut3D, const float *levels3D, const int *vertIndex3D,
                                 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 numGP, long nhlev, double missval);
+                                const int *vertIndex3D, const double *levels, long numLevels, long numGP, long nhlev,
+                                double missval);
 
 template <typename T>
 void
@@ -422,5 +422,5 @@ 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 *pnumMissVals,
                                 bool lreverse);
-template void gen_vert_index_mv(int *vertIndex, const double *plev, long ngp, long nplev, const double *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 *pnumMissVals, bool lreverse);
diff --git a/src/vertint_util.cc b/src/vertint_util.cc
index 053cf68274eff51eb4785ae3f4022c5814b42212..671aabd8df251ed1d1476b28f5233d160bb863d3 100644
--- a/src/vertint_util.cc
+++ b/src/vertint_util.cc
@@ -1,5 +1,4 @@
-#include <ctype.h>
-#include <stdlib.h>
+#include <cstdlib>
 
 #include "cdo_output.h"
 
diff --git a/src/zaxis_print.cc b/src/zaxis_print.cc
index 02ed21f7da5cf77c87ee1239edfe44b7b023a1bd..89124078b3f56f182f770a4b59ac3485f57c2ab3 100644
--- a/src/zaxis_print.cc
+++ b/src/zaxis_print.cc
@@ -6,11 +6,12 @@
 */
 #include <cdi.h>
 
+#include <cstring>
+
 #include "cdi_uuid.h"
 #include "cdo_options.h"
-#include "process_int.h"
+#include "cdo_cdi_wrapper.h"
 
-void cdoPrintAttributes(FILE *fp, int cdiID, int varID, int nblanks);
 
 static void
 printDblsPrefixAutoBrk(FILE *fp, int dig, const char *prefix, size_t n, const double vals[], size_t extbreak)
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..0e0a5545cf17e6416c9dc331e002a75169a683bc
--- /dev/null
+++ b/test/CMakeLists.txt
@@ -0,0 +1,2 @@
+add_subdirectory(bandit_tests)
+add_subdirectory(pytest)
diff --git a/test/bandit_tests/CMakeLists.txt b/test/bandit_tests/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..21d9d796ac1bab15e51d68e4d1a67e14d705bb2c
--- /dev/null
+++ b/test/bandit_tests/CMakeLists.txt
@@ -0,0 +1,30 @@
+cmake_minimum_required( VERSION 3.12 FATAL_ERROR )
+project(cdo_test VERSION 2.4.0 LANGUAGES C CXX )
+
+add_subdirectory(bandit)
+set(HAVE_NETCDF ${netCDF_FOUND} )
+
+set(SOURCES parser.cc  test_module_list.h)
+
+add_executable(test_parser                parser.cc )
+add_executable(test_module_interface      module_interface.cc)
+add_executable(test_process_init          process_init.cc)
+add_executable(test_module_definitions    module_definitions.cc)
+add_executable(test_param_conversion_test param_conversion_test.cc)
+add_executable(test_pmlist                pmlist.cc)
+add_executable(test_util_string           util_string.cc)
+add_executable(test_wildcards             wildcards.cc)
+add_executable(test_operator_args         operator_args.cc)
+
+set(labels_test_wildcards ENABLE_ON_HAVE_WORDEXP)
+
+get_property(current_targets DIRECTORY ${dir} PROPERTY BUILDSYSTEM_TARGETS)
+foreach(target ${current_targets})
+  target_include_directories(${target} PUBLIC bandit/)
+  target_link_libraries(${target} cdolib cdilib)
+  add_test(NAME ${target} COMMAND ${target})
+  if(labels_${target})
+    set_property(TEST ${target} PROPERTY LABELS labels_${target})
+  endif()
+endforeach()
+
diff --git a/test/bandit_tests/Seltime_test.cc b/test/bandit_tests/Seltime_test.cc
index 920c1c5bf80a79896ecefa776c256530f7d9c390..e06c33d0ffa7f99305e55850ba6b8a93676fdebc 100644
--- a/test/bandit_tests/Seltime_test.cc
+++ b/test/bandit_tests/Seltime_test.cc
@@ -12,8 +12,9 @@
 #include "../../src/Select.cc"
 #include "../../src/Seltime.cc"
 void
-cdoTestExit()
+cdoTestExit(std::string errmsg = "SeltimeTestError")
 {
+  (void) errmsg;
   exit(EXIT_FAILURE);
 }
 std::string testContext = "SeltimeTest";
diff --git a/test/bandit_tests/operator_args.cc b/test/bandit_tests/operator_args.cc
new file mode 100644
index 0000000000000000000000000000000000000000..3e744e5145584ebbbbd2c2b602aa747d527af30a
--- /dev/null
+++ b/test/bandit_tests/operator_args.cc
@@ -0,0 +1,159 @@
+#include "bandit/bandit/bandit.h"
+#include <iostream>
+#include <stdlib.h>
+
+#include "../../src/cdo_module.h"
+#include "../../src/factory.h"
+#include "../../src/modules.h"
+#include "../../src/param_conversion.h"
+#include "../../src/process.h"
+
+using namespace snowhouse;
+
+#include "test_module_list.h"
+class OptionsModule : 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 },
+    .arguments = {
+        required("required", convert<std::string>),
+        optional("optional", convert<double>),
+        optional(
+            "lambda", [](std::string val) -> size_t { return val.size(); }, "exclusive"),
+        optional(
+            "exclusive", [](std::string val) -> size_t { return val.size(); }, "lambda"),
+    },
+  };
+  inline static RegisterEntry<TestModule> registration
+      = RegisterEntry<TestModule>(module);
+
+  void required_and_optional(
+      size_t& lam,
+      std::string& required,
+      double& optional)
+  {
+    parse_arguments();
+
+    get_argument("required", required);
+    get_argument("optional", optional);
+    get_argument("lambda", lam);
+  }
+  void required_missing()
+  {
+    parse_arguments();
+
+    std::string required;
+    get_argument("required", required);
+  }
+  void excl()
+  {
+    parse_arguments();
+
+    size_t lambda;
+    size_t excl;
+    get_argument("lambda", lambda);
+    get_argument("exclusive", excl);
+  }
+  void no_value()
+  { /* TODO */
+
+    parse_arguments();
+  }
+
+  void bad_any_cast()
+  {
+    parse_arguments();
+
+    size_t optional;
+    get_argument("optional", optional);
+  }
+};
+void testExit(std::string msg)
+{
+  throw std::invalid_argument(msg);
+}
+go_bandit([]() {
+  // setup
+  cdo::progname = "cdo_operator_args_test";
+  cdo::set_exit_function(testExit);
+
+  // tests
+  bandit::describe("the operator arguments",
+      [&]() {
+        bandit::it("throw an error if a required arg is missing", [&]() {
+          int p_ID = 0;
+          std::string operName = "oper1-1";
+          std::vector<std::string> operatorArguments = { "optional=1.337" };
+          auto new_process = std::make_shared<OptionsModule>(0, operName, operatorArguments, OptionsModule::module);
+
+          AssertThrows(std::invalid_argument, new_process->required_missing());
+          AssertThat(LastException<std::invalid_argument>().what(),
+              Contains("required"));
+        });
+        bandit::it("throw an error no value was given", [&]() {
+          int p_ID = 0;
+          std::string operName = "oper1-1";
+          std::vector<std::string> operatorArguments = { "optional=" };
+          auto new_process = std::make_shared<OptionsModule>(0, operName, operatorArguments, OptionsModule::module);
+
+          AssertThrows(std::invalid_argument, new_process->no_value());
+          AssertThat(LastException<std::invalid_argument>().what(),
+              Contains("no value"));
+        });
+
+        bandit::it("does not allow mutualy exclusive args", [&]() {
+          int p_ID = 0;
+          std::string operName = "oper1-1";
+          std::vector<std::string> operatorArguments = { "lambda=1234", "exclusive=1234" };
+          auto new_process = std::make_shared<OptionsModule>(0, operName, operatorArguments, OptionsModule::module);
+
+          AssertThrows(std::invalid_argument, new_process->excl());
+          AssertThat(LastException<std::invalid_argument>().what(),
+              Contains("can not be combined with any of"));
+        });
+        bandit::it("handles bad any casts and gives a useful error", [&]() {
+          int p_ID = 0;
+          std::string operName = "oper1-1";
+          std::vector<std::string> operatorArguments = { "optional=1335" };
+          auto new_process = std::make_shared<OptionsModule>(0, operName, operatorArguments, OptionsModule::module);
+
+          AssertThrows(std::invalid_argument, new_process->bad_any_cast());
+          AssertThat(LastException<std::invalid_argument>().what(),
+              Contains("Mismatch while getting argument"));
+        });
+
+        bandit::it("have the right values", [&]() {
+          int p_ID = 0;
+          std::string operName = "oper1-1";
+          std::vector<std::string> operatorArguments = { "required=test", "optional=1.337", "lambda=lam" };
+          auto new_process = std::make_shared<OptionsModule>(0, operName, operatorArguments, OptionsModule::module);
+
+          size_t lam;
+          std::string required;
+          double optional;
+
+          new_process->required_and_optional(lam, required, optional);
+
+          AssertThat(lam, Equals(3));
+          AssertThat(required, Equals("test"));
+          AssertThat(optional, Equals(1.337));
+        });
+      });
+});
+
+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/parser.cc b/test/bandit_tests/parser.cc
index 031cca553f63193e8ebb60d6033d492aa6e9074e..16474952784c10f2c891111a45aa9a203ec5b3d6 100644
--- a/test/bandit_tests/parser.cc
+++ b/test/bandit_tests/parser.cc
@@ -2,18 +2,18 @@
 // BANDIT NEEDS TO BE INCLUDED FIRST!!!
 
 #include <iostream>
-#include <vector>
+#include <iterator>
 #include <memory>
 #include <string>
-#include <iterator>
+#include <vector>
 
-#include "../../src/parser.h"
-#include "../../src/node.h"
+#include "../../src/cdo_exception.h"
+#include "../../src/cdo_node_attach_exception.h"
+#include "../../src/cdo_options.h"
 #include "../../src/modules.h"
+#include "../../src/node.h"
+#include "../../src/parser.h"
 #include "../../src/process_int.h"
-#include "../../src/cdo_options.h"
-#include "../../src/cdo_node_attach_exception.h"
-#include "../../src/cdo_exception.h"
 
 #include "test_module_list.h"
 
@@ -21,18 +21,17 @@
 using namespace snowhouse;
 
 // Required functions
-void
-cdoExit()
+void cdoExit(std::string msg)
 {
 }
-const char *
+const char*
 test_process_inq_prompt(void)
 {
-  static const char *context = "cdo_test";
+  static const char* context = "cdo_test";
   return context;
 }
 
-const char *
+const char*
 process_inq_err_prompt(void)
 {
   return "cdo (Abort): ";
@@ -42,93 +41,79 @@ unsigned int
 getRequiredElements(std::vector<std::string> input)
 {
   unsigned int numNodes = 0;
-  for (auto x : input)
-    {
-      if (x != "[" && x != "]" && x != ":")
-        {
-          numNodes += 1;
-        }
+  for (auto x : input) {
+    if (x != "[" && x != "]" && x != ":") {
+      numNodes += 1;
     }
+  }
   return numNodes;
 }
 
 unsigned int
 getNumChildren(std::shared_ptr<Node> root)
 {
-  if (root->children.size() == 0) return 1;
+  if (root->children.size() == 0)
+    return 1;
   unsigned int sum = 1;
-  for (auto &c : root->children)
-    {
-      sum += getNumChildren(c);
-    }
+  for (auto& c : root->children) {
+    sum += getNumChildren(c);
+  }
   return sum;
 }
 
-void
-check(std::string description, std::vector<std::string> in, std::string out)
+void check(std::string description, std::vector<std::string> in, std::string out)
 {
   bandit::it(description, [&]() {
     std::string err_location = " ";
     std::string node_structure = "";
     unsigned numChildrenExpected = -1337;
-    try
-      {
-        auto res = Parser::parse(in, test_process_inq_prompt);
-        AssertThat(res, Is().OfLength(1));
-        node_structure = res[0]->to_string();
-        numChildrenExpected = getNumChildren(res[0]);
-        AssertThat(node_structure, Equals(out));
-        AssertThat(numChildrenExpected, Equals(getRequiredElements(in)));
-      }
-    catch (CdoException &exp)
-      {
-        err_location = "thrown from: " + exp.file + ":" + exp.line;
-        AssertionException(exp.what() + std::string("\n") + err_location);
-      }
+    try {
+      auto res = Parser::parse(in, test_process_inq_prompt);
+      AssertThat(res, Is().OfLength(1));
+      node_structure = res[0]->to_string();
+      numChildrenExpected = getNumChildren(res[0]);
+      AssertThat(node_structure, Equals(out));
+      AssertThat(numChildrenExpected, Equals(getRequiredElements(in)));
+    } catch (CdoException& exp) {
+      err_location = "thrown from: " + exp.file + ":" + exp.line;
+      AssertionException(exp.what() + std::string("\n") + err_location);
+    }
   });
 }
 
-void
-checkNegative(std::string description, std::vector<std::string> in,
-              std::string expected_err_msg)
+void checkNegative(std::string description, std::vector<std::string> in,
+    std::string expected_err_msg)
 {
   bandit::it(description, [&, in]() {
     AssertThrows(CdoSyntaxError, Parser::_parse(in));
     AssertThat(LastException<CdoSyntaxError>().what(),
-               Contains(expected_err_msg));
+        Contains(expected_err_msg));
   });
 }
 
-void
-checkApply(std::string description, std::vector<std::string> in,
-           std::string out, unsigned int numChildren)
+void checkApply(std::string description, std::vector<std::string> in,
+    std::string out, unsigned int numChildren)
 {
   bandit::it(description, [&]() {
     std::string node_structure;
     std::string err_location = "";
     unsigned numChildrenExpected;
-    try
-      {
-        auto res = Parser::parse(in, test_process_inq_prompt);
-        if (res.size() > 0)
-          {
-            node_structure = res[0]->to_string();
-            numChildrenExpected = getNumChildren(res[0]);
-          }
-        else
-          {
-            node_structure = "";
-            numChildrenExpected = -1337;
-          }
-        AssertThat(numChildrenExpected, Equals(numChildren));
-        AssertThat(node_structure, Equals(out));
-        AssertThat(res, Is().OfLength(1));
-      }
-    catch (CdoException &exp)
-      {
-        err_location = "thrown from: " + exp.file + ":" + exp.line;
-        AssertionException(exp.what() + std::string("\n") + err_location);
+    try {
+      auto res = Parser::parse(in, test_process_inq_prompt);
+      if (res.size() > 0) {
+        node_structure = res[0]->to_string();
+        numChildrenExpected = getNumChildren(res[0]);
+      } else {
+        node_structure = "";
+        numChildrenExpected = -1337;
       }
+      AssertThat(numChildrenExpected, Equals(numChildren));
+      AssertThat(node_structure, Equals(out));
+      AssertThat(res, Is().OfLength(1));
+    } catch (CdoException& exp) {
+      err_location = "thrown from: " + exp.file + ":" + exp.line;
+      AssertionException(exp.what() + std::string("\n") + err_location);
+    }
   });
 }
 
@@ -146,166 +131,166 @@ go_bandit([]() {
   bandit::describe("Parser", [&]() {
     bandit::describe("The Core Functionality", [&]() {
       check("handles a single operator with in and output files",
-            { "-in1_out1", "in", "out" }, "out [ in1_out1 [ in ] ]");
+          { "-in1_out1", "in", "out" }, "out [ in1_out1 [ in ] ]");
       check("handles a single operator with 2 in files and a single output "
             "file",
-            { "-in2_out1", "infile1", "infile2", "out" },
-            "out [ in2_out1 [ infile1 infile2 ] ]");
+          { "-in2_out1", "infile1", "infile2", "out" },
+          "out [ in2_out1 [ infile1 infile2 ] ]");
       check("handles a single operator with 0 input and a single output",
-            { "-in0_out1", "out" }, "out [ in0_out1 ]");
+          { "-in0_out1", "out" }, "out [ in0_out1 ]");
 
       check("handles a single operator with 1 input and no output",
-            { "-in1_out0", "in" }, "in1_out0 [ in ]");
+          { "-in1_out0", "in" }, "in1_out0 [ in ]");
 
       check("handles a single operator with variable input and no output",
-            { "-inVariable_out0", "infile1", "infile2", "infile3" },
-            "inVariable_out0 [ infile1 infile2 infile3 ]");
+          { "-inVariable_out0", "infile1", "infile2", "infile3" },
+          "inVariable_out0 [ infile1 infile2 infile3 ]");
 
       check("handles multiple variable input operators without the fist one "
             "requireing brackets",
-            { "-inVariable_out0", "-inVariable_out1", "[", "infile1", "infile2",
+          { "-inVariable_out0", "-inVariable_out1", "[", "infile1", "infile2",
               "infile3", "]" },
-            "inVariable_out0 [ inVariable_out1 [ infile1 infile2 infile3 ] ]");
+          "inVariable_out0 [ inVariable_out1 [ infile1 infile2 infile3 ] ]");
 
       check("handles a single operator with 2 input and no output",
-            { "-in2_out0", "infile1", "infile2" },
-            "in2_out0 [ infile1 infile2 ]");
+          { "-in2_out0", "infile1", "infile2" },
+          "in2_out0 [ infile1 infile2 ]");
 
       check("handles a operator with obase feature "
             "subgroups",
-            { "-in2_outObase", "f1", "f2", "obase" },
-            "obase [ in2_outObase [ f1 f2 ] ]");
+          { "-in2_outObase", "f1", "f2", "obase" },
+          "obase [ in2_outObase [ f1 f2 ] ]");
 
       check("handles a operator that takes only files ",
-            { "files_only", "f1", "out" }, "out [ files_only [ f1 ] ]");
+          { "files_only", "f1", "out" }, "out [ files_only [ f1 ] ]");
     });
     bandit::describe("Variable input Functionality", [&]() {
       check("handles a single operator with variable inputs (1) and a single "
             "outfile",
-            { "-inVariable_out1", "infile1", "out" },
-            "out [ inVariable_out1 [ infile1 ] ]");
+          { "-inVariable_out1", "infile1", "out" },
+          "out [ inVariable_out1 [ infile1 ] ]");
 
       check("handles a single operator with variable inputs (2) and a single "
             "outfile",
-            { "-inVariable_out1", "infile1", "infile2", "out" },
-            "out [ inVariable_out1 [ infile1 infile2 ] ]");
+          { "-inVariable_out1", "infile1", "infile2", "out" },
+          "out [ inVariable_out1 [ infile1 infile2 ] ]");
 
       check("handles a single operator with variable inputs (3) and a single "
             "outfile",
-            { "-inVariable_out1", "infile1", "infile2", "infile3", "out" },
-            "out [ inVariable_out1 [ infile1 infile2 infile3 ] ]");
+          { "-inVariable_out1", "infile1", "infile2", "infile3", "out" },
+          "out [ inVariable_out1 [ infile1 infile2 infile3 ] ]");
 
       check("handles nested subgroups in subgrups",
-            { "-inVariable_out1", "[", "[", "infile1", "infile2", "]", "[",
+          { "-inVariable_out1", "[", "[", "infile1", "infile2", "]", "[",
               "infile3", "infile4", "]", "]", "out" },
-            "out [ inVariable_out1 [ infile1 infile2 infile3 infile4 ] ]");
+          "out [ inVariable_out1 [ infile1 infile2 infile3 infile4 ] ]");
     });
 
     check("calls that are decidale are accepted (1)",
-          { "-inVariable_out1", "-inVariable_out1", "-inVariable_out1", "[",
+        { "-inVariable_out1", "-inVariable_out1", "-inVariable_out1", "[",
             "infile1", "infile2", "]", "out" },
-          "out [ inVariable_out1 [ inVariable_out1 [ inVariable_out1 [ infile1 "
-          "infile2 ] ] ] ]");
+        "out [ inVariable_out1 [ inVariable_out1 [ inVariable_out1 [ infile1 "
+        "infile2 ] ] ] ]");
     check(
         "calls that are decidale are accepted (2)",
         { "-inVariable_out1", "-in1_out1", "-inVariable_out1", "-in0_out1",
-          "out" },
+            "out" },
         "out [ inVariable_out1 [ in1_out1 [ inVariable_out1 [ in0_out1 ] ] ] ]");
     check("calls that are decidale are accepted (3)",
-          { "-inVariable_out1", "-in1_out1", "-inVariable_out1", "-in1_out1",
+        { "-inVariable_out1", "-in1_out1", "-inVariable_out1", "-in1_out1",
             "infile1", "out" },
-          "out [ inVariable_out1 [ in1_out1 [ inVariable_out1 [ in1_out1 [ "
-          "infile1 ] ] ] ] ]");
+        "out [ inVariable_out1 [ in1_out1 [ inVariable_out1 [ in1_out1 [ "
+        "infile1 ] ] ] ] ]");
 
     bandit::describe("Subgroup Functionality", [&]() {
       check("handles a multiple nested variable input operators while using "
             "subgroups",
-            { "-inVariable_out1", "[", "infile1", "-inVariable_out1", "infile2",
+          { "-inVariable_out1", "[", "infile1", "-inVariable_out1", "infile2",
               "infile3", "]", "out" },
-            "out [ inVariable_out1 [ infile1 inVariable_out1 [ infile2 infile3 "
-            "] ] "
-            "]");
+          "out [ inVariable_out1 [ infile1 inVariable_out1 [ infile2 infile3 "
+          "] ] "
+          "]");
 
       check("handles mixed input (files then operators)",
-            { "-inVariable_out1", "[", "infile1", "-in0_out1", "]", "out" },
-            "out [ inVariable_out1 [ infile1 in0_out1 ] ]");
+          { "-inVariable_out1", "[", "infile1", "-in0_out1", "]", "out" },
+          "out [ inVariable_out1 [ infile1 in0_out1 ] ]");
 
       check("handles mixed input (operators then files)",
-            { "-inVariable_out1", "[", "-in0_out1", "infile1", "]", "out" },
-            "out [ inVariable_out1 [ in0_out1 infile1 ] ]");
+          { "-inVariable_out1", "[", "-in0_out1", "infile1", "]", "out" },
+          "out [ inVariable_out1 [ in0_out1 infile1 ] ]");
 
       check("handles only files",
-            { "-inVariable_out1", "[", "infile1", "infile2", "]", "out" },
-            "out [ inVariable_out1 [ infile1 infile2 ] ]");
+          { "-inVariable_out1", "[", "infile1", "infile2", "]", "out" },
+          "out [ inVariable_out1 [ infile1 infile2 ] ]");
 
       check("handles operators with no input",
-            { "-inVariable_out1", "[", "-in0_out1", "-in0_out1", "]", "out" },
-            "out [ inVariable_out1 [ in0_out1 in0_out1 ] ]");
+          { "-inVariable_out1", "[", "-in0_out1", "-in0_out1", "]", "out" },
+          "out [ inVariable_out1 [ in0_out1 in0_out1 ] ]");
 
       check("handles multiple variable input operators",
-            { "-inVariable_out1", "[", "-inVariable_out1", "[", "-in0_out1",
+          { "-inVariable_out1", "[", "-inVariable_out1", "[", "-in0_out1",
               "-in0_out1", "]", "-inVariable_out1", "[", "infile1", "infile2",
               "]", "]", "outfile" },
-            "outfile [ inVariable_out1 [ inVariable_out1 [ in0_out1 in0_out1 ] "
-            "inVariable_out1 [ infile1 infile2 ] ] ]");
+          "outfile [ inVariable_out1 [ inVariable_out1 [ in0_out1 in0_out1 ] "
+          "inVariable_out1 [ infile1 infile2 ] ] ]");
 
       check("handles nested variable input operators",
-            { "-inVariable_out1", "[", "[", "infile", "-inVariable_out1",
+          { "-inVariable_out1", "[", "[", "infile", "-inVariable_out1",
               "file1", "file2", "]", "-inVariable_out1", "[", "file3", "]", "]",
               "out" },
-            "out [ inVariable_out1 [ infile inVariable_out1 [ file1 file2 ] "
-            "inVariable_out1 [ file3 ] ] ]");
+          "out [ inVariable_out1 [ infile inVariable_out1 [ file1 file2 ] "
+          "inVariable_out1 [ file3 ] ] ]");
 
       check("handles nested variable input operators and a unbracketed "
             "variable input at the end",
-            { "-inVariable_out1", "[", "[", "infile", "-inVariable_out1",
+          { "-inVariable_out1", "[", "[", "infile", "-inVariable_out1",
               "file1", "file2", "]", "-inVariable_out1", "file3", "]", "out" },
-            "out [ inVariable_out1 [ infile inVariable_out1 [ file1 file2 ] "
-            "inVariable_out1 [ file3 ] ] ]");
+          "out [ inVariable_out1 [ infile inVariable_out1 [ file1 file2 ] "
+          "inVariable_out1 [ file3 ] ] ]");
 
       check("handles finished variable input correctly "
             "variable input at the end",
-            { "-in2_out1", "-inVariable_out1", "[", "file1", "file2", "]",
+          { "-in2_out1", "-inVariable_out1", "[", "file1", "file2", "]",
               "-inVariable_out1", "file3", "out" },
-            "out [ in2_out1 [ inVariable_out1 [ file1 file2 ] inVariable_out1 "
-            "[ file3 ] ] ]");
+          "out [ in2_out1 [ inVariable_out1 [ file1 file2 ] inVariable_out1 "
+          "[ file3 ] ] ]");
 
       check("handles preceeding operator of finished variable input correctly "
             "variable input at the end",
-            { "-in2_out1", "-inVariable_out1", "[", "file1", "file2", "]",
+          { "-in2_out1", "-inVariable_out1", "[", "file1", "file2", "]",
               "-inVariable_out1", "file3", "out" },
-            "out [ in2_out1 [ inVariable_out1 [ file1 file2 ] inVariable_out1 "
-            "[ file3 ] ] ]");
+          "out [ in2_out1 [ inVariable_out1 [ file1 file2 ] inVariable_out1 "
+          "[ file3 ] ] ]");
       check("handles preceeding operator of finished variable input correctly when positioned between in2out1 and variable",
-            { "-in2_out1", "-in1_out1", "-inVariable_out1", "[", "file1", "file2", "]",
+          { "-in2_out1", "-in1_out1", "-inVariable_out1", "[", "file1", "file2", "]",
               "-inVariable_out1", "file3", "out" },
-            "out [ in2_out1 [ in1_out1 [ inVariable_out1 [ file1 file2 ] ] inVariable_out1 "
-            "[ file3 ] ] ]");
+          "out [ in2_out1 [ in1_out1 [ inVariable_out1 [ file1 file2 ] ] inVariable_out1 "
+          "[ file3 ] ] ]");
     });
 
     bandit::describe("Apply feature", [&]() {
       checkApply(
           "handles merge with multiple groups as input",
           { "-inVariable_out1", "[", "[", "-in1_out1",
-            "E5ml00_1H_2000-01-01_129-1", "]", "[", "-in1_out1",
-            "E5ml00_1H_2000-01-01_152-1", "]", "]", "tmp1" },
+              "E5ml00_1H_2000-01-01_129-1", "]", "[", "-in1_out1",
+              "E5ml00_1H_2000-01-01_152-1", "]", "]", "tmp1" },
           "tmp1 [ inVariable_out1 [ in1_out1 [ E5ml00_1H_2000-01-01_129-1 ] "
           "in1_out1 [ E5ml00_1H_2000-01-01_152-1 ] ] ]",
           6);
 
       checkApply("handles apply with chains as argument",
-                 { "-inVariable_out0", "[", "-in1_out1", "-in1_out1", ":", "f1",
-                   "f2", "]" },
-                 "inVariable_out0 [ in1_out1 [ in1_out1 [ f1 ] ] in1_out1 [ "
-                 "in1_out1 [ "
-                 "f2 ] ] ]",
-                 7);
+          { "-inVariable_out0", "[", "-in1_out1", "-in1_out1", ":", "f1",
+              "f2", "]" },
+          "inVariable_out0 [ in1_out1 [ in1_out1 [ f1 ] ] in1_out1 [ "
+          "in1_out1 [ "
+          "f2 ] ] ]",
+          7);
 
       // DO NOT SOURROUND THE APPLY WITH \" does not work
       checkApply(
           "handles the old way apply worked",
           { "-inVariable_out1", "-apply,-in1_out1", "[", "infile1", "infile2",
-            "infile3", "]", "out" },
+              "infile3", "]", "out" },
           "out [ inVariable_out1 [ in1_out1 [ infile1 ] in1_out1 [ infile2 ] "
           "in1_out1 [ infile3 ] ] ]",
           8);
@@ -313,7 +298,7 @@ go_bandit([]() {
       checkApply(
           "Apply symbol '[ : ]' works",
           { "-inVariable_out1", "[", "-in1_out1", ":", "infile1", "infile2",
-            "infile3", "]", "out" },
+              "infile3", "]", "out" },
           "out [ inVariable_out1 [ in1_out1 [ infile1 ] in1_out1 [ infile2 ] "
           "in1_out1 [ infile3 ] ] ]",
           8);
@@ -324,16 +309,20 @@ go_bandit([]() {
     bandit::describe("Syntax Errors:", [&]() {
       std::vector<std::string> argv = { "in", "infile2" };
       checkNegative("fails on file at pos 1", argv,
-                    Parser::err_msg_oper_not_found(argv[0]));
+          Factory::err_msg_oper_not_found(argv[0]));
 
       argv = { "in", "in1_out1", "infile2", "out" };
       checkNegative("fails on file at pos 1 with other operators follwing",
-                    argv, Parser::err_msg_oper_not_found(argv[0]));
+          argv, Factory::err_msg_oper_not_found(argv[0]));
 
-      argv = { "in2_out1",      "[",  "infile1", "infile2", "]",
-               "file_too_much", "out" };
+      argv = { "in2_out1", "[", "infile1", "infile2", "]",
+        "file_too_much", "out" };
       checkNegative("aborts with an unattached file after subgroup", argv,
-                    Parser::errmsg_unprocessed_inputs);
+          Parser::errmsg_unprocessed_inputs);
+
+      argv = {"operatorDoesNotExist"};
+      checkNegative("aborts when operator was not found and prints similar operators", argv,
+          Factory::err_msg_oper_not_found(argv[0]));
 
       checkNegative(
           "aborts detects too much inputs",
@@ -342,18 +331,18 @@ go_bandit([]() {
 
       argv = { "only_a_file" };
       checkNegative("aborts when no in- and output are present (file)", argv,
-                    Parser::err_msg_oper_not_found(argv[0]));
+          Factory::err_msg_oper_not_found(argv[0]));
 
       argv = { "-in1_out1" };
       checkNegative("aborts when no in- and output are present (in1 out1)",
-                    argv, Parser::errmsg_missing_outputs);
+          argv, Parser::errmsg_missing_outputs);
       argv = { "-in1_out0" };
       checkNegative("aborts when no in- and output are present (in1 out0)",
-                    argv, Parser::errmsg_missing_inputs);
+          argv, Parser::errmsg_missing_inputs);
 
       argv = { "-in0_out1" };
       checkNegative("aborts when no in- and output are present (in0 out1)",
-                    argv, Parser::errmsg_missing_outputs);
+          argv, Parser::errmsg_missing_outputs);
 
       checkNegative(
           "error on a single operator with variable inputs (0) and a single "
@@ -365,90 +354,90 @@ go_bandit([]() {
           "grouping "
           "feature ",
           { "-inVariable_out1", "-inVariable_out1", "infile1", "infile2",
-            "out" },
+              "out" },
           Parser::errmsg_multiple_variable);
       checkNegative("multiple (3 where first has backets) variable input "
                     "operators are not allowed without "
                     "grouping "
                     "feature because the second and thrird variable inp oper are unassignable",
-                    { "-inVariable_out1", "[", "-inVariable_out1",
-                      "-inVariable_out1", "infile1", "infile2", "]", "out" },
-                    Parser::errmsg_multiple_variable);
+          { "-inVariable_out1", "[", "-inVariable_out1",
+              "-inVariable_out1", "infile1", "infile2", "]", "out" },
+          Parser::errmsg_multiple_variable);
 
       checkNegative("Missing inputs are detected in top most parser",
-                    { "-in1_out1", "-in1_out1", "out" },
-                    Parser::errmsg_missing_inputs);
+          { "-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);
+          { "-in1_out1", "-only_first_oper", "-in0_out1", "out" },
+          errmsg_node_not_in_first_position);
     });
     bandit::describe("Apply Errors:", [&]() {
       checkNegative("detects arguments with multiple inputs ",
-                    { "inVariable_out1", "[", "in2_out1", ":", "infile1",
-                      "infile2", "infile3", "]", "out" },
-                    Parser::errmsg_only_1_to_1_operators);
+          { "inVariable_out1", "[", "in2_out1", ":", "infile1",
+              "infile2", "infile3", "]", "out" },
+          Parser::errmsg_only_1_to_1_operators);
 
       checkNegative("apply detects if a file is in front",
-                    { "inVariable_out1", "WRONG", "[", "-in1_out1", ":",
-                      "infile1", "infile2", "infile3", "]", "out" },
-                    Parser::errmsg_mixed_input);
+          { "inVariable_out1", "WRONG", "[", "-in1_out1", ":",
+              "infile1", "infile2", "infile3", "]", "out" },
+          Parser::errmsg_mixed_input);
 
       checkNegative("apply detects if a operator is in front",
-                    { "inVariable_out1", "-in0_out1", "[", "-in1_out1", ":",
-                      "infile1", "infile2", "]", "out" },
-                    Parser::errmsg_mixed_input);
+          { "inVariable_out1", "-in0_out1", "[", "-in1_out1", ":",
+              "infile1", "infile2", "]", "out" },
+          Parser::errmsg_mixed_input);
 
       checkNegative(
           "apply detects if a subgroup returns to many for the target",
           { "inVariable_out1", "-in1_out1", "[", "-in1_out1", ":", "infile1",
-            "infile2", "]", "out" },
+              "infile2", "]", "out" },
           errmsg_node_to_many_inputs);
 
       checkNegative("aborts when apply is used as first operator",
-                    { "-apply,-in1_out1", "[", "-in0out1", "]", "out" },
-                    Parser::errmsg_apply_in_first_pos);
+          { "-apply,-in1_out1", "[", "-in0out1", "]", "out" },
+          Parser::errmsg_apply_in_first_pos);
 
       checkNegative(
           "aborts when old apply argument contains unkown operator",
           { "-inVariable_out1", "-apply,NOOPER", "[", "f1", "f2", "]", "out" },
-          Parser::err_msg_oper_not_found("NOOPER"));
+          Factory::err_msg_oper_not_found("NOOPER"));
       checkNegative("aborts when old apply has no inputs",
-                    { "-inVariable_out1", "-apply,-in1_out1", "out" },
-                    Parser::errmsg_apply_requires_bracket);
+          { "-inVariable_out1", "-apply,-in1_out1", "out" },
+          Parser::errmsg_apply_requires_bracket);
 
       checkNegative("aborts when old apply has no inputs but '[ ]'",
-                    { "-inVariable_out1", "-apply,-in1_out1", "[", "]", "out" },
-                    Parser::errmsg_apply_missing_argument);
+          { "-inVariable_out1", "-apply,-in1_out1", "[", "]", "out" },
+          Parser::errmsg_apply_missing_argument);
 
       checkNegative("aborts when new apply has no inputs",
-                    { "-inVariable_out1", "[", "-in1_out1", ":", "]", "out" },
-                    Parser::errmsg_apply_missing_argument);
+          { "-inVariable_out1", "[", "-in1_out1", ":", "]", "out" },
+          Parser::errmsg_apply_missing_argument);
 
       checkNegative("aborts when old apply has no inputs and "
                     "variable input has no output",
-                    { "-inVariable_out0", "-apply,-in1_out1" },
-                    Parser::errmsg_apply_requires_bracket);
+          { "-inVariable_out0", "-apply,-in1_out1" },
+          Parser::errmsg_apply_requires_bracket);
       checkNegative("aborts when old apply has the '[' but nothing else",
-                    { "-inVariable_out0", "-apply,-in1_out1", "[" },
-                    Parser::errmsg_bracket_not_closed);
+          { "-inVariable_out0", "-apply,-in1_out1", "[" },
+          Parser::errmsg_bracket_not_closed);
       checkNegative("detects a missing duplicate bracket",
-                    { "inVariable_out1", "[", "in2_out1", ":", "infile1",
-                      "infile2", "infile3", "out" },
-                    Parser::errmsg_bracket_not_closed);
+          { "inVariable_out1", "[", "in2_out1", ":", "infile1",
+              "infile2", "infile3", "out" },
+          Parser::errmsg_bracket_not_closed);
       checkNegative("apply only allows chains with single in and output",
-                    { "inVariable_out1", "[", "-in2_out0", "-in0_out1",
-                      "-in1_out1", ":", "infile1", "infile2", "]", "out" },
-                    Parser::errmsg_only_1_to_1_operators);
+          { "inVariable_out1", "[", "-in2_out0", "-in0_out1",
+              "-in1_out1", ":", "infile1", "infile2", "]", "out" },
+          Parser::errmsg_only_1_to_1_operators);
     });
     bandit::describe("SubGroups Errors:", [&]() {
       checkNegative(
           "handles nested subgroups in subgrups with too many brackets",
           { "-inVariable_out1", "[", "[", "infile1", "infile2", "]", "[", "[",
-            "infile3", "infile4", "]", "]", "out" },
+              "infile3", "infile4", "]", "]", "out" },
           Parser::errmsg_bracket_not_closed);
       checkNegative("empty [ ] are not ignored",
-                    { "-in2_out0", "infile1", "[", "]", "infile2", "[", "]" },
-                    Parser::errmsg_empty_subgroup);
+          { "-in2_out0", "infile1", "[", "]", "infile2", "[", "]" },
+          Parser::errmsg_empty_subgroup);
 
       checkNegative(
           "missing ']' bracket detected",
@@ -463,13 +452,13 @@ go_bandit([]() {
       checkNegative(
           "handles nested subgroups in subgrups with too many '[' brackets",
           { "-inVariable_out1", "[", "[", "infile1", "infile2", "]", "[", "[",
-            "infile3", "infile4", "]", "]", "out" },
+              "infile3", "infile4", "]", "]", "out" },
           Parser::errmsg_bracket_not_closed);
 
       checkNegative(
           "handles nested subgroups in subgrups with too many ']' brackets",
           { "-inVariable_out1", "[", "[", "infile1", "infile2", "]", "]", "[",
-            "infile3", "infile4", "]", "]", "out" },
+              "infile3", "infile4", "]", "]", "out" },
           Parser::errmsg_missing_sub_group);
       checkNegative(
           "handles a variable input operator inside a subgroup that has no "
@@ -480,8 +469,8 @@ go_bandit([]() {
 
     bandit::describe("Errors handled by Node instead of Parser", [&]() {
       checkNegative("abort when only_file operator has pipe",
-                    { "files_only", "-in0_out1", "out" },
-                    errmsg_node_only_accepts_files);
+          { "files_only", "-in0_out1", "out" },
+          errmsg_node_only_accepts_files);
       checkNegative(
           "using 0 output operators as input for other node is caught",
           { "-in1_out1", "-in1_out0", "out" }, errmsg_node_no_output);
@@ -492,19 +481,18 @@ go_bandit([]() {
           errmsg_node_to_many_inputs);
 
       checkNegative("detects malformed e.g. subgroup operators without inptu",
-                    { "-inVariable_out1", "[", "[", "-in2_out1", "]", "[",
-                      "infile2", "infile3", "]", "]", "out" },
-                    Parser::errmsg_missing_inputs);
+          { "-inVariable_out1", "[", "[", "-in2_out1", "]", "[",
+              "infile2", "infile3", "]", "]", "out" },
+          Parser::errmsg_missing_inputs);
     });
   });
 });
 
 //==============================================================================
 #define EXCEPTION_EXTRA_INFO = 1;
-int
-main(int argc, char **argv)
+int main(int argc, char** argv)
 {
-  std::vector<char *> argv_v(argv, argv + argc);
+  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());
diff --git a/test/bandit_tests/test_module_list.h b/test/bandit_tests/test_module_list.h
index 30cb82c99f20c6d44238bbc4a10a761adeb94a99..300e46c6be455e744519ad926b1556f09f8412ea 100644
--- a/test/bandit_tests/test_module_list.h
+++ b/test/bandit_tests/test_module_list.h
@@ -1,6 +1,7 @@
 #ifndef TEST_MODULE_LIST_H
 #define TEST_MODULE_LIST_H
 #include "../../src/modules.h"
+#include "../../src/factory.h"
 #include "../../src/process.h"
 #include "../../src/cdo_module.h"
 
diff --git a/test/bandit_tests/wildcards.cc b/test/bandit_tests/wildcards.cc
index b5a868789fa8eee735e7c06bae3e27511bfc777e..ac5af61fc2fa87ea2a81c58250192d48310f1f43 100644
--- a/test/bandit_tests/wildcards.cc
+++ b/test/bandit_tests/wildcards.cc
@@ -1,7 +1,7 @@
 #include "bandit/bandit/bandit.h"
 
 #ifdef HAVE_CONFIG_H
-#include "../../src/config.h"
+#include <config.h>
 #endif
 
 #ifdef HAVE_WORDEXP_H
diff --git a/test/pytest/Adisit.py.test.in b/test/pytest/Adisit.py.test.in
index 249a2e4e9d53dc4b675a5822fb3ec2458e08c138..46a5e9734c428bae8cc34be944bc66e2a575cd77 100644
--- a/test/pytest/Adisit.py.test.in
+++ b/test/pytest/Adisit.py.test.in
@@ -2,7 +2,7 @@
 
 from cdoTest import *
 
-IFILES=(DATAPATH +"mpiom_tho_sao.srv", DATAPATH + "adisit_ref")
+IFILES=(f'{DATAPATH}/mpiom_tho_sao.srv', f'{DATAPATH}/adisit_ref')
 
 FORMAT="-f srv -b 32"
 OPERATORS=["adisit" ,"adipot"]
diff --git a/test/pytest/Arith_extra.py.test.in b/test/pytest/Arith_extra.py.test.in
index a7408bff52bba4ab51c4119361937df0a807b684..605087c90d6325e1cff6a0f7d870b14603c3d569 100644
--- a/test/pytest/Arith_extra.py.test.in
+++ b/test/pytest/Arith_extra.py.test.in
@@ -4,7 +4,7 @@ from cdoTest import *
 import os
 
 CDOTESTDATA=os.getenv("CDOTESTDATA") or ""
-XTESTDIR=f'{CDOTESTDATA}/arith'
+XTESTDIR=f'{CDOTESTDATA}/arith/'
 
 HAS_NETCDF=cdo_check_req("has-nc")
 
diff --git a/test/pytest/CMakeLists.txt b/test/pytest/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..945b66baaecf25cd543030c6f37ce8c51cfe4d69
--- /dev/null
+++ b/test/pytest/CMakeLists.txt
@@ -0,0 +1,124 @@
+set(python_tests
+  Adisit.py
+  Afterburner.py
+  Arith.py
+  Arith_extra.py
+  Arithc.py
+  CDO_test.py
+  CMOR.py
+  Cat.py
+  Change.py
+  Collgrid.py
+  Comp.py
+  Compc.py
+  Cond.py
+  Cond2.py
+  Condc.py
+  Copy_netcdf.py
+  Dayarith.py
+  Detrend.py
+  EOF.py
+  EOFcoeff.py
+  Eca.py
+  Enspctl.py
+  Ensstat.py
+  Etccdi.py
+  Etccdi2.py
+  Expr.py
+  File.py
+  Filter.py
+  Fldpctl.py
+  Fldstat.py
+  Fldstat2.py
+  Genweights.py
+  Gradsdes.py
+  Gridarea.py
+  Gridboxstat.py
+  Importcmsaf.py
+  Intgrid.py
+  Inttime.py
+  Intyear.py
+  Isosurface.py
+  Maggraph.py
+  Magplot.py
+  Magvector.py
+  MapReduce.py
+  Maskregion.py
+  Math.py
+  Merge.py
+  Mergetime.py
+  Merstat.py
+  Monarith.py
+  Multiyearstat.py
+  Ninfo.py
+  Pack.py
+  Percentile.py
+  Read_grib.py
+  Read_netcdf.py
+  Remap.py
+  Remap2.py
+  Remap3.py
+  Remap4.py
+  Remap_extra.py
+  Remap_extra_file.py
+  Remap_extra_file2.py
+  Remap_global_5_grid.py
+  Remap_healpix.py
+  Remap_noweights.py
+  Remap_small.py
+  Remapeta.py
+  Remapstat.py
+  Runpctl.py
+  Runstat.py
+  Seasstat.py
+  Select.py
+  Selregion.py
+  Setmiss.py
+  Smooth.py
+  Spectral.py
+  Split.py
+  Timfillmiss.py
+  Timpctl.py
+  Timselpctl.py
+  Timselstat.py
+  Timstat.py
+  Timstat2.py
+  Timstat3.py
+  Vargen.py
+  Varsstat.py
+  Vertfillmiss.py
+  Vertint.py
+  Vertstat.py
+  Wind.py
+  Ydayarith.py
+  Ydrunpctl.py
+  Ydrunstat.py
+  Yeararith.py
+  Yearmonstat.py
+  Ymonarith.py
+  Zonstat.py
+  cdoReturnValues.py
+  threads.py
+  tsformat.py
+  userInput.py
+  wildcard.py
+  )
+
+#TODO: fix these paths this is horrible
+set(abs_top_builddir ${CMAKE_CURRENT_BINARY_DIR}/../..)
+set(abs_top_srcdir ${CMAKE_CURRENT_SOURCE_DIR}/../..)
+
+set(AM_TAP_AWK "/usr/bin/awk")
+set(TEST_LOG_DRIVER "${CMAKE_SOURCE_DIR}/config/tap-driver.sh")
+
+configure_file(cdoTest.py.in cdoTest.py @ONLY)
+
+foreach(python_test ${python_tests})
+  configure_file(${python_test}.test.in ${python_test} @ONLY)
+  add_test(NAME ${python_test} 
+    COMMAND python ${CMAKE_CURRENT_BINARY_DIR}/${python_test}
+    WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
+  set_property(TEST ${python_test} PROPERTY FAIL_REGULAR_EXPRESSION "FAILURE")
+  set_property(TEST ${python_test} PROPERTY SKIP_REGULAR_EXPRESSION "SKIP")
+endforeach()
+
diff --git a/test/pytest/Collgrid.py.test.in b/test/pytest/Collgrid.py.test.in
index 233cc23d88497e6449428fe0259844b288dd2949..a537b6a2ebd78a473091d1e5fc1eb8be776f251b 100644
--- a/test/pytest/Collgrid.py.test.in
+++ b/test/pytest/Collgrid.py.test.in
@@ -12,7 +12,7 @@ test_module = TestModule()
 for GRIDTYPE in GRIDTYPES:
     for DIST in DISTS:
         FILE=f'data{GRIDTYPE[0]}.nc'
-        IFILE=f'{DATAPATH}{FILE}'
+        IFILE=f'{DATAPATH}/{FILE}'
         OFILE=f'{OPERATOR}_{FILE}'
 
         if (HAS_NETCDF):
diff --git a/test/pytest/Comp.py.test.in b/test/pytest/Comp.py.test.in
index e9663004ac733c8968602f25d1a90454ee3e2454..2ef6522e3c717c33efb59719f52ec56968f29c4c 100644
--- a/test/pytest/Comp.py.test.in
+++ b/test/pytest/Comp.py.test.in
@@ -6,13 +6,13 @@ FORMAT="-f srv -b 32"
 OPERATORS=["eq", "ne", "le", "lt", "ge", "gt"]
 CONST=300
 
-IFILE=f'{DATAPATH}comptest.srv'
+IFILE=f'{DATAPATH}/comptest.srv'
 CFILE="constdata"
 
 test_module = TestModule()
 test_module.prepare(f'{CDO} {FORMAT} const,{CONST},{IFILE} {CFILE}')
 for OPERATOR in OPERATORS:
-    RFILE=f'{DATAPATH}comp_{OPERATOR}c_ref'
+    RFILE=f'{DATAPATH}/comp_{OPERATOR}c_ref'
     OFILE=f'comp_{OPERATOR}_res'
 
     t = TAPTest(OPERATOR)
diff --git a/test/pytest/Copy_netcdf.py.test.in b/test/pytest/Copy_netcdf.py.test.in
index ce8580ddeffc35c2ef8860ee589e38c0d99a0aea..2bec3faab126c7b655f25357319d72186e9db390 100644
--- a/test/pytest/Copy_netcdf.py.test.in
+++ b/test/pytest/Copy_netcdf.py.test.in
@@ -10,7 +10,7 @@ HAS_NETCDF=cdo_check_req("has-nc")
 
 test_module = TestModule()
 for FILE in FILES:
-    IFILE=f'{DATAPATH}{FILE}'
+    IFILE=f'{DATAPATH}/{FILE}'
     OFILE=f'{OPERATOR}_{FILE}'
 
     if (HAS_NETCDF):
diff --git a/test/pytest/Dayarith.py.test.in b/test/pytest/Dayarith.py.test.in
index 32438b874885edbd6cc46ba5376100c83c34f476..43b4a9892118e9f014deb177b5dd5eb3d12552d7 100644
--- a/test/pytest/Dayarith.py.test.in
+++ b/test/pytest/Dayarith.py.test.in
@@ -10,7 +10,7 @@ OPERATORS=["dayadd", "daysub", "daymul", "daydiv"]
 
 test_module = TestModule()
 for OPER in OPERATORS:
-    RFILE=f'{DATAPATH}{OPER}_ref'
+    RFILE=f'{DATAPATH}/{OPER}_ref'
     OFILE=f'{OPER}_res'
     t = TAPTest(OPER)
     t.add(f'{CDO} {FORMAT} {OPER} {IFILE1} {IFILE2} {OFILE}')
diff --git a/test/pytest/Detrend.py.test.in b/test/pytest/Detrend.py.test.in
index ed8bd4650278e608bb4dbf7dc2df1a61ea1ed061..bdb1d5c4cbdbbd30e4f1bb33bb8fe98a13f09dcb 100644
--- a/test/pytest/Detrend.py.test.in
+++ b/test/pytest/Detrend.py.test.in
@@ -4,9 +4,9 @@ from cdoTest import *
 #
 OPERATORS=["detrend","trend","subtrend"]
 #
-IFILE=f'{DATAPATH}detrend_data'
+IFILE=f'{DATAPATH}/detrend_data'
 #
-RFILE=f'{DATAPATH}detrend_ref'
+RFILE=f'{DATAPATH}/detrend_ref'
 OFILE="detrend_res"
 
 test_module = TestModule()
diff --git a/test/pytest/EOFcoeff.py.test.in b/test/pytest/EOFcoeff.py.test.in
index 16bd8d96ca5bee34e964e0e1779d922bf1480e1e..d275b3453b58fa3f08363781b98600d80282cd0a 100644
--- a/test/pytest/EOFcoeff.py.test.in
+++ b/test/pytest/EOFcoeff.py.test.in
@@ -10,8 +10,8 @@ os.environ['CDO_WEIGHT_MODE'] = "off"
 FORMAT="-f srv -b 32"
 OPERATORS=["eofcoeff","eofcoeff3d"]
 
-IFILE=f'{DATAPATH}psl_DJF_anom.grb'
-RFILE=f'{DATAPATH}pcoeff00000'
+IFILE=f'{DATAPATH}/psl_DJF_anom.grb'
+RFILE=f'{DATAPATH}/pcoeff00000'
 OFILE="res_pcoeff"
 
 MODES=["jacobi","danielson_lanczos"]
diff --git a/test/pytest/Eca.py.test.in b/test/pytest/Eca.py.test.in
index 9e0676250cee43f168fae8c72ac544db2aae32b1..92819d574ea377869808287171e9f4622f427240 100644
--- a/test/pytest/Eca.py.test.in
+++ b/test/pytest/Eca.py.test.in
@@ -12,7 +12,7 @@ IFILE2=f'{DATAPATH}/tsurf_runpctl_1d_1year'
 test_module = TestModule()
 
 for OPERATOR in OPERATORS:
-    RFILE=f'{DATAPATH}{OPERATOR}_ref'
+    RFILE=f'{DATAPATH}/{OPERATOR}_ref'
     OFILE=f'{OPERATOR}_res'
 
     t=TAPTest(OPERATOR)
diff --git a/test/pytest/Enspctl.py.test.in b/test/pytest/Enspctl.py.test.in
index b48e9321801d53597330b9f836aaea1ec645747a..c82b9c12affd4997460a52051676a51345f0e3ac 100644
--- a/test/pytest/Enspctl.py.test.in
+++ b/test/pytest/Enspctl.py.test.in
@@ -9,7 +9,7 @@ os.environ['CDO_FILE_SUFFIX'] = "NULL"
 
 test_module = TestModule()
 
-IFILE=f'{DATAPATH}ts_mm_5years'
+IFILE=f'{DATAPATH}/ts_mm_5years'
 test_module.prepare(f'{CDO} splityear {IFILE} {os.getpid()}ts_year')
 
 IFILES=f'{os.getpid()}ts_year'
diff --git a/test/pytest/Ensstat.py.test.in b/test/pytest/Ensstat.py.test.in
index 9d4c18746032c5de46b73884fb67492ecf7a09d6..15f3a6655e0f6576fad9fbe58c58e9b9f630b0ae 100644
--- a/test/pytest/Ensstat.py.test.in
+++ b/test/pytest/Ensstat.py.test.in
@@ -12,11 +12,11 @@ test_module = TestModule()
 for KEY in MISS_VALS.keys():
     WITHMISSVALS="+missvals" if (KEY == "yes") else ""
     IFILES=f'{os.getpid()}ts_year'
-    FILE=f'{DATAPATH}{MISS_VALS[KEY][0]}'
+    FILE=f'{DATAPATH}/{MISS_VALS[KEY][0]}'
     test_module.prepare(f'{CDO} splityear {FILE} {os.getpid()}ts_year')
     for OPERATOR in OPERATORS:
         RFORMAT=MISS_VALS[KEY][1].format(OPERATOR)
-        RFILE=f'{DATAPATH}{RFORMAT}_ref'
+        RFILE=f'{DATAPATH}/{RFORMAT}_ref'
         OFILE=f'{RFORMAT}_ref'
         t = TAPTest(f'{OPERATOR}   {WITHMISSVALS}')
         t.add(f'{CDO} {OPERATOR} "{IFILES}????" {OFILE}')
diff --git a/test/pytest/Etccdi.py.test.in b/test/pytest/Etccdi.py.test.in
index da080cae92d1a235f39e30a52916030d28455515..83bf31514aa7f18f1976ee5e6d28c3096c4318b9 100644
--- a/test/pytest/Etccdi.py.test.in
+++ b/test/pytest/Etccdi.py.test.in
@@ -13,10 +13,10 @@ OPERATORS=["etccdi_tx90p","etccdi_tx10p"]
 
 test_module=TestModule()
 
-DATAPATH_etccdi=f'{DATAPATH}{MODULE}'
+DATAPATH_etccdi=f'{DATAPATH}/{MODULE}'
 
 for OPERATOR in OPERATORS:
-    RFILE=f'{DATAPATH}{OPERATOR}_ref'
+    RFILE=f'{DATAPATH}/{OPERATOR}_ref'
     OFILE=f'{OPERATOR}_res'
     MIN_REF=f'{DATAPATH_etccdi}_min_ref'
     MAX_REF=f'{DATAPATH_etccdi}_max_ref'
diff --git a/test/pytest/Expr.py.test.in b/test/pytest/Expr.py.test.in
index 4dbf9ef65ccbf01f3fbc30099bc99bba7e0d1437..f7f7e675ff99074ee2839f7d37bc878a75efe947 100644
--- a/test/pytest/Expr.py.test.in
+++ b/test/pytest/Expr.py.test.in
@@ -7,7 +7,7 @@ FORMAT="-f srv -b 32"
 OPERATORS=["expr","aexpr"]
 ABSLIMMAX="0.001"
 
-IFILE=f'{DATAPATH}pl_data'
+IFILE=f'{DATAPATH}/pl_data'
 
 INSTR_LIST=[
 "var152;C1=0.287;_clev=clev(var130);pottemp=var130*((100000/_clev)^C1);",
@@ -19,7 +19,7 @@ test_module = TestModule()
 for I,INSTR in enumerate(INSTR_LIST,1):
     t = TAPTest(f'expr/aexpr instruction set {I}')
     for OPERATOR in OPERATORS:
-        RFILE=f'{DATAPATH}{OPERATOR}{I}_ref'
+        RFILE=f'{DATAPATH}/{OPERATOR}{I}_ref'
         OFILE=f'{OPERATOR}{I}_res'
         t.add(f'{CDO} {FORMAT} {OPERATOR},"{INSTR}" {IFILE} {OFILE}')
         t.add(f'{CDO} diff,abslim={ABSLIMMAX} {RFILE} {OFILE}')
@@ -28,12 +28,12 @@ for I,INSTR in enumerate(INSTR_LIST,1):
 
 # heat index
 
-IFILE=f'{DATAPATH}temp_and_hum.srv'
+IFILE=f'{DATAPATH}/temp_and_hum.srv'
 
 INSTR="_tF = ((var0 - 273.15) * 1.8) + 32; _hi = -42.379 + (2.04901523 * _tF) + (10.14333127 * var1) - (0.22475541 * _tF * var1) - (6.83783 * pow(10, -3) * pow(_tF, 2)) - (5.481717 * pow(10, -2) * pow(var1, 2)) + (1.22874 * pow(10, -3) * pow(_tF, 2) * var1) + (8.5282 * pow(10, -4) * _tF * pow(var1, 2)) - (1.99 * pow(10, -6) * pow(_tF, 2) * pow(var1, 2)); _hi = _tF < 80 ? _tF : _hi; heatx = (_hi - 32) * 0.55555556 + 273.15;"
 
 t = TAPTest("heat index")
-RFILE=f'{DATAPATH}heat_index.srv'
+RFILE=f'{DATAPATH}/heat_index.srv'
 OFILE="heat_index_res"
 t.add(f'{CDO} {FORMAT} expr,\'{INSTR}\' {IFILE} {OFILE}')
 t.add(f'{CDO} diff,abslim={ABSLIMMAX} {RFILE} {OFILE}')
diff --git a/test/pytest/File.py.test.in b/test/pytest/File.py.test.in
index 6e58e1d2c516c32652f64c0104fd53f5266ff2bb..a2319d03edd31bd36421d7ea831bc59b90ccabed 100644
--- a/test/pytest/File.py.test.in
+++ b/test/pytest/File.py.test.in
@@ -15,7 +15,7 @@ for FMS in FORMATS:
         for DATATYPE in DATATYPES:
 
             FILE=f'file{DATATYPE}_{FMS}'
-            RFILE=f'{DATAPATH}file_F32_srv_ref'
+            RFILE=f'{DATAPATH}/file_F32_srv_ref'
 
             t_cdiwrite = TAPTest(f'cdiwrite {FMS} {DATATYPE}')
             t_cdiwrite.add(f'{CDO} -f {FMS} -b {DATATYPE} cdiwrite,nruns=1,grid=global_10,nvars=3,nlevs=3,nsteps=3 {FILE}')
diff --git a/test/pytest/Fldpctl.py.test.in b/test/pytest/Fldpctl.py.test.in
index 1bc5e0abc5ff6a61e74c01e736e1942d585e99b3..bdf6d33e12beadd3adeff1fea6bab22540442751 100644
--- a/test/pytest/Fldpctl.py.test.in
+++ b/test/pytest/Fldpctl.py.test.in
@@ -6,7 +6,7 @@ FORMAT="-f srv -b 32"
 #
 PCTLS=[1, 20, 25, 33, 50, 66, 75, 80, 99, 100]
 #
-IFILE=f'{DATAPATH}t21_geosp_tsurf.grb'
+IFILE=f'{DATAPATH}/t21_geosp_tsurf.grb'
 #
 OPER="fldpctl"
 #
diff --git a/test/pytest/Fldstat.py.test.in b/test/pytest/Fldstat.py.test.in
index 281a61dbbe8100e291a3aa4a1ea0cd7d3882f756..9f98d1fcb6b515b5b93ae579e286de8b0bb4ea35 100644
--- a/test/pytest/Fldstat.py.test.in
+++ b/test/pytest/Fldstat.py.test.in
@@ -11,7 +11,7 @@ test_module = TestModule()
 #
 for OPER in OPERATORS:
     OFILE=f'{OPER}_res'
-    RFILE=f'{DATAPATH}{OPER}_ref'
+    RFILE=f'{DATAPATH}/{OPER}_ref'
     t = TAPTest(OPER)
     t.add(f'{CDO} {FORMAT} {OPER} {IFILE} {OFILE}')
     t.diff(OFILE,RFILE)
diff --git a/test/pytest/Fldstat2.py.test.in b/test/pytest/Fldstat2.py.test.in
index eeb75d17ea287f5259fa91d6a54624dd2e295494..66c8f8d61b8a58c98260911e536d752007d58cd2 100644
--- a/test/pytest/Fldstat2.py.test.in
+++ b/test/pytest/Fldstat2.py.test.in
@@ -5,7 +5,7 @@ import os
 #
 OPERATORS=["fldcor","fldcovar"]
 #
-IFILE=f'{DATAPATH}t21_geosp_tsurf.grb'
+IFILE=f'{DATAPATH}/t21_geosp_tsurf.grb'
 IFILE1=f'var1_{os.getpid()}'
 IFILE2=f'var2_{os.getpid()}'
 #
@@ -17,7 +17,7 @@ test_module.prepare(f'{CDO} selcode,169 {IFILE} {IFILE2}')
 
 for OPER in OPERATORS:
     OFILE=f'{OPER}_res'
-    RFILE=f'{DATAPATH}{OPER}_ref'
+    RFILE=f'{DATAPATH}/{OPER}_ref'
 
     t = TAPTest(OPER)
     t.add(f'{CDO} {FORMAT} {OPER} {IFILE1} {IFILE2} {OFILE}')
diff --git a/test/pytest/Genweights.py.test.in b/test/pytest/Genweights.py.test.in
index 10e7028428923808f603db91de03b20772a119d4..2f6ffb9e9ead7005595faee446b11b0bfc43b8c4 100644
--- a/test/pytest/Genweights.py.test.in
+++ b/test/pytest/Genweights.py.test.in
@@ -11,7 +11,7 @@ ABSLIMSCON=0.25
 FORMAT="-f srv -b 32"
 GRIDS="n16","n32"
 
-IFILE=f'{DATAPATH}bathy4.grb'
+IFILE=f'{DATAPATH}/bathy4.grb'
 
 HAS_NETCDF=cdo_check_req("has-nc")
 HAS_THREADS=cdo_check_req("has-threads")
@@ -33,7 +33,7 @@ def GENWEIGHTS_TEST(GRIDTYPE,OPERATORS):
                 continue
 
             OFILE=f'{GRID}_{OPERATOR}_{os.getpid()}'
-            RFILE=f'{DATAPATH}{GRID}_{OPERATOR[3:]}_ref'
+            RFILE=f'{DATAPATH}/{GRID}_{OPERATOR[3:]}_ref'
 
             t = TAPTest(f'{GRIDTYPE} {GRID} {OPERATOR}')
 
diff --git a/test/pytest/Gradsdes.py.test.in b/test/pytest/Gradsdes.py.test.in
index 614cbb65856ebba73eec97998276213cf8789fbe..f0fde85cef985a939447d32754e93d1dc69056b4 100644
--- a/test/pytest/Gradsdes.py.test.in
+++ b/test/pytest/Gradsdes.py.test.in
@@ -14,10 +14,10 @@ test_module.prepare(f'cp {IFILE} {OFILE}')
 t = TAPTest(OPER)
 #
 t.add(f'{CDO} {OPER} {OFILE}')
-t.add(f'head -12 {DATAPATH}{RFILE1} > h1')
+t.add(f'head -12 {DATAPATH}/{RFILE1} > h1')
 t.add(f'head -12 {RFILE1} > h2')
 t.add('diff h1 h2')
-t.add(f'cmp -s {DATAPATH}{RFILE2}  {RFILE2}')
+t.add(f'cmp -s {DATAPATH}/{RFILE2}  {RFILE2}')
 #
 t.clean("h1","h2",OFILE,RFILE2,RFILE1)
 
diff --git a/test/pytest/Gridboxstat.py.test.in b/test/pytest/Gridboxstat.py.test.in
index 8908ddba8a1f7568c1ac42dc8ef36e31870f8e82..d51f4ef0fb5111c0a9ad023c598a3a9913784ba9 100644
--- a/test/pytest/Gridboxstat.py.test.in
+++ b/test/pytest/Gridboxstat.py.test.in
@@ -6,12 +6,12 @@ FORMAT="-f srv -b 32"
 
 OPERATORS=["gridboxmin","gridboxmax","gridboxrange","gridboxsum","gridboxavg","gridboxmean","gridboxstd","gridboxstd1","gridboxvar","gridboxvar1","gridboxkurt","gridboxskew","gridboxmedian"]
 
-IFILE=f'{DATAPATH}t21_geosp_tsurf.grb'
+IFILE=f'{DATAPATH}/t21_geosp_tsurf.grb'
 
 test_module = TestModule()
 
 for OPERATOR in OPERATORS:
-    RFILE=f'{DATAPATH}fld{OPERATOR[7:]}_ref'
+    RFILE=f'{DATAPATH}/fld{OPERATOR[7:]}_ref'
     OFILE=f'{OPERATOR}_res'
 
     t = TAPTest(f'{OPERATOR}')
diff --git a/test/pytest/Intyear.py.test.in b/test/pytest/Intyear.py.test.in
index e10ac3ef785c1b383c66afd070a32cde759b5f1d..b52c980eb443b6bbf822732beaa0741a3411060f 100644
--- a/test/pytest/Intyear.py.test.in
+++ b/test/pytest/Intyear.py.test.in
@@ -16,7 +16,7 @@ test_module.prepare(f'{CDO} selyear,1991 {IFILE} {IFILE1}')
 test_module.prepare(f'{CDO} selyear,1995 {IFILE} {IFILE2}')
 
 for YEAR in YEARS:
-    RFILE=f'{DATAPATH}{OPER}{YEAR}_ref'
+    RFILE=f'{DATAPATH}/{OPER}{YEAR}_ref'
     OBASE=f'{OPER}'
     OFILE=f'{OPER}{YEAR}.srv'
     t=TAPTest(f'{OPER}')
diff --git a/test/pytest/Isosurface.py.test.in b/test/pytest/Isosurface.py.test.in
index b985818a68fc78f165c06e6613fd44d747c1be0b..86425068ac38563f20553c6174e9798e65db2dfe 100644
--- a/test/pytest/Isosurface.py.test.in
+++ b/test/pytest/Isosurface.py.test.in
@@ -12,7 +12,7 @@ IFILE=f'{DATAPATH}/pl_data.grb'
 test_module=TestModule()
 for OPER in OPERATORS:
 
-    RFILE=f'{DATAPATH}{OPER}_ref'
+    RFILE=f'{DATAPATH}/{OPER}_ref'
     OFILE=f'{OPER}_res'
 
     t=TAPTest(f'{OPER}{PARAMS[OPER]}')
diff --git a/test/pytest/Magplot.py.test.in b/test/pytest/Magplot.py.test.in
index ef1b08e38444911edf4dac5fca085f1dbdb896b4..5d26953e736c055006f2ca5d66f14c3b27548b41 100644
--- a/test/pytest/Magplot.py.test.in
+++ b/test/pytest/Magplot.py.test.in
@@ -6,7 +6,7 @@ import os
 HAS_MAGICS=cdo_check_req("has-magics")
 
 CDOTESTDATA=os.getenv("CDOTESTDATA") or ""
-XTESTDIR=f'{CDOTESTDATA}/magics'
+XTESTDIR=f'{CDOTESTDATA}/magics/'
 
 DEVICE="png"
 OPERATORS=["shaded","grfill"]
diff --git a/test/pytest/Merstat.py.test.in b/test/pytest/Merstat.py.test.in
index 3c1ca9aad093abce0489fcaca5efea3644a79134..83ffe4229e973b242991c8c2b32e6358361fbcf0 100644
--- a/test/pytest/Merstat.py.test.in
+++ b/test/pytest/Merstat.py.test.in
@@ -10,7 +10,7 @@ FORMAT="-f srv -b 32"
 test_module = TestModule()
 for OPER in OPERATORS:
     OFILE=f'{OPER}_res'
-    RFILE=f'{DATAPATH}{OPER}_ref'
+    RFILE=f'{DATAPATH}/{OPER}_ref'
 
     t = TAPTest(OPER)
     t.add(f'{CDO} {FORMAT} {OPER} {IFILE} {OFILE}')
diff --git a/test/pytest/Pack.py.test.in b/test/pytest/Pack.py.test.in
index 1051680d598f32ee6ae606ed5b29c23b05b90a42..fd1d2124f6ef5a22fd888d1d94863df9b577283a 100644
--- a/test/pytest/Pack.py.test.in
+++ b/test/pytest/Pack.py.test.in
@@ -15,7 +15,7 @@ OPERATOR="pack"
 test_module = TestModule()
 
 for NBITS in ["i16", "i8", "u16", "u8"]:
-    RFILE=f'{DATAPATH}{OPERATOR}_{NBITS}_ref'
+    RFILE=f'{DATAPATH}/{OPERATOR}_{NBITS}_ref'
     OFILE=f'{OPERATOR}_{NBITS}_res'
     if (HAS_NETCDF):
         t=TAPTest(f'{OPERATOR} {NBITS}')
@@ -29,7 +29,7 @@ for NBITS in ["i16", "i8", "u16", "u8"]:
 OPERATOR="unpack"
 
 for NBITS in ["i16", "i8", "u16", "u8"]:
-    RFILE=f'{DATAPATH}pack_{NBITS}_ref'
+    RFILE=f'{DATAPATH}/pack_{NBITS}_ref'
     OFILE=f'{OPERATOR}_{NBITS}_res'
     if (HAS_NETCDF):
         t=TAPTest(f'{OPERATOR} {NBITS}')
diff --git a/test/pytest/Percentile.py.test.in b/test/pytest/Percentile.py.test.in
index c96a657618b9cfee8261dfcf4ad319eee16047ca..f0abc6ab2b19bfdd0e1d04994e6594f553395f29 100644
--- a/test/pytest/Percentile.py.test.in
+++ b/test/pytest/Percentile.py.test.in
@@ -8,7 +8,7 @@ PCTLS=[0, 1, 20, 25, 33, 50, 66, 75, 80, 99, 100]
 #
 METHODS=["nrank", "nist", "rtype8", "linear", "lower", "higher", "nearest", "midpoint", "inverted_cdf", "averaged_inverted_cdf", "closest_observation", "interpolated_inverted_cdf", "hazen", "weibull", "median_unbiased", "normal_unbiased"]
 #
-IFILE=f'{DATAPATH}topo5.srv'
+IFILE=f'{DATAPATH}/topo5.srv'
 #
 OPER="fldpctl"
 #
diff --git a/test/pytest/Read_grib.py.test.in b/test/pytest/Read_grib.py.test.in
index 89b4a84e97dfd0b8d73331afbeecbcdf3f201d70..f152f36817dcd26dd252d48b44494b2ad017d91b 100644
--- a/test/pytest/Read_grib.py.test.in
+++ b/test/pytest/Read_grib.py.test.in
@@ -13,9 +13,9 @@ for OPERATOR in STATS:
             test_module.add_skip("GRIB not enabled")
             continue 
 
-        IFILE=f'{DATAPATH}grib_{FILE}.grb'
+        IFILE=f'{DATAPATH}/grib_{FILE}.grb'
         OFILE=f'grib_{FILE}_{OPERATOR}'
-        RFILE=f'{DATAPATH}{OFILE}_ref'
+        RFILE=f'{DATAPATH}/{OFILE}_ref'
         t=TAPTest(f'{OPERATOR} {FILE}')
         t.add(f'{CDO} -s {OPERATOR} {IFILE} > {OFILE}')
         t.add(f'diff {OFILE} {RFILE}')
diff --git a/test/pytest/Read_netcdf.py.test.in b/test/pytest/Read_netcdf.py.test.in
index 130520d46017c26b5d91fea3d0b507f977a1e33b..59c3bd026168761cd820792bd55291485308737f 100644
--- a/test/pytest/Read_netcdf.py.test.in
+++ b/test/pytest/Read_netcdf.py.test.in
@@ -15,9 +15,9 @@ for OPERATOR in OPERATORS:
             test_module.add_skip("NetCDF not enabled")
             continue 
 
-        IFILE=f'{DATAPATH}netcdf_{FILE}.nc'
+        IFILE=f'{DATAPATH}/netcdf_{FILE}.nc'
         OFILE=f'netcdf_{FILE}_{OPERATOR}'
-        RFILE=f'{DATAPATH}{OFILE}_ref'
+        RFILE=f'{DATAPATH}/{OFILE}_ref'
         t=TAPTest(f'{OPERATOR} {FILE}')
         t.add(f'{CDO} -s {OPERATOR} {IFILE} > {OFILE}')
         t.add(f'diff {OFILE} {RFILE}')
diff --git a/test/pytest/Smooth.py.test.in b/test/pytest/Smooth.py.test.in
index 2b75e02d995d10d5b1a22f50ea58c30a412bd526..20c7999932ce8308bc9790b29e8ebf23a71dedda 100644
--- a/test/pytest/Smooth.py.test.in
+++ b/test/pytest/Smooth.py.test.in
@@ -13,7 +13,7 @@ SMOOTHOPT=["smooth,radius=5deg", "smooth,radius=5deg,maxpoints=3", "smooth,radiu
 test_module = TestModule()
 
 for NTEST,SO in enumerate(SMOOTHOPT,1):
-    RFILE=f'{DATAPATH}{SO[:6]}{NTEST}_ref'
+    RFILE=f'{DATAPATH}/{SO[:6]}{NTEST}_ref'
     OFILE=f'smooth{NTEST}_res'
     t=TAPTest(f'parameter set {NTEST}')
     t.add(f'{CDO}  {FORMAT} {SO} {IFILE} {OFILE}')
diff --git a/test/pytest/Spectral.py.test.in b/test/pytest/Spectral.py.test.in
index 4641ebc4657beb16f146434fe0dc98311f5779f3..7e8551687194f4eb3373cdf13e82f45de5b24feb 100644
--- a/test/pytest/Spectral.py.test.in
+++ b/test/pytest/Spectral.py.test.in
@@ -12,8 +12,8 @@ for OPERATOR in OPERATORS:
     RFILE=f'{OPERATOR}_ref'
 
     t = TAPTest(OPERATOR)
-    t.add(f'{CDO} {OPERATOR} {DATAPATH}{IFILE} {OFILE}')
-    t.add(f'{CDO} diff,abslim=0.008 {OFILE} {DATAPATH}{RFILE}')
+    t.add(f'{CDO} {OPERATOR} {DATAPATH}/{IFILE} {OFILE}')
+    t.add(f'{CDO} diff,abslim=0.008 {OFILE} {DATAPATH}/{RFILE}')
     t.clean(OFILE)
     test_module.add(t)
 
@@ -25,8 +25,8 @@ for OPERATOR,IFILE in zip(OPERATORS,IFILES):
     RFILE=f'{OPERATOR}_ref'
 
     t = TAPTest(OPERATOR)
-    t.add(f'{CDO} {OPERATOR} {DATAPATH}{IFILE} {OFILE}')
-    t.add(f'{CDO} diff {OFILE} {DATAPATH}{RFILE}')
+    t.add(f'{CDO} {OPERATOR} {DATAPATH}/{IFILE} {OFILE}')
+    t.add(f'{CDO} diff {OFILE} {DATAPATH}/{RFILE}')
     t.clean(OFILE)
     test_module.add(t)
 
diff --git a/test/pytest/Timpctl.py.test.in b/test/pytest/Timpctl.py.test.in
index 358362f29e163bf0af0bf827cfbf53776aec8fdd..aa08bcc729233291e4026c3433d92f08467a41db 100644
--- a/test/pytest/Timpctl.py.test.in
+++ b/test/pytest/Timpctl.py.test.in
@@ -9,7 +9,7 @@ OPERATORS=["seaspctl","timpctl","yearpctl","monpctl","daypctl","yseaspctl","ymon
 
 PCTL=50
 
-INFILES=defaultdict(lambda : f'{DATAPATH}ts_mm_5years',{"daypctl" : f'{DATAPATH}ts_6h_1mon',"monpctl" : f'{DATAPATH}ts_1d_1year'})
+INFILES=defaultdict(lambda : f'{DATAPATH}/ts_mm_5years',{"daypctl" : f'{DATAPATH}/ts_6h_1mon',"monpctl" : f'{DATAPATH}/ts_1d_1year'})
 
 test_module = TestModule()
 
diff --git a/test/pytest/Timstat.py.test.in b/test/pytest/Timstat.py.test.in
index 2514bb90bcf81423d093b6596d2328ef0f618f02..4c9216fd34e6c8671308d5f0d7ab29601ecc7989 100644
--- a/test/pytest/Timstat.py.test.in
+++ b/test/pytest/Timstat.py.test.in
@@ -11,7 +11,7 @@ OPERATORS=[
 "monmin", "monmax", "monrange", "monsum", "monavg", "monmean", "monstd", "monstd1", "monvar", "monvar1",
 "daymin", "daymax", "dayrange", "daysum", "dayavg", "daymean", "daystd", "daystd1", "dayvar", "dayvar1"]
 
-IFILES=defaultdict(lambda : f'{DATAPATH}ts_mm_5years',{"day" : f'{DATAPATH}ts_6h_1mon' , "mon" : f'{DATAPATH}ts_1d_1year'})
+IFILES=defaultdict(lambda : f'{DATAPATH}/ts_mm_5years',{"day" : f'{DATAPATH}/ts_6h_1mon' , "mon" : f'{DATAPATH}/ts_1d_1year'})
 
 FMS=["srv","grb"]
 if (HAS_NETCDF):
diff --git a/test/pytest/Varsstat.py.test.in b/test/pytest/Varsstat.py.test.in
index 787df06b08e6d41e6b1620fad32bf509512b784f..4193e0a5e322a9fb7cac02ec499f191dfc16ec23 100644
--- a/test/pytest/Varsstat.py.test.in
+++ b/test/pytest/Varsstat.py.test.in
@@ -10,7 +10,7 @@ FORMAT="-f srv -b 32"
 test_module = TestModule()
 for OPERATOR in OPERATORS:
     OFILE=f'{OPERATOR}_res'
-    RFILE=f'{DATAPATH}{OPERATOR}_ref'
+    RFILE=f'{DATAPATH}/{OPERATOR}_ref'
     t=TAPTest(OPERATOR)
     t.add(f'{CDO} {FORMAT} {OPERATOR} {IFILE} {OFILE}')
     t.add(f'{CDO} diff {OFILE} {RFILE}')
diff --git a/test/pytest/Vertstat.py.test.in b/test/pytest/Vertstat.py.test.in
index ab2a86713b46ec5f460c8e397e2507f895b1e02f..e60349aebf5ae52a0b467ca9a21568a908e0b903 100644
--- a/test/pytest/Vertstat.py.test.in
+++ b/test/pytest/Vertstat.py.test.in
@@ -10,7 +10,7 @@ FORMAT="-f srv -b 32"
 test_module = TestModule()
 for OPER in OPERATORS:
     OFILE=f'{OPER}_res'
-    RFILE=f'{DATAPATH}{OPER}_ref'
+    RFILE=f'{DATAPATH}/{OPER}_ref'
     t=TAPTest(OPER)
     t.add(f'{CDO} {FORMAT} {OPER} {IFILE} {OFILE}')
     t.diff(RFILE,OFILE)
diff --git a/test/pytest/Wind.py.test.in b/test/pytest/Wind.py.test.in
index 5846f2b76680a22f2d2340446b72ac6e76bb78fd..72de75e71adadf5dd2b11a568647144c91f195a3 100644
--- a/test/pytest/Wind.py.test.in
+++ b/test/pytest/Wind.py.test.in
@@ -11,9 +11,9 @@ test_module= TestModule()
 
 for OPERATOR,IFILE in zip(OPERATORS,INFILES):
     OFILE=f'{OPERATOR}_res'
-    RFILE=f'{DATAPATH}{OPERATOR}_ref'
+    RFILE=f'{DATAPATH}/{OPERATOR}_ref'
     t=TAPTest(OPERATOR)
-    t.add(f'{CDO} {OPERATOR} {DATAPATH}{IFILE} {OFILE}')
+    t.add(f'{CDO} {OPERATOR} {DATAPATH}/{IFILE} {OFILE}')
     t.add(f'{CDO} diff,abslim={ABSLIM[OPERATOR]} {RFILE} {OFILE}')
     t.clean(OFILE)
     test_module.add(t)
diff --git a/test/pytest/Zonstat.py.test.in b/test/pytest/Zonstat.py.test.in
index f7e956dcebea8fb57d28b62d940abd44059d0172..8fbe3d12a5d2a1f061ad47987b7fd9c770817a5f 100644
--- a/test/pytest/Zonstat.py.test.in
+++ b/test/pytest/Zonstat.py.test.in
@@ -10,7 +10,7 @@ FORMAT="-f srv -b 32"
 test_module = TestModule()
 for OPER in OPERATORS:
     OFILE=f'{OPER}_res'
-    RFILE=f'{DATAPATH}{OPER}_ref'
+    RFILE=f'{DATAPATH}/{OPER}_ref'
     t=TAPTest(OPER)
     t.add(f'{CDO} {FORMAT} {OPER} {IFILE} {OFILE}')
     t.diff(OFILE,RFILE)
diff --git a/test/pytest/cdoTest.py.in b/test/pytest/cdoTest.py.in
index 5c82c018d2c886c19f4cb7b8ea4996df21a918a3..98be5f04b7114d8e61037c2bf0076fc7c799b09b 100644
--- a/test/pytest/cdoTest.py.in
+++ b/test/pytest/cdoTest.py.in
@@ -9,7 +9,7 @@ from collections import defaultdict
 
 BUILD_DIR = "@abs_top_builddir@"
 CDO="@abs_top_builddir@/src/cdo $CDO_DEBUG"
-DATAPATH="@abs_top_srcdir@/test/data/"
+DATAPATH="@abs_top_srcdir@/test/data"
 lineLength = 80
 """
 Overview:
@@ -211,10 +211,13 @@ class TestModule:
         test_status = t.go()
         if(test_status == 0):
             print_flush(f'ok: {t.message}')
+            return 0
         elif(test_status == 77):
             print_flush("ok: # SKIP: {}".format(t.message))
+            return 77;
         else:
             print_flush("not ok: {}".format(t.message))
+            return -1;
 
         print_seperator('-',lineLength)
         self.testID += 1
@@ -223,16 +226,17 @@ class TestModule:
     def run(self):
         print_flush("1..%s" % self.__get_num_test())
         print_seperator('=',lineLength)
+        retval = 0
 
         self.testID = 1
         for c in self.commands:
             if(c[1] == False):
                 self.__prepare_data(c[0])
             else:
-                self.__run_check(c[0])
+                retval += abs(self.__run_check(c[0]))
 
         self.cleanUp()
-        sys.exit(0)
+        exit(0) # important to exit here: tap tests/ctest need this return value
 
 __all__ = ["cdo_check_req","TAPTest","TestModule","print_flush","DATAPATH","CDO","BUILD_DIR","fileformat"]
 
@@ -245,8 +249,8 @@ def main():
     t = TAPTest("check if cdoTest works error is returned")
     t.add(f'{CDO} -add adasd', 1)
     testMod.add(t)
-    testMod.run()
-    return 0
+    retval = testMod.run()
+    return retval
 
 if __name__ == '__main__':
     sys.exit(main())  # next section explains the use of sys.exit
diff --git a/test/pytest/tsformat.py.test.in b/test/pytest/tsformat.py.test.in
index 81bfbe45ab0bdd41500c7a61031073aeebdd6fa1..ee23dd42701652359a153eed3eed5ea61b7615b0 100644
--- a/test/pytest/tsformat.py.test.in
+++ b/test/pytest/tsformat.py.test.in
@@ -27,7 +27,7 @@ for NTEST,FORMAT in enumerate(FORMATS,1):
 
     RFILE=f'{DATAPATH}/tsformat{INSET}_ref'
     OFILE=f'thread{NTEST}_res'
-    t=TAPTest()
+    t=TAPTest(FORMAT)
     OS_PROCESS_ID = os.getpid()
     IFILE2=f'infile{OS_PROCESS_ID}'