diff --git a/src/randomProcesses/noise/noiseModels/surfaceNoise/surfaceNoise.C b/src/randomProcesses/noise/noiseModels/surfaceNoise/surfaceNoise.C
new file mode 100644
index 0000000000..1dcc319766
--- /dev/null
+++ b/src/randomProcesses/noise/noiseModels/surfaceNoise/surfaceNoise.C
@@ -0,0 +1,490 @@
+/*---------------------------------------------------------------------------*\
+ ========= |
+ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
+ \\ / O peration |
+ \\ / A nd | Copyright (C) 2015-2016 OpenCFD Ltd.
+ \\/ M anipulation |
+-------------------------------------------------------------------------------
+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 .
+
+\*---------------------------------------------------------------------------*/
+
+#include "surfaceNoise.H"
+#include "surfaceReader.H"
+#include "surfaceWriter.H"
+#include "noiseFFT.H"
+#include "graph.H"
+#include "addToRunTimeSelectionTable.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+namespace noiseModels
+{
+
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
+
+defineTypeNameAndDebug(surfaceNoise, 0);
+addToRunTimeSelectionTable(noiseModel, surfaceNoise, dictionary);
+
+// * * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * //
+
+void surfaceNoise::initialise(const dictionary& dict)
+{
+ // All reading performed on the master processor only
+ if (Pstream::master())
+ {
+ // Create the surface reader
+ const word readerType(dict.lookup("reader"));
+ readerPtr_.reset(surfaceReader::New(readerType, inputFileName_).ptr());
+
+ // Find the index of the pressure data
+ const word pName(dict.lookupOrDefault("pName", "p"));
+ const List fieldNames(readerPtr_->fieldNames(0));
+ pIndex_ = findIndex(fieldNames, pName);
+ if (pIndex_ == -1)
+ {
+ FatalErrorInFunction
+ << "Unable to find pressure field name " << pName
+ << " in list of available fields: " << fieldNames
+ << exit(FatalError);
+ }
+
+ // Create the surface writer
+ // - Could be done later, but since this utility can process a lot of
+ // data we can ensure that the user-input is correct prior to doing
+ // the heavy lifting
+ const word writerType(dict.lookup("writer"));
+ dictionary optDict
+ (
+ dict.subOrEmptyDict("writeOptions").subOrEmptyDict(writerType)
+ );
+ writerPtr_.reset(surfaceWriter::New(writerType, optDict).ptr());
+
+ // Set the time range
+ const instantList allTimes = readerPtr_->times();
+ startTimeIndex_ = findStartTimeIndex(allTimes, startTime_);
+
+ // Determine the windowing
+ label nAvailableTimes = allTimes.size() - startTimeIndex_;
+ label nRequiredTimes = windowModelPtr_->validate(nAvailableTimes);
+
+ // Restrict times
+ times_.setSize(nRequiredTimes);
+ forAll(times_, timeI)
+ {
+ times_[timeI] = allTimes[timeI + startTimeIndex_].value();
+ }
+ deltaT_ = checkUniformTimeStep(times_);
+
+ const meshedSurface& surf = readerPtr_->geometry();
+ nFace_ = surf.size();
+ }
+
+ Pstream::scatter(pIndex_);
+ Pstream::scatter(times_);
+ Pstream::scatter(startTimeIndex_);
+ Pstream::scatter(deltaT_);
+ Pstream::scatter(nFace_);
+}
+
+
+void surfaceNoise::readSurfaceData
+(
+ const labelList& procFaceOffset,
+ List& pData
+)
+{
+ // Data is stored as pressure on surface at a given time. Now we need to
+ // pivot the data so that we have the complete pressure time history per
+ // surface face. In serial mode, this results in all pressure data being
+ // loaded into memory (!)
+
+ Info << "Reading pressure data" << endl;
+
+ if (Pstream::parRun())
+ {
+ PstreamBuffers pBufs(Pstream::nonBlocking);
+
+ // Procedure:
+ // 1. Master processor reads pressure data for all faces for all times
+ // 2. Master sends each processor a subset of faces
+ // 3. Local processor reconstructs the full time history for the subset
+ // of faces
+ // Note: reading all data on master to avoid potential NFS problems...
+
+ const label myProcI = Pstream::myProcNo();
+ const label nLocalFace =
+ procFaceOffset[myProcI + 1] - procFaceOffset[myProcI];
+
+ // Complete pressure time history data for subset of faces
+ pData.setSize(nLocalFace);
+ const label nTimes = times_.size();
+ forAll(pData, faceI)
+ {
+ pData[faceI].setSize(nTimes);
+ }
+
+ // Read and send data
+ forAll(times_, i)
+ {
+ pBufs.clear();
+
+ if (Pstream::master())
+ {
+ label timeI = i + startTimeIndex_;
+
+ Info<< " time: " << times_[i] << endl;
+
+ // Read pressure at all faces for time timeI
+ scalarField p(readerPtr_->field(timeI, pIndex_, scalar(0)));
+
+ // Send subset of faces to each processor
+ for (label procI = 0; procI < Pstream::nProcs(); procI++)
+ {
+ label face0 = procFaceOffset[procI];
+ label nLocalFace =
+ procFaceOffset[procI + 1] - procFaceOffset[procI];
+
+ UOPstream toProc(procI, pBufs);
+ toProc << SubList(p, nLocalFace, face0);
+ }
+ }
+
+ pBufs.finishedSends();
+
+ // Receive data from the master
+ UIPstream fromProc(0, pBufs);
+
+ scalarList pSlice(fromProc);
+
+ forAll(pSlice, faceI)
+ {
+ pData[faceI][i] = pSlice[faceI];
+ }
+ }
+ }
+ else
+ {
+ const label nLocalFace = procFaceOffset[0];
+
+ pData.setSize(nLocalFace);
+ forAll(times_, timeI)
+ {
+ forAll(pData, faceI)
+ {
+ pData[faceI].setSize(times_.size());
+ }
+ }
+
+ forAll(times_, i)
+ {
+ label timeI = i + startTimeIndex_;
+
+ Info<< " time: " << times_[i] << endl;
+ const scalarField p(readerPtr_->field(timeI, pIndex_, scalar(0)));
+
+ forAll(p, faceI)
+ {
+ pData[faceI][i] = p[faceI];
+ }
+ }
+ }
+
+ Info<< "Read "
+ << returnReduce(pData.size(), sumOp