diff --git a/Docker/Dockerfile b/Docker/Dockerfile
index f92f9bd55c201cd18e20ff1d67b87976b4d4e011..702cf2bf0c1b5c1c0b5380b2e33afed64da6fd63 100644
--- a/Docker/Dockerfile
+++ b/Docker/Dockerfile
@@ -11,9 +11,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
     apt-get install -y \
         bison \
         build-essential \
-        casacore-data \
         casacore-dev \
-        casacore-tools \
         cmake \
         flex \
         gfortran \
@@ -59,10 +57,12 @@ RUN cmake \
 RUN ninja -C /src/idg/build install
 
 # Install EveryBeam
+# Do not compile python bindings, they will interfere with the ones in the 
+# binary wheel on PyPI.
 RUN git shallow-clone https://git.astron.nl/RD/EveryBeam.git
 RUN cmake \
     -DCMAKE_BUILD_TYPE:STRING=Release \
-    -DBUILD_WITH_PYTHON=ON \
+    -DBUILD_WITH_PYTHON=OFF \
     -DBUILD_TESTING=OFF \
     -DPORTABLE=${PORTABLE} \
     -H/src/EveryBeam \
@@ -154,7 +154,8 @@ COPY --from=builder /usr/local /usr/local
 # Only install run-time required packages
 RUN export DEBIAN_FRONTEND=noninteractive && \
     apt-get update && \
-    apt-get install -y \
+    apt-get install -y --no-install-recommends \
+        ca-certificates \
         casacore-tools \
         libarmadillo9 \
         libatkmm-1.6-1v5 \
@@ -191,17 +192,15 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
 
 RUN rm -rf /var/lib/apt/lists/*
 
-# Install WSRT Measures (extra casacore data). We purposely do this in the
-# `runner` stage and not in the `builder` stage, because otherwise the
-# previous `apt-get install` would clobber the files we had installed in
-# in the `builder` stage (as `casacore-data` is installed as a dependency).
+# Install the casacore measures data. We purposely do not install these from
+# the Ubuntu repository, but download the latest version directly from the
+# ASTRON ftp site.
 # Note: The file on the ftp site is updated daily. When warnings regarding
 # leap seconds appear, ignore them or regenerate the docker image.
-RUN wget -q -O /WSRT_Measures.ztar \
-        ftp://ftp.astron.nl/outgoing/Measures/WSRT_Measures.ztar && \
-    cd /var/lib/casacore/data && \
-    tar xfz /WSRT_Measures.ztar && \
-    rm /WSRT_Measures.ztar
+RUN mkdir -p /usr/share/casacore/data && \
+    ln -s /usr/share/casacore /var/lib/casacore && \
+    wget -qO - ftp://ftp.astron.nl/outgoing/Measures/WSRT_Measures.ztar | \
+        tar -C /usr/share/casacore/data -xzf -
 
 # Try to run the compiled tools to make sure they run without
 # a problem (e.g. no missing libraries).
@@ -213,4 +212,3 @@ RUN aoflagger --version && \
 COPY . /tmp/rapthor
 RUN python3 -m pip install --upgrade --no-cache-dir /tmp/rapthor
 RUN rm -rf /tmp/*
-
diff --git a/debug/everybeam/Dockerfile b/debug/everybeam/Dockerfile
new file mode 100644
index 0000000000000000000000000000000000000000..11322f76fadf51b4bd03cf4d269d8491952dbf33
--- /dev/null
+++ b/debug/everybeam/Dockerfile
@@ -0,0 +1,26 @@
+FROM python:3.9
+
+RUN apt-get update && \
+    apt-get install -y \
+        gdb \
+        less \
+        strace \
+        vim
+
+RUN groupadd \
+    -g 1001 \
+    rapthor
+
+RUN useradd \
+    -g 1001 \
+    -m \
+    -s /bin/bash \
+    -u 15511 \
+    rapthor
+
+USER rapthor
+WORKDIR /home/rapthor
+
+RUN python3 -m venv venv && \
+    . venv/bin/activate && \
+    pip install git+https://git.astron.nl/RD/rapthor.git@RAP-296_invalid-pointer-when-loading-skymodel
diff --git a/debug/build.sh b/debug/everybeam/build.sh
similarity index 100%
rename from debug/build.sh
rename to debug/everybeam/build.sh
diff --git a/debug/everybeam/fails.py b/debug/everybeam/fails.py
new file mode 100644
index 0000000000000000000000000000000000000000..eae3fffdbab40870687e980f77e61e6e2ca76733
--- /dev/null
+++ b/debug/everybeam/fails.py
@@ -0,0 +1,5 @@
+import lsmtool
+s = lsmtool.load(
+    '/project/rapthor/Share/rapthor/run_480ch_5chunks_flag/skymodels/calibrate_1/calibration_skymodel_small.txt',
+    beamMS='/project/rapthor/Share/rapthor/L632477_480ch_50min/allbands.ms.mjd5020562823_field.ms')
+s.getColValues('I', applyBeam=True)   
diff --git a/debug/everybeam/run.sh b/debug/everybeam/run.sh
new file mode 100755
index 0000000000000000000000000000000000000000..9067e2ccf39468b175e7a7bad61e7d2802292ff7
--- /dev/null
+++ b/debug/everybeam/run.sh
@@ -0,0 +1,8 @@
+#!/bin/bash -x
+mkdir -p /tmp/loose/rapthor-debug
+docker run -it --rm \
+    -v /project/rapthor/Share/rapthor/run_480ch_5chunks_flag:/project/rapthor/Share/rapthor/run_480ch_5chunks_flag:ro \
+    -v /project/rapthor/Share/rapthor/L632477_480ch_50min:/project/rapthor/Share/rapthor/L632477_480ch_50min:ro \
+    -v /tmp/loose/rapthor-debug:/tmp:rw \
+    --entrypoint /bin/bash \
+    rapthor-debug
diff --git a/debug/Dockerfile b/debug/scan_ms/Dockerfile
similarity index 100%
rename from debug/Dockerfile
rename to debug/scan_ms/Dockerfile
diff --git a/debug/scan_ms/build.sh b/debug/scan_ms/build.sh
new file mode 100755
index 0000000000000000000000000000000000000000..2dbb9da7a6050eebca88eb8ede9764489653be45
--- /dev/null
+++ b/debug/scan_ms/build.sh
@@ -0,0 +1 @@
+docker build -t rapthor-debug .
diff --git a/debug/entry.sh b/debug/scan_ms/entry.sh
similarity index 100%
rename from debug/entry.sh
rename to debug/scan_ms/entry.sh
diff --git a/debug/fails.py b/debug/scan_ms/fails.py
similarity index 100%
rename from debug/fails.py
rename to debug/scan_ms/fails.py
diff --git a/debug/run.sh b/debug/scan_ms/run.sh
similarity index 100%
rename from debug/run.sh
rename to debug/scan_ms/run.sh
diff --git a/debug/succeeds.py b/debug/scan_ms/succeeds.py
similarity index 100%
rename from debug/succeeds.py
rename to debug/scan_ms/succeeds.py
diff --git a/docs/source/parset.rst b/docs/source/parset.rst
index 93bce5edacba365450179c194de56c94988868b0..ae76f95888ffb9a0a085adb94c9c2597f0b22346 100644
--- a/docs/source/parset.rst
+++ b/docs/source/parset.rst
@@ -117,7 +117,7 @@ The available options are described below under their respective sections.
 
     llssolver
         The linear least-squares solver to use (one of "qr", "svd", or "lsmr";
-        default = "qr")
+        default = ``qr``)
 
     maxiter
         Maximum number of iterations to perform during calibration (default = 50).
@@ -127,7 +127,8 @@ The available options are described below under their respective sections.
 
     solveralgorithm
         The algorithm used for solving (one of "directionsolve", "directioniterative",
-        "lbfgs", or "hybrid"; default = "hybrid")? When using "lbfgs", the "stepsize" should be set to a small value like 0.001.
+        "lbfgs", or "hybrid"; default = ``hybrid``)? When using "lbfgs", the :term:`stepsize`
+        should be set to a small value like 0.001.
 
     onebeamperpatch
         Calculate the beam correction once per calibration patch (default =
@@ -140,7 +141,8 @@ The available options are described below under their respective sections.
         Parallelize model calculation over baselines, instead of parallelizing over directions (default = ``False``).
 
     stepsize
-        Size of steps used during calibration (default = 0.02).
+        Size of steps used during calibration (default = 0.02). When using
+        ``solveralgorithm = lbfgs``, the stepsize should be set to a small value like 0.001.
 
     tolerance
         Tolerance used to check convergence during calibration (default = 1e-3).
@@ -214,24 +216,23 @@ The available options are described below under their respective sections.
     taper_arcsec
         Taper to apply when imaging, in arcsec (default = 0).
 
-    multiscale_scales_pixel
-        Scale sizes in pixels to use during multiscale clean (default = ``[0, 5, 10, 15]``).
+    do_multiscale_clean
+        Use multiscale cleaning (default = ``True``)?
 
-    do_multiscale
-        Use multiscale cleaning (default = auto)?
-
-    use_screens
-        Use screens during imaging (default = ``True``)? If ``False``, the
-        solutions closest to the image centers will be used.
+    dde_method
+        Method to use to correct for direction-dependent effects during imaging: "none",
+        "facets", or "screens" (default = ``facets``). If "none", the solutions closest to the image centers
+        will be used. If "facets", Voronoi faceting is used. If "screens", smooth 2-D
+        screens are used.
 
     screen_type
-        Type of screen to use (default = "tessellated"), if use_screens = ``True``:
+        Type of screen to use (default = ``tessellated``), if ``dde_method = screens``:
         "tessellated" (simple, smoothed Voronoi tessellated screens) or
         "kl" (Karhunen-Lo`eve screens).
 
     idg_mode
         IDG (image domain gridder) mode to use in WSClean (default = "hybrid").
-        The mode can be "cpu" or "hybrid"".
+        The mode can be "cpu" or "hybrid".
 
     mem_fraction
         Fraction of the total memory (per node) to use for WSClean jobs (default = 0.9).
@@ -240,7 +241,8 @@ The available options are described below under their respective sections.
         Use MPI to distribute WSClean jobs over multiple nodes (default =
         ``False``)? If ``True`` and more than one node can be allocated to each
         WSClean job (i.e., max_nodes / num_images >= 2), then distributed
-        imaging will be used (only available if batch_system = slurm).
+        imaging will be used (only available if ``batch_system = slurm`` and
+        ``dde_method = screens``).
 
         .. note::
 
@@ -249,7 +251,10 @@ The available options are described below under their respective sections.
             it is on a shared filesystem.
 
     reweight
-        Reweight the visibility data before imaging (default = ``True``).
+        Reweight the visibility data before imaging (default = ``False``). If
+        ``True``, data with high residuals (compared to the predicted model
+        visibilities) are down-weighted. This feature is experimental and
+        should be used with caution.
 
     grid_width_ra_deg
         Size of area to image when using a grid (default = mean FWHM of the
@@ -287,11 +292,6 @@ The available options are described below under their respective sections.
     sector_width_dec_deg_list
         List of image  widths, in degrees (default = ``[]``).
 
-    sector_do_multiscale_list
-        List of multiscale flags, one per sector (default = ``[]``). ``None``
-        indicates that multiscale clean should be activated automatically if a
-        large source is detected in the sector.
-
     max_peak_smearing
         Max desired peak flux density reduction at center of the image edges due
         to bandwidth smearing (at the mean frequency) and time smearing (default
@@ -334,7 +334,12 @@ The available options are described below under their respective sections.
         all).
 
     deconvolution_threads
-        Number of threads to use by WSClean during deconvolution (default = 0 = all).
+        Number of threads to use by WSClean during deconvolution (default = 0 = 2/5
+        of ``max_threads``).
+
+    parallel_gridding_threads
+        Number of threads to use by WSClean during parallel gridding (default = 0 = 2/5
+        of ``max_threads``).
 
     dir_local
         Full path to a local disk on the nodes for IO-intensive processing (default =
diff --git a/examples/rapthor.parset b/examples/rapthor.parset
index 94022e360aab87d20e71f70f8c02bb04b087c231..08455296a8ae4d4b3be029b94671eb410ff80c9a 100644
--- a/examples/rapthor.parset
+++ b/examples/rapthor.parset
@@ -82,19 +82,18 @@ download_initial_skymodel_server = TGSS
 # slow_smoothnessconstraint_joint = 3e6
 # slow_smoothnessconstraint_separate = 3e6
 
-# Do extra solve for debugging purposes
-# debug = False
-
 
 [imaging]
 # Imaging parameters: pixel size in arcsec (default = 1.25, suitable for HBA data), Briggs
-# robust parameter (default = -0.5) and minimum uv distance in lambda (default = 80)
+# robust parameter (default = -0.5), min and max uv distance in lambda (default = 80, none),
+# taper in arcsec (default = none), and whether multiscale clean should be used (default =
+# True)
 # cellsize_arcsec = 1.25
 # robust = -0.5
 # min_uv_lambda = 80.0
 # max_uv_lambda = 0.0
 # taper_arcsec = 0.0
-# multiscale_scales_pixel = [0, 5, 10, 15]
+# do_multiscale_clean = True
 
 # Method to use to correct for direction-dependent effects during imaging: "none",
 # "facets", or "screens". If "none", the solutions closest to the image centers
@@ -135,27 +134,24 @@ download_initial_skymodel_server = TGSS
 # grid_nsectors_ra = 3
 
 # Instead of a grid, imaging sectors can be defined individually by specifying
-# their centers and widths. Multiscale clean can also be set (with None indicating that
-# multiscale clean should be activated automatically if a large source is detected in the
-# sector). If sectors are specified in this way, they will be
+# their centers and widths. If sectors are specified in this way, they will be
 # used instead of the sector grid. Note that the sectors should not overlap
 # sector_center_ra_list = [14h41m01.884, 14h13m23.234]
 # sector_center_dec_list = [+35d30m31.52, +37d21m56.86]
 # sector_width_ra_deg_list = [0.532, 0.127]
 # sector_width_dec_deg_list = [0.532, 0.127]
-# sector_do_multiscale_list = [None, True]
 
 # Max desired peak flux density reduction at center of the image edges due to
 # bandwidth smearing (at the mean frequency) and time smearing (default = 0.15 =
-# 15% reduction in peak flux). Higher values result in shorter run times but
+# 15% reduction in peak flux). Higher values can result in shorter run times but
 # more smearing away from the sector centers
 # max_peak_smearing = 0.15
 
 
 [cluster]
-# Cluster batch system (default = singleMachine). Use batch_system = slurm to
+# Cluster batch system (default = single_machine). Use batch_system = slurm to
 # use a SLURM-based cluster
-# batch_system = singleMachine
+# batch_system = single_machine
 
 # For batch_system = slurm, the maximum number of nodes of the cluster to use at
 # once can be specified with the max_nodes option (default = 12), the number of
@@ -168,16 +164,15 @@ download_initial_skymodel_server = TGSS
 # cpus_per_task = 0
 # mem_per_node_gb = 0
 
-# TODO: Slurm partition names?
-# cpu_partition = batch
-# gpu_partition = batch
-
 # Maximum number of cores and threads per task to use on each node (default = 0 = all)
 # max_cores = 0
 # max_threads = 0
 
-# Number of threads to use by WSClean during deconvolution (default = 0 = all)
+# Number of threads to use by WSClean during deconvolution and parallel gridding
+# (default = 0 = 2/5 of max_threads). Higher values will speed up imaging at the
+# expense of higher memory usage
 # deconvolution_threads = 0
+# parallel_gridding_threads = 0
 
 # Full path to a local disk on the nodes for IO-intensive processing (default =
 # not used). The path must exist on all nodes (but does not have to be on a
diff --git a/rapthor/lib/parset.py b/rapthor/lib/parset.py
index 118a6f1a61f7ce9f9ddd3d0709c638988ec967c8..c0b1a091e7da488b9c3a13ffbbff36ae38ded9e3 100644
--- a/rapthor/lib/parset.py
+++ b/rapthor/lib/parset.py
@@ -85,7 +85,7 @@ def parset_read(parset_file, use_log_file=True, skip_cluster=False):
     if 'input_skymodel' not in parset_dict:
         if parset_dict['download_initial_skymodel']:
             log.info('No input sky model file given and download requested. Will automatically download skymodel.')
-            parset_dict.update({'input_skymodel':os.path.join(parset_dict['dir_working'], 'skymodels', 'initial_skymodel.txt')})
+            parset_dict.update({'input_skymodel': os.path.join(parset_dict['dir_working'], 'skymodels', 'initial_skymodel.txt')})
         else:
             log.error('No input sky model file given and no download requested. Exiting...')
             raise RuntimeError('No input sky model file given and no download requested.')
@@ -384,17 +384,17 @@ def get_calibration_options(parset):
 
     # LBFGS solver parameters
     if 'solverlbfgs_dof' in parset_dict:
-       parset_dict['solverlbfgs_dof'] = parset.getfloat('calibration', 'solverlbfgs_dof')
+        parset_dict['solverlbfgs_dof'] = parset.getfloat('calibration', 'solverlbfgs_dof')
     else:
-       parset_dict['solverlbfgs_dof'] = 200.0
+        parset_dict['solverlbfgs_dof'] = 200.0
     if 'solverlbfgs_iter' in parset_dict:
-       parset_dict['solverlbfgs_iter'] = parset.getint('calibration', 'solverlbfgs_iter')
+        parset_dict['solverlbfgs_iter'] = parset.getint('calibration', 'solverlbfgs_iter')
     else:
-       parset_dict['solverlbfgs_iter'] = 4
+        parset_dict['solverlbfgs_iter'] = 4
     if 'solverlbfgs_minibatches' in parset_dict:
-       parset_dict['solverlbfgs_minibatches'] = parset.getint('calibration', 'solverlbfgs_minibatches')
+        parset_dict['solverlbfgs_minibatches'] = parset.getint('calibration', 'solverlbfgs_minibatches')
     else:
-       parset_dict['solverlbfgs_minibatches'] = 1
+        parset_dict['solverlbfgs_minibatches'] = 1
 
     # Do a extra "debug" step during calibration (default = False)?
     if 'debug' in parset_dict:
@@ -524,40 +524,12 @@ def get_imaging_options(parset):
         len_list.append(len(val_list))
     else:
         parset_dict['sector_width_dec_deg_list'] = []
-    if 'sector_do_multiscale_list' in parset_dict:
-        val_list = parset_dict['sector_do_multiscale_list'].strip('[]').split(',')
-        if val_list[0] == '':
-            val_list = []
-        bool_list = []
-        for v in val_list:
-            if v.lower().strip() == 'true':
-                b = True
-            elif v.lower().strip() == 'false':
-                b = False
-            elif v.lower().strip() == 'none':
-                b = None
-            else:
-                log.error('The entry "{}" in sector_do_multiscale_list is invalid. It must '
-                          'be one of True, False, or None'.format(v))
-                sys.exit(1)
-            bool_list.append(b)
-        parset_dict['sector_do_multiscale_list'] = bool_list
-        len_list.append(len(bool_list))
-    else:
-        parset_dict['sector_do_multiscale_list'] = []
-    if (len(parset_dict['sector_do_multiscale_list']) == 0 or
-            (True not in parset_dict['sector_do_multiscale_list'] and
-             None not in parset_dict['sector_do_multiscale_list'])):
-        parset_dict['do_multiscale_clean'] = False
-    else:
-        parset_dict['do_multiscale_clean'] = True
 
     # Check that all the above options have the same number of entries
     if len(set(len_list)) > 1:
         log.error('The options sector_center_ra_list, sector_center_dec_list, '
-                  'sector_width_ra_deg_list, sector_width_dec_deg_list, and '
-                  'sector_do_multiscale_list (if specified) must all have the same number of '
-                  'entires')
+                  'sector_width_ra_deg_list, and sector_width_dec_deg_list '
+                  'must all have the same number of entries')
         sys.exit(1)
 
     # IDG (image domain gridder) mode to use in WSClean (default = cpu). The mode can
@@ -613,24 +585,10 @@ def get_imaging_options(parset):
     else:
         parset_dict['max_peak_smearing'] = 0.15
 
-    # List of scales in pixels to use when multiscale clean is activated (default =
-    # auto). Note that multiscale clean is activated for a direction only when the
-    # calibrator or a source in the facet is determined to be larger than 4 arcmin,
-    # the facet contains the target (specified below with target_ra and target_dec),
-    # or mscale_selfcal_do / mscale_facet_do is set for the direction in the
-    # directions file
-    if 'multiscale_scales_pixel' in parset_dict:
-        val_list = parset_dict['multiscale_scales_pixel'].strip('[]').split(',')
-        str_list = ','.join([v.strip() for v in val_list])
-        parset_dict['multiscale_scales_pixel'] = str_list
-    else:
-        parset_dict['multiscale_scales_pixel'] = None
-
-    # Selfcal imaging parameters: pixel size in arcsec (default = 1.25), Briggs
-    # robust parameter (default = -0.5) and minimum uv distance in lambda
-    # (default = 80). These settings apply both to selfcal images and to the
-    # full facet image used to make the improved facet model that is subtracted
-    # from the data
+    # Imaging parameters: pixel size in arcsec (default = 1.25, suitable for HBA data), Briggs
+    # robust parameter (default = -0.5), min and max uv distance in lambda (default = 80, none),
+    # taper in arcsec (default = none), and whether multiscale clean should be used (default =
+    # True)
     if 'cellsize_arcsec' in parset_dict:
         parset_dict['cellsize_arcsec'] = parset.getfloat('imaging', 'cellsize_arcsec')
     else:
@@ -651,15 +609,19 @@ def get_imaging_options(parset):
         parset_dict['taper_arcsec'] = parset.getfloat('imaging', 'taper_arcsec')
     else:
         parset_dict['taper_arcsec'] = 0.0
+    if 'do_multiscale_clean' in parset_dict:
+        parset_dict['do_multiscale_clean'] = parset.getboolean('imaging', 'do_multiscale_clean')
+    else:
+        parset_dict['do_multiscale_clean'] = True
 
     # Check for invalid options
     allowed_options = ['max_peak_smearing', 'cellsize_arcsec', 'robust', 'reweight',
-                       'multiscale_scales_pixel', 'grid_center_ra', 'grid_center_dec',
+                       'grid_center_ra', 'grid_center_dec',
                        'grid_width_ra_deg', 'grid_width_dec_deg', 'grid_nsectors_ra',
                        'min_uv_lambda', 'max_uv_lambda', 'mem_fraction', 'screen_type',
                        'robust', 'sector_center_ra_list', 'sector_center_dec_list',
                        'sector_width_ra_deg_list', 'sector_width_dec_deg_list',
-                       'idg_mode', 'sector_do_multiscale_list', 'use_mpi',
+                       'idg_mode', 'do_multiscale_clean', 'use_mpi',
                        'dde_method', 'skip_corner_sectors']
     for option in given_options:
         if option not in allowed_options:
@@ -738,13 +700,21 @@ def get_cluster_options(parset):
     if parset_dict['max_threads'] == 0:
         parset_dict['max_threads'] = multiprocessing.cpu_count()
 
-    # Number of threads to use by WSClean during deconvolution (default = 0 = all)
+    # Number of threads to use by WSClean during deconvolution and parallel gridding
+    # (default = 0 = 2/5 of max_threads). Higher values will speed up imaging at the
+    # expense of higher memory usage
     if 'deconvolution_threads' in parset_dict:
         parset_dict['deconvolution_threads'] = parset.getint('cluster', 'deconvolution_threads')
     else:
         parset_dict['deconvolution_threads'] = 0
     if parset_dict['deconvolution_threads'] == 0:
-        parset_dict['deconvolution_threads'] = multiprocessing.cpu_count()
+        parset_dict['deconvolution_threads'] = max(1, int(parset_dict['max_threads'] * 2 / 5))
+    if 'parallel_gridding_threads' in parset_dict:
+        parset_dict['parallel_gridding_threads'] = parset.getint('cluster', 'parallel_gridding_threads')
+    else:
+        parset_dict['parallel_gridding_threads'] = 0
+    if parset_dict['parallel_gridding_threads'] == 0:
+        parset_dict['parallel_gridding_threads'] = max(1, int(parset_dict['max_threads'] * 2 / 5))
 
     # Full path to a local disk on the nodes for I/O-intensive processing. The path
     # must be the same for all nodes
@@ -768,7 +738,7 @@ def get_cluster_options(parset):
     if 'cwl_runner' not in parset_dict:
         parset_dict['cwl_runner'] = 'toil'
     cwl_runner = parset_dict['cwl_runner']
-    supported_cwl_runners = ('cwltool', 'toil') #, 'toil-cwl-runner')
+    supported_cwl_runners = ('cwltool', 'toil')
     if cwl_runner not in supported_cwl_runners:
         log.critical("CWL runner '%s' is not supported; select one of: %s",
                      cwl_runner, ', '.join(supported_cwl_runners))
diff --git a/rapthor/lib/sector.py b/rapthor/lib/sector.py
index 8f8d016db4e75023659cb312cb026458527e30bb..c5d018b23ba2bf39f920a77304b4c1f20f9c1c38 100644
--- a/rapthor/lib/sector.py
+++ b/rapthor/lib/sector.py
@@ -82,15 +82,14 @@ class Sector(object):
             obs.set_prediction_parameters(self.name, self.patches,
                                           os.path.join(self.field.working_dir, 'scratch'))
 
-    def set_imaging_parameters(self, do_multiscale=None, recalculate_imsize=False):
+    def set_imaging_parameters(self, do_multiscale=False, recalculate_imsize=False):
         """
         Sets the parameters needed for the imaging pipeline
 
         Parameters
         ----------
         do_multiscale : bool, optional
-            If True, multiscale clean is done. If None, multiscale clean is done only
-            when a large source is detected
+            If True, multiscale clean is done
         recalculate_imsize : bool, optional
             If True, the image size is recalculated based on the current sector region
         """
@@ -179,21 +178,8 @@ class Sector(object):
             self.wsclean_niter = int(1e7)  # set to high value and just use nmiter to limit clean
             self.wsclean_nmiter = max(2, int(round(self.wsclean_nmiter * 0.75)))
 
-        # Set multiscale: get source sizes and check for large sources
+        # Set multiscale clean
         self.multiscale = do_multiscale
-        if self.multiscale is None:
-            # TODO: figure out good way to determine whether multiscale should be used
-            # and the scales, maybe using the presence of Gaussians on larger wavelet
-            # scales? For now, force it to off.
-            self.multiscale = False
-
-#             largest_scale = np.max(self.source_sizes) / self.cellsize_deg / 3.0
-#             large_size_arcmin = 4.0  # threshold source size for multiscale to be activated
-#             sizes_arcmin = self.source_sizes * 60.0
-#             if sizes_arcmin is not None and any([s > large_size_arcmin for s in sizes_arcmin]):
-#                 self.multiscale = True
-#             else:
-#                 self.multiscale = False
         if self.multiscale:
             self.wsclean_niter = int(self.wsclean_niter/1.5)  # fewer iterations are needed
             self.log.debug("Will do multiscale cleaning.")
diff --git a/rapthor/operations/calibrate.py b/rapthor/operations/calibrate.py
index caaf013fe0d2ed52ee179d110baa7c581c1f8a1f..30bf261b4398cd5ae08928d9142378ec7b8e024e 100644
--- a/rapthor/operations/calibrate.py
+++ b/rapthor/operations/calibrate.py
@@ -34,7 +34,6 @@ class Calibrate(Operation):
                              'do_slowgain_solve': self.field.do_slowgain_solve,
                              'use_scalarphase': self.field.use_scalarphase,
                              'max_cores': max_cores,
-                             'max_threads': self.field.parset['cluster_specific']['max_threads'],
                              'debug': self.field.debug}
 
     def set_input_parameters(self):
@@ -196,7 +195,8 @@ class Calibrate(Operation):
                             'combined_h5parms2': combined_h5parms2,
                             'solverlbfgs_dof': solverlbfgs_dof,
                             'solverlbfgs_iter': solverlbfgs_iter,
-                            'solverlbfgs_minibatches': solverlbfgs_minibatches}
+                            'solverlbfgs_minibatches': solverlbfgs_minibatches,
+                            'max_threads': self.field.parset['cluster_specific']['max_threads']}
 
         if self.field.debug:
             output_slow_h5parm_debug = ['slow_gain_{}_debug.h5parm'.format(i)
diff --git a/rapthor/operations/image.py b/rapthor/operations/image.py
index ecc9bc919aaebd710da4b467a260179b6636dbd5..5359908ed10d59409b18f8e58dd575530599b697 100644
--- a/rapthor/operations/image.py
+++ b/rapthor/operations/image.py
@@ -43,9 +43,6 @@ class Image(Operation):
                              'use_facets': use_facets,
                              'peel_bright_sources': self.field.peel_bright_sources,
                              'max_cores': max_cores,
-                             'max_threads': self.field.parset['cluster_specific']['max_threads'],
-                             'deconvolution_threads': self.field.parset['cluster_specific']['deconvolution_threads'],
-                             'do_multiscale_clean': self.field.do_multiscale_clean,
                              'use_mpi': self.field.use_mpi,
                              'toil_version': self.toil_major_version}
 
@@ -75,15 +72,8 @@ class Image(Operation):
             # Generally, this should work fine, since we do not expect large changes in
             # the size of the sector from iteration to iteration (small changes are OK,
             # given the padding we use during imaging)
-            if self.field.do_multiscale_clean:
-                sector_do_multiscale_list = self.field.parset['imaging_specific']['sector_do_multiscale_list']
-                if len(sector_do_multiscale_list) == nsectors:
-                    do_multiscale = sector_do_multiscale_list[i]
-                else:
-                    do_multiscale = None
-            else:
-                do_multiscale = False
-            sector.set_imaging_parameters(do_multiscale=do_multiscale, recalculate_imsize=False)
+            sector.set_imaging_parameters(do_multiscale=self.field.do_multiscale_clean,
+                                          recalculate_imsize=False)
 
             # Set input MS filenames
             if self.field.do_predict:
@@ -149,7 +139,10 @@ class Image(Operation):
                             'idg_mode': [sector.idg_mode for sector in self.field.imaging_sectors],
                             'wsclean_mem': [sector.mem_percent for sector in self.field.imaging_sectors],
                             'threshisl': [sector.threshisl for sector in self.field.imaging_sectors],
-                            'threshpix': [sector.threshpix for sector in self.field.imaging_sectors]}
+                            'threshpix': [sector.threshpix for sector in self.field.imaging_sectors],
+                            'do_multiscale': [sector.multiscale for sector in self.field.imaging_sectors],
+                            'max_threads': self.field.parset['cluster_specific']['max_threads'],
+                            'deconvolution_threads': self.field.parset['cluster_specific']['deconvolution_threads']}
 
         if self.field.peel_bright_sources:
             self.input_parms.update({'bright_skymodel_pb': CWLFile(self.field.bright_source_skymodel_file).to_json()})
@@ -197,6 +190,8 @@ class Image(Operation):
                     self.input_parms.update({'soltabs': 'amplitude000,phase000'})
                 else:
                     self.input_parms.update({'soltabs': 'phase000'})
+                self.input_parms.update({'parallel_gridding_threads':
+                                         self.field.parset['cluster_specific']['parallel_gridding_threads']})
             else:
                 self.input_parms.update({'central_patch_name': central_patch_name})
 
diff --git a/rapthor/operations/mosaic.py b/rapthor/operations/mosaic.py
index 2af036aad1dcbe832e774c23024d2e18d836d3a9..f33eccc48b2c7d15e49ece47c76d8796f14792ab 100644
--- a/rapthor/operations/mosaic.py
+++ b/rapthor/operations/mosaic.py
@@ -32,7 +32,6 @@ class Mosaic(Operation):
             max_cores = self.field.parset['cluster_specific']['max_cores']
         self.parset_parms = {'rapthor_pipeline_dir': self.rapthor_pipeline_dir,
                              'max_cores': max_cores,
-                             'max_threads': self.field.parset['cluster_specific']['max_threads'],
                              'skip_processing': self.skip_processing,
                              'do_slowgain_solve': self.field.do_slowgain_solve}
 
diff --git a/rapthor/operations/predict.py b/rapthor/operations/predict.py
index 4ac9da3592d60c0f44d674e780423408b15ab200..77d58573ae08d41f56a57606ce9505fae30108ce 100644
--- a/rapthor/operations/predict.py
+++ b/rapthor/operations/predict.py
@@ -28,7 +28,6 @@ class Predict(Operation):
             max_cores = self.field.parset['cluster_specific']['max_cores']
         self.parset_parms = {'rapthor_pipeline_dir': self.rapthor_pipeline_dir,
                              'max_cores': max_cores,
-                             'max_threads': self.field.parset['cluster_specific']['max_threads'],
                              'do_slowgain_solve': self.field.do_slowgain_solve}
 
     def set_input_parameters(self):
@@ -110,7 +109,8 @@ class Predict(Operation):
                             'peel_outliers': peel_outliers,
                             'nr_bright': nr_bright,
                             'peel_bright': peel_bright,
-                            'reweight': reweight}
+                            'reweight': reweight,
+                            'max_threads': self.field.parset['cluster_specific']['max_threads']}
 
     def finalize(self):
         """
diff --git a/rapthor/pipeline/parsets/calibrate_pipeline.cwl b/rapthor/pipeline/parsets/calibrate_pipeline.cwl
index 3764db47fd25676ac6ded1d18582d1be8305a3ed..839ba570bc67fa17eb895df27e2c0036197321c8 100644
--- a/rapthor/pipeline/parsets/calibrate_pipeline.cwl
+++ b/rapthor/pipeline/parsets/calibrate_pipeline.cwl
@@ -220,6 +220,12 @@ inputs:
       The screen type to use to derive the a-term images (length = 1).
     type: string
 
+  - id: max_threads
+    label: Max number of threads
+    doc: |
+      The maximum number of threads to use for a job (length = 1).
+    type: int
+
 {% if do_slowgain_solve %}
 # start do_slowgain_solve
   - id: freqchunk_filename
@@ -395,7 +401,7 @@ steps:
   - id: solve_fast_phases
     label: Solve for fast phases
     doc: |
-      This step uses DDECal (in DPPP) to solve for phase corrections on short
+      This step uses DDECal (in DP3) to solve for phase corrections on short
       timescales (< 1 minute), using the input MS files and sourcedb. These
       corrections are used to correct primarily for ionospheric effects.
 {% if use_scalarphase %}
@@ -457,7 +463,7 @@ steps:
       - id: antennaconstraint
         source: fast_antennaconstraint
       - id: numthreads
-        valueFrom: '{{ max_threads }}'
+        source: max_threads
     scatter: [msin, starttime, ntimes, h5parm, solint, nchan, smoothnessreffrequency]
     scatterMethod: dotproduct
     out:
@@ -498,7 +504,7 @@ steps:
   - id: solve_slow_gains1
     label: Solve for slow gains 1
     doc: |
-      This step uses DDECal (in DPPP) to solve for gain corrections on long
+      This step uses DDECal (in DP3) to solve for gain corrections on long
       timescales (> 10 minute), using the input MS files and sourcedb. These
       corrections are used to correct primarily for beam errors. The fast-
       phase solutions are preapplied and all stations are constrained to
@@ -560,7 +566,7 @@ steps:
       - id: antennaconstraint
         source: slow_antennaconstraint
       - id: numthreads
-        valueFrom: '{{ max_threads }}'
+        source: max_threads
     scatter: [msin, starttime, ntimes, startchan, nchan, h5parm, solint, solve_nchan]
     scatterMethod: dotproduct
     out:
@@ -621,7 +627,7 @@ steps:
   - id: solve_slow_gains2
     label: Solve for slow gains 2
     doc: |
-      This step uses DDECal (in DPPP) to solve for gain corrections on long
+      This step uses DDECal (in DP3) to solve for gain corrections on long
       timescales (> 10 minute), using the input MS files and sourcedb. These
       corrections are used to correct primarily for beam errors. The fast-
       phase solutions and first slow-gain solutions are preapplied and stations
@@ -682,7 +688,7 @@ steps:
       - id: smoothnessconstraint
         source: slow_smoothnessconstraint2
       - id: numthreads
-        valueFrom: '{{ max_threads }}'
+        source: max_threads
     scatter: [msin, starttime, ntimes, startchan, nchan, h5parm, solint, solve_nchan]
     scatterMethod: dotproduct
     out:
@@ -865,7 +871,7 @@ steps:
       - id: sector_bounds_mid_deg
         source: sector_bounds_mid_deg
       - id: ncpu
-        valueFrom: '{{ max_threads }}'
+        source: max_threads
     scatter: [h5parm, outroot]
     scatterMethod: dotproduct
     out:
@@ -935,7 +941,7 @@ steps:
       - id: smoothnessconstraint
         source: slow_smoothnessconstraint
       - id: numthreads
-        valueFrom: '{{ max_threads }}'
+        source: max_threads
     scatter: [msin, starttime, ntimes, startchan, nchan, h5parm, solint, solve_nchan]
     scatterMethod: dotproduct
     out:
@@ -1011,7 +1017,7 @@ steps:
       - id: sector_bounds_mid_deg
         source: sector_bounds_mid_deg
       - id: ncpu
-        valueFrom: '{{ max_threads }}'
+        source: max_threads
     scatter: [h5parm, outroot]
     scatterMethod: dotproduct
     out:
diff --git a/rapthor/pipeline/parsets/image_pipeline.cwl b/rapthor/pipeline/parsets/image_pipeline.cwl
index db818e6eb4803bad8438871aec47c51d1e8e87b8..419c3f057f8fee9fd9e0c579c879f2c76db7fd60 100644
--- a/rapthor/pipeline/parsets/image_pipeline.cwl
+++ b/rapthor/pipeline/parsets/image_pipeline.cwl
@@ -3,7 +3,7 @@ class: Workflow
 label: Rapthor imaging pipeline
 doc: |
   This workflow performs imaging with direction-dependent corrections. The
-  imaging data are generated (and averaged if possible) and WSClean+IDG is
+  imaging data are generated (and averaged if possible) and WSClean is
   used to perform the imaging. Masking and sky model filtering is then done
   using PyBDSF.
 
@@ -229,6 +229,12 @@ inputs:
       The name of the calibration solution table (length = 1).
     type: string
 
+  - id: parallel_gridding_threads
+    label: Max number of gridding threads
+    doc: |
+      The maximum number of threads to use during parallel gridding (length = 1).
+    type: int
+
 {% else %}
 # start not use_facets
 
@@ -274,17 +280,23 @@ inputs:
     type: float[]
 
   - id: min_uv_lambda
-    label: Minimum us distance
+    label: Minimum uv distance
     doc: |
       The WSClean minimum uv distance in lambda (length = n_sectors).
     type: float[]
 
   - id: max_uv_lambda
-    label: Maximum us distance
+    label: Maximum uv distance
     doc: |
       The WSClean maximum uv distance in lambda (length = n_sectors).
     type: float[]
 
+  - id: do_multiscale
+    label: Activate multiscale
+    doc: |
+      Activate multiscale clean (length = n_sectors).
+    type: boolean[]
+
   - id: taper_arcsec
     label: Taper value
     doc: |
@@ -329,6 +341,19 @@ inputs:
     type: File
 {% endif %}
 
+  - id: max_threads
+    label: Max number of threads
+    doc: |
+      The maximum number of threads to use for a job (length = 1).
+    type: int
+
+  - id: deconvolution_threads
+    label: Max number of deconvolution threads
+    doc: |
+      The maximum number of threads to use during deconvolution (length = 1).
+    type: int
+
+
 outputs:
   - id: filtered_skymodels
     outputSource:
@@ -452,6 +477,8 @@ steps:
         source: min_uv_lambda
       - id: max_uv_lambda
         source: max_uv_lambda
+      - id: do_multiscale
+        source: do_multiscale
       - id: taper_arcsec
         source: taper_arcsec
       - id: wsclean_mem
@@ -464,6 +491,14 @@ steps:
         source: threshisl
       - id: threshpix
         source: threshpix
+      - id: max_threads
+        source: max_threads
+      - id: deconvolution_threads
+        source: deconvolution_threads
+{% if use_facets %}
+      - id: parallel_gridding_threads
+        source: parallel_gridding_threads
+{% endif %}
 {% if peel_bright_sources %}
       - id: bright_skymodel_pb
         source: bright_skymodel_pb
@@ -479,7 +514,7 @@ steps:
 {% endif %}
               channels_out, deconvolution_channels, wsclean_niter,
               wsclean_nmiter, robust, min_uv_lambda,
-              max_uv_lambda, taper_arcsec, wsclean_mem,
+              max_uv_lambda, do_multiscale, taper_arcsec, wsclean_mem,
               auto_mask, idg_mode, threshisl, threshpix]
 {% else %}
 # start not use_screens
@@ -497,7 +532,7 @@ steps:
 {% endif %}
               channels_out, deconvolution_channels, wsclean_niter,
               wsclean_nmiter, robust, min_uv_lambda,
-              max_uv_lambda, taper_arcsec, wsclean_mem,
+              max_uv_lambda, do_multiscale, taper_arcsec, wsclean_mem,
               auto_mask, idg_mode, threshisl, threshpix]
 {% endif %}
 # end use_screens / not use_screens
diff --git a/rapthor/pipeline/parsets/image_sector_pipeline.cwl b/rapthor/pipeline/parsets/image_sector_pipeline.cwl
index 4956db42962efc30704b33e73290cd826c25a866..2bff70745ea1a9e36219855040af82980d4642a0 100644
--- a/rapthor/pipeline/parsets/image_sector_pipeline.cwl
+++ b/rapthor/pipeline/parsets/image_sector_pipeline.cwl
@@ -3,7 +3,7 @@ class: Workflow
 label: Rapthor imaging subpipeline
 doc: |
   This subworkflow performs imaging with direction-dependent corrections. The
-  imaging data are generated (and averaged if possible) and WSClean+IDG is
+  imaging data are generated (and averaged if possible) and WSClean is
   used to perform the imaging. Masking and sky model filtering is then done
   using PyBDSF.
 
@@ -194,6 +194,12 @@ inputs:
       The names of the calibration solution tables (length = 1).
     type: string
 
+  - id: parallel_gridding_threads
+    label: Max number of gridding threads
+    doc: |
+      The maximum number of threads to use during parallel gridding (length = 1).
+    type: int
+
 {% else %}
 # start not use_facets
 
@@ -239,17 +245,23 @@ inputs:
     type: float
 
   - id: min_uv_lambda
-    label: Minimum us distance
+    label: Minimum uv distance
     doc: |
       The WSClean minimum uv distance in lambda (length = 1).
     type: float
 
   - id: max_uv_lambda
-    label: Maximum us distance
+    label: Maximum uv distance
     doc: |
       The WSClean maximum uv distance in lambda (length = 1).
     type: float
 
+  - id: do_multiscale
+    label: Activate multiscale
+    doc: |
+      Activate multiscale clean (length = 1).
+    type: boolean
+
   - id: taper_arcsec
     label: Taper value
     doc: |
@@ -294,6 +306,18 @@ inputs:
     type: File
 {% endif %}
 
+  - id: max_threads
+    label: Max number of threads
+    doc: |
+      The maximum number of threads to use for a job (length = 1).
+    type: int
+
+  - id: deconvolution_threads
+    label: Max number of deconvolution threads
+    doc: |
+      The maximum number of threads to use during deconvolution (length = 1).
+    type: int
+
 outputs:
   - id: filtered_skymodels
     outputSource:
@@ -326,7 +350,7 @@ steps:
   - id: prepare_imaging_data
     label: Prepare imaging data
     doc: |
-      This step uses DPPP to prepare the input data for imaging. This involves
+      This step uses DP3 to prepare the input data for imaging. This involves
       averaging, phase shifting, and optionally the application of the
       calibration solutions at the center.
 {% if use_screens or use_facets %}
@@ -368,7 +392,7 @@ steps:
       - id: beamdir
         source: phasecenter
       - id: numthreads
-        valueFrom: '{{ max_threads }}'
+        source: max_threads
 {% if use_screens or use_facets %}
     scatter: [msin, msout, starttime, ntimes, freqstep, timestep]
 {% else %}
@@ -434,60 +458,25 @@ steps:
     label: Make an image
     doc: |
       This step makes an image using WSClean. Direction-dependent effects
-      can be corrected for using a-term images.
+      can be corrected for using a-term images or facet-based corrections.
 {% if use_screens %}
 # start use_screens
-{% if use_mpi %}
-# start use_mpi
-{% if do_multiscale_clean %}
-# start do_multiscale_clean
-
-{% if toil_version < 5 %}
-    run: {{ rapthor_pipeline_dir }}/steps/wsclean_mpi_image_multiscale_toil4.cwl
-{% else %}
-    run: {{ rapthor_pipeline_dir }}/steps/wsclean_mpi_image_multiscale.cwl
-{% endif %}
-{% else %}
-# start not do_multiscale_clean
 
-{% if toil_version < 5 %}
-    run: {{ rapthor_pipeline_dir }}/steps/wsclean_mpi_image_toil4.cwl
-{% else %}
-    run: {{ rapthor_pipeline_dir }}/steps/wsclean_mpi_image.cwl
-{% endif %}
-{% endif %}
-# end do_multiscale_clean / not do_multiscale_clean
-
-{% else %}
-# start not use_mpi
-
-{% if do_multiscale_clean %}
-    run: {{ rapthor_pipeline_dir }}/steps/wsclean_image_multiscale.cwl
+{% if use_mpi %}
+    run: {{ rapthor_pipeline_dir }}/steps/wsclean_mpi_image_screens.cwl
 {% else %}
-    run: {{ rapthor_pipeline_dir }}/steps/wsclean_image.cwl
-{% endif %}
+    run: {{ rapthor_pipeline_dir }}/steps/wsclean_image_screens.cwl
 {% endif %}
-# end use_mpi / not use_mpi
 
 {% else %}
 # start not use_screens
 
 {% if use_facets %}
-# start use_facets
-{% if do_multiscale_clean %}
-    run: {{ rapthor_pipeline_dir }}/steps/wsclean_image_facets_multiscale.cwl
-{% else %}
     run: {{ rapthor_pipeline_dir }}/steps/wsclean_image_facets.cwl
-{% endif %}
-{% else %}
-# start not use_facets
-{% if do_multiscale_clean %}
-    run: {{ rapthor_pipeline_dir }}/steps/wsclean_image_no_screens_multiscale.cwl
 {% else %}
-    run: {{ rapthor_pipeline_dir }}/steps/wsclean_image_no_screens.cwl
-{% endif %}
+    run: {{ rapthor_pipeline_dir }}/steps/wsclean_image_no_dde.cwl
 {% endif %}
-# end use_facets / not use_facets
+
 {% endif %}
 # end use_screens / not use_screens
 
@@ -521,6 +510,8 @@ steps:
         source: soltabs
       - id: region_file
         source: make_region_file/region_file
+      - id: num_gridding_threads
+        source: parallel_gridding_threads
 {% endif %}
       - id: wsclean_imsize
         source: wsclean_imsize
@@ -534,6 +525,8 @@ steps:
         source: min_uv_lambda
       - id: max_uv_lambda
         source: max_uv_lambda
+      - id: multiscale
+        source: do_multiscale
       - id: cellsize_deg
         source: cellsize_deg
       - id: channels_out
@@ -549,9 +542,9 @@ steps:
       - id: idg_mode
         source: idg_mode
       - id: num_threads
-        valueFrom: '{{ max_threads }}'
+        source: max_threads
       - id: num_deconvolution_threads
-        valueFrom: '{{ deconvolution_threads }}'
+        source: deconvolution_threads
     out:
       - id: image_nonpb_name
       - id: image_pb_name
@@ -581,7 +574,7 @@ steps:
         source: image/image_pb_name
         valueFrom: $(self.basename)
       - id: numthreads
-        valueFrom: '{{ max_threads }}'
+        source: max_threads
     out:
       - id: restored_image
 
@@ -606,7 +599,7 @@ steps:
         source: image/image_nonpb_name
         valueFrom: $(self.basename)
       - id: numthreads
-        valueFrom: '{{ max_threads }}'
+        source: max_threads
     out:
       - id: restored_image
 {% endif %}
@@ -616,7 +609,8 @@ steps:
     label: Filter sources
     doc: |
       This step uses PyBDSF to filter artifacts from the sky model and make
-      a clean mask for the next iteration.
+      a clean mask for the next iteration, as well as derive various image
+      diagnostics.
     run: {{ rapthor_pipeline_dir }}/steps/filter_skymodel.cwl
     in:
 {% if peel_bright_sources %}
diff --git a/rapthor/pipeline/parsets/predict_pipeline.cwl b/rapthor/pipeline/parsets/predict_pipeline.cwl
index 55d8912ebb9522051b4822162e266c530be8a0dc..377eed2a0745f26d5db48212047e66b862ef8cec 100644
--- a/rapthor/pipeline/parsets/predict_pipeline.cwl
+++ b/rapthor/pipeline/parsets/predict_pipeline.cwl
@@ -152,6 +152,13 @@ inputs:
       The flag that sets reweighting of uv data (length = 1).
     type: boolean
 
+  - id: max_threads
+    label: Max number of threads
+    doc: |
+      The maximum number of threads to use for a job (length = 1).
+    type: int
+
+
 outputs:
   - id: subtract_models
     outputSource:
@@ -166,7 +173,7 @@ steps:
   - id: predict_model_data
     label: Predict the model uv data
     doc: |
-      This step uses DPPP to predict uv data (using the input sourcedb) from the
+      This step uses DP3 to predict uv data (using the input sky model) from the
       input MS files. It also corrupts the model data with the calibration
       solutions. For each sector, prediction is done for all observations.
 {% if do_slowgain_solve %}
@@ -200,7 +207,7 @@ steps:
       - id: directions
         source: sector_patches
       - id: numthreads
-        valueFrom: '{{ max_threads }}'
+        source: max_threads
     scatter: [msin, msout, starttime, ntimes, sourcedb, directions]
     scatterMethod: dotproduct
     out:
diff --git a/rapthor/pipeline/steps/ddecal_solve_complexgain.cwl b/rapthor/pipeline/steps/ddecal_solve_complexgain.cwl
index ad52f418da76890f8893998b08b2b52b4dde37f2..5291def1f6ede2150d3f677e962ec4cc27eb2c5a 100644
--- a/rapthor/pipeline/steps/ddecal_solve_complexgain.cwl
+++ b/rapthor/pipeline/steps/ddecal_solve_complexgain.cwl
@@ -153,7 +153,7 @@ inputs:
       prefix: solve.smoothnessconstraint=
       separate: False
   - id: numthreads
-    type: string
+    type: int
     inputBinding:
       prefix: numthreads=
       separate: False
diff --git a/rapthor/pipeline/steps/ddecal_solve_complexgain1.cwl b/rapthor/pipeline/steps/ddecal_solve_complexgain1.cwl
index 5263acbf8042c0b46d9fab3fc58303843bd1b1f0..6787fcf25482ae912ca8761ec2f32caa7a7d0e76 100644
--- a/rapthor/pipeline/steps/ddecal_solve_complexgain1.cwl
+++ b/rapthor/pipeline/steps/ddecal_solve_complexgain1.cwl
@@ -158,7 +158,7 @@ inputs:
       prefix: solve.antennaconstraint=
       separate: False
   - id: numthreads
-    type: string
+    type: int
     inputBinding:
       prefix: numthreads=
       separate: False
diff --git a/rapthor/pipeline/steps/ddecal_solve_complexgain2.cwl b/rapthor/pipeline/steps/ddecal_solve_complexgain2.cwl
index 37ace284a87ca4f9cacb70132287bb6f29033510..320a5a697497a56c2b88160509dd365b5f0ed027 100644
--- a/rapthor/pipeline/steps/ddecal_solve_complexgain2.cwl
+++ b/rapthor/pipeline/steps/ddecal_solve_complexgain2.cwl
@@ -154,7 +154,7 @@ inputs:
       prefix: solve.smoothnessconstraint=
       separate: False
   - id: numthreads
-    type: string
+    type: int
     inputBinding:
       prefix: numthreads=
       separate: False
diff --git a/rapthor/pipeline/steps/ddecal_solve_complexgain_debug.cwl b/rapthor/pipeline/steps/ddecal_solve_complexgain_debug.cwl
index 4c02f8b671ad2e36c897b3c424114baecec9cb0d..db4cc0850eff628ab6013bec090e31829020967e 100644
--- a/rapthor/pipeline/steps/ddecal_solve_complexgain_debug.cwl
+++ b/rapthor/pipeline/steps/ddecal_solve_complexgain_debug.cwl
@@ -140,7 +140,7 @@ inputs:
       prefix: solve.smoothnessconstraint=
       separate: False
   - id: numthreads
-    type: string
+    type: int
     inputBinding:
       prefix: numthreads=
       separate: False
diff --git a/rapthor/pipeline/steps/ddecal_solve_scalarcomplexgain.cwl b/rapthor/pipeline/steps/ddecal_solve_scalarcomplexgain.cwl
index fddf76fd2b6892adf8312b71df4a1f7fcfcf9fe8..6a86f9f4a59f857449ac5bd85ffbbc2acafa570a 100644
--- a/rapthor/pipeline/steps/ddecal_solve_scalarcomplexgain.cwl
+++ b/rapthor/pipeline/steps/ddecal_solve_scalarcomplexgain.cwl
@@ -146,7 +146,7 @@ inputs:
       prefix: solve.antennaconstraint=
       separate: False
   - id: numthreads
-    type: string
+    type: int
     inputBinding:
       prefix: numthreads=
       separate: False
diff --git a/rapthor/pipeline/steps/ddecal_solve_scalarphase.cwl b/rapthor/pipeline/steps/ddecal_solve_scalarphase.cwl
index 9d9676a93f1004885f9341ada28e787079f1443c..79fcfcfa4c7da8285fc0d62e1cc6822993938e42 100644
--- a/rapthor/pipeline/steps/ddecal_solve_scalarphase.cwl
+++ b/rapthor/pipeline/steps/ddecal_solve_scalarphase.cwl
@@ -232,7 +232,7 @@ inputs:
     label: Number of threads
     doc: |
       The number of threads to use during solve (0 = all).
-    type: string
+    type: int
     inputBinding:
       prefix: numthreads=
       separate: False
diff --git a/rapthor/pipeline/steps/predict_model_data.cwl b/rapthor/pipeline/steps/predict_model_data.cwl
index 27ea3e8cb888eb55e23bcbf22351a18cfea0fa23..fd7aaa0d3629aa259afe4d6692ac7767bed4ea9d 100644
--- a/rapthor/pipeline/steps/predict_model_data.cwl
+++ b/rapthor/pipeline/steps/predict_model_data.cwl
@@ -115,7 +115,7 @@ inputs:
     label: Number of threads
     doc: |
       The number of threads for DPPP.
-    type: string
+    type: int
     inputBinding:
       prefix: numthreads=
       separate: False
diff --git a/rapthor/pipeline/steps/predict_model_data_phase_only.cwl b/rapthor/pipeline/steps/predict_model_data_phase_only.cwl
index d664fd836c58d5c2de33fa3aa21c80fa88de271d..4992707b07973e824305a6883203395a8c5da887 100644
--- a/rapthor/pipeline/steps/predict_model_data_phase_only.cwl
+++ b/rapthor/pipeline/steps/predict_model_data_phase_only.cwl
@@ -77,7 +77,7 @@ inputs:
       itemSeparator: ','
       separate: False
   - id: numthreads
-    type: string
+    type: int
     inputBinding:
       prefix: numthreads=
       separate: False
diff --git a/rapthor/pipeline/steps/prepare_imaging_data.cwl b/rapthor/pipeline/steps/prepare_imaging_data.cwl
index b04225a65a6bec6002b5fbcdac48324beaae58be..490a9ff225ea392fd22008e4e6c6a1ea15102c9b 100644
--- a/rapthor/pipeline/steps/prepare_imaging_data.cwl
+++ b/rapthor/pipeline/steps/prepare_imaging_data.cwl
@@ -96,7 +96,7 @@ inputs:
     label: Number of threads
     doc: |
       The number of threads to use during solve (0 = all).
-    type: string
+    type: int
     inputBinding:
       prefix: numthreads=
       separate: False
diff --git a/rapthor/pipeline/steps/prepare_imaging_data_no_screens.cwl b/rapthor/pipeline/steps/prepare_imaging_data_no_screens.cwl
index 04a8a91c61b9a18eb66cbe75f8a2b48f658664cd..230c079155a0b7740bf468aa18b15c8e534a8d9f 100644
--- a/rapthor/pipeline/steps/prepare_imaging_data_no_screens.cwl
+++ b/rapthor/pipeline/steps/prepare_imaging_data_no_screens.cwl
@@ -93,7 +93,7 @@ inputs:
       prefix: applycal.direction=
       separate: False
   - id: numthreads
-    type: string
+    type: int
     inputBinding:
       prefix: numthreads=
       separate: False
diff --git a/rapthor/pipeline/steps/prepare_imaging_data_no_screens_phase_only.cwl b/rapthor/pipeline/steps/prepare_imaging_data_no_screens_phase_only.cwl
index 10dbbe25ac7a6fde6e7c6c7d1cfce171f49fc984..b809adf99eeaaf50b03d7d9590a872dc3ce33608 100644
--- a/rapthor/pipeline/steps/prepare_imaging_data_no_screens_phase_only.cwl
+++ b/rapthor/pipeline/steps/prepare_imaging_data_no_screens_phase_only.cwl
@@ -92,7 +92,7 @@ inputs:
       prefix: applycal.direction=
       separate: False
   - id: numthreads
-    type: string
+    type: int
     inputBinding:
       prefix: numthreads=
       separate: False
diff --git a/rapthor/pipeline/steps/wsclean_image_facets.cwl b/rapthor/pipeline/steps/wsclean_image_facets.cwl
index 84d6e60872d843c544e227ef5ff7d729b86488ec..452fe2679ff5c7735430326d5d91345d6bc93d9a 100644
--- a/rapthor/pipeline/steps/wsclean_image_facets.cwl
+++ b/rapthor/pipeline/steps/wsclean_image_facets.cwl
@@ -3,8 +3,8 @@ class: CommandLineTool
 baseCommand: [wsclean]
 label: Make an image
 doc: |
-  This tool makes an image using WSClean with facets corrections. See
-  wsclean_image.cwl for a detailed description of the inputs and outputs.
+  This tool makes an image using WSClean with facet-based corrections. See
+  wsclean_image_screens.cwl for a detailed description of the inputs and outputs.
 
 requirements:
   InlineJavascriptRequirement: {}
@@ -16,11 +16,12 @@ arguments:
   - -join-channels
   - -apply-facet-beam
   - -log-time
-  - -use-wgridder
+  - valueFrom: 'wgridder'
+    prefix: -gridder
   - valueFrom: '$(runtime.tmpdir)'
     prefix: -temp-dir
-  - valueFrom: '4'
-    prefix: -parallel-gridding
+  - valueFrom: '2048'
+    prefix: -parallel-deconvolution
   - valueFrom: 'I'
     prefix: -pol
   - valueFrom: '0.85'
@@ -80,6 +81,10 @@ inputs:
     type: float
     inputBinding:
       prefix: -maxuv-l
+  - id: multiscale
+    type: boolean
+    inputBinding:
+      prefix: -multiscale
   - id: cellsize_deg
     type: float
     inputBinding:
@@ -109,23 +114,39 @@ inputs:
     inputBinding:
       prefix: -idg-mode
   - id: num_threads
-    type: string
+    type: int
     inputBinding:
       prefix: -j
   - id: num_deconvolution_threads
-    type: string
+    type: int
     inputBinding:
       prefix: -deconvolution-threads
+  - id: num_gridding_threads
+    label: Number of gridding threads
+    doc: |
+      The number of threads to use during gridding.
+    type: int
+    inputBinding:
+      prefix: -parallel-gridding
   - id: h5parm
+    label: h5parm filename
+    doc: |
+      The filename of the h5parm containing the solutions to apply to correct for DDEs.
     type: File
     inputBinding:
       prefix: -apply-facet-solutions
       position: 3
   - id: soltabs
+    label: Solution tables
+    doc: |
+      The solution table names to apply to correct for DDEs.
     type: string
     inputBinding:
       position: 4
   - id: region_file
+    label: ds9 region file
+    doc: |
+      The ds9 region file that defines the facets.
     type: File
     inputBinding:
       prefix: -facet-regions
diff --git a/rapthor/pipeline/steps/wsclean_image_facets_multiscale.cwl b/rapthor/pipeline/steps/wsclean_image_facets_multiscale.cwl
deleted file mode 100644
index b6b865438e7856e375b3be1cb17b51fdc85ca668..0000000000000000000000000000000000000000
--- a/rapthor/pipeline/steps/wsclean_image_facets_multiscale.cwl
+++ /dev/null
@@ -1,160 +0,0 @@
-cwlVersion: v1.2
-class: CommandLineTool
-baseCommand: [wsclean]
-label: Make an image
-doc: |
-  This tool makes an image using WSClean with facets corrections. See
-  wsclean_image.cwl for a detailed description of the inputs and outputs.
-
-requirements:
-  InlineJavascriptRequirement: {}
-
-arguments:
-  - -no-update-model-required
-  - -save-source-list
-  - -local-rms
-  - -join-channels
-  - -apply-facet-beam
-  - -multiscale
-  - -log-time
-  - -use-wgridder
-  - valueFrom: '$(runtime.tmpdir)'
-    prefix: -temp-dir
-  - valueFrom: '4'
-    prefix: -parallel-gridding
-  - valueFrom: '2048'
-    prefix: -parallel-deconvolution
-  - valueFrom: '6'
-    prefix: -deconvolution-threads
-  - valueFrom: 'I'
-    prefix: -pol
-  - valueFrom: '0.85'
-    prefix: -mgain
-  - valueFrom: '3'
-    prefix: -fit-spectral-pol
-  - valueFrom: 'gaussian'
-    prefix: -multiscale-shape
-  - valueFrom: '1.0'
-    prefix: -auto-threshold
-  - valueFrom: '50'
-    prefix: -local-rms-window
-  - valueFrom: 'rms-with-min'
-    prefix: -local-rms-method
-  - valueFrom: '120'
-    prefix: -facet-beam-update
-  - valueFrom: 'briggs'
-    # Note: we have to set part of the 'weight' argument here and part below, as it has
-    # three parts (e.g., '-weight briggs -0.5'), and WSClean will not parse the value
-    # correctly if it's given together with 'briggs'. We force the parts to be adjacent
-    # using the position arg here and below
-    prefix: -weight
-    position: 1
-
-inputs:
-  - id: msin
-    type: Directory[]
-    inputBinding:
-      position: 5
-  - id: name
-    type: string
-    inputBinding:
-      prefix: -name
-  - id: mask
-    type: File
-    inputBinding:
-      prefix: -fits-mask
-  - id: wsclean_imsize
-    type: int[]
-    inputBinding:
-      prefix: -size
-  - id: wsclean_niter
-    type: int
-    inputBinding:
-      prefix: -niter
-  - id: wsclean_nmiter
-    type: int
-    inputBinding:
-      prefix: -nmiter
-  - id: robust
-    type: float
-    inputBinding:
-      position: 2
-  - id: min_uv_lambda
-    type: float
-    inputBinding:
-      prefix: -minuv-l
-  - id: max_uv_lambda
-    type: float
-    inputBinding:
-      prefix: -maxuv-l
-  - id: cellsize_deg
-    type: float
-    inputBinding:
-      prefix: -scale
-  - id: channels_out
-    type: int
-    inputBinding:
-      prefix: -channels-out
-  - id: deconvolution_channels
-    type: int
-    inputBinding:
-      prefix: -deconvolution-channels
-  - id: taper_arcsec
-    type: float
-    inputBinding:
-      prefix: -taper-gaussian
-  - id: wsclean_mem
-    type: float
-    inputBinding:
-      prefix: -mem
-  - id: auto_mask
-    type: float
-    inputBinding:
-      prefix: -auto-mask
-  - id: idg_mode
-    type: string
-    inputBinding:
-      prefix: -idg-mode
-  - id: num_threads
-    type: string
-    inputBinding:
-      prefix: -j
-  - id: num_deconvolution_threads
-    type: string
-    inputBinding:
-      prefix: -deconvolution-threads
-  - id: h5parm
-    type: File
-    inputBinding:
-      prefix: -apply-facet-solutions
-      position: 3
-  - id: soltabs
-    type: string
-    inputBinding:
-      position: 4
-  - id: region_file
-    type: File
-    inputBinding:
-      prefix: -facet-regions
-
-outputs:
-  - id: image_nonpb_name
-    type: File
-    outputBinding:
-      glob: $(inputs.name)-MFS-image.fits
-  - id: image_pb_name
-    type: File
-    outputBinding:
-      glob: $(inputs.name)-MFS-image-pb.fits
-  - id: skymodel_nonpb
-    type: File
-    outputBinding:
-      glob: $(inputs.name)-sources.txt
-  - id: skymodel_pb
-    type: File
-    outputBinding:
-      glob: $(inputs.name)-sources-pb.txt
-
-hints:
-  - class: DockerRequirement
-    dockerPull: 'astronrd/rapthor'
diff --git a/rapthor/pipeline/steps/wsclean_image_multiscale.cwl b/rapthor/pipeline/steps/wsclean_image_multiscale.cwl
deleted file mode 100644
index 8971adf0bd91b4afaceb7b5e0ca8db6ebb7907c4..0000000000000000000000000000000000000000
--- a/rapthor/pipeline/steps/wsclean_image_multiscale.cwl
+++ /dev/null
@@ -1,177 +0,0 @@
-cwlVersion: v1.2
-class: CommandLineTool
-baseCommand: [wsclean]
-label: Make an image
-doc: |
-  This tool makes an image using WSClean with a-term corrections and
-  multiscale cleaning. See wsclean_image.cwl for a detailed description
-  of the inputs and outputs.
-
-requirements:
-  - class: InitialWorkDirRequirement
-    listing:
-      - entryname: aterm_plus_beam.cfg
-        # Note: WSClean requires that the aterm image filenames be input as part of an
-        # aterm config file (and not directly on the command line). Therefore, a config
-        # file is made here that contains the filenames defined in the aterm_images
-        # input parameter. Also, the required beam parameters are set here
-        entry: |
-          aterms = [diagonal, beam]
-          diagonal.images = [$(inputs.aterm_images.map( (e,i) => (e.path) ).join(' '))]
-          beam.differential = true
-          beam.update_interval = 120
-          beam.usechannelfreq = true
-        writable: false
-  - class: InlineJavascriptRequirement
-
-arguments:
-  - -no-update-model-required
-  - -multiscale
-  - -save-source-list
-  - -local-rms
-  - -join-channels
-  - -use-idg
-  - -log-time
-  - valueFrom: '$(runtime.tmpdir)'
-    prefix: -temp-dir
-  - valueFrom: 'I'
-    prefix: -pol
-  - valueFrom: '0.85'
-    prefix: -mgain
-  - valueFrom: '3'
-    prefix: -fit-spectral-pol
-  - valueFrom: '2048'
-    prefix: -parallel-deconvolution
-  - valueFrom: 'gaussian'
-    prefix: -multiscale-shape
-  - valueFrom: '1.0'
-    prefix: -auto-threshold
-  - valueFrom: '50'
-    prefix: -local-rms-window
-  - valueFrom: 'rms-with-min'
-    prefix: -local-rms-method
-  - valueFrom: '32'
-    prefix: -aterm-kernel-size
-  - valueFrom: 'aterm_plus_beam.cfg'
-    # Note: this file is generated on the fly in the requirements section above
-    prefix: -aterm-config
-  - valueFrom: 'briggs'
-    # Note: we have to set part of the 'weight' argument here and part below, as it has
-    # three parts (e.g., '-weight briggs -0.5'), and WSClean will not parse the value
-    # correctly if it's given together with 'briggs'. We force the parts to be adjacent
-    # using the position arg here and below
-    prefix: -weight
-    position: 1
-
-inputs:
-  - id: msin
-    type: Directory[]
-    inputBinding:
-      position: 3
-  - id: name
-    type: string
-    inputBinding:
-      prefix: -name
-  - id: mask
-    type: File
-    inputBinding:
-      prefix: -fits-mask
-  - id: aterm_images
-    label: Filenames of aterm files
-    doc: |
-      The filenames of the a-term image files. These filenames are not used directly in the
-      WSClean call (they are read by WSClean from the aterm config file, defined in the
-      requirements section above), hence the value is set to "null" (which results in
-      nothing being added to the command for this input).
-    type: File[]
-    inputBinding:
-      valueFrom: null
-  - id: wsclean_imsize
-    type: int[]
-    inputBinding:
-      prefix: -size
-  - id: wsclean_niter
-    type: int
-    inputBinding:
-      prefix: -niter
-  - id: wsclean_nmiter
-    type: int
-    inputBinding:
-      prefix: -nmiter
-  - id: robust
-    type: float
-    inputBinding:
-      position: 2
-  - id: min_uv_lambda
-    type: float
-    inputBinding:
-      prefix: -minuv-l
-  - id: max_uv_lambda
-    type: float
-    inputBinding:
-      prefix: -maxuv-l
-  - id: cellsize_deg
-    type: float
-    inputBinding:
-      prefix: -scale
-  - id: multiscale_scales_pixel
-    label: Multiscale scales
-    doc: |
-      The multiscale scales in pixels.
-    type: string
-    inputBinding:
-      prefix: -multiscale-scales
-  - id: channels_out
-    type: int
-    inputBinding:
-      prefix: -channels-out
-  - id: deconvolution_channels
-    type: int
-    inputBinding:
-      prefix: -deconvolution-channels
-  - id: taper_arcsec
-    type: float
-    inputBinding:
-      prefix: -taper-gaussian
-  - id: wsclean_mem
-    type: float
-    inputBinding:
-      prefix: -mem
-  - id: auto_mask
-    type: float
-    inputBinding:
-      prefix: -auto-mask
-  - id: idg_mode
-    type: string
-    inputBinding:
-      prefix: -idg-mode
-  - id: num_threads
-    type: string
-    inputBinding:
-      prefix: -j
-  - id: num_deconvolution_threads
-    type: string
-    inputBinding:
-      prefix: -deconvolution-threads
-
-outputs:
-  - id: image_nonpb_name
-    type: File
-    outputBinding:
-      glob: $(inputs.name)-MFS-image.fits
-  - id: image_pb_name
-    type: File
-    outputBinding:
-      glob: $(inputs.name)-MFS-image-pb.fits
-  - id: skymodel_nonpb
-    type: File
-    outputBinding:
-      glob: $(inputs.name)-sources.txt
-  - id: skymodel_pb
-    type: File
-    outputBinding:
-      glob: $(inputs.name)-sources-pb.txt
-
-hints:
-  - class: DockerRequirement
-    dockerPull: 'astronrd/rapthor'
diff --git a/rapthor/pipeline/steps/wsclean_image_no_screens.cwl b/rapthor/pipeline/steps/wsclean_image_no_dde.cwl
similarity index 93%
rename from rapthor/pipeline/steps/wsclean_image_no_screens.cwl
rename to rapthor/pipeline/steps/wsclean_image_no_dde.cwl
index 254927c6b61ac5673873aa4d8b68d40b485b1fbb..c0ee62644a32cc03056ccbb36115e654702df385 100644
--- a/rapthor/pipeline/steps/wsclean_image_no_screens.cwl
+++ b/rapthor/pipeline/steps/wsclean_image_no_dde.cwl
@@ -4,7 +4,7 @@ baseCommand: [wsclean]
 label: Make an image
 doc: |
   This tool makes an image using WSClean with no a-term corrections. See
-  wsclean_image.cwl for a detailed description of the inputs and outputs.
+  wsclean_image_screens.cwl for a detailed description of the inputs and outputs.
 
 requirements:
   InlineJavascriptRequirement: {}
@@ -79,6 +79,10 @@ inputs:
     type: float
     inputBinding:
       prefix: -maxuv-l
+  - id: multiscale
+    type: boolean
+    inputBinding:
+      prefix: -multiscale
   - id: cellsize_deg
     type: float
     inputBinding:
@@ -104,11 +108,11 @@ inputs:
     inputBinding:
       prefix: -idg-mode
   - id: num_threads
-    type: string
+    type: int
     inputBinding:
       prefix: -j
   - id: num_deconvolution_threads
-    type: string
+    type: int
     inputBinding:
       prefix: -deconvolution-threads
 
diff --git a/rapthor/pipeline/steps/wsclean_image_no_screens_multiscale.cwl b/rapthor/pipeline/steps/wsclean_image_no_screens_multiscale.cwl
deleted file mode 100644
index b2c1b2345b30a6b8dffd45ab3af94213fa466439..0000000000000000000000000000000000000000
--- a/rapthor/pipeline/steps/wsclean_image_no_screens_multiscale.cwl
+++ /dev/null
@@ -1,143 +0,0 @@
-cwlVersion: v1.2
-class: CommandLineTool
-baseCommand: [wsclean]
-label: Make an image
-doc: |
-  This tool makes an image using WSClean with no a-term corrections and
-  multiscale cleaning. See wsclean_image.cwl for a detailed description
-  of the inputs and outputs.
-
-requirements:
-  InlineJavascriptRequirement: {}
-
-arguments:
-  - -no-update-model-required
-  - -multiscale
-  - -save-source-list
-  - -local-rms
-  - -join-channels
-  - -use-idg
-  - -grid-with-beam
-  - -use-differential-lofar-beam
-  - -log-time
-  - valueFrom: '$(runtime.tmpdir)'
-    prefix: -temp-dir
-  - valueFrom: 'I'
-    prefix: -pol
-  - valueFrom: '0.85'
-    prefix: -mgain
-  - valueFrom: '4'
-    prefix: -deconvolution-channels
-  - valueFrom: '3'
-    prefix: -fit-spectral-pol
-  - valueFrom: 'gaussian'
-    prefix: -multiscale-shape
-  - valueFrom: '1.0'
-    prefix: -auto-threshold
-  - valueFrom: '50'
-    prefix: -local-rms-window
-  - valueFrom: 'rms-with-min'
-    prefix: -local-rms-method
-  - valueFrom: 'briggs'
-    # Note: we have to set part of the 'weight' argument here and part below, as it has
-    # three parts (e.g., '-weight briggs -0.5'), and WSClean will not parse the value
-    # correctly if it's given together with 'briggs'. We force the parts to be adjacent
-    # using the position arg here and below
-    prefix: -weight
-    position: 1
-
-inputs:
-  - id: msin
-    type: Directory[]
-    inputBinding:
-      position: 3
-  - id: name
-    type: string
-    inputBinding:
-      prefix: -name
-  - id: mask
-    type: File
-    inputBinding:
-      prefix: -fits-mask
-  - id: wsclean_imsize
-    type: int[]
-    inputBinding:
-      prefix: -size
-  - id: wsclean_niter
-    type: int
-    inputBinding:
-      prefix: -niter
-  - id: wsclean_nmiter
-    type: int
-    inputBinding:
-      prefix: -nmiter
-  - id: robust
-    type: float
-    inputBinding:
-      position: 2
-  - id: min_uv_lambda
-    type: float
-    inputBinding:
-      prefix: -minuv-l
-  - id: max_uv_lambda
-    type: float
-    inputBinding:
-      prefix: -maxuv-l
-  - id: cellsize_deg
-    type: float
-    inputBinding:
-      prefix: -scale
-  - id: multiscale_scales_pixel
-    type: string
-    inputBinding:
-      prefix: -multiscale-scales
-  - id: channels_out
-    type: int
-    inputBinding:
-      prefix: -channels-out
-  - id: taper_arcsec
-    type: float
-    inputBinding:
-      prefix: -taper-gaussian
-  - id: wsclean_mem
-    type: float
-    inputBinding:
-      prefix: -mem
-  - id: auto_mask
-    type: float
-    inputBinding:
-      prefix: -auto-mask
-  - id: idg_mode
-    type: string
-    inputBinding:
-      prefix: -idg-mode
-  - id: num_threads
-    type: string
-    inputBinding:
-      prefix: -j
-  - id: num_deconvolution_threads
-    type: string
-    inputBinding:
-      prefix: -deconvolution-threads
-
-outputs:
-  - id: image_nonpb_name
-    type: File
-    outputBinding:
-      glob: $(inputs.name)-MFS-image.fits
-  - id: image_pb_name
-    type: File
-    outputBinding:
-      glob: $(inputs.name)-MFS-image-pb.fits
-  - id: skymodel_nonpb
-    type: File
-    outputBinding:
-      glob: $(inputs.name)-sources.txt
-  - id: skymodel_pb
-    type: File
-    outputBinding:
-      glob: $(inputs.name)-sources-pb.txt
-
-hints:
-  - class: DockerRequirement
-    dockerPull: 'astronrd/rapthor'
diff --git a/rapthor/pipeline/steps/wsclean_image.cwl b/rapthor/pipeline/steps/wsclean_image_screens.cwl
similarity index 96%
rename from rapthor/pipeline/steps/wsclean_image.cwl
rename to rapthor/pipeline/steps/wsclean_image_screens.cwl
index cfb40c0da8a704676d5f6f7431ab090971df5517..53f8de37c464f76c964c379782dba35e707701cd 100644
--- a/rapthor/pipeline/steps/wsclean_image.cwl
+++ b/rapthor/pipeline/steps/wsclean_image_screens.cwl
@@ -119,19 +119,26 @@ inputs:
     inputBinding:
       position: 2
   - id: min_uv_lambda
-    label: Minimum us distance
+    label: Minimum uv distance
     doc: |
       The minimum uv distance in lambda.
     type: float
     inputBinding:
       prefix: -minuv-l
   - id: max_uv_lambda
-    label: Maximum us distance
+    label: Maximum uv distance
     doc: |
       The maximum uv distance in lambda.
     type: float
     inputBinding:
       prefix: -maxuv-l
+  - id: multiscale
+    label: Activate multiscale
+    doc: |
+      Activates multiscale clean.
+    type: boolean
+    inputBinding:
+      prefix: -multiscale
   - id: cellsize_deg
     label: Pixel size
     doc: |
@@ -182,14 +189,14 @@ inputs:
     inputBinding:
       prefix: -idg-mode
   - id: num_threads
-    type: string
+    type: int
     inputBinding:
       prefix: -j
   - id: num_deconvolution_threads
     label: Number of threads
     doc: |
       The number of threads to use.
-    type: string
+    type: int
     inputBinding:
       prefix: -deconvolution-threads
 
diff --git a/rapthor/pipeline/steps/wsclean_mpi_image_multiscale.cwl b/rapthor/pipeline/steps/wsclean_mpi_image_multiscale.cwl
deleted file mode 100644
index a76b2f930c0b27244a27cf20813cff4eaee3b44d..0000000000000000000000000000000000000000
--- a/rapthor/pipeline/steps/wsclean_mpi_image_multiscale.cwl
+++ /dev/null
@@ -1,187 +0,0 @@
-#!/usr/bin/env cwl-runner
-cwlVersion: v1.1
-class: CommandLineTool
-$namespaces:
-  cwltool: "http://commonwl.org/cwltool#"
-baseCommand: wsclean-mp
-label: Make an image
-doc: |
-  This tool makes an image using WSClean with a-term corrections and
-  multiscale cleaning, distributed over multiple nodes with MPI. See
-  wsclean_image.cwl for a detailed description of the inputs and outputs.
-
-requirements:
-  - class: InitialWorkDirRequirement
-    listing:
-      - entryname: aterm_plus_beam.cfg
-        # Note: WSClean requires that the aterm image filenames be input as part of an
-        # aterm config file (and not directly on the command line). Therefore, a config
-        # file is made here that contains the filenames defined in the aterm_images
-        # input parameter. Also, the required beam parameters are set here
-        entry: |
-          aterms = [diagonal, beam]
-          diagonal.images = [$(inputs.aterm_images.map( (e,i) => (e.path) ).join(' '))]
-          beam.differential = true
-          beam.update_interval = 120
-          beam.usechannelfreq = true
-        writable: false
-  - class: InlineJavascriptRequirement
-  - class: cwltool:MPIRequirement
-    processes: $(inputs.nnodes)
-
-arguments:
-  - -no-update-model-required
-  - -multiscale
-  - -save-source-list
-  - -local-rms
-  - -join-channels
-  - -use-idg
-  - -log-time
-  - valueFrom: '$(runtime.tmpdir)'
-    prefix: -temp-dir
-  - valueFrom: 'I'
-    prefix: -pol
-  - valueFrom: '0.85'
-    prefix: -mgain
-  - valueFrom: '3'
-    prefix: -fit-spectral-pol
-  - valueFrom: '2048'
-    prefix: -parallel-deconvolution
-  - valueFrom: 'gaussian'
-    prefix: -multiscale-shape
-  - valueFrom: '1.0'
-    prefix: -auto-threshold
-  - valueFrom: '50'
-    prefix: -local-rms-window
-  - valueFrom: 'rms-with-min'
-    prefix: -local-rms-method
-  - valueFrom: '32'
-    prefix: -aterm-kernel-size
-  - valueFrom: 'aterm_plus_beam.cfg'
-    # Note: this file is generated on the fly in the requirements section above
-    prefix: -aterm-config
-  - valueFrom: 'briggs'
-    # Note: we have to set part of the 'weight' argument here and part below, as it has
-    # three parts (e.g., '-weight briggs -0.5'), and WSClean will not parse the value
-    # correctly if it's given together with 'briggs'. We force the parts to be adjacent
-    # using the position arg here and below
-    prefix: -weight
-    position: 1
-
-inputs:
-  - id: msin
-    type: Directory[]
-    inputBinding:
-      position: 3
-  - id: name
-    type: string
-    inputBinding:
-      prefix: -name
-  - id: mask
-    type: File
-    inputBinding:
-      prefix: -fits-mask
-  - id: aterm_images
-    label: Filenames of aterm files
-    doc: |
-      The filenames of the a-term image files. These filenames are not used directly in the
-      WSClean call (they are read by WSClean from the aterm config file, defined in the
-      requirements section above), hence the value is set to "null" (which results in
-      nothing being added to the command for this input).
-    type: File[]
-    inputBinding:
-      valueFrom: null
-  - id: wsclean_imsize
-    type: int[]
-    inputBinding:
-      prefix: -size
-  - id: wsclean_niter
-    type: int
-    inputBinding:
-      prefix: -niter
-  - id: wsclean_nmiter
-    type: int
-    inputBinding:
-      prefix: -nmiter
-  - id: robust
-    type: float
-    inputBinding:
-      position: 2
-  - id: min_uv_lambda
-    type: float
-    inputBinding:
-      prefix: -minuv-l
-  - id: max_uv_lambda
-    type: float
-    inputBinding:
-      prefix: -maxuv-l
-  - id: cellsize_deg
-    type: float
-    inputBinding:
-      prefix: -scale
-  - id: multiscale_scales_pixel
-    label: Multiscale scales
-    doc: |
-      The multiscale scales in pixels.
-    type: string
-    inputBinding:
-      prefix: -multiscale-scales
-  - id: channels_out
-    type: int
-    inputBinding:
-      prefix: -channels-out
-  - id: deconvolution_channels
-    type: int
-    inputBinding:
-      prefix: -deconvolution-channels
-  - id: taper_arcsec
-    type: float
-    inputBinding:
-      prefix: -taper-gaussian
-  - id: wsclean_mem
-    type: float
-    inputBinding:
-      prefix: -mem
-  - id: auto_mask
-    type: float
-    inputBinding:
-      prefix: -auto-mask
-  - id: idg_mode
-    type: string
-    inputBinding:
-      prefix: -idg-mode
-  - id: num_threads
-    type: string
-    inputBinding:
-      prefix: -j
-  - id: num_deconvolution_threads
-    type: string
-    inputBinding:
-      prefix: -deconvolution-threads
-  - id: nnodes
-    label: Number of nodes
-    doc: |
-      The number of nodes to use for the MPI job.
-    type: int
-
-outputs:
-  - id: image_nonpb_name
-    type: File
-    outputBinding:
-      glob: $(inputs.name)-MFS-image.fits
-  - id: image_pb_name
-    type: File
-    outputBinding:
-      glob: $(inputs.name)-MFS-image-pb.fits
-  - id: skymodel_nonpb
-    type: File
-    outputBinding:
-      glob: $(inputs.name)-sources.txt
-  - id: skymodel_pb
-    type: File
-    outputBinding:
-      glob: $(inputs.name)-sources-pb.txt
-
-hints:
-  - class: DockerRequirement
-    dockerPull: 'astronrd/rapthor'
diff --git a/rapthor/pipeline/steps/wsclean_mpi_image_multiscale_toil4.cwl b/rapthor/pipeline/steps/wsclean_mpi_image_multiscale_toil4.cwl
deleted file mode 100644
index 99b414c20a11414f8894c32031433c1b8631a154..0000000000000000000000000000000000000000
--- a/rapthor/pipeline/steps/wsclean_mpi_image_multiscale_toil4.cwl
+++ /dev/null
@@ -1,143 +0,0 @@
-cwlVersion: v1.2
-class: CommandLineTool
-baseCommand: [run_wsclean_mpi.sh]
-label: Make an image
-doc: |
-  This tool makes an image using WSClean with a-term corrections and
-  multiscale cleaning, distributed over multiple nodes with MPI. See
-  wsclean_image.cwl for a detailed description of the inputs and outputs.
-
-requirements:
-  InlineJavascriptRequirement: {}
-  ShellCommandRequirement: {}
-
-inputs:
-  - id: msin
-    type: Directory[]
-    inputBinding:
-      prefix: -m
-      itemSeparator: " "
-  - id: name
-    type: string
-    inputBinding:
-      prefix: -n
-  - id: mask
-    type: File
-    inputBinding:
-      prefix: -k
-  - id: config
-    type: File
-    inputBinding:
-      prefix: -c
-  - id: aterm_images
-    type: File[]
-    inputBinding:
-      valueFrom: ""
-  - id: wsclean_imsize
-    type: int[]
-    inputBinding:
-      prefix: -z
-      itemSeparator: " "
-  - id: wsclean_niter
-    type: int
-    inputBinding:
-      prefix: -i
-  - id: wsclean_nmiter
-    type: int
-    inputBinding:
-      prefix: -j
-  - id: robust
-    type: float
-    inputBinding:
-      prefix: -r
-  - id: min_uv_lambda
-    type: float
-    inputBinding:
-      prefix: -u
-  - id: max_uv_lambda
-    type: float
-    inputBinding:
-      prefix: -v
-  - id: cellsize_deg
-    type: float
-    inputBinding:
-      prefix: -x
-  - id: multiscale_scales_pixel
-    label: Multiscale scales
-    doc: |
-      The multiscale scales in pixels.
-    type: string
-    inputBinding:
-      prefix: -s
-  - id: dir_local
-    type: string
-    inputBinding:
-      prefix: -l
-  - id: channels_out
-    type: int
-    inputBinding:
-      prefix: -o
-  - id: deconvolution_channels
-    type: int
-    inputBinding:
-      prefix: -d
-  - id: taper_arcsec
-    type: float
-    inputBinding:
-      prefix: -t
-  - id: wsclean_mem
-    type: float
-    inputBinding:
-      prefix: -p
-  - id: auto_mask
-    type: float
-    inputBinding:
-      prefix: -a
-  - id: idg_mode
-    type: string
-    inputBinding:
-      prefix: -g
-  - id: ntasks
-    label: Number of tasks
-    doc: |
-      The number of tasks per node for MPI jobs.
-    type: int
-    inputBinding:
-      prefix: -y
-  - id: nnodes
-    label: Number of nodes
-    doc: |
-      The number of nodes for MPI jobs.
-    type: int
-    inputBinding:
-      prefix: -q
-  - id: num_threads
-    type: string
-    inputBinding:
-      prefix: -b
-  - id: num_deconvolution_threads
-    type: string
-    inputBinding:
-      prefix: -h
-
-outputs:
-  - id: image_nonpb_name
-    type: File
-    outputBinding:
-      glob: $(inputs.name)-MFS-image.fits
-  - id: image_pb_name
-    type: File
-    outputBinding:
-      glob: $(inputs.name)-MFS-image-pb.fits
-  - id: skymodel_nonpb
-    type: File
-    outputBinding:
-      glob: $(inputs.name)-sources.txt
-  - id: skymodel_pb
-    type: File
-    outputBinding:
-      glob: $(inputs.name)-sources-pb.txt
-
-hints:
-  - class: DockerRequirement
-    dockerPull: 'astronrd/rapthor'
diff --git a/rapthor/pipeline/steps/wsclean_mpi_image.cwl b/rapthor/pipeline/steps/wsclean_mpi_image_screens.cwl
similarity index 95%
rename from rapthor/pipeline/steps/wsclean_mpi_image.cwl
rename to rapthor/pipeline/steps/wsclean_mpi_image_screens.cwl
index 95e8445f4d7624aa0b06e705f253baab3bd18dfb..f64430a0d8f2fb56a2fde690d3dd0d40c65a1a63 100644
--- a/rapthor/pipeline/steps/wsclean_mpi_image.cwl
+++ b/rapthor/pipeline/steps/wsclean_mpi_image_screens.cwl
@@ -7,8 +7,8 @@ baseCommand: wsclean-mp
 label: Make an image
 doc: |
   This tool makes an image using WSClean with a-term corrections, distributed
-  over multiple nodes with MPI. See wsclean_image.cwl for a detailed description
-  of the inputs and outputs.
+  over multiple nodes with MPI. See wsclean_image_screens.cwl for a detailed
+  description of the inputs and outputs.
 
 requirements:
   - class: InitialWorkDirRequirement
@@ -112,6 +112,10 @@ inputs:
     type: float
     inputBinding:
       prefix: -maxuv-l
+  - id: multiscale
+    type: boolean
+    inputBinding:
+      prefix: -multiscale
   - id: cellsize_deg
     type: float
     inputBinding:
@@ -141,11 +145,11 @@ inputs:
     inputBinding:
       prefix: -idg-mode
   - id: num_threads
-    type: string
+    type: int
     inputBinding:
       prefix: -j
   - id: num_deconvolution_threads
-    type: string
+    type: int
     inputBinding:
       prefix: -deconvolution-threads
   - id: nnodes
diff --git a/rapthor/pipeline/steps/wsclean_mpi_image_toil4.cwl b/rapthor/pipeline/steps/wsclean_mpi_image_toil4.cwl
deleted file mode 100644
index df304499b58aa7903f0d1c48f920ea64a622d3d2..0000000000000000000000000000000000000000
--- a/rapthor/pipeline/steps/wsclean_mpi_image_toil4.cwl
+++ /dev/null
@@ -1,136 +0,0 @@
-cwlVersion: v1.2
-class: CommandLineTool
-baseCommand: [run_wsclean_mpi.sh]
-label: Make an image
-doc: |
-  This tool makes an image using WSClean with a-term corrections,
-  distributed over multiple nodes with MPI. See wsclean_image.cwl
-  for a detailed description of the inputs and outputs.
-
-requirements:
-  InlineJavascriptRequirement: {}
-  ShellCommandRequirement: {}
-
-inputs:
-  - id: msin
-    type: Directory[]
-    inputBinding:
-      prefix: -m
-      itemSeparator: " "
-  - id: name
-    type: string
-    inputBinding:
-      prefix: -n
-  - id: mask
-    type: File
-    inputBinding:
-      prefix: -k
-  - id: config
-    type: File
-    inputBinding:
-      prefix: -c
-  - id: aterm_images
-    type: File[]
-    inputBinding:
-      valueFrom: ""
-  - id: wsclean_imsize
-    type: int[]
-    inputBinding:
-      prefix: -z
-      itemSeparator: " "
-  - id: wsclean_niter
-    type: int
-    inputBinding:
-      prefix: -i
-  - id: wsclean_nmiter
-    type: int
-    inputBinding:
-      prefix: -j
-  - id: robust
-    type: float
-    inputBinding:
-      prefix: -r
-  - id: min_uv_lambda
-    type: float
-    inputBinding:
-      prefix: -u
-  - id: max_uv_lambda
-    type: float
-    inputBinding:
-      prefix: -v
-  - id: cellsize_deg
-    type: float
-    inputBinding:
-      prefix: -x
-  - id: dir_local
-    type: string
-    inputBinding:
-      prefix: -l
-  - id: channels_out
-    type: int
-    inputBinding:
-      prefix: -o
-  - id: deconvolution_channels
-    type: int
-    inputBinding:
-      prefix: -d
-  - id: taper_arcsec
-    type: float
-    inputBinding:
-      prefix: -t
-  - id: wsclean_mem
-    type: float
-    inputBinding:
-      prefix: -p
-  - id: auto_mask
-    type: float
-    inputBinding:
-      prefix: -a
-  - id: idg_mode
-    type: string
-    inputBinding:
-      prefix: -g
-  - id: ntasks
-    label: Number of tasks
-    doc: |
-      The number of tasks per node for MPI jobs.
-    type: int
-    inputBinding:
-      prefix: -y
-  - id: nnodes
-    label: Number of nodes
-    doc: |
-      The number of nodes for MPI jobs.
-    type: int
-    inputBinding:
-      prefix: -q
-  - id: num_threads
-    type: string
-    inputBinding:
-      prefix: -b
-  - id: num_deconvolution_threads
-    type: string
-    inputBinding:
-      prefix: -h
-
-outputs:
-  - id: image_nonpb_name
-    type: File
-    outputBinding:
-      glob: $(inputs.name)-MFS-image.fits
-  - id: image_pb_name
-    type: File
-    outputBinding:
-      glob: $(inputs.name)-MFS-image-pb.fits
-  - id: skymodel_nonpb
-    type: File
-    outputBinding:
-      glob: $(inputs.name)-sources.txt
-  - id: skymodel_pb
-    type: File
-    outputBinding:
-      glob: $(inputs.name)-sources-pb.txt
-
-hints:
-  - class: DockerRequirement
-    dockerPull: 'astronrd/rapthor'
diff --git a/rapthor/pipeline/steps/wsclean_restore.cwl b/rapthor/pipeline/steps/wsclean_restore.cwl
index bb94fd6528b3ceaca1bf8d9fbe1fc91f7ff4330f..0cc7beb76e9fbd6e067abb9bf5594855479cc740 100644
--- a/rapthor/pipeline/steps/wsclean_restore.cwl
+++ b/rapthor/pipeline/steps/wsclean_restore.cwl
@@ -13,7 +13,7 @@ inputs:
     label: Number of threads
     doc: |
       The number of threads to use.
-    type: string
+    type: int
     inputBinding:
       prefix: -j
       position: 1