diff --git a/src/optimisation/adjointOptimisation/adjoint/Make/options b/src/optimisation/adjointOptimisation/adjoint/Make/options
index 44dec84580915e1aaa7dbfd4be72b95d756be05e..f3041856b6bfa06999842fbb96bea046f58490db 100644
--- a/src/optimisation/adjointOptimisation/adjoint/Make/options
+++ b/src/optimisation/adjointOptimisation/adjoint/Make/options
@@ -1,5 +1,6 @@
 EXE_INC = \
     -I$(LIB_SRC)/finiteVolume/lnInclude \
+    -I$(LIB_SRC)/finiteArea/lnInclude \
     -I$(LIB_SRC)/meshTools/lnInclude \
     -I$(LIB_SRC)/surfMesh/lnInclude \
     -I$(LIB_SRC)/sampling/lnInclude \
@@ -14,6 +15,7 @@ EXE_INC = \
 
 LIB_LIBS = \
     -lfiniteVolume \
+    -lfiniteArea \
     -lmeshTools \
     -lsurfMesh \
     -lsampling \
diff --git a/src/optimisation/adjointOptimisation/adjoint/optimisation/adjointSensitivity/incompressible/sensitivitySurface/sensitivitySurfaceIncompressible.C b/src/optimisation/adjointOptimisation/adjoint/optimisation/adjointSensitivity/incompressible/sensitivitySurface/sensitivitySurfaceIncompressible.C
index 90bcce43aa60227cbbcc7eb83a4562aa48ced394..e17c5365a87c9c1f91c186900be0687b7481a1c4 100644
--- a/src/optimisation/adjointOptimisation/adjoint/optimisation/adjointSensitivity/incompressible/sensitivitySurface/sensitivitySurfaceIncompressible.C
+++ b/src/optimisation/adjointOptimisation/adjoint/optimisation/adjointSensitivity/incompressible/sensitivitySurface/sensitivitySurfaceIncompressible.C
@@ -5,8 +5,8 @@
     \\  /    A nd           | www.openfoam.com
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
-    Copyright (C) 2007-2020 PCOpt/NTUA
-    Copyright (C) 2013-2020 FOSS GP
+    Copyright (C) 2007-2021 PCOpt/NTUA
+    Copyright (C) 2013-2021 FOSS GP
     Copyright (C) 2019-2020 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
@@ -31,6 +31,11 @@ License
 #include "PrimitivePatchInterpolation.H"
 #include "syncTools.H"
 #include "addToRunTimeSelectionTable.H"
+#include "faCFD.H"
+#include "fixedValueFaPatchFieldsFwd.H"
+#include "fixedValueFaPatchFields.H"
+#include "zeroGradientFaPatchFieldsFwd.H"
+#include "zeroGradientFaPatchFields.H"
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
@@ -42,7 +47,7 @@ namespace incompressible
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
-defineTypeNameAndDebug(sensitivitySurface, 0);
+defineTypeNameAndDebug(sensitivitySurface, 1);
 addToRunTimeSelectionTable
 (
     adjointSensitivity,
@@ -214,6 +219,188 @@ void sensitivitySurface::setSuffixName()
 }
 
 
+void sensitivitySurface::smoothSensitivities()
+{
+    // Read in parameters
+    const label iters(dict().getOrDefault<label>("iters", 500));
+    const scalar tolerance(dict().getOrDefault<scalar>("tolerance", 1.e-06));
+    autoPtr<faMesh> aMeshPtr(nullptr);
+
+    IOobject faceLabels
+    (
+        "faceLabels",
+        mesh_.time().findInstance
+        (
+            mesh_.dbDir()/faMesh::meshSubDir, 
+            "faceLabels", 
+            IOobject::READ_IF_PRESENT
+        ),
+        faMesh::meshSubDir,
+        mesh_,
+        IOobject::READ_IF_PRESENT,
+        IOobject::NO_WRITE
+    );
+
+    // If the faMesh already exists, read it
+    if (faceLabels.typeHeaderOk<labelIOList>(false))
+    {
+        Info<< "Reading the already constructed faMesh" << endl;
+        aMeshPtr.reset(new faMesh(mesh_));
+    }
+    else
+    {
+        // Dictionary used to construct the faMesh
+        dictionary faMeshDefinition;
+
+        IOobject faMeshDefinitionDict
+        (
+            "faMeshDefinition",
+            mesh_.time().caseSystem(),
+            mesh_,
+            IOobject::MUST_READ,
+            IOobject::NO_WRITE
+        );
+        
+        // If the faMeshDefinitionDict exists, use it to construct the mesh
+        if (faMeshDefinitionDict.typeHeaderOk<IOdictionary>(false))
+        {
+            Info<< "Reading faMeshDefinition from system " << endl;
+            faMeshDefinition = IOdictionary(faMeshDefinitionDict);
+        }
+        // Otherwise, faMesh is generated from all patches on which we compute
+        // sensitivities
+        else
+        {
+            Info<< "Constructing faMeshDefinition from sensitivity patches" 
+                << endl;
+            wordList polyMeshPatches(sensitivityPatchIDs_.size());
+            label i(0);
+            for (const label patchID : sensitivityPatchIDs_)
+            {
+                polyMeshPatches[i++] = mesh_.boundary()[patchID].name();    
+            }
+            faMeshDefinition.add<wordList>("polyMeshPatches", polyMeshPatches);
+            dictionary& boundary = faMeshDefinition.subDictOrAdd("boundary");
+        }
+
+        // Construct faMesh
+        aMeshPtr.reset(new faMesh(mesh_, faMeshDefinition));
+    }
+    faMesh& aMesh = aMeshPtr.ref();
+
+    // Physical radius of the smoothing, provided either directly or computed
+    // based on the average 'length' of boundary faces
+    const scalar Rphysical
+        (dict().getOrDefault<scalar>("radius", computeRadius(aMesh)));
+    DebugInfo
+        << "Physical radius of the sensitivity smoothing " 
+        << Rphysical << nl << endl;
+
+    // Radius used as the diffusivity in the Helmholtz filter, computed as a 
+    // function of the physical radius
+    const dimensionedScalar RpdeSqr
+    (
+        "RpdeSqr", dimArea, sqr(Rphysical/(2.*::sqrt(3.)))
+    );
+
+    dimensionedScalar one("1", dimless, 1.);
+
+    // Mapping engine
+    volSurfaceMapping vsm(aMesh);
+
+    // Source term in faMatrix needs to be an areaField
+    areaScalarField sens
+    (
+        IOobject
+        (
+            "sens",
+            mesh_.time().timeName(),
+            mesh_,
+            IOobject::NO_READ,
+            IOobject::NO_WRITE
+        ),
+        aMesh,
+        dimensionedScalar(dimless, Zero),
+        zeroGradientFaPatchField<scalar>::typeName
+    );
+
+    // Copy sensitivities to area field
+    sens.primitiveFieldRef() =
+        vsm.mapToSurface<scalar>(wallFaceSensNormalPtr_());
+
+    // Initialisation of the smoothed sensitivities field based on the original
+    // sensitivities
+    areaScalarField smoothedSens("smoothedSens", sens);
+    for (label iter = 0; iter < iters; ++iter)
+    {
+        Info<< "Sensitivity smoothing iteration " << iter << endl;
+
+        faScalarMatrix smoothEqn
+        (
+            fam::Sp(one, smoothedSens)
+          - fam::laplacian(RpdeSqr, smoothedSens)
+         ==
+            sens
+        );
+
+        smoothEqn.relax();
+
+        const scalar residual(mag(smoothEqn.solve().initialResidual()));
+
+        DebugInfo
+            << "Max smoothSens " << gMax(mag(smoothedSens)()) << endl;
+
+        // Print execution time
+        mesh_.time().printExecutionTime(Info);
+
+        // Check convergence
+        if (residual < tolerance)
+        {
+            Info<< "\n***Reached smoothing equation convergence limit, "
+                   "iteration " << iter << "***\n\n";
+            break;
+        }
+    }
+
+    // Field used to write the smoothed sensitivity field to file
+    volScalarField volSmoothedSens
+    (
+        IOobject
+        (
+            "smoothedSurfaceSens" + surfaceFieldSuffix_,
+            mesh_.time().timeName(),
+            mesh_,
+            IOobject::NO_READ,
+            IOobject::NO_WRITE
+        ),
+        mesh_,
+        dimensionedScalar(dimless, Zero)
+    );
+
+    // Transfer result back to volField and write
+    vsm.mapToVolume(smoothedSens, volSmoothedSens.boundaryFieldRef());
+    volSmoothedSens.write();
+}
+
+
+scalar sensitivitySurface::computeRadius(const faMesh& aMesh)
+{
+    scalar averageArea(gAverage(aMesh.S().field()));
+    const Vector<label>& geometricD = mesh_.geometricD();
+    const boundBox& bounds = mesh_.bounds();
+    forAll(geometricD, iDir)
+    {
+        if (geometricD[iDir] == -1)
+        {
+            averageArea /= bounds.span()[iDir];
+        }
+    }
+    scalar mult = dict().getOrDefault<scalar>("meanRadiusMultiplier", 10);
+
+    return mult*pow(averageArea, scalar(1)/scalar(mesh_.nGeometricD() - 1));
+}
+
+
 // * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
 
 sensitivitySurface::sensitivitySurface
@@ -244,6 +431,7 @@ sensitivitySurface::sensitivitySurface
     includeMeshMovement_(false),
     includeObjective_(false),
     writeGeometricInfo_(false),
+    smoothSensitivities_(false),
     eikonalSolver_(nullptr),
     meshMovementSolver_(nullptr),
 
@@ -346,6 +534,8 @@ void sensitivitySurface::read()
         dict().getOrDefault<bool>("includeObjectiveContribution", true);
     writeGeometricInfo_ =
         dict().getOrDefault<bool>("writeGeometricInfo", false);
+    smoothSensitivities_ =
+        dict().getOrDefault<bool>("smoothSensitivities", false);
 
     // Allocate new solvers if necessary
     if (includeDistance_ && !eikonalSolver_)
@@ -700,6 +890,12 @@ void sensitivitySurface::assembleSensitivities()
         }
         nPassedFaces += patch.size();
     }
+
+    // Smooth sensitivities if needed
+    if (smoothSensitivities_)
+    {
+        smoothSensitivities();
+    }
 }
 
 
diff --git a/src/optimisation/adjointOptimisation/adjoint/optimisation/adjointSensitivity/incompressible/sensitivitySurface/sensitivitySurfaceIncompressible.H b/src/optimisation/adjointOptimisation/adjoint/optimisation/adjointSensitivity/incompressible/sensitivitySurface/sensitivitySurfaceIncompressible.H
index ec070cb0da457f41ccd04ff0fde00a0f06d6e50f..baff1c1ecf74bca73c676a345f6e32d6f627d045 100644
--- a/src/optimisation/adjointOptimisation/adjoint/optimisation/adjointSensitivity/incompressible/sensitivitySurface/sensitivitySurfaceIncompressible.H
+++ b/src/optimisation/adjointOptimisation/adjoint/optimisation/adjointSensitivity/incompressible/sensitivitySurface/sensitivitySurfaceIncompressible.H
@@ -5,8 +5,8 @@
     \\  /    A nd           | www.openfoam.com
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
-    Copyright (C) 2007-2020 PCOpt/NTUA
-    Copyright (C) 2013-2020 FOSS GP
+    Copyright (C) 2007-2021 PCOpt/NTUA
+    Copyright (C) 2013-2021 FOSS GP
     Copyright (C) 2019 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
@@ -31,6 +31,27 @@ Class
 Description
     Calculation of adjoint based sensitivities at wall faces
 
+    Reference:
+    \verbatim
+        The computation of the sensitivity derivatives  follows the (E-)SI
+        formulation of
+
+            Kavvadias, I. S., Papoutsis-Kiachagias, E. M., 
+            Giannakoglou, K. C. (2015).
+            On the proper treatment of grid sensitivities in continuous adjoint
+            methods for shape optimization.
+            Journal of computational physics, 301, 1-18.
+            https://doi.org/10.1016/j.jcp.2015.08.012
+
+        whereas their smoothing based on the computation of the 'Sobolev
+        gradient' is derived from
+    
+            Vassberg J. C., Jameson A. (2006).
+            Aerodynamic Shape Optimization Part I: Theoretical Background.
+            VKI Lecture Series, Introduction to Optimization and
+            Multidisciplinary Design, Brussels, Belgium, 8 March, 2006.
+    \endverbatim
+
 SourceFiles
     sensitivitySurface.C
 
@@ -44,6 +65,7 @@ SourceFiles
 #include "adjointEikonalSolverIncompressible.H"
 #include "adjointMeshMovementSolverIncompressible.H"
 #include "deltaBoundary.H"
+#include "faMesh.H"
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
@@ -97,6 +119,10 @@ protected:
         //- Write geometric info for use by external programs
         bool writeGeometricInfo_;
 
+        //- Smooth sensitivity derivatives based on the computation of the 
+        //- 'Sobolev gradient'
+        bool smoothSensitivities_;
+
         autoPtr<adjointEikonalSolver> eikonalSolver_;
 
         autoPtr<adjointMeshMovementSolver> meshMovementSolver_;
@@ -116,6 +142,14 @@ protected:
         //- Set suffix name for sensitivity fields
         void setSuffixName();
 
+        //- Smooth sensitivity derivatives based on the computation of the 
+        //- 'Sobolev gradient'
+        void smoothSensitivities();
+
+        //- Compute the physical smoothing radius based on the average boundary
+        //- face 'length'
+        scalar computeRadius(const faMesh& aMesh);
+
 
 private:
 
@@ -177,7 +211,7 @@ public:
        //- Write sensitivity maps
        virtual void write(const word& baseName = word::null);
 
-       // Inline geters and setters
+       // Inline getters and setters
 
            //- Get access to the includeObjective bool
            inline bool getIncludeObjective() const;