diff --git a/applications/utilities/preProcessing/smoothSurfaceData/Make/files b/applications/utilities/preProcessing/smoothSurfaceData/Make/files
new file mode 100644
index 0000000000..cb031ed9d6
--- /dev/null
+++ b/applications/utilities/preProcessing/smoothSurfaceData/Make/files
@@ -0,0 +1,3 @@
+smoothSurfaceData.C
+
+EXE = $(FOAM_USER_APPBIN)/smoothSurfaceData
diff --git a/applications/utilities/preProcessing/smoothSurfaceData/Make/options b/applications/utilities/preProcessing/smoothSurfaceData/Make/options
new file mode 100644
index 0000000000..128a319f08
--- /dev/null
+++ b/applications/utilities/preProcessing/smoothSurfaceData/Make/options
@@ -0,0 +1,11 @@
+/* Can compile with -fopenmp etc */
+
+EXE_INC = \
+ -I$(LIB_SRC)/fileFormats/lnInclude \
+ -I$(LIB_SRC)/surfMesh/lnInclude \
+ -I$(LIB_SRC)/meshTools/lnInclude
+
+EXE_LIBS = \
+ -lfileFormats \
+ -lsurfMesh \
+ -lmeshTools
diff --git a/applications/utilities/preProcessing/smoothSurfaceData/smoothSurfaceData.C b/applications/utilities/preProcessing/smoothSurfaceData/smoothSurfaceData.C
new file mode 100644
index 0000000000..7ca090ed43
--- /dev/null
+++ b/applications/utilities/preProcessing/smoothSurfaceData/smoothSurfaceData.C
@@ -0,0 +1,180 @@
+/*---------------------------------------------------------------------------*\
+ ========= |
+ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
+ \\ / O peration |
+ \\ / A nd | www.openfoam.com
+ \\/ M anipulation |
+-------------------------------------------------------------------------------
+ Copyright (C) 2022 OpenCFD Ltd.
+-------------------------------------------------------------------------------
+License
+ This file is part of OpenFOAM.
+
+ OpenFOAM is free software: you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with OpenFOAM. If not, see .
+
+Application
+ smoothSurfaceData
+
+Description
+ Pre-processing, filtering of surface field data.
+ Currently this is just used to test filter settings
+ (for MappedFile) and only generates vtk output, which can be
+ easily loaded in ParaView.
+
+\*---------------------------------------------------------------------------*/
+
+#include "argList.H"
+#include "Time.H"
+#include "clockTime.H"
+#include "primitiveFields.H"
+#include "surfaceReader.H"
+#include "surfaceWriter.H"
+#include "foamVtkSurfaceWriter.H"
+#include "MappedFileFilterField.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+using namespace Foam;
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+int main(int argc, char *argv[])
+{
+ argList::addNote
+ (
+ "Testing, pre-processing, filtering of surface field data"
+ );
+
+ argList::noCheckProcessorDirectories();
+ argList::addVerboseOption("Additional verbosity");
+
+ argList::addArgument("input", "The input surface file");
+
+ argList::addOption("radius", "m", "Specify filter radius [metres]");
+ argList::addOption("sweeps", "N", "Number of median filter stages");
+ argList::addOption
+ (
+ "field",
+ "name",
+ "Field to process (default: T)"
+ );
+
+ argList::addOption
+ (
+ "read-format",
+ "type",
+ "Input format (default: ensight)"
+ );
+
+ argList args(argc, argv);
+ // #include "setRootCase.H"
+
+ const int optVerbose = args.verbose();
+
+ const word readFileType
+ (
+ args.getOrDefault("read-format", "ensight")
+ );
+
+ // Constant radius searching
+ label filterSweeps_(1);
+ scalar filterRadius_(0);
+ args.readIfPresent("sweeps", filterSweeps_);
+ args.readIfPresent("radius", filterRadius_);
+
+ Info<< nl
+ << "Filter: radius=" << filterRadius_
+ << " sweeps=" << filterSweeps_ << endl;
+
+ // Simple sanity check
+ if ((filterSweeps_ < 1) || (filterRadius_ <= VSMALL))
+ {
+ Info<< nl << "Nothing to do. Exiting..." << nl << endl;
+ return 0;
+ }
+
+ word fieldName("T");
+ args.readIfPresent("field", fieldName);
+
+
+ const fileName importName = args.get(1);
+
+ auto readerPtr_ = surfaceReader::New(readFileType, importName);
+
+ auto& reader = readerPtr_();
+
+ const label fieldIndex = reader.fieldNames(0).find(fieldName);
+ if (fieldIndex == -1)
+ {
+ FatalErrorInFunction
+ << "Unable to find field name: " << fieldName
+ << " in list of available fields: " << reader.fieldNames(0)
+ << exit(FatalError);
+ }
+
+ clockTime timing;
+
+ const meshedSurface& geom = reader.geometry(0);
+
+ Info<< nl << "Read " << geom.nFaces() << " faces and "
+ << geom.nPoints() << " points in "
+ << timing.timeIncrement() << "s" << endl;
+
+ PatchFunction1Types::FilterField fieldFilter;
+
+ PatchFunction1Types::FilterField::debug = optVerbose;
+
+ fieldFilter.reset(geom, filterRadius_);
+
+ Info<< nl << "Built weights/addressing "
+ << timing.timeIncrement() << "s" << endl;
+
+ Info<< nl << "Processing " << reader.times().size() << " times" << nl;
+
+ const instantList times(reader.times());
+
+ forAll(times, timeIndex)
+ {
+ tmp tfield
+ (
+ reader.field(timeIndex, fieldIndex, pTraits::zero)
+ );
+
+ tfield = fieldFilter.evaluate(tfield, filterSweeps_);
+
+ vtk::surfaceWriter writer
+ (
+ geom.points(),
+ geom,
+ word::printf("filtered_%06d", timeIndex)
+ );
+
+ writer.piece(geom.points(), geom);
+
+ writer.writeGeometry();
+ writer.beginCellData();
+ writer.writeCellData(fieldName, tfield());
+ }
+
+ Info<< nl << "Smoothing/writing "
+ << timing.timeIncrement() << "s" << endl;
+
+ Info<< nl << "End\n" << endl;
+
+ return 0;
+}
+
+
+// ************************************************************************* //
diff --git a/src/meshTools/Make/files b/src/meshTools/Make/files
index 116463e51c..5f3a5d7d25 100644
--- a/src/meshTools/Make/files
+++ b/src/meshTools/Make/files
@@ -327,6 +327,8 @@ PatchFunction1/makePatchFunction1s.C
PatchFunction1/coordinateScaling/coordinateScalings.C
PatchFunction1/CodedField/makeCodedFields.C
+PatchFunction1/MappedFile/MappedFileFilterField.C
+
meshStructure/meshStructure.C
coupling/externalFileCoupler.C
diff --git a/src/meshTools/PatchFunction1/MappedFile/MappedFile.C b/src/meshTools/PatchFunction1/MappedFile/MappedFile.C
index 00f118662e..270ceaeaf1 100644
--- a/src/meshTools/PatchFunction1/MappedFile/MappedFile.C
+++ b/src/meshTools/PatchFunction1/MappedFile/MappedFile.C
@@ -47,6 +47,12 @@ Foam::PatchFunction1Types::MappedFile::MappedFile
fieldTableName_(dict.getOrDefault("fieldTable", entryName)),
pointsName_(dict.getOrDefault("points", "points")),
mapMethod_(),
+ filterRadius_(dict.getOrDefault("filterRadius", 0)),
+ filterSweeps_(dict.getOrDefault