diff --git a/applications/utilities/mesh/generation/snappyHexMesh/snappyHexMeshDict b/applications/utilities/mesh/generation/snappyHexMesh/snappyHexMeshDict
index b3be8fe96f68bfd6240a98ef05fb71c0b45b7e15..f0cc40a8e4867cc7d1de6408832d6360006e9985 100644
--- a/applications/utilities/mesh/generation/snappyHexMesh/snappyHexMeshDict
+++ b/applications/utilities/mesh/generation/snappyHexMesh/snappyHexMeshDict
@@ -245,6 +245,10 @@ snapControls
 
         //- Use castellatedMeshControls::features (default = true)
         explicitFeatureSnap true;
+
+        //- Detect features between multiple surfaces
+        //  (only for explicitFeatureSnap, default = false)
+        multiRegionFeatureSnap false;
 }
 
 // Settings for the layer addition.
diff --git a/applications/utilities/miscellaneous/foamDebugSwitches/Make/options b/applications/utilities/miscellaneous/foamDebugSwitches/Make/options
index 6fe40ed9f19464475c06f704c9842935d8fcdfd1..6fa4104188a79deb511308fe1cf34d9e80243768 100644
--- a/applications/utilities/miscellaneous/foamDebugSwitches/Make/options
+++ b/applications/utilities/miscellaneous/foamDebugSwitches/Make/options
@@ -60,7 +60,6 @@ EXE_LIBS = \
     -lsurfaceFilmModels \
     -lsurfMesh \
     -lsystemCall \
-    -lthermalPorousZone \
     -lthermophysicalFunctions \
     -ltopoChangerFvMesh \
     -ltriSurface \
diff --git a/applications/utilities/surface/surfaceFeatureExtract/surfaceFeatureExtract.C b/applications/utilities/surface/surfaceFeatureExtract/surfaceFeatureExtract.C
index d06e5d03cb8b12ff53e10e575d6a01fc31a58cdc..b3b390af515122423aaf6a99116a267366cd1da2 100644
--- a/applications/utilities/surface/surfaceFeatureExtract/surfaceFeatureExtract.C
+++ b/applications/utilities/surface/surfaceFeatureExtract/surfaceFeatureExtract.C
@@ -25,7 +25,8 @@ Application
     surfaceFeatureExtract
 
 Description
-    Extracts and writes surface features to file
+    Extracts and writes surface features to file. All but the basic feature
+    extraction is WIP.
 
 \*---------------------------------------------------------------------------*/
 
@@ -472,6 +473,176 @@ void unmarkBaffles
 }
 
 
+//- Divide into multiple normal bins
+//  - return REGION if != 2 normals
+//  - return REGION if 2 normals that make feature angle
+//  - otherwise return NONE and set normals,bins
+surfaceFeatures::edgeStatus checkFlatRegionEdge
+(
+    const triSurface& surf,
+    const scalar tol,
+    const scalar includedAngle,
+    const label edgeI
+)
+{
+    const edge& e = surf.edges()[edgeI];
+    const labelList& eFaces = surf.edgeFaces()[edgeI];
+
+    // Bin according to normal
+
+    DynamicList<Foam::vector> normals(2);
+    DynamicList<labelList> bins(2);
+
+    forAll(eFaces, eFaceI)
+    {
+        const Foam::vector& n = surf.faceNormals()[eFaces[eFaceI]];
+
+        // Find the normal in normals
+        label index = -1;
+        forAll(normals, normalI)
+        {
+            if (mag(n&normals[normalI]) > (1-tol))
+            {
+                index = normalI;
+                break;
+            }
+        }
+
+        if (index != -1)
+        {
+            bins[index].append(eFaceI);
+        }
+        else if (normals.size() >= 2)
+        {
+            // Would be third normal. Mark as feature.
+            //Pout<< "** at edge:" << surf.localPoints()[e[0]]
+            //    << surf.localPoints()[e[1]]
+            //    << " have normals:" << normals
+            //    << " and " << n << endl;
+            return surfaceFeatures::REGION;
+        }
+        else
+        {
+            normals.append(n);
+            bins.append(labelList(1, eFaceI));
+        }
+    }
+
+
+    // Check resulting number of bins
+    if (bins.size() == 1)
+    {
+        // Note: should check here whether they are two sets of faces
+        // that are planar or indeed 4 faces al coming together at an edge.
+        //Pout<< "** at edge:"
+        //    << surf.localPoints()[e[0]]
+        //    << surf.localPoints()[e[1]]
+        //    << " have single normal:" << normals[0]
+        //    << endl;
+        return surfaceFeatures::NONE;
+    }
+    else
+    {
+        // Two bins. Check if normals make an angle
+
+        //Pout<< "** at edge:"
+        //    << surf.localPoints()[e[0]]
+        //    << surf.localPoints()[e[1]] << nl
+        //    << "    normals:" << normals << nl
+        //    << "    bins   :" << bins << nl
+        //    << endl;
+
+        if (includedAngle >= 0)
+        {
+            scalar minCos = Foam::cos(degToRad(180.0 - includedAngle));
+
+            forAll(eFaces, i)
+            {
+                const Foam::vector& ni = surf.faceNormals()[eFaces[i]];
+                for (label j=i+1; j<eFaces.size(); j++)
+                {
+                    const Foam::vector& nj = surf.faceNormals()[eFaces[j]];
+                    if (mag(ni & nj) < minCos)
+                    {
+                        //Pout<< "have sharp feature between normal:" << ni
+                        //    << " and " << nj << endl;
+
+                        // Is feature. Keep as region or convert to
+                        // feature angle? For now keep as region.
+                        return surfaceFeatures::REGION;
+                    }
+                }
+            }
+        }
+
+
+        // So now we have two normals bins but need to make sure both
+        // bins have the same regions in it.
+
+         // 1. store + or - region number depending
+        //    on orientation of triangle in bins[0]
+        const labelList& bin0 = bins[0];
+        labelList regionAndNormal(bin0.size());
+        forAll(bin0, i)
+        {
+            const labelledTri& t = surf.localFaces()[eFaces[bin0[i]]];
+            int dir = t.edgeDirection(e);
+
+            if (dir > 0)
+            {
+                regionAndNormal[i] = t.region()+1;
+            }
+            else if (dir == 0)
+            {
+                FatalErrorIn("problem.")
+                    << exit(FatalError);
+            }
+            else
+            {
+                regionAndNormal[i] = -(t.region()+1);
+            }
+        }
+
+        // 2. check against bin1
+        const labelList& bin1 = bins[1];
+        labelList regionAndNormal1(bin1.size());
+        forAll(bin1, i)
+        {
+            const labelledTri& t = surf.localFaces()[eFaces[bin1[i]]];
+            int dir = t.edgeDirection(e);
+
+            label myRegionAndNormal;
+            if (dir > 0)
+            {
+                myRegionAndNormal = t.region()+1;
+            }
+            else
+            {
+                myRegionAndNormal = -(t.region()+1);
+            }
+
+            regionAndNormal1[i] = myRegionAndNormal;
+
+            label index = findIndex(regionAndNormal, -myRegionAndNormal);
+            if (index == -1)
+            {
+                // Not found.
+                //Pout<< "cannot find region " << myRegionAndNormal
+                //    << " in regions " << regionAndNormal << endl;
+
+                return surfaceFeatures::REGION;
+            }
+        }
+
+        // Passed all checks, two normal bins with the same contents.
+        //Pout<< "regionAndNormal:" << regionAndNormal << endl;
+        //Pout<< "myRegionAndNormal:" << regionAndNormal1 << endl;
+
+        return surfaceFeatures::NONE;
+    }
+}
+
+
 void writeStats(const extendedFeatureEdgeMesh& fem, Ostream& os)
 {
     os  << "    points : " << fem.points().size() << nl
@@ -610,6 +781,8 @@ int main(int argc, char *argv[])
 
         surfaceFeatures set(surf);
 
+        scalar includedAngle = -1;
+
         if (extractionMethod == "extractFromFile")
         {
             const fileName featureEdgeFile =
@@ -630,14 +803,13 @@ int main(int argc, char *argv[])
         }
         else if (extractionMethod == "extractFromSurface")
         {
-            const scalar includedAngle =
-                readScalar
+            includedAngle = readScalar
+            (
+                surfaceDict.subDict("extractFromSurfaceCoeffs").lookup
                 (
-                    surfaceDict.subDict("extractFromSurfaceCoeffs").lookup
-                    (
-                        "includedAngle"
-                    )
-                );
+                    "includedAngle"
+                )
+            );
 
             Info<< nl << "Constructing feature set from included angle "
                 << includedAngle << endl;
@@ -727,13 +899,27 @@ int main(int argc, char *argv[])
             if (!nonManifoldEdges)
             {
                 Info<< "Removing all non-manifold edges"
-                    << " (edges with > 2 connected faces)" << endl;
+                    << " (edges with > 2 connected faces) unless they"
+                    << " cross multiple regions" << endl;
 
                 forAll(edgeStat, edgeI)
                 {
-                    if (surf.edgeFaces()[edgeI].size() > 2)
+                    const labelList& eFaces = surf.edgeFaces()[edgeI];
+
+                    if
+                    (
+                        eFaces.size() > 2
+                     && edgeStat[edgeI] == surfaceFeatures::REGION
+                     && (eFaces.size() % 2) == 0
+                    )
                     {
-                        edgeStat[edgeI] = surfaceFeatures::NONE;
+                        edgeStat[edgeI] = checkFlatRegionEdge
+                        (
+                            surf,
+                            1e-5,   //tol,
+                            includedAngle,
+                            edgeI
+                        );
                     }
                 }
             }
diff --git a/applications/utilities/surface/surfaceFeatureExtract/surfaceFeatureExtractDict b/applications/utilities/surface/surfaceFeatureExtract/surfaceFeatureExtractDict
index 6c1eab66e9c5cc466540055c8b0382dc633bf109..a52ebdfa555755da0ff951721f0d26823ad6b2b1 100644
--- a/applications/utilities/surface/surfaceFeatureExtract/surfaceFeatureExtractDict
+++ b/applications/utilities/surface/surfaceFeatureExtract/surfaceFeatureExtractDict
@@ -69,7 +69,8 @@ surface2.nas
         // Keep edges outside the box:
         outsideBox          (0 0 0)(1 1 1);
 
-        // Keep nonManifold edges (edges with >2 connected faces)
+        // Keep nonManifold edges (edges with >2 connected faces where
+        // the faces form more than two different normal planes)
         nonManifoldEdges       yes;
 
         // Keep open edges (edges with 1 connected face)
diff --git a/src/OSspecific/POSIX/regExp.C b/src/OSspecific/POSIX/regExp.C
index 3ce0d2c4471b69f487781c4bbc64d5971ac5f4fa..2023f9ce59870df5786c1b58638c9c3f24ee882f 100644
--- a/src/OSspecific/POSIX/regExp.C
+++ b/src/OSspecific/POSIX/regExp.C
@@ -2,7 +2,7 @@
   =========                 |
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2011 OpenFOAM Foundation
+    \\  /    A nd           | Copyright (C) 2011-2012 OpenFOAM Foundation
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
 License
@@ -80,12 +80,18 @@ void Foam::regExp::set(const char* pattern, const bool ignoreCase) const
             cflags |= REG_ICASE;
         }
 
-        if (regcomp(preg_, pattern, cflags) != 0)
+        int err = regcomp(preg_, pattern, cflags);
+
+        if (err != 0)
         {
+            char errbuf[200];
+            regerror(err, preg_, errbuf, sizeof(errbuf));
+
             FatalErrorIn
             (
                 "regExp::set(const char*)"
             )   << "Failed to compile regular expression '" << pattern << "'"
+                << nl << errbuf
                 << exit(FatalError);
         }
     }
diff --git a/src/OpenFOAM/db/IOstreams/Sstreams/ISstream.C b/src/OpenFOAM/db/IOstreams/Sstreams/ISstream.C
index a3161386e69677e283c5b2d7872a6465dfdfde2d..c93aad5a36f956d6075d1b89e4329cd601ba5a93 100644
--- a/src/OpenFOAM/db/IOstreams/Sstreams/ISstream.C
+++ b/src/OpenFOAM/db/IOstreams/Sstreams/ISstream.C
@@ -266,7 +266,7 @@ Foam::Istream& Foam::ISstream::read(token& t)
                 else
                 {
                     t = sPtr;
-                    t.type() = token::STRING;
+                    t.type() = token::VARIABLE;
                 }
                 return *this;
             }
diff --git a/src/OpenFOAM/db/IOstreams/token/token.H b/src/OpenFOAM/db/IOstreams/token/token.H
index 75c946d1d407c48fc2da2a73ab9fd783c9a72e1a..a1523fd3ffd5194b3e309457c7a069abb5026998 100644
--- a/src/OpenFOAM/db/IOstreams/token/token.H
+++ b/src/OpenFOAM/db/IOstreams/token/token.H
@@ -2,7 +2,7 @@
   =========                 |
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2011 OpenFOAM Foundation
+    \\  /    A nd           | Copyright (C) 2011-2012 OpenFOAM Foundation
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
 License
@@ -77,6 +77,7 @@ public:
 
         PUNCTUATION,
         WORD,
+        VARIABLE,
         STRING,
         VERBATIMSTRING,
         LABEL,
@@ -331,6 +332,8 @@ public:
             inline bool isWord() const;
             inline const word& wordToken() const;
 
+            inline bool isVariable() const;
+
             inline bool isString() const;
             inline const string& stringToken() const;
 
diff --git a/src/OpenFOAM/db/IOstreams/token/tokenI.H b/src/OpenFOAM/db/IOstreams/token/tokenI.H
index dfa6cdf93e453162c30fe7cb7546921dab6ee618..e23fc83679463bcaa3acb00a9857129ddbabd4f1 100644
--- a/src/OpenFOAM/db/IOstreams/token/tokenI.H
+++ b/src/OpenFOAM/db/IOstreams/token/tokenI.H
@@ -2,7 +2,7 @@
   =========                 |
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2011 OpenFOAM Foundation
+    \\  /    A nd           | Copyright (C) 2011-2012 OpenFOAM Foundation
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
 License
@@ -39,7 +39,7 @@ inline void token::clear()
     {
         delete wordTokenPtr_;
     }
-    else if (type_ == STRING || type_ == VERBATIMSTRING)
+    else if (type_ == STRING || type_ == VARIABLE || type_ == VERBATIMSTRING)
     {
         delete stringTokenPtr_;
     }
@@ -88,6 +88,7 @@ inline token::token(const token& t)
         break;
 
         case STRING:
+        case VARIABLE:
         case VERBATIMSTRING:
             stringTokenPtr_ = new string(*t.stringTokenPtr_);
         break;
@@ -235,14 +236,19 @@ inline const word& token::wordToken() const
     }
 }
 
+inline bool token::isVariable() const
+{
+    return (type_ == VARIABLE);
+}
+
 inline bool token::isString() const
 {
-    return (type_ == STRING || type_ == VERBATIMSTRING);
+    return (type_ == STRING || type_ == VARIABLE || type_ == VERBATIMSTRING);
 }
 
 inline const string& token::stringToken() const
 {
-    if (type_ == STRING || type_ == VERBATIMSTRING)
+    if (type_ == STRING || type_ == VARIABLE || type_ == VERBATIMSTRING)
     {
         return *stringTokenPtr_;
     }
@@ -411,6 +417,7 @@ inline void token::operator=(const token& t)
         break;
 
         case STRING:
+        case VARIABLE:
         case VERBATIMSTRING:
             stringTokenPtr_ = new string(*t.stringTokenPtr_);
         break;
@@ -518,6 +525,7 @@ inline bool token::operator==(const token& t) const
             return *wordTokenPtr_ == *t.wordTokenPtr_;
 
         case STRING:
+        case VARIABLE:
         case VERBATIMSTRING:
             return *stringTokenPtr_ == *t.stringTokenPtr_;
 
@@ -552,7 +560,11 @@ inline bool token::operator==(const word& w) const
 
 inline bool token::operator==(const string& s) const
 {
-    return ((type_ == STRING || type_ == VERBATIMSTRING) && stringToken() == s);
+    return
+    (
+        (type_ == STRING || type_ == VARIABLE || type_ == VERBATIMSTRING)
+     && stringToken() == s
+    );
 }
 
 inline bool token::operator==(const label l) const
diff --git a/src/OpenFOAM/db/IOstreams/token/tokenIO.C b/src/OpenFOAM/db/IOstreams/token/tokenIO.C
index 2aedebe6814f737da7bfd6c8574337e13286402b..b513a4be0eda7d991b10f538ae11499ced859134 100644
--- a/src/OpenFOAM/db/IOstreams/token/tokenIO.C
+++ b/src/OpenFOAM/db/IOstreams/token/tokenIO.C
@@ -2,7 +2,7 @@
   =========                 |
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2011 OpenFOAM Foundation
+    \\  /    A nd           | Copyright (C) 2011-2012 OpenFOAM Foundation
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
 License
@@ -74,6 +74,11 @@ Foam::Ostream& Foam::operator<<(Ostream& os, const token& t)
             os << *t.stringTokenPtr_;
         break;
 
+        case token::VARIABLE:
+            // Write variable as word so without ""
+            os.writeQuoted(*t.stringTokenPtr_, false);
+        break;
+
         case token::LABEL:
             os << t.labelToken_;
         break;
@@ -157,6 +162,10 @@ ostream& Foam::operator<<(ostream& os, const InfoProxy<token>& ip)
             os  << " the string " << t.stringToken();
         break;
 
+        case token::VARIABLE:
+            os  << " the variable " << t.stringToken();
+        break;
+
         case token::VERBATIMSTRING:
             os  << " the verbatim string " << t.stringToken();
         break;
@@ -231,6 +240,10 @@ Ostream& operator<<(Ostream& os, const InfoProxy<token>& ip)
             os  << " the string " << t.stringToken();
         break;
 
+        case token::VARIABLE:
+            os  << " the variable " << t.stringToken();
+        break;
+
         case token::VERBATIMSTRING:
             os  << " the verbatim string " << t.stringToken();
         break;
diff --git a/src/OpenFOAM/db/dictionary/primitiveEntry/primitiveEntryIO.C b/src/OpenFOAM/db/dictionary/primitiveEntry/primitiveEntryIO.C
index d5626972cc37fc25f92bb87655613bd1ecf6ccac..04cb8cf1652a357bdfed346982650de532c0d493 100644
--- a/src/OpenFOAM/db/dictionary/primitiveEntry/primitiveEntryIO.C
+++ b/src/OpenFOAM/db/dictionary/primitiveEntry/primitiveEntryIO.C
@@ -55,7 +55,7 @@ void Foam::primitiveEntry::append
             newElmt(tokenIndex()++) = currToken;
         }
     }
-    else if (currToken.isString())
+    else if (currToken.isVariable())
     {
         const string& w = currToken.stringToken();
 
diff --git a/src/OpenFOAM/primitives/functions/DataEntry/Table/TableBase.H b/src/OpenFOAM/primitives/functions/DataEntry/Table/TableBase.H
index 929e151a0c7aca1b197546435cf9d928c4de1dde..da670e2db56a80ec8eecc6e781963219464e79ce 100644
--- a/src/OpenFOAM/primitives/functions/DataEntry/Table/TableBase.H
+++ b/src/OpenFOAM/primitives/functions/DataEntry/Table/TableBase.H
@@ -125,7 +125,7 @@ public:
         //- Construct from dictionary - note table is not populated
         TableBase(const word& name, const dictionary& dict);
 
-        //- Copy constructor
+        //- Copy constructor. Note: steals interpolator, tableSamples
         TableBase(const TableBase<Type>& tbl);
 
 
diff --git a/tutorials/mesh/snappyHexMesh/flange/system/snappyHexMeshDict b/tutorials/mesh/snappyHexMesh/flange/system/snappyHexMeshDict
index 520d52950fd68de40456313e06c8e895313a99a2..a1c0e1162f43801b6007ec103c38b837ec14209c 100644
--- a/tutorials/mesh/snappyHexMesh/flange/system/snappyHexMeshDict
+++ b/tutorials/mesh/snappyHexMesh/flange/system/snappyHexMeshDict
@@ -186,6 +186,10 @@ snapControls
 
         //- Use castellatedMeshControls::features
         explicitFeatureSnap true;
+
+        //- Detect features between multiple surfaces
+        //  (only for explicitFeatureSnap, default = false)
+        multiRegionFeatureSnap true;
 }