From 88fac3ed9f90ffb7ddac3da6c8af07bb5cf2024e Mon Sep 17 00:00:00 2001
From: mattijs <mattijs>
Date: Mon, 11 Dec 2017 17:23:33 +0000
Subject: [PATCH] ENH: snappyHexMesh: initial version of directional refinement

---
 .../generation/snappyHexMesh/snappyHexMesh.C  |  25 +-
 .../snappyHexMesh/snappyHexMeshDict           |  23 +
 .../meshRefinement/meshRefinement.C           |   2 +
 .../meshRefinement/meshRefinement.H           |  22 +-
 .../meshRefinement/meshRefinementRefine.C     |  64 +++
 .../shellSurfaces/shellSurfaces.C             |  96 +++++
 .../shellSurfaces/shellSurfaces.H             |  25 ++
 .../snappyHexMeshDriver/snappyRefineDriver.C  | 404 ++++++++++++++++++
 .../snappyHexMeshDriver/snappyRefineDriver.H  |  33 +-
 9 files changed, 690 insertions(+), 4 deletions(-)

diff --git a/applications/utilities/mesh/generation/snappyHexMesh/snappyHexMesh.C b/applications/utilities/mesh/generation/snappyHexMesh/snappyHexMesh.C
index c5ebc807cc5..3313726b9f0 100644
--- a/applications/utilities/mesh/generation/snappyHexMesh/snappyHexMesh.C
+++ b/applications/utilities/mesh/generation/snappyHexMesh/snappyHexMesh.C
@@ -1082,7 +1082,29 @@ int main(int argc, char *argv[])
 
     if (!limitDict.empty())
     {
-        Info<< "Read refinement shells in = "
+        Info<< "Read limit shells in = "
+            << mesh.time().cpuTimeIncrement() << " s" << nl << endl;
+    }
+
+
+    // Optionally read directional refinement shells
+    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+    const dictionary dirRefDict
+    (
+        refineDict.subOrEmptyDict("directionalRefinementRegions")
+    );
+
+    if (!dirRefDict.empty())
+    {
+        Info<< "Reading directional refinement shells." << endl;
+    }
+
+    shellSurfaces dirShells(allGeometry, dirRefDict);
+
+    if (!dirRefDict.empty())
+    {
+        Info<< "Read directional refinement shells in = "
             << mesh.time().cpuTimeIncrement() << " s" << nl << endl;
     }
 
@@ -1119,6 +1141,7 @@ int main(int argc, char *argv[])
         surfaces,           // for surface intersection refinement
         features,           // for feature edges/point based refinement
         shells,             // for volume (inside/outside) refinement
+        dirShells,          // vol volume directional refinement
         limitShells         // limit of volume refinement
     );
     Info<< "Calculated surface intersections in = "
diff --git a/applications/utilities/mesh/generation/snappyHexMesh/snappyHexMeshDict b/applications/utilities/mesh/generation/snappyHexMesh/snappyHexMeshDict
index d2c3a1a690a..640aaa97901 100644
--- a/applications/utilities/mesh/generation/snappyHexMesh/snappyHexMeshDict
+++ b/applications/utilities/mesh/generation/snappyHexMesh/snappyHexMeshDict
@@ -49,6 +49,14 @@ geometry
         max (3.5 2 0.5);
     }
 
+    // Shell for directional refinement
+    refinementBox
+    {
+        type searchableBox;
+        min (1.5 1 -0.5);
+        max (3.5 2 0.5);
+    }
+
     sphere.stl
     {
         type triSurfaceMesh;
@@ -278,6 +286,21 @@ castellatedMeshControls
     }
 
 
+    directionalRefinementRegions
+    {
+        refinementBox       // Closed surface
+        {
+            mode inside;
+            levelIncrement  // Specification of additional refinement
+            (
+                (0 (1 0 0)) // For level 0 cells: add one level refinement in x
+                (1 (1 0 0)) // For level 1 cells: add one level refinement in x
+            );
+        }
+    }
+
+
+
     // Optionally limit refinement in geometric region. This limits all
     // refinement (from features, refinementSurfaces, refinementRegions)
     // in a given geometric region. The syntax is exactly the same as for the
diff --git a/src/mesh/snappyHexMesh/meshRefinement/meshRefinement.C b/src/mesh/snappyHexMesh/meshRefinement/meshRefinement.C
index 98a26202310..8cfe94a498d 100644
--- a/src/mesh/snappyHexMesh/meshRefinement/meshRefinement.C
+++ b/src/mesh/snappyHexMesh/meshRefinement/meshRefinement.C
@@ -1222,6 +1222,7 @@ Foam::meshRefinement::meshRefinement
     const refinementSurfaces& surfaces,
     const refinementFeatures& features,
     const shellSurfaces& shells,
+    const shellSurfaces& dirShells,
     const shellSurfaces& limitShells
 )
 :
@@ -1232,6 +1233,7 @@ Foam::meshRefinement::meshRefinement
     surfaces_(surfaces),
     features_(features),
     shells_(shells),
+    dirShells_(dirShells),
     limitShells_(limitShells),
     meshCutter_
     (
diff --git a/src/mesh/snappyHexMesh/meshRefinement/meshRefinement.H b/src/mesh/snappyHexMesh/meshRefinement/meshRefinement.H
index fd36951239a..e7c88838888 100644
--- a/src/mesh/snappyHexMesh/meshRefinement/meshRefinement.H
+++ b/src/mesh/snappyHexMesh/meshRefinement/meshRefinement.H
@@ -161,6 +161,9 @@ private:
         //- All shell-refinement interaction
         const shellSurfaces& shells_;
 
+        //- All directional shell-refinement interaction
+        const shellSurfaces& dirShells_;
+
         //- All limit-refinement interaction
         const shellSurfaces& limitShells_;
 
@@ -807,8 +810,9 @@ public:
             const bool overwrite,
             const refinementSurfaces&,
             const refinementFeatures&,
-            const shellSurfaces&,
-            const shellSurfaces&
+            const shellSurfaces&,   // omnidirectional refinement
+            const shellSurfaces&,   // directional refinement
+            const shellSurfaces&    // limit refinement
         );
 
 
@@ -861,6 +865,12 @@ public:
                 return shells_;
             }
 
+            //- Reference to directional shell-refinement shells
+            const shellSurfaces& dirShells() const
+            {
+                return dirShells_;
+            }
+
             //- Reference to meshcutting engine
             const hexRef8& meshCutter() const
             {
@@ -1037,6 +1047,14 @@ public:
                 const scalar maxLoadUnbalance
             );
 
+            //- Directionally refine in direction cmpt
+            autoPtr<mapPolyMesh> directionalRefine
+            (
+                const string& msg,
+                const direction cmpt,
+                const labelList& cellsToRefine
+            );
+
 
         // Baffle handling
 
diff --git a/src/mesh/snappyHexMesh/meshRefinement/meshRefinementRefine.C b/src/mesh/snappyHexMesh/meshRefinement/meshRefinementRefine.C
index 22863c4b803..53728b952a6 100644
--- a/src/mesh/snappyHexMesh/meshRefinement/meshRefinementRefine.C
+++ b/src/mesh/snappyHexMesh/meshRefinement/meshRefinementRefine.C
@@ -41,6 +41,11 @@ License
 #include "cellSet.H"
 #include "treeDataCell.H"
 
+#include "cellCuts.H"
+#include "refineCell.H"
+#include "hexCellLooper.H"
+#include "meshCutter.H"
+
 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
 
 namespace Foam
@@ -2624,4 +2629,63 @@ Foam::meshRefinement::balanceAndRefine
 }
 
 
+Foam::autoPtr<Foam::mapPolyMesh> Foam::meshRefinement::directionalRefine
+(
+    const string& msg,
+    const direction cmpt,
+    const labelList& cellsToRefine
+)
+{
+    // Set splitting direction
+    vector refDir(Zero);
+    refDir[cmpt] = 1;
+    List<refineCell> refCells(cellsToRefine.size());
+    forAll(cellsToRefine, i)
+    {
+        refCells[i] = refineCell(cellsToRefine[i], refDir);
+    }
+
+    // How to walk circumference of cells
+    hexCellLooper cellWalker(mesh_);
+
+    // Analyse cuts
+    cellCuts cuts(mesh_, cellWalker, refCells);
+
+    // Cell cutter
+    Foam::meshCutter meshRefiner(mesh_);
+
+    polyTopoChange meshMod(mesh_);
+
+    // Insert mesh refinement into polyTopoChange.
+    meshRefiner.setRefinement(cuts, meshMod);
+
+    autoPtr<mapPolyMesh> morphMap = meshMod.changeMesh(mesh_, false);
+
+    // Update fields
+    mesh_.updateMesh(morphMap);
+
+    // Move mesh (since morphing does not do this)
+    if (morphMap().hasMotionPoints())
+    {
+        mesh_.movePoints(morphMap().preMotionPoints());
+    }
+    else
+    {
+        // Delete mesh volumes.
+        mesh_.clearOut();
+    }
+
+    // Reset the instance for if in overwrite mode
+    mesh_.setInstance(timeName());
+
+    // Update stored refinement pattern
+    meshRefiner.updateMesh(morphMap);
+
+    // Update intersection info
+    updateMesh(morphMap, getChangedFaces(morphMap, cellsToRefine));
+
+    return morphMap;
+}
+
+
 // ************************************************************************* //
diff --git a/src/mesh/snappyHexMesh/shellSurfaces/shellSurfaces.C b/src/mesh/snappyHexMesh/shellSurfaces/shellSurfaces.C
index 955afee4e7f..b984ef4f10d 100644
--- a/src/mesh/snappyHexMesh/shellSurfaces/shellSurfaces.C
+++ b/src/mesh/snappyHexMesh/shellSurfaces/shellSurfaces.C
@@ -584,6 +584,7 @@ Foam::shellSurfaces::shellSurfaces
     modes_.setSize(shellI);
     distances_.setSize(shellI);
     levels_.setSize(shellI);
+    dirLevels_.setSize(shellI);
 
     extendedGapLevel_.setSize(shellI);
     extendedGapMode_.setSize(shellI);
@@ -615,6 +616,31 @@ Foam::shellSurfaces::shellSurfaces
             setAndCheckLevels(shellI, dict.lookup("levels"));
 
 
+            // Directional refinement
+            // ~~~~~~~~~~~~~~~~~~~~~~
+
+            if (dict.readIfPresent("levelIncrement", dirLevels_[shellI]))
+            {
+                if (modes_[shellI] == INSIDE)
+                {
+                    Info<< "Additional directional refinement level"
+                        << " for all cells inside " << geomName << endl;
+                }
+                else if (modes_[shellI] == OUTSIDE)
+                {
+                    Info<< "Additional directional refinement level"
+                        << " for all cells outside " << geomName << endl;
+                }
+                else
+                {
+                    FatalIOErrorInFunction(shellsDict)
+                        << "Unsupported mode "
+                        << refineModeNames_[modes_[shellI]]
+                        << exit(FatalIOError);
+                }
+            }
+
+
 
             // Gap specification
             // ~~~~~~~~~~~~~~~~~
@@ -824,4 +850,74 @@ void Foam::shellSurfaces::findLevel
 }
 
 
+void Foam::shellSurfaces::findDirectionalLevel
+(
+    const pointField& pt,
+    const labelList& ptLevel,
+    const labelList& dirLevel,  // directional level
+    const direction dir,
+    labelList& shell
+) const
+{
+    shell.setSize(pt.size());
+    shell = -1;
+
+    List<volumeType> volType;
+
+    // Current back to original
+    DynamicList<label> candidateMap(pt.size());
+
+    forAll(shells_, shelli)
+    {
+        if (modes_[shelli] == INSIDE || modes_[shelli] == OUTSIDE)
+        {
+            const LevelAndDirList& shellLevels = dirLevels_[shelli];
+
+            // Collect the cells that are of the right original level
+            candidateMap.clear();
+            forAll(ptLevel, celli)
+            {
+                label level = ptLevel[celli];
+                forAll(shellLevels, leveli)
+                {
+                    label selectLevel = shellLevels[leveli].first();
+                    label addLevel = shellLevels[leveli].second()[dir];
+
+                    if
+                    (
+                        level == selectLevel
+                     && dirLevel[celli] < level+addLevel
+                    )
+                    {
+                        candidateMap.append(celli);
+                        break;
+                    }
+                }
+            }
+
+            pointField candidatePt(pt, candidateMap);
+            allGeometry_[shells_[shelli]].getVolumeType(candidatePt, volType);
+
+            forAll(candidateMap, i)
+            {
+                if
+                (
+                    (
+                        modes_[shelli] == INSIDE
+                     && volType[i] == volumeType::INSIDE
+                    )
+                 || (
+                        modes_[shelli] == OUTSIDE
+                     && volType[i] == volumeType::OUTSIDE
+                    )
+                )
+                {
+                    shell[candidateMap[i]] = shelli;
+                }
+            }
+        }
+    }
+}
+
+
 // ************************************************************************* //
diff --git a/src/mesh/snappyHexMesh/shellSurfaces/shellSurfaces.H b/src/mesh/snappyHexMesh/shellSurfaces/shellSurfaces.H
index 93ff45b52e6..647a18b2bcb 100644
--- a/src/mesh/snappyHexMesh/shellSurfaces/shellSurfaces.H
+++ b/src/mesh/snappyHexMesh/shellSurfaces/shellSurfaces.H
@@ -39,12 +39,17 @@ SourceFiles
 #include "searchableSurface.H"
 #include "Enum.H"
 #include "Tuple2.H"
+#include "labelVector.H"
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
 namespace Foam
 {
 
+typedef Tuple2<label,labelVector> LevelAndDir;
+typedef List<LevelAndDir> LevelAndDirList;
+typedef List<LevelAndDirList> LevelAndDirListList;
+
 class searchableSurfaces;
 
 /*---------------------------------------------------------------------------*\
@@ -85,6 +90,9 @@ private:
         //- Per shell per distance the refinement level
         labelListList levels_;
 
+        //- Per shell, per refinement level additional directional refinement
+        LevelAndDirListList dirLevels_;
+
 
         // Gap level refinement
 
@@ -171,6 +179,13 @@ public:
                 return shells_;
             }
 
+            //- Raw access to directional refinement
+            //  Per shell a list of (level + additional level)
+            const LevelAndDirListList& dirLevels() const
+            {
+                return dirLevels_;
+            }
+
 
         // Query
 
@@ -215,6 +230,16 @@ public:
                 const labelList& ptLevel,
                 labelList& shell
             ) const;
+
+            //- Find any shell (or -1) with higher wanted directional level
+            void findDirectionalLevel
+            (
+                const pointField& pt,
+                const labelList& ptLevel,   // omnidirectional level
+                const labelList& dirLevel,  // directional level
+                const direction dir,
+                labelList& shell
+            ) const;
 };
 
 
diff --git a/src/mesh/snappyHexMesh/snappyHexMeshDriver/snappyRefineDriver.C b/src/mesh/snappyHexMesh/snappyHexMeshDriver/snappyRefineDriver.C
index 51da101189b..d6846f68fb2 100644
--- a/src/mesh/snappyHexMesh/snappyHexMeshDriver/snappyRefineDriver.C
+++ b/src/mesh/snappyHexMesh/snappyHexMeshDriver/snappyRefineDriver.C
@@ -40,6 +40,7 @@ License
 #include "IOmanip.H"
 #include "labelVector.H"
 #include "profiling.H"
+#include "searchableSurfaces.H"
 
 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
 
@@ -1571,6 +1572,400 @@ Foam::label Foam::snappyRefineDriver::shellRefine
 
     return iter;
 }
+//XXXXXXXXXX
+Foam::List<Foam::direction> Foam::snappyRefineDriver::faceDirection() const
+{
+    const fvMesh& mesh = meshRefiner_.mesh();
+
+    List<direction> faceDir(mesh.nFaces());
+
+    vectorField nf(mesh.faceAreas());
+    nf /= mag(nf);
+
+    forAll(nf, facei)
+    {
+        const vector& n = nf[facei];
+        if (mag(n[0]) > 0.5)
+        {
+            faceDir[facei] = 0;
+        }
+        else if (mag(n[1]) > 0.5)
+        {
+            faceDir[facei] = 1;
+        }
+        else if (mag(n[2]) > 0.5)
+        {
+            faceDir[facei] = 2;
+        }
+    }
+
+    return faceDir;
+}
+Foam::label Foam::snappyRefineDriver::faceConsistentRefinement
+(
+    const PackedBoolList& isDirFace,
+    const List<labelVector>& dirCellLevel,
+    const direction dir,
+    const bool maxSet,
+    PackedBoolList& refineCell
+) const
+{
+    const fvMesh& mesh = meshRefiner_.mesh();
+
+    label nChanged = 0;
+
+    // Internal faces.
+    for (label facei = 0; facei < mesh.nInternalFaces(); facei++)
+    {
+        if (isDirFace[facei])
+        {
+            label own = mesh.faceOwner()[facei];
+            label ownLevel = dirCellLevel[own][dir] + refineCell.get(own);
+
+            label nei = mesh.faceNeighbour()[facei];
+            label neiLevel = dirCellLevel[nei][dir] + refineCell.get(nei);
+
+            if (ownLevel > (neiLevel+1))
+            {
+                if (maxSet)
+                {
+                    refineCell.set(nei);
+                }
+                else
+                {
+                    refineCell.unset(own);
+                }
+                nChanged++;
+            }
+            else if (neiLevel > (ownLevel+1))
+            {
+                if (maxSet)
+                {
+                    refineCell.set(own);
+                }
+                else
+                {
+                    refineCell.unset(nei);
+                }
+                nChanged++;
+            }
+        }
+    }
+
+
+    // Coupled faces. Swap owner level to get neighbouring cell level.
+    // (only boundary faces of neiLevel used)
+    labelList neiLevel(mesh.nFaces()-mesh.nInternalFaces());
+
+    forAll(neiLevel, i)
+    {
+        label own = mesh.faceOwner()[i+mesh.nInternalFaces()];
+
+        neiLevel[i] = dirCellLevel[own][dir] + refineCell.get(own);
+    }
+
+    // Swap to neighbour
+    syncTools::swapBoundaryFaceList(mesh, neiLevel);
+
+    // Now we have neighbour value see which cells need refinement
+    forAll(neiLevel, i)
+    {
+        label facei = i+mesh.nInternalFaces();
+        if (isDirFace[facei])
+        {
+            label own = mesh.faceOwner()[facei];
+            label ownLevel = dirCellLevel[own][dir] + refineCell.get(own);
+
+            if (ownLevel > (neiLevel[i]+1))
+            {
+                if (!maxSet)
+                {
+                    refineCell.unset(own);
+                    nChanged++;
+                }
+            }
+            else if (neiLevel[i] > (ownLevel+1))
+            {
+                if (maxSet)
+                {
+                    refineCell.set(own);
+                    nChanged++;
+                }
+            }
+        }
+    }
+
+    return nChanged;
+}
+Foam::labelList Foam::snappyRefineDriver::consistentDirectionalRefinement
+(
+    const direction dir,
+    const List<labelVector>& dirCellLevel,
+    const labelUList& candidateCells,
+    const bool maxSet
+) const
+{
+    const fvMesh& mesh = meshRefiner_.mesh();
+
+    const List<direction> faceDir(faceDirection());
+
+    PackedBoolList isDirFace(mesh.nFaces());
+    forAll(faceDir, facei)
+    {
+        if (faceDir[facei] == dir)
+        {
+            isDirFace[facei] = true;
+        }
+    }
+
+    // Loop, modifying cellsToRefine, until no more changes to due to 2:1
+    // conflicts.
+    // maxSet = false : unselect cells to refine
+    // maxSet = true  : select cells to refine
+
+    // Go to straight boolList.
+    PackedBoolList isRefineCell(mesh.nCells());
+    isRefineCell.set(candidateCells);
+
+    while (true)
+    {
+        label nChanged = faceConsistentRefinement
+        (
+            isDirFace,
+            dirCellLevel,
+            dir,
+            maxSet,
+            isRefineCell
+        );
+
+        reduce(nChanged, sumOp<label>());
+
+        if (debug)
+        {
+            Pout<< "snappyRefineDriver::consistentDirectionalRefinement"
+                << " : Changed " << nChanged
+                << " refinement levels due to 2:1 conflicts."
+                << endl;
+        }
+
+        if (nChanged == 0)
+        {
+            break;
+        }
+    }
+
+    return isRefineCell.used();
+}
+
+
+Foam::label Foam::snappyRefineDriver::directionalShellRefine
+(
+    const refinementParameters& refineParams,
+    const label maxIter
+)
+{
+    addProfiling(shell, "snappyHexMesh::refine::directionalShell");
+    const fvMesh& mesh = meshRefiner_.mesh();
+    const shellSurfaces& shells = meshRefiner_.dirShells();
+
+    labelList& cellLevel =
+        const_cast<labelIOList&>(meshRefiner_.meshCutter().cellLevel());
+
+
+    // Determine the minimum and maximum cell levels that are candidates for
+    // directional refinement
+    label overallMinLevel = labelMax;
+    label overallMaxLevel = labelMin;
+    {
+        const LevelAndDirListList& dirLevels = shells.dirLevels();
+        forAll(dirLevels, shelli)
+        {
+            const LevelAndDirList& dirLevel = dirLevels[shelli];
+            forAll(dirLevel, i)
+            {
+                overallMinLevel = min(dirLevel[i].first(), overallMinLevel);
+                overallMaxLevel = max(dirLevel[i].first(), overallMaxLevel);
+            }
+        }
+    }
+
+    if (overallMinLevel > overallMaxLevel)
+    {
+        return 0;
+    }
+
+    // Maintain directional refinement levels
+    List<labelVector> dirCellLevel(cellLevel.size());
+    forAll(cellLevel, celli)
+    {
+        label l = cellLevel[celli];
+        dirCellLevel[celli] = labelVector(l, l, l);
+    }
+
+    label iter;
+    for (iter = 0; iter < maxIter; iter++)
+    {
+        Info<< nl
+            << "Directional shell refinement iteration " << iter << nl
+            << "--------------------------------------" << nl
+            << endl;
+
+        label nAllRefine = 0;
+
+        for (direction dir = 0; dir < vector::nComponents; dir++)
+        {
+            // Select the cells that need to be refined in certain direction:
+            // 
+            // - cell inside/outside shell
+            // - original cellLevel (using mapping) mentioned in levelIncrement
+            // - dirCellLevel not yet up to cellLevel+levelIncrement
+
+            labelList candidateCells;
+            {
+                // Extract component of directional level
+                labelList currentLevel(dirCellLevel.size());
+                forAll(dirCellLevel, celli)
+                {
+                    currentLevel[celli] = dirCellLevel[celli][dir];
+                }
+
+                // Find cells inside the shells
+                labelList insideShell;
+                shells.findDirectionalLevel
+                (
+                    mesh.cellCentres(),
+                    cellLevel,
+                    currentLevel,  // directional level
+                    dir,
+                    insideShell
+                );
+
+                // Extract
+                label n = 0;
+                forAll(insideShell, celli)
+                {
+                    if (insideShell[celli] >= 0)
+                    {
+                        n++;
+                    }
+                }
+                candidateCells.setSize(n);
+                n = 0;
+                forAll(insideShell, celli)
+                {
+                    if (insideShell[celli] >= 0)
+                    {
+                        candidateCells[n++] = celli;
+                    }
+                }
+            }
+
+            // Extend to keep 2:1 ratio
+            labelList cellsToRefine
+            (
+                consistentDirectionalRefinement
+                (
+                    dir,
+                    dirCellLevel,
+                    candidateCells,
+                    true
+                )
+            );
+
+            Info<< "Determined cells to refine in = "
+                << mesh.time().cpuTimeIncrement() << " s" << endl;
+
+            label nCellsToRefine = cellsToRefine.size();
+            reduce(nCellsToRefine, sumOp<label>());
+
+            Info<< "Selected for direction " << vector::componentNames[dir]
+                << " refinement : " << nCellsToRefine
+                << " cells (out of " << mesh.globalData().nTotalCells()
+                << ')' << endl;
+
+            nAllRefine += nCellsToRefine;
+
+            // Stop when no cells to refine or have done minimum necessary
+            // iterations and not enough cells to refine.
+            if (nCellsToRefine == 0)
+            {
+                Info<< "Not refining direction " << dir
+                    << " since too few cells selected."
+                    << nl << endl;
+                break;
+            }
+
+
+            if (debug)
+            {
+                const_cast<Time&>(mesh.time())++;
+            }
+
+            PackedBoolList isRefineCell(mesh.nCells());
+            isRefineCell.set(cellsToRefine);
+
+            autoPtr<mapPolyMesh> map
+            (
+                meshRefiner_.directionalRefine
+                (
+                    "directional refinement iteration " + name(iter),
+                    dir,
+                    cellsToRefine
+                )
+            );
+
+            meshRefinement::updateList
+            (
+                map().cellMap(),
+                labelVector(0, 0, 0),
+                dirCellLevel
+            );
+
+            forAll(map().cellMap(), celli)
+            {
+                if (isRefineCell[map().cellMap()[celli]])
+                {
+                    dirCellLevel[celli][dir]++;
+                }
+            }
+        }
+
+
+        if (nAllRefine == 0)
+        {
+            Info<< "Stopping refining since no cells selected."
+                << nl << endl;
+            break;
+        }
+
+        meshRefiner_.printMeshInfo
+        (
+            debug,
+            "After directional refinement iteration " + name(iter)
+        );
+
+        if (debug&meshRefinement::MESH)
+        {
+            Pout<< "Writing directional refinement iteration " + name(iter)
+                << " mesh to time " << meshRefiner_.timeName() << endl;
+            meshRefiner_.write
+            (
+                meshRefinement::debugType(debug),
+                meshRefinement::writeType
+                (
+                    meshRefinement::writeLevel()
+                  | meshRefinement::WRITEMESH
+                ),
+                mesh.time().path()/meshRefiner_.timeName()
+            );
+        }
+    }
+
+    // Adjust cellLevel from dirLevel?
+    // Is cellLevel the max of dirLevel? Or the min?
+
+    return iter;
+}
 
 
 void Foam::snappyRefineDriver::baffleAndSplitMesh
@@ -2065,6 +2460,15 @@ void Foam::snappyRefineDriver::doRefine
         10      // maxIter
     );
 
+    // Directional refinement
+//XXXXX
+    directionalShellRefine
+    (
+        refineParams,
+        100    // maxIter
+    );
+//XXXXX
+
     // Introduce baffles at surface intersections. Remove sections unreachable
     // from keepPoint.
     baffleAndSplitMesh
diff --git a/src/mesh/snappyHexMesh/snappyHexMeshDriver/snappyRefineDriver.H b/src/mesh/snappyHexMesh/snappyHexMeshDriver/snappyRefineDriver.H
index af7870e25e0..dc0936ae48a 100644
--- a/src/mesh/snappyHexMesh/snappyHexMeshDriver/snappyRefineDriver.H
+++ b/src/mesh/snappyHexMesh/snappyHexMeshDriver/snappyRefineDriver.H
@@ -36,6 +36,8 @@ SourceFiles
 
 #include "wordPairHashTable.H"
 #include "labelList.H"
+#include "PackedBoolList.H"
+#include "labelVector.H"
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
@@ -135,13 +137,42 @@ class snappyRefineDriver
             const label nBufferLayers
         );
 
-        //- Remove all cells inside/outside shell
+        //- Refine all cells inside/outside shell
         label shellRefine
         (
             const refinementParameters& refineParams,
             const label maxIter
         );
 
+        // Directional refinement
+
+            //- Calculate direction of face (using geometry)
+            List<direction> faceDirection() const;
+
+            //- Check if level across (directed) face obeys 2:1
+            label faceConsistentRefinement
+            (
+                const PackedBoolList& isDirFace,
+                const List<labelVector>& dirCellLevel,
+                const direction dir,
+                const bool maxSet,
+                PackedBoolList& refineCell
+            ) const;
+            //- Make sure 2:1 refinement in given direction
+            labelList consistentDirectionalRefinement
+            (
+                const direction dir,
+                const List<labelVector>& dirCellLevel,
+                const labelUList& candidateCells,
+                const bool maxSet
+            ) const;
+            //- Refine (directional) all cells inside/outside shell
+            label directionalShellRefine
+            (
+                const refinementParameters& refineParams,
+                const label maxIter
+            );
+
         //- Add baffles and remove unreachable cells
         void baffleAndSplitMesh
         (
-- 
GitLab