From 938084438e9993600997ec4c3375a4c0cfcc2d52 Mon Sep 17 00:00:00 2001
From: mattijs <mattijs>
Date: Thu, 25 Nov 2010 13:33:27 +0000
Subject: [PATCH] ENH: directMapped: allow offset specification; allow
 fieldName specification; allow non-constant interpolation

---
 .../directMappedFixedValueFvPatchField.C      | 137 +++++++++--
 .../directMappedFixedValueFvPatchField.H      |  33 ++-
 ...MappedVelocityFluxFixedValueFvPatchField.C |  23 ++
 .../directMappedPatchBase.C                   | 230 ++++++++++++------
 .../directMappedPatchBase.H                   |  69 +++++-
 5 files changed, 389 insertions(+), 103 deletions(-)

diff --git a/src/finiteVolume/fields/fvPatchFields/derived/directMappedFixedValue/directMappedFixedValueFvPatchField.C b/src/finiteVolume/fields/fvPatchFields/derived/directMappedFixedValue/directMappedFixedValueFvPatchField.C
index 4dd232091d1..0fb5b64b780 100644
--- a/src/finiteVolume/fields/fvPatchFields/derived/directMappedFixedValue/directMappedFixedValueFvPatchField.C
+++ b/src/finiteVolume/fields/fvPatchFields/derived/directMappedFixedValue/directMappedFixedValueFvPatchField.C
@@ -26,6 +26,7 @@ License
 #include "directMappedFixedValueFvPatchField.H"
 #include "directMappedPatchBase.H"
 #include "volFields.H"
+#include "interpolationCell.H"
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
@@ -42,8 +43,10 @@ directMappedFixedValueFvPatchField<Type>::directMappedFixedValueFvPatchField
 )
 :
     fixedValueFvPatchField<Type>(p, iF),
+    fieldName_(iF.name()),
     setAverage_(false),
-    average_(pTraits<Type>::zero)
+    average_(pTraits<Type>::zero),
+    interpolationScheme_(interpolationCell<Type>::typeName)
 {}
 
 
@@ -57,8 +60,10 @@ directMappedFixedValueFvPatchField<Type>::directMappedFixedValueFvPatchField
 )
 :
     fixedValueFvPatchField<Type>(ptf, p, iF, mapper),
+    fieldName_(ptf.fieldName_),
     setAverage_(ptf.setAverage_),
-    average_(ptf.average_)
+    average_(ptf.average_),
+    interpolationScheme_(ptf.interpolationScheme_)
 {
     if (!isA<directMappedPatchBase>(this->patch().patch()))
     {
@@ -91,8 +96,10 @@ directMappedFixedValueFvPatchField<Type>::directMappedFixedValueFvPatchField
 )
 :
     fixedValueFvPatchField<Type>(p, iF, dict),
+    fieldName_(dict.lookupOrDefault<word>("fieldName", iF.name())),
     setAverage_(readBool(dict.lookup("setAverage"))),
-    average_(pTraits<Type>(dict.lookup("average")))
+    average_(pTraits<Type>(dict.lookup("average"))),
+    interpolationScheme_(interpolationCell<Type>::typeName)
 {
     if (!isA<directMappedPatchBase>(this->patch().patch()))
     {
@@ -112,6 +119,15 @@ directMappedFixedValueFvPatchField<Type>::directMappedFixedValueFvPatchField
             << " in file " << this->dimensionedInternalField().objectPath()
             << exit(FatalError);
     }
+
+    const directMappedPatchBase& mpp = refCast<const directMappedPatchBase>
+    (
+        directMappedFixedValueFvPatchField<Type>::patch().patch()
+    );
+    if (mpp.mode() == directMappedPatchBase::NEARESTCELL)
+    {
+        dict.lookup("interpolationScheme") >> interpolationScheme_;
+    }
 }
 
 
@@ -122,8 +138,10 @@ directMappedFixedValueFvPatchField<Type>::directMappedFixedValueFvPatchField
 )
 :
     fixedValueFvPatchField<Type>(ptf),
+    fieldName_(ptf.fieldName_),
     setAverage_(ptf.setAverage_),
-    average_(ptf.average_)
+    average_(ptf.average_),
+    interpolationScheme_(ptf.interpolationScheme_)
 {}
 
 
@@ -135,13 +153,67 @@ directMappedFixedValueFvPatchField<Type>::directMappedFixedValueFvPatchField
 )
 :
     fixedValueFvPatchField<Type>(ptf, iF),
+    fieldName_(ptf.fieldName_),
     setAverage_(ptf.setAverage_),
-    average_(ptf.average_)
+    average_(ptf.average_),
+    interpolationScheme_(ptf.interpolationScheme_)
 {}
 
 
 // * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
 
+template<class Type>
+const GeometricField<Type, fvPatchField, volMesh>&
+directMappedFixedValueFvPatchField<Type>::sampleField() const
+{
+    typedef GeometricField<Type, fvPatchField, volMesh> fieldType;
+
+    const directMappedPatchBase& mpp = refCast<const directMappedPatchBase>
+    (
+        directMappedFixedValueFvPatchField<Type>::patch().patch()
+    );
+    const fvMesh& nbrMesh = refCast<const fvMesh>(mpp.sampleMesh());
+
+    if (mpp.sameRegion())
+    {
+        if (fieldName_ == this->dimensionedInternalField().name())
+        {
+            // Optimisation: bypass field lookup
+            return
+                dynamic_cast<const fieldType&>
+                (
+                    this->dimensionedInternalField()
+                );
+        }
+        else
+        {
+            const fvMesh& thisMesh = this->patch().boundaryMesh().mesh();
+            return thisMesh.lookupObject<fieldType>(fieldName_);
+        }
+    }
+    else
+    {
+        return nbrMesh.lookupObject<fieldType>(fieldName_);
+    }
+}
+
+
+template<class Type>
+const interpolation<Type>&
+directMappedFixedValueFvPatchField<Type>::interpolator() const
+{
+    if (!interpolator_.valid())
+    {
+        interpolator_ = interpolation<Type>::New
+        (
+            interpolationScheme_,
+            sampleField()
+        );
+    }
+    return interpolator_();
+}
+
+
 template<class Type>
 void directMappedFixedValueFvPatchField<Type>::updateCoeffs()
 {
@@ -159,11 +231,8 @@ void directMappedFixedValueFvPatchField<Type>::updateCoeffs()
     );
     const mapDistribute& distMap = mpp.map();
 
-    // Force recalculation of schedule
-    (void)distMap.schedule();
-
+    const fvMesh& thisMesh = this->patch().boundaryMesh().mesh();
     const fvMesh& nbrMesh = refCast<const fvMesh>(mpp.sampleMesh());
-    const word& fldName = this->dimensionedInternalField().name();
 
     // Result of obtaining remote values
     Field<Type> newValues;
@@ -172,17 +241,37 @@ void directMappedFixedValueFvPatchField<Type>::updateCoeffs()
     {
         case directMappedPatchBase::NEARESTCELL:
         {
-            if (mpp.sameRegion())
+            if (interpolationScheme_ != interpolationCell<Type>::typeName)
             {
-                newValues = this->internalField();
+                // Send back sample points to the processor that holds the cell
+                vectorField samples(mpp.samplePoints());
+                distMap.reverseDistribute
+                (
+                    (mpp.sameRegion() ? thisMesh.nCells() : nbrMesh.nCells()),
+                    point::max,
+                    samples
+                );
+
+                const interpolation<Type>& interp = interpolator();
+
+                newValues.setSize(samples.size(), pTraits<Type>::max);
+                forAll(samples, cellI)
+                {
+                    if (samples[cellI] != point::max)
+                    {
+                        newValues[cellI] = interp.interpolate
+                        (
+                            samples[cellI],
+                            cellI
+                        );
+                    }
+                }
             }
             else
             {
-                newValues = nbrMesh.lookupObject<fieldType>
-                (
-                    fldName
-                ).internalField();
+                newValues = sampleField();
             }
+
             mapDistribute::distribute
             (
                 Pstream::defaultCommsType,
@@ -213,10 +302,8 @@ void directMappedFixedValueFvPatchField<Type>::updateCoeffs()
                  << abort(FatalError);
             }
 
-            const fieldType& nbrField = nbrMesh.lookupObject<fieldType>
-            (
-                fldName
-            );
+            const fieldType& nbrField = sampleField();
+
             newValues = nbrField.boundaryField()[nbrPatchID];
             mapDistribute::distribute
             (
@@ -234,10 +321,8 @@ void directMappedFixedValueFvPatchField<Type>::updateCoeffs()
         {
             Field<Type> allValues(nbrMesh.nFaces(), pTraits<Type>::zero);
 
-            const fieldType& nbrField = nbrMesh.lookupObject<fieldType>
-            (
-                fldName
-            );
+            const fieldType& nbrField = sampleField();
+
             forAll(nbrField.boundaryField(), patchI)
             {
                 const fvPatchField<Type>& pf =
@@ -294,7 +379,8 @@ void directMappedFixedValueFvPatchField<Type>::updateCoeffs()
 
     if (debug)
     {
-        Info<< "directMapped on field:" << fldName
+        Info<< "directMapped on field:"
+            << this->dimensionedInternalField().name()
             << " patch:" << this->patch().name()
             << "  avg:" << gAverage(*this)
             << "  min:" << gMin(*this)
@@ -310,8 +396,11 @@ template<class Type>
 void directMappedFixedValueFvPatchField<Type>::write(Ostream& os) const
 {
     fvPatchField<Type>::write(os);
+    os.writeKeyword("fieldName") << fieldName_ << token::END_STATEMENT << nl;
     os.writeKeyword("setAverage") << setAverage_ << token::END_STATEMENT << nl;
     os.writeKeyword("average") << average_ << token::END_STATEMENT << nl;
+    os.writeKeyword("interpolationScheme") << interpolationScheme_
+        << token::END_STATEMENT << nl;
     this->writeEntry("value", os);
 }
 
diff --git a/src/finiteVolume/fields/fvPatchFields/derived/directMappedFixedValue/directMappedFixedValueFvPatchField.H b/src/finiteVolume/fields/fvPatchFields/derived/directMappedFixedValue/directMappedFixedValueFvPatchField.H
index 8d6038913ed..07960cd0e5e 100644
--- a/src/finiteVolume/fields/fvPatchFields/derived/directMappedFixedValue/directMappedFixedValueFvPatchField.H
+++ b/src/finiteVolume/fields/fvPatchFields/derived/directMappedFixedValue/directMappedFixedValueFvPatchField.H
@@ -33,6 +33,17 @@ Description
     mode = NEARESTFACE : sample nearest face on any patch. Note: does not
                          warn if nearest actually is on internal face!
 
+    For NEARESTCELL you have to provide an 'interpolationScheme' entry
+    which can be any one of the interpolation schemes (cell, cellPoint, etc.)
+    In case of interpolation (so scheme != cell) the limitation is that
+    there is only one value per cell. So e.g. if you have a cell with two
+    boundary faces and both faces sample into the cell both faces will get
+    the same value.
+
+    See directMappedPatchBase for options on sampling.
+
+    Optional 'fieldName' entry to supply a different filename
+
 SourceFiles
     directMappedFixedValueFvPatchField.C
 
@@ -43,6 +54,7 @@ SourceFiles
 
 #include "fixedValueFvPatchFields.H"
 #include "directMappedFvPatch.H"
+#include "interpolation.H"
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
@@ -60,12 +72,29 @@ class directMappedFixedValueFvPatchField
 {
     // Private data
 
+        //- Name of field to sample - defaults to field associated with this
+        //  patchField if not specified
+        word fieldName_;
+
         //- If true adjust the mapped field to maintain average value average_
-        bool setAverage_;
+        const bool setAverage_;
 
         //- Average value the mapped field is adjusted to maintain if
         //  setAverage_ is set true
-        Type average_;
+        const Type average_;
+
+        //- Interpolation scheme to use for nearestcell mode
+        word interpolationScheme_;
+
+        mutable autoPtr<interpolation<Type> > interpolator_;
+
+    // Private Member Functions
+
+        //- Field to sample. Either on my or nbr mesh
+        const GeometricField<Type, fvPatchField, volMesh>& sampleField() const;
+
+        //- Access the interpolation method
+        const interpolation<Type>& interpolator() const;
 
 public:
 
diff --git a/src/finiteVolume/fields/fvPatchFields/derived/directMappedVelocityFluxFixedValue/directMappedVelocityFluxFixedValueFvPatchField.C b/src/finiteVolume/fields/fvPatchFields/derived/directMappedVelocityFluxFixedValue/directMappedVelocityFluxFixedValueFvPatchField.C
index 36821fe72f0..8f64aedc7eb 100644
--- a/src/finiteVolume/fields/fvPatchFields/derived/directMappedVelocityFluxFixedValue/directMappedVelocityFluxFixedValueFvPatchField.C
+++ b/src/finiteVolume/fields/fvPatchFields/derived/directMappedVelocityFluxFixedValue/directMappedVelocityFluxFixedValueFvPatchField.C
@@ -112,6 +112,29 @@ directMappedVelocityFluxFixedValueFvPatchField
             << " in file " << dimensionedInternalField().objectPath()
             << exit(FatalError);
     }
+
+    const directMappedPatchBase& mpp = refCast<const directMappedPatchBase>
+    (
+        this->patch().patch()
+    );
+    if (mpp.mode() == directMappedPolyPatch::NEARESTCELL)
+    {
+        FatalErrorIn
+        (
+            "directMappedVelocityFluxFixedValueFvPatchField::"
+            "directMappedVelocityFluxFixedValueFvPatchField"
+            "("
+                "const fvPatch&, "
+                "const DimensionedField<vector, volMesh>&, "
+                "const dictionary&"
+            ")"
+        )   << "Patch " << p.name()
+            << " of type '" << p.type()
+            << "' can not be used in 'nearestCell' mode"
+            << " of field " << dimensionedInternalField().name()
+            << " in file " << dimensionedInternalField().objectPath()
+            << exit(FatalError);
+    }
 }
 
 
diff --git a/src/meshTools/directMapped/directMappedPolyPatch/directMappedPatchBase.C b/src/meshTools/directMapped/directMappedPolyPatch/directMappedPatchBase.C
index f3b679154c1..957fd0281bc 100644
--- a/src/meshTools/directMapped/directMappedPolyPatch/directMappedPatchBase.C
+++ b/src/meshTools/directMapped/directMappedPolyPatch/directMappedPatchBase.C
@@ -54,6 +54,18 @@ namespace Foam
         directMappedPatchBase::sampleModeNames_;
 
 
+    template<>
+    const char* NamedEnum<directMappedPatchBase::offsetMode, 3>::names[] =
+    {
+        "uniform",
+        "nonuniform",
+        "normal"
+    };
+
+    const NamedEnum<directMappedPatchBase::offsetMode, 3>
+        directMappedPatchBase::offsetModeNames_;
+
+
     //- Private class for finding nearest
     //  - point+local index
     //  - sqr(distance)
@@ -100,7 +112,7 @@ void Foam::directMappedPatchBase::collectSamples
     labelListList globalFaces(Pstream::nProcs());
 
     globalFc[Pstream::myProcNo()] = patch_.faceCentres();
-    globalSamples[Pstream::myProcNo()] = globalFc[Pstream::myProcNo()]+offsets_;
+    globalSamples[Pstream::myProcNo()] = samplePoints();
     globalFaces[Pstream::myProcNo()] = identity(patch_.size());
 
     // Distribute to all processors
@@ -393,19 +405,34 @@ void Foam::directMappedPatchBase::calcMapping() const
             << "Mapping already calculated" << exit(FatalError);
     }
 
-    if
+    // Do a sanity check
+    // Am I sampling my own patch? This only makes sense for a non-zero
+    // offset.
+    bool sampleMyself =
     (
-        gAverage(mag(offsets_)) <= ROOTVSMALL
-     && mode_ == NEARESTPATCHFACE
+        mode_ == NEARESTPATCHFACE
      && sampleRegion_ == patch_.boundaryMesh().mesh().name()
      && samplePatch_ == patch_.name()
-    )
+    );
+
+    // Check offset
+    vectorField d(samplePoints()-patch_.faceCentres());
+    if (sampleMyself && gAverage(mag(d)) <= ROOTVSMALL)
     {
-        WarningIn("directMappedPatchBase::calcMapping() const")
-            << "Invalid offset " << offsets_ << endl
+        WarningIn
+        (
+            "directMappedPatchBase::directMappedPatchBase\n"
+            "(\n"
+            "    const polyPatch& pp,\n"
+            "    const word& sampleRegion,\n"
+            "    const sampleMode mode,\n"
+            "    const word& samplePatch,\n"
+            "    const vector& offset\n"
+            ")\n"
+        )   << "Invalid offset " << d << endl
             << "Offset is the vector added to the patch face centres to"
             << " find the patch face supplying the data." << endl
-            << "Setting it to " << offsets_
+            << "Setting it to " << d
             << " on the same patch, on the same region"
             << " will find the faces themselves which does not make sense"
             << " for anything but testing." << endl
@@ -413,10 +440,11 @@ void Foam::directMappedPatchBase::calcMapping() const
             << "sampleRegion_:" << sampleRegion_ << endl
             << "mode_:" << sampleModeNames_[mode_] << endl
             << "samplePatch_:" << samplePatch_ << endl
-            << "offsets_:" << offsets_ << endl;
+            << "offsetMode_:" << offsetModeNames_[offsetMode_] << endl;
     }
 
 
+
     // Get global list of all samples and the processor and face they come from.
     pointField samples;
     labelList patchFaceProcs;
@@ -477,40 +505,6 @@ void Foam::directMappedPatchBase::calcMapping() const
     }
 
 
-    //// Check that actual offset vector (sampleLocations - patchFc) is more or
-    //// less constant.
-    //if (Pstream::master())
-    //{
-    //    const scalarField magOffset(mag(sampleLocations - patchFc));
-    //    const scalar avgOffset(average(magOffset));
-    //
-    //    forAll(magOffset, sampleI)
-    //    {
-    //        if
-    //        (
-    //            mag(magOffset[sampleI]-avgOffset)
-    //          > max(SMALL, 0.001*avgOffset)
-    //        )
-    //        {
-    //            WarningIn("directMappedPatchBase::calcMapping() const")
-    //                << "The actual cell/face centres picked up using offset "
-    //                << offsets_ << " are not" << endl
-    //                << "    on a single plane."
-    //                << " This might give numerical problems." << endl
-    //                << "    At patchface " << patchFc[sampleI]
-    //                << " the sampled cell/face " << sampleLocations[sampleI]
-    //                << endl
-    //                << "    is not on a plane " << avgOffset
-    //                << " offset from the patch." << endl
-    //                << "    You might want to shift your plane offset."
-    //                << " Set the debug flag to get a dump of sampled cells."
-    //                << endl;
-    //            break;
-    //        }
-    //    }
-    //}
-
-
     // Determine schedule.
     mapPtr_.reset(new mapDistribute(sampleProcs, patchFaceProcs));
 
@@ -597,9 +591,10 @@ Foam::directMappedPatchBase::directMappedPatchBase
     sampleRegion_(patch_.boundaryMesh().mesh().name()),
     mode_(NEARESTPATCHFACE),
     samplePatch_("none"),
-    uniformOffset_(true),
+    offsetMode_(UNIFORM),
     offset_(vector::zero),
     offsets_(pp.size(), offset_),
+    distance_(0),
     sameRegion_(sampleRegion_ == patch_.boundaryMesh().mesh().name()),
     mapPtr_(NULL)
 {}
@@ -618,8 +613,10 @@ Foam::directMappedPatchBase::directMappedPatchBase
     sampleRegion_(sampleRegion),
     mode_(mode),
     samplePatch_(samplePatch),
-    uniformOffset_(false),
+    offsetMode_(NONUNIFORM),
+    offset_(vector::zero),
     offsets_(offsets),
+    distance_(0),
     sameRegion_(sampleRegion_ == patch_.boundaryMesh().mesh().name()),
     mapPtr_(NULL)
 {}
@@ -638,9 +635,10 @@ Foam::directMappedPatchBase::directMappedPatchBase
     sampleRegion_(sampleRegion),
     mode_(mode),
     samplePatch_(samplePatch),
-    uniformOffset_(true),
+    offsetMode_(UNIFORM),
     offset_(offset),
-    offsets_(pp.size(), offset_),
+    offsets_(0),
+    distance_(0),
     sameRegion_(sampleRegion_ == patch_.boundaryMesh().mesh().name()),
     mapPtr_(NULL)
 {}
@@ -663,22 +661,62 @@ Foam::directMappedPatchBase::directMappedPatchBase
     ),
     mode_(sampleModeNames_.read(dict.lookup("sampleMode"))),
     samplePatch_(dict.lookup("samplePatch")),
-    uniformOffset_(dict.found("offset")),
-    offset_
-    (
-        uniformOffset_
-      ? point(dict.lookup("offset"))
-      : vector::zero
-    ),
-    offsets_
-    (
-        uniformOffset_
-      ? pointField(pp.size(), offset_)
-      : dict.lookup("offsets")
-    ),
+    offsetMode_(UNIFORM),
+    offset_(vector::zero),
+    offsets_(0),
+    distance_(0.0),
     sameRegion_(sampleRegion_ == patch_.boundaryMesh().mesh().name()),
     mapPtr_(NULL)
-{}
+{
+    if (dict.found("offsetMode"))
+    {
+        offsetMode_ = offsetModeNames_.read(dict.lookup("offsetMode"));
+
+        switch(offsetMode_)
+        {
+            case UNIFORM:
+            {
+                offset_ = point(dict.lookup("offset"));
+            }
+            break;
+
+            case NONUNIFORM:
+            {
+                offsets_ = pointField(dict.lookup("offsets"));
+            }
+            break;
+
+            case NORMAL:
+            {
+                distance_ = readScalar(dict.lookup("distance"));
+            }
+            break;
+        }
+    }
+    else if (dict.found("offset"))
+    {
+        offsetMode_ = UNIFORM;
+        offset_ = point(dict.lookup("offset"));
+    }
+    else if (dict.found("offsets"))
+    {
+        offsetMode_ = NONUNIFORM;
+        offsets_ = pointField(dict.lookup("offsets"));
+    }
+    else
+    {
+        FatalErrorIn
+        (
+            "directMappedPatchBase::directMappedPatchBase\n"
+            "(\n"
+            "    const polyPatch& pp,\n"
+            "    const dictionary& dict\n"
+            ")\n"
+        )   << "Please supply the offsetMode as one of "
+            << NamedEnum<offsetMode, 3>::words()
+            << exit(FatalError);
+    }
+}
 
 
 Foam::directMappedPatchBase::directMappedPatchBase
@@ -691,9 +729,10 @@ Foam::directMappedPatchBase::directMappedPatchBase
     sampleRegion_(dmp.sampleRegion_),
     mode_(dmp.mode_),
     samplePatch_(dmp.samplePatch_),
-    uniformOffset_(dmp.uniformOffset_),
+    offsetMode_(dmp.offsetMode_),
     offset_(dmp.offset_),
     offsets_(dmp.offsets_),
+    distance_(dmp.distance_),
     sameRegion_(dmp.sameRegion_),
     mapPtr_(NULL)
 {}
@@ -710,9 +749,10 @@ Foam::directMappedPatchBase::directMappedPatchBase
     sampleRegion_(dmp.sampleRegion_),
     mode_(dmp.mode_),
     samplePatch_(dmp.samplePatch_),
-    uniformOffset_(dmp.uniformOffset_),
+    offsetMode_(dmp.offsetMode_),
     offset_(dmp.offset_),
     offsets_(dmp.offsets_, mapAddressing),
+    distance_(dmp.distance_),
     sameRegion_(dmp.sameRegion_),
     mapPtr_(NULL)
 {}
@@ -762,6 +802,40 @@ const Foam::polyPatch& Foam::directMappedPatchBase::samplePolyPatch() const
 }
 
 
+Foam::tmp<Foam::pointField> Foam::directMappedPatchBase::samplePoints() const
+{
+    tmp<pointField> tfld(new pointField(patch_.faceCentres()));
+    pointField& fld = tfld();
+
+    switch(offsetMode_)
+    {
+        case UNIFORM:
+        {
+            fld += offset_;
+        }
+        break;
+
+        case NONUNIFORM:
+        {
+            fld += offsets_;
+        }
+        break;
+
+        case NORMAL:
+        {
+            // Get outwards pointing normal
+            vectorField n(patch_.faceAreas());
+            n /= mag(n);
+
+            fld += distance_*n;
+        }
+        break;
+    }
+
+    return tfld;
+}
+
+
 void Foam::directMappedPatchBase::write(Ostream& os) const
 {
     os.writeKeyword("sampleMode") << sampleModeNames_[mode_]
@@ -770,13 +844,31 @@ void Foam::directMappedPatchBase::write(Ostream& os) const
         << token::END_STATEMENT << nl;
     os.writeKeyword("samplePatch") << samplePatch_
         << token::END_STATEMENT << nl;
-    if (uniformOffset_)
-    {
-        os.writeKeyword("offset") << offset_ << token::END_STATEMENT << nl;
-    }
-    else
+
+    os.writeKeyword("offsetMode") << offsetModeNames_[offsetMode_]
+        << token::END_STATEMENT << nl;
+
+    switch(offsetMode_)
     {
-        os.writeKeyword("offsets") << offsets_ << token::END_STATEMENT << nl;
+        case UNIFORM:
+        {
+            os.writeKeyword("offset") << offset_ << token::END_STATEMENT << nl;
+        }
+        break;
+
+        case NONUNIFORM:
+        {
+            os.writeKeyword("offsets") << offsets_ << token::END_STATEMENT
+                << nl;
+        }
+        break;
+
+        case NORMAL:
+        {
+            os.writeKeyword("distance") << distance_ << token::END_STATEMENT
+                << nl;
+        }
+        break;
     }
 }
 
diff --git a/src/meshTools/directMapped/directMappedPolyPatch/directMappedPatchBase.H b/src/meshTools/directMapped/directMappedPolyPatch/directMappedPatchBase.H
index 281e24ec6e9..96ac05bf7c2 100644
--- a/src/meshTools/directMapped/directMappedPolyPatch/directMappedPatchBase.H
+++ b/src/meshTools/directMapped/directMappedPolyPatch/directMappedPatchBase.H
@@ -28,6 +28,33 @@ Description
     Determines a mapping between patch face centres and mesh cell or face
     centres and processors they're on.
 
+    If constructed from dictionary:
+        // Region to sample (default is region0)
+        sampleRegion region0;
+
+        // What to sample:
+        // - nearestCell : sample nearest cell
+        // - nearestPatchFace : nearest face on selected patch
+        // - nearestFace : nearest boundary face on any patch
+        sampleMode nearestCell;
+
+        // If sampleMod is nearestPatchFace : patch to find faces of
+        samplePatch movingWall;
+
+        // How to supply offset (w.r.t. my patch face centres):
+        // - uniform : single offset vector
+        // - nonuniform : per-face offset vector
+        // - normal : using supplied distance and face normal
+        offsetMode uniform;
+
+        // According to offsetMode (see above) supply one of
+        // offset, offsets or distance
+        offset  (1 0 0);
+
+    Note: if offsetMode is 'normal' it uses outwards pointing normals. So
+    supply a negative distance if sampling inside the domain.
+
+
 Note
     Storage is not optimal. It temporary collects all (patch)face centres
     on all processors to keep the addressing calculation simple.
@@ -71,12 +98,22 @@ public:
             NEARESTFACE         // nearest face
         };
 
+        //- How to project face centres
+        enum offsetMode
+        {
+            UNIFORM,            // single offset vector
+            NONUNIFORM,         // per-face offset vector
+            NORMAL              // use face normal + distance
+        };
+
 private:
 
     // Private data
 
         static const NamedEnum<sampleMode, 3> sampleModeNames_;
 
+        static const NamedEnum<offsetMode, 3> offsetModeNames_;
+
         //- Patch to sample
         const polyPatch& patch_;
 
@@ -89,14 +126,17 @@ private:
         //- Patch (only if NEARESTPATCHFACE)
         const word samplePatch_;
 
-        //- For backwards compatibility : reading/writing of uniform offset.
-        const bool uniformOffset_;
+        //- How to obtain samples
+        offsetMode offsetMode_;
 
         //- Offset vector (uniform)
-        const vector offset_;
+        vector offset_;
 
-        //- Offset vector
-        const vectorField offsets_;
+        //- Offset vector (nonuniform)
+        vectorField offsets_;
+
+        //- Offset distance (normal)
+        scalar distance_;
 
         //- Same region
         const bool sameRegion_;
@@ -146,17 +186,17 @@ public:
         //- Construct from patch
         directMappedPatchBase(const polyPatch&);
 
-        //- Construct from components
+        //- Construct with offsetMode=non-uniform
         directMappedPatchBase
         (
             const polyPatch& pp,
             const word& sampleRegion,
             const sampleMode sampleMode,
             const word& samplePatch,
-            const vectorField& offset
+            const vectorField& offsets
         );
 
-        //- Construct from components
+        //- Construct from offsetMode=uniform
         directMappedPatchBase
         (
             const polyPatch& pp,
@@ -166,6 +206,16 @@ public:
             const vector& offset
         );
 
+        ////- Construct from normal and distance
+        //directMappedPatchBase
+        //(
+        //    const polyPatch& pp,
+        //    const word& sampleRegion,
+        //    const word& samplePatch,
+        //    const sampleMode sampleMode,
+        //    const vector& offset
+        //);
+
         //- Construct from dictionary
         directMappedPatchBase(const polyPatch&, const dictionary&);
 
@@ -241,6 +291,9 @@ public:
         //- Get the patch on the region
         const polyPatch& samplePolyPatch() const;
 
+        //- Get the sample points
+        tmp<pointField> samplePoints() const;
+
         //- Write as a dictionary
         virtual void write(Ostream&) const;
 };
-- 
GitLab