diff --git a/applications/test/syncTools/Test-syncTools.C b/applications/test/syncTools/Test-syncTools.C
index 270a083e24269432b3a45110f523ee405496d970..bdfd2007e863cbc3d75aad1f7a425e775f175e21 100644
--- a/applications/test/syncTools/Test-syncTools.C
+++ b/applications/test/syncTools/Test-syncTools.C
@@ -3,7 +3,7 @@
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
     \\  /    A nd           | www.openfoam.com
-     \\/     M anipulation  |
+     \\/     M anipulation  | Copyright (C) 2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
 -------------------------------------------------------------------------------
@@ -37,6 +37,7 @@ Description
 #include "Time.H"
 #include "Random.H"
 #include "PackedList.H"
+#include "flipOp.H"
 
 using namespace Foam;
 
@@ -408,7 +409,7 @@ void testPointSync(const polyMesh& mesh, Random& rndGen)
     {
         labelList nMasters(mesh.nPoints(), Zero);
 
-        bitSet isMasterPoint(syncTools::getMasterPoints(mesh));
+        const bitSet isMasterPoint(syncTools::getMasterPoints(mesh));
 
         forAll(isMasterPoint, pointi)
         {
@@ -484,7 +485,7 @@ void testEdgeSync(const polyMesh& mesh, Random& rndGen)
     {
         labelList nMasters(edges.size(), Zero);
 
-        bitSet isMasterEdge(syncTools::getMasterEdges(mesh));
+        const bitSet isMasterEdge(syncTools::getMasterEdges(mesh));
 
         forAll(isMasterEdge, edgeI)
         {
@@ -519,6 +520,252 @@ void testEdgeSync(const polyMesh& mesh, Random& rndGen)
 }
 
 
+typedef Pair<point> PointPair;
+class edgePointCombineOp
+{
+public:
+    void operator()(PointPair& x, const PointPair& y) const
+    {
+        if
+        (
+            (y[0] < x[0])
+         || (y[0] == x[0] && y[1] < x[1])
+        )
+        {
+            x = y;
+        }
+    }
+};
+class edgePointTransformOp
+{
+public:
+    void operator()
+    (
+        const vectorTensorTransform& vt,
+        const bool forward,
+        List<PointPair>& fld
+    ) const
+    {
+        pointField points0(fld.size());
+        pointField points1(fld.size());
+        forAll(fld, i)
+        {
+            points0[i] = fld[i].first();
+            points1[i] = fld[i].second();
+        }
+
+        pointField points0New;
+        pointField points1New;
+        if (forward)
+        {
+            points0New = vt.transformPosition(points0);
+            points1New = vt.transformPosition(points1);
+        }
+        else
+        {
+            points0New = vt.invTransformPosition(points0);
+            points1New = vt.invTransformPosition(points1);
+        }
+
+        forAll(fld, i)
+        {
+            fld[i] = PointPair(points0New[i], points1New[i]);
+        }
+    }
+};
+class edgePointFlipOp
+{
+public:
+    PointPair operator()(const PointPair& val) const
+    {
+        PointPair newVal(val);
+        newVal.flip();
+        return newVal;
+    }
+};
+void testEdgeFlip2(const polyMesh& mesh, Random& rndGen)
+{
+    Pout<< nl << "Testing edge-wise (oriented) data synchronisation." << endl;
+
+    const edgeList& edges = mesh.edges();
+    const pointField& points = mesh.points();
+
+    // Test position.
+
+    List<PointPair> synEdgeEnds(edges.size());
+    {
+        forAll(synEdgeEnds, edgeI)
+        {
+            const edge& e = edges[edgeI];
+            synEdgeEnds[edgeI] = PointPair
+            (
+                points[e[0]],
+                points[e[1]]
+            );
+        }
+    }
+
+    // 1. Ignore flipping
+    {
+        List<PointPair> fld(synEdgeEnds);
+
+        syncTools::syncEdgeList
+        (
+            mesh,
+            fld,
+            edgePointCombineOp(),
+            PointPair(point::max, point::max),
+            edgePointTransformOp(),
+            noOp()
+        );
+
+        forAll(fld, edgeI)
+        {
+            const edge& e = edges[edgeI];
+            const PointPair edgeEnd
+            (
+                points[e[0]],
+                points[e[1]]
+            );
+
+            const PointPair& sync = fld[edgeI];
+
+            if
+            (
+                (mag(edgeEnd[0] - sync[0]) > SMALL)
+             || (mag(edgeEnd[1] - sync[1]) > SMALL)
+            )
+            {
+                WarningInFunction
+                    << "Edge " << edgeI
+                    << " original endpoints " << edgeEnd
+                    << " synced endpoints " << sync
+                    << endl;
+            }
+        }
+    }
+
+    // 2. Use flipping operator. Should produce no warnings
+    {
+        syncTools::syncEdgeList
+        (
+            mesh,
+            synEdgeEnds,
+            edgePointCombineOp(),
+            PointPair(point::max, point::max),
+            edgePointTransformOp(),
+            edgePointFlipOp()
+        );
+
+        forAll(synEdgeEnds, edgeI)
+        {
+            const edge& e = edges[edgeI];
+            const PointPair edgeEnd
+            (
+                points[e[0]],
+                points[e[1]]
+            );
+
+            const PointPair& sync = synEdgeEnds[edgeI];
+
+            if
+            (
+                (mag(edgeEnd[0] - sync[0]) > SMALL)
+             || (mag(edgeEnd[1] - sync[1]) > SMALL)
+            )
+            {
+                FatalErrorInFunction
+                    << "Edge " << edgeI
+                    << " original endpoints " << edgeEnd
+                    << " synced endpoints " << sync
+                    << exit(FatalError);
+            }
+        }
+    }
+}
+
+
+void testEdgeFlip(const polyMesh& mesh, Random& rndGen)
+{
+    Info<< nl << "Testing edge-wise (oriented) data synchronisation."
+        << endl;
+
+    const edgeList& edges = mesh.edges();
+    const pointField& points = mesh.points();
+
+    // Test vector.
+
+    vectorField synEdgeVecs(edges.size());
+    {
+        forAll(synEdgeVecs, edgeI)
+        {
+            synEdgeVecs[edgeI] = edges[edgeI].unitVec(points);
+        }
+    }
+
+    // Without flipping (should produce warnings)
+    {
+        vectorField fld(synEdgeVecs);
+        // Ignore flipping
+        syncTools::syncEdgeList
+        (
+            mesh,
+            fld,
+            minEqOp<vector>(),
+            point::max
+        );
+
+        forAll(fld, edgeI)
+        {
+            const edge& e = edges[edgeI];
+            const vector eVec(e.unitVec(points));
+
+            if ((eVec & fld[edgeI]) < (1-SMALL))
+            {
+                WarningInFunction
+                    << "Edge " << edgeI
+                    << " at " << e.line(points)
+                    << " original vector " << eVec
+                    << " synced vector " << fld[edgeI]
+                    << " diff:" << (eVec & fld[edgeI])
+                    << endl;
+            }
+        }
+    }
+
+
+    // With consistent flipping. Should never produce difference
+    {
+        syncTools::syncEdgeList
+        (
+            mesh,
+            synEdgeVecs,
+            minMagSqrEqOp<vector>(),
+            point::max,
+            mapDistribute::transform(),
+            flipOp()
+        );
+
+        forAll(synEdgeVecs, edgeI)
+        {
+            const edge& e = edges[edgeI];
+            const vector eVec(e.unitVec(points));
+
+            if ((eVec & synEdgeVecs[edgeI]) < (1-SMALL))
+            {
+                FatalErrorInFunction
+                    << "Edge " << edgeI
+                    << " at " << e.line(points)
+                    << " original vector " << eVec
+                    << " synced vector " << synEdgeVecs[edgeI]
+                    << " diff:" << (eVec & synEdgeVecs[edgeI])
+                    << exit(FatalError);
+            }
+        }
+    }
+}
+
+
 void testFaceSync(const polyMesh& mesh, Random& rndGen)
 {
     Info<< nl << "Testing face-wise data synchronisation." << endl;
@@ -553,7 +800,7 @@ void testFaceSync(const polyMesh& mesh, Random& rndGen)
     {
         labelList nMasters(mesh.nFaces(), Zero);
 
-        bitSet isMasterFace(syncTools::getMasterFaces(mesh));
+        const bitSet isMasterFace(syncTools::getMasterFaces(mesh));
 
         forAll(isMasterFace, facei)
         {
@@ -604,6 +851,12 @@ int main(int argc, char *argv[])
     // Edge sync
     testEdgeSync(mesh, rndGen);
 
+    // Edge sync and flip
+    testEdgeFlip(mesh, rndGen);
+
+    // Edge sync and flip of more complex structure
+    testEdgeFlip2(mesh, rndGen);
+
     // Point sync
     testPointSync(mesh, rndGen);
 
diff --git a/applications/test/syncTools/block_2x2x1/Allrun b/applications/test/syncTools/block_2x2x1/Allrun
new file mode 100755
index 0000000000000000000000000000000000000000..c0d364b068c297597d0b8498e8388f98a0e8b612
--- /dev/null
+++ b/applications/test/syncTools/block_2x2x1/Allrun
@@ -0,0 +1,17 @@
+#!/bin/sh
+cd "${0%/*}" || exit                                # Run from this directory
+. ${WM_PROJECT_DIR:?}/bin/tools/RunFunctions        # Tutorial run functions
+#------------------------------------------------------------------------------
+
+runApplication blockMesh
+runApplication decomposePar
+
+# Swap point 2 and 6 in processor1
+(   cd processor1/constant/polyMesh && \
+    sed -i -e 's/(1 1 0)/point1/;s/(1 1 2)/(1 1 0)/;s/point1/(1 1 2)/' points && \
+    sed -i -e 's/^4\([^2]*\)2/4\1TWO/;s/^4\([^6]*\)6/4\12/;s/TWO/6/' faces \
+)
+
+runParallel Test-syncTools
+
+#------------------------------------------------------------------------------
diff --git a/applications/test/syncTools/block_2x2x1/README.txt b/applications/test/syncTools/block_2x2x1/README.txt
new file mode 100644
index 0000000000000000000000000000000000000000..16fa0d9e3dd938ec0cbae2813621f2f79a43c661
--- /dev/null
+++ b/applications/test/syncTools/block_2x2x1/README.txt
@@ -0,0 +1,7 @@
+- blockMesh
+- decomposePar
+- in processor1:
+    - swap points 2 (1 1 0) and 6 (1 1 2) in polyMesh/points
+    - swap indices 2 and 6 in polyMesh/faces
+
+so now we have the same mesh but different edge orientation on processor1
diff --git a/applications/test/syncTools/block_2x2x1/constant/transportProperties b/applications/test/syncTools/block_2x2x1/constant/transportProperties
new file mode 100644
index 0000000000000000000000000000000000000000..fc05a376222c9ce0fd94d4e4134855e949ee5d28
--- /dev/null
+++ b/applications/test/syncTools/block_2x2x1/constant/transportProperties
@@ -0,0 +1,21 @@
+/*--------------------------------*- C++ -*----------------------------------*\
+| =========                 |                                                 |
+| \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox           |
+|  \\    /   O peration     | Version:  v1812                                 |
+|   \\  /    A nd           | Web:      www.OpenFOAM.com                      |
+|    \\/     M anipulation  |                                                 |
+\*---------------------------------------------------------------------------*/
+FoamFile
+{
+    version     2.0;
+    format      ascii;
+    class       dictionary;
+    location    "constant";
+    object      transportProperties;
+}
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+nu              0.01;
+
+
+// ************************************************************************* //
diff --git a/applications/test/syncTools/block_2x2x1/system/blockMeshDict b/applications/test/syncTools/block_2x2x1/system/blockMeshDict
new file mode 100644
index 0000000000000000000000000000000000000000..6666fc0b092a58fb9cf68d97705e9b7fda19a5f1
--- /dev/null
+++ b/applications/test/syncTools/block_2x2x1/system/blockMeshDict
@@ -0,0 +1,103 @@
+/*--------------------------------*- C++ -*----------------------------------*\
+| =========                 |                                                 |
+| \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox           |
+|  \\    /   O peration     | Version:  v1812                                 |
+|   \\  /    A nd           | Web:      www.OpenFOAM.com                      |
+|    \\/     M anipulation  |                                                 |
+\*---------------------------------------------------------------------------*/
+FoamFile
+{
+    version     2.0;
+    format      ascii;
+    class       dictionary;
+    object      blockMeshDict;
+}
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+scale   1;
+
+vertices
+(
+    //- Skewed hexes
+    //(0 0 0)
+    //(0.1 0 0)
+    //(0.2 1 0)
+    //(0.03 1 0)
+    //(0 0 0.1)
+    //(0.1 0 0.1)
+    //(0.2 1 0.1)
+    //(0.03 1 0.1)
+
+    ////- Single block
+    //(0 0 0)
+    //(4 0 0)
+    //(4.4 4 0)
+    //(0.2 4 0)
+    //(0 0 0.4)
+    //(4 0 0.4)
+    //(4.6 4 0.4)
+    //(0.7 4 0.4)
+
+    //- Single block
+    (0  0   0)
+    (2  0   0)
+    (2  2   0)
+    (0  2   0)
+    (0  0   2)
+    (2  0   2)
+    (2  2   2)
+    (0  2   2)
+);
+
+blocks
+(
+    hex (0 1 2 3 4 5 6 7) (2 2 1) simpleGrading (1 1 1)
+);
+
+edges
+(
+);
+
+boundary
+(
+    topWall
+    {
+        type wall;
+        faces
+        (
+            (3 7 6 2)
+        );
+    }
+    bottomWall
+    {
+        type wall;
+        faces
+        (
+            (1 5 4 0)
+        );
+    }
+    fixedWalls
+    {
+        type wall;
+        faces
+        (
+            (0 4 7 3)
+            (2 6 5 1)
+        );
+    }
+    frontAndBack
+    {
+        type patch;
+        faces
+        (
+            (0 3 2 1)
+            (4 5 6 7)
+        );
+    }
+);
+
+mergePatchPairs
+(
+);
+
+// ************************************************************************* //
diff --git a/applications/test/syncTools/block_2x2x1/system/controlDict b/applications/test/syncTools/block_2x2x1/system/controlDict
new file mode 100644
index 0000000000000000000000000000000000000000..f7fc368d3ec3dce246bee04acfd898df08f735c1
--- /dev/null
+++ b/applications/test/syncTools/block_2x2x1/system/controlDict
@@ -0,0 +1,48 @@
+/*--------------------------------*- C++ -*----------------------------------*\
+| =========                 |                                                 |
+| \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox           |
+|  \\    /   O peration     | Version:  v1812                                 |
+|   \\  /    A nd           | Web:      www.OpenFOAM.com                      |
+|    \\/     M anipulation  |                                                 |
+\*---------------------------------------------------------------------------*/
+FoamFile
+{
+    version     2.0;
+    format      ascii;
+    class       dictionary;
+    location    "system";
+    object      controlDict;
+}
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+application     icoFoam;
+
+startFrom       startTime;
+
+startTime       0;
+
+stopAt          endTime;
+
+endTime         0.5;
+
+deltaT          0.005;
+
+writeControl    timeStep;
+
+writeInterval   20;
+
+purgeWrite      0;
+
+writeFormat     ascii;
+
+writePrecision  16;
+
+writeCompression off;
+
+timeFormat      general;
+
+timePrecision   6;
+
+runTimeModifiable true;
+
+// ************************************************************************* //
diff --git a/applications/test/syncTools/block_2x2x1/system/decomposeParDict b/applications/test/syncTools/block_2x2x1/system/decomposeParDict
new file mode 100644
index 0000000000000000000000000000000000000000..ac6ac49fa0011322d26adb0165a6d3f98acac29c
--- /dev/null
+++ b/applications/test/syncTools/block_2x2x1/system/decomposeParDict
@@ -0,0 +1,26 @@
+/*--------------------------------*- C++ -*----------------------------------*\
+| =========                 |                                                 |
+| \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox           |
+|  \\    /   O peration     | Version:  v1806                                 |
+|   \\  /    A nd           | Web:      www.OpenFOAM.com                      |
+|    \\/     M anipulation  |                                                 |
+\*---------------------------------------------------------------------------*/
+FoamFile
+{
+    version     2.0;
+    format      ascii;
+    class       dictionary;
+    note        "mesh decomposition control dictionary";
+    object      decomposeParDict;
+}
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+//- The total number of domains (mandatory)
+numberOfSubdomains  4;
+
+//- The decomposition method (mandatory)
+method              hierarchical;
+
+n                   (2 2 1);
+
+// ************************************************************************* //
diff --git a/applications/test/syncTools/block_2x2x1/system/fvSchemes b/applications/test/syncTools/block_2x2x1/system/fvSchemes
new file mode 100644
index 0000000000000000000000000000000000000000..9e65bd597658e93fa69212bb055504ff3f4a53ad
--- /dev/null
+++ b/applications/test/syncTools/block_2x2x1/system/fvSchemes
@@ -0,0 +1,51 @@
+/*--------------------------------*- C++ -*----------------------------------*\
+| =========                 |                                                 |
+| \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox           |
+|  \\    /   O peration     | Version:  v1812                                 |
+|   \\  /    A nd           | Web:      www.OpenFOAM.com                      |
+|    \\/     M anipulation  |                                                 |
+\*---------------------------------------------------------------------------*/
+FoamFile
+{
+    version     2.0;
+    format      ascii;
+    class       dictionary;
+    location    "system";
+    object      fvSchemes;
+}
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+ddtSchemes
+{
+    default         Euler;
+}
+
+gradSchemes
+{
+    default         Gauss linear;
+    grad(p)         Gauss linear;
+}
+
+divSchemes
+{
+    default         none;
+    div(phi,U)      Gauss linear;
+}
+
+laplacianSchemes
+{
+    default         Gauss linear orthogonal;
+}
+
+interpolationSchemes
+{
+    default         linear;
+}
+
+snGradSchemes
+{
+    default         orthogonal;
+}
+
+
+// ************************************************************************* //
diff --git a/applications/test/syncTools/block_2x2x1/system/fvSolution b/applications/test/syncTools/block_2x2x1/system/fvSolution
new file mode 100644
index 0000000000000000000000000000000000000000..fa297c1118f4b55e8bbdcaffbd0116aea0463b39
--- /dev/null
+++ b/applications/test/syncTools/block_2x2x1/system/fvSolution
@@ -0,0 +1,52 @@
+/*--------------------------------*- C++ -*----------------------------------*\
+| =========                 |                                                 |
+| \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox           |
+|  \\    /   O peration     | Version:  v1812                                 |
+|   \\  /    A nd           | Web:      www.OpenFOAM.com                      |
+|    \\/     M anipulation  |                                                 |
+\*---------------------------------------------------------------------------*/
+FoamFile
+{
+    version     2.0;
+    format      ascii;
+    class       dictionary;
+    location    "system";
+    object      fvSolution;
+}
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+solvers
+{
+    p
+    {
+        solver          PCG;
+        preconditioner  DIC;
+        tolerance       1e-06;
+        relTol          0.05;
+    }
+
+    pFinal
+    {
+        $p;
+        relTol          0;
+    }
+
+    U
+    {
+        solver          smoothSolver;
+        smoother        symGaussSeidel;
+        tolerance       1e-05;
+        relTol          0;
+    }
+}
+
+PISO
+{
+    nCorrectors     2;
+    nNonOrthogonalCorrectors 0;
+    pRefCell        0;
+    pRefValue       0;
+}
+
+
+// ************************************************************************* //