From ebb9a9e1acc66df9eac9d85363810d1a4814e655 Mon Sep 17 00:00:00 2001
From: graham <g.macpherson@opencfd.co.uk>
Date: Fri, 17 Sep 2010 16:59:17 +0100
Subject: [PATCH] ENH: tet decomposed particle tracking.

Squashed merge of particleInteractions up to
commit e7cb5bcf0315c359539ef1e715e1d51991343391
---
 .../combustion/dieselEngineFoam/Make/options  |    2 +
 .../combustion/dieselFoam/Make/options        |    2 +
 .../dsmc/dsmcFoam/Make/options                |    1 -
 .../Make/files                                |    3 +
 .../Make/options                              |   33 +
 .../createFields.H                            |  147 +++
 ...pressibleUncoupledKinematicParcelDyMFoam.C |   92 ++
 .../test/findCell-octree/findCell-octree.C    |   61 +-
 .../snappyHexMesh/snappyHexMeshDict           |   13 +-
 .../manipulation/checkMesh/checkGeometry.C    |   28 +-
 .../decomposePar/decomposePar.C               |   28 +-
 .../decomposePar/lagrangianFieldDecomposer.C  |   36 +
 .../decomposePar/lagrangianFieldDecomposer.H  |    1 +
 ...lagrangianFieldDecomposerDecomposeFields.C |    4 +-
 .../dataConversion/foamToEnsight/Make/options |    2 +
 .../foamToEnsightParts/Make/options           |    2 +
 .../foamToFieldview9/Make/options             |    2 +
 .../dataConversion/foamToGMV/Make/options     |    2 +
 .../graphics/ensightFoamReader/Make/options   |    2 +
 .../lagrangian/particleTracks/Make/options    |    2 +
 .../preProcessing/dsmcInitialise/Make/options |    1 -
 .../mapFields/MapLagrangianFields.H           |  130 +-
 .../preProcessing/mapFields/mapLagrangian.C   |   23 +-
 .../surface/surfaceSubset/surfaceSubset.C     |    1 -
 etc/controlDict                               |    1 +
 src/Allwmake                                  |    3 +-
 src/OpenFOAM/Make/files                       |    2 +
 src/OpenFOAM/meshes/polyMesh/polyMesh.C       |   26 +
 src/OpenFOAM/meshes/polyMesh/polyMesh.H       |    7 +
 src/OpenFOAM/meshes/polyMesh/polyMeshClear.C  |    3 +
 .../meshes/polyMesh/polyMeshFromShapeMesh.C   |    2 +
 .../polyMeshTetDecomposition.C                |  627 ++++++++++
 .../polyMeshTetDecomposition.H                |  151 +++
 .../polyMeshTetDecomposition/tetIndices.C     |  148 +++
 .../polyMeshTetDecomposition/tetIndices.H     |  201 +++
 .../polyMeshTetDecomposition/tetIndicesI.H    |  202 +++
 .../constraint/processor/processorPolyPatch.H |   13 +
 .../polyMesh/syncTools/syncToolsTemplates.C   |    6 +-
 .../meshes/primitiveMesh/primitiveMesh.H      |    9 +-
 .../primitiveMeshCheck/primitiveMeshCheck.C   |  108 --
 .../primitiveMesh/primitiveMeshFindCell.C     |   34 +-
 .../primitiveShapes/tetrahedron/tetrahedron.C |    1 -
 .../primitiveShapes/tetrahedron/tetrahedron.H |   29 +-
 .../tetrahedron/tetrahedronI.H                |  209 +++-
 .../primitiveShapes/triangle/triangle.H       |   21 +-
 .../primitiveShapes/triangle/triangleI.H      |   73 +-
 src/OpenFOAM/primitives/Tensor/TensorI.H      |    2 +-
 .../motionSmoother/motionSmootherCheck.C      |   24 +-
 .../polyMeshGeometry/polyMeshGeometry.C       |  158 ++-
 .../polyMeshGeometry/polyMeshGeometry.H       |    5 +-
 .../interpolation/interpolation.H             |   19 +-
 .../interpolationCell/interpolationCell.C     |    4 +-
 .../interpolationCell/interpolationCell.H     |    4 +-
 .../cellPointWeight/cellPointWeight.C         |  365 +++---
 .../cellPointWeight/cellPointWeight.H         |   20 +-
 .../interpolationCellPoint.H                  |   15 +-
 .../interpolationCellPointI.H                 |   73 +-
 .../interpolationCellPointFace.C              |   78 +-
 .../interpolationCellPointFace.H              |    4 +-
 .../cellPointWeightWallModified.C             |   29 +-
 .../cellPointWeightWallModified.H             |    7 +-
 .../interpolationCellPointWallModified.H      |   13 +-
 .../interpolationCellPointWallModifiedI.H     |   84 +-
 .../interpolationPoint/interpolationPoint.H   |    4 +-
 .../interpolationPoint/interpolationPointI.H  |    6 +-
 .../interpolationPoint/pointMVCWeight.H       |    4 +-
 src/lagrangian/basic/Cloud/Cloud.C            |  340 +++++-
 src/lagrangian/basic/Cloud/Cloud.H            |   95 +-
 src/lagrangian/basic/Cloud/CloudIO.C          |   19 +-
 src/lagrangian/basic/Make/options             |    5 +
 src/lagrangian/basic/Particle/Particle.C      |  643 +++++++---
 src/lagrangian/basic/Particle/Particle.H      |  173 ++-
 src/lagrangian/basic/Particle/ParticleI.H     | 1082 ++++++++++++++---
 src/lagrangian/basic/Particle/ParticleIO.C    |   56 +-
 .../basic/indexedParticle/indexedParticle.H   |   19 +-
 .../basic/passiveParticle/passiveParticle.H   |   17 +-
 .../coalCombustion/coalParcel/coalParcel.C    |   17 +-
 .../coalCombustion/coalParcel/coalParcel.H    |    6 +-
 src/lagrangian/dieselSpray/Make/options       |    2 +
 .../dieselSpray/parcel/boundaryTreatment.H    |   14 +-
 src/lagrangian/dieselSpray/parcel/parcel.C    |   71 +-
 src/lagrangian/dieselSpray/parcel/parcel.H    |   10 +-
 .../dieselSpray/parcel/setRelaxationTimes.C   |   10 +-
 .../dieselSpray/spray/findInjectorCell.H      |    9 +-
 src/lagrangian/dieselSpray/spray/spray.C      |    8 +-
 .../dieselSpray/spray/sprayFunctions.C        |    8 +-
 .../dieselSpray/spray/sprayInject.C           |   14 +-
 src/lagrangian/dieselSpray/spray/sprayOps.C   |    4 +-
 .../spraySubModels/breakupModel/SHF/SHF.C     |    8 +-
 .../breakupModel/reitzKHRT/reitzKHRT.C        |    8 +-
 .../wallModel/reflectParcel/reflectParcel.C   |   30 +-
 .../wallModel/reflectParcel/reflectParcel.H   |    2 +-
 .../wallModel/removeParcel/removeParcel.C     |    2 +-
 .../wallModel/removeParcel/removeParcel.H     |    2 +-
 .../wallModel/wallModel/wallModel.H           |    2 +-
 .../clouds/Templates/DsmcCloud/DsmcCloud.C    |  238 ++--
 .../clouds/Templates/DsmcCloud/DsmcCloud.H    |    8 +-
 .../parcels/Templates/DsmcParcel/DsmcParcel.C |   17 +-
 .../parcels/Templates/DsmcParcel/DsmcParcel.H |   15 +-
 .../Templates/DsmcParcel/DsmcParcelI.H        |    6 +-
 .../parcels/derived/dsmcParcel/dsmcParcel.C   |    8 +-
 .../parcels/derived/dsmcParcel/dsmcParcel.H   |    4 +-
 .../BinaryCollisionModel.H                    |   14 +-
 .../LarsenBorgnakkeVariableHardSphere.C       |   26 +-
 .../LarsenBorgnakkeVariableHardSphere.H       |   14 +-
 .../NoBinaryCollision/NoBinaryCollision.C     |   14 +-
 .../NoBinaryCollision/NoBinaryCollision.H     |   14 +-
 .../VariableHardSphere/VariableHardSphere.C   |   24 +-
 .../VariableHardSphere/VariableHardSphere.H   |   14 +-
 .../FreeStream/FreeStream.C                   |   89 +-
 .../MaxwellianThermal/MaxwellianThermal.C     |   19 +-
 .../MaxwellianThermal/MaxwellianThermal.H     |   12 +-
 .../MixedDiffuseSpecular.C                    |   19 +-
 .../MixedDiffuseSpecular.H                    |   14 +-
 .../SpecularReflection/SpecularReflection.C   |   11 +-
 .../SpecularReflection/SpecularReflection.H   |    7 +-
 .../WallInteractionModel.H                    |    7 +-
 src/lagrangian/intermediate/Make/options      |    5 +-
 .../Templates/KinematicCloud/KinematicCloud.H |    7 +
 .../KinematicCloud/KinematicCloudI.H          |    7 +
 .../KinematicParcel/KinematicParcel.C         |   52 +-
 .../KinematicParcel/KinematicParcel.H         |   22 +-
 .../KinematicParcel/KinematicParcelI.H        |   28 +-
 .../ReactingMultiphaseParcel.H                |    6 +-
 .../ReactingMultiphaseParcelI.H               |   10 +-
 .../Templates/ReactingParcel/ReactingParcel.C |    7 +-
 .../Templates/ReactingParcel/ReactingParcel.H |    6 +-
 .../ReactingParcel/ReactingParcelI.H          |   10 +-
 .../Templates/ThermoParcel/ThermoParcel.C     |   14 +-
 .../Templates/ThermoParcel/ThermoParcel.H     |    6 +-
 .../Templates/ThermoParcel/ThermoParcelI.H    |   10 +-
 .../basicKinematicParcel.C                    |   17 +-
 .../basicKinematicParcel.H                    |    6 +-
 .../basicReactingMultiphaseParcel.C           |   14 +-
 .../basicReactingMultiphaseParcel.H           |    6 +-
 .../basicReactingParcel/basicReactingParcel.C |   25 +-
 .../basicReactingParcel/basicReactingParcel.H |    6 +-
 .../basicThermoParcel/basicThermoParcel.C     |   10 +-
 .../basicThermoParcel/basicThermoParcel.H     |    6 +-
 .../particleForces/particleForces.C           |   12 +-
 .../particleForces/particleForces.H           |    5 +-
 .../DispersionModel/DispersionModel.H         |    2 +-
 .../GradientDispersionRAS.C                   |   10 +-
 .../GradientDispersionRAS.H                   |    2 +-
 .../NoDispersion/NoDispersion.H               |    2 +-
 .../StochasticDispersionRAS.C                 |    8 +-
 .../StochasticDispersionRAS.H                 |    2 +-
 .../ConeInjection/ConeInjection.C             |   16 +-
 .../ConeInjection/ConeInjection.H             |   12 +-
 .../ConeInjectionMP/ConeInjectionMP.C         |   12 +-
 .../ConeInjectionMP/ConeInjectionMP.H         |   14 +-
 .../FieldActivatedInjection.C                 |   10 +-
 .../FieldActivatedInjection.H                 |   18 +-
 .../InjectionModel/InjectionModel.C           |   42 +-
 .../InjectionModel/InjectionModel.H           |    8 +-
 .../KinematicLookupTableInjection.C           |   21 +-
 .../KinematicLookupTableInjection.H           |   16 +-
 .../ManualInjection/ManualInjection.C         |   62 +-
 .../ManualInjection/ManualInjection.H         |   17 +-
 .../InjectionModel/NoInjection/NoInjection.C  |    2 +
 .../InjectionModel/NoInjection/NoInjection.H  |    6 +-
 .../PatchInjection/PatchInjection.C           |   32 +-
 .../PatchInjection/PatchInjection.H           |   10 +-
 .../LocalInteraction/LocalInteraction.C       |   46 +-
 .../LocalInteraction/LocalInteraction.H       |    8 +-
 .../PatchInteractionModel.C                   |  165 ++-
 .../PatchInteractionModel.H                   |   26 +-
 .../PatchInteractionModel/Rebound/Rebound.C   |   22 +-
 .../PatchInteractionModel/Rebound/Rebound.H   |    6 +-
 .../StandardWallInteraction.C                 |   22 +-
 .../StandardWallInteraction.H                 |    6 +-
 .../SurfaceFilmModel/SurfaceFilmModel.C       |   21 +-
 .../ReactingLookupTableInjection.C            |   21 +-
 .../ReactingLookupTableInjection.H            |   16 +-
 .../ReactingMultiphaseLookupTableInjection.C  |   21 +-
 .../ReactingMultiphaseLookupTableInjection.H  |   16 +-
 .../ThermoLookupTableInjection.C              |   21 +-
 .../ThermoLookupTableInjection.H              |   16 +-
 .../molecule/molecule/molecule.C              |   18 +-
 .../molecule/molecule/molecule.H              |   18 +-
 .../molecule/molecule/moleculeI.H             |    6 +-
 .../molecule/moleculeCloud/moleculeCloud.C    |   46 +-
 .../molecule/moleculeCloud/moleculeCloud.H    |    2 +
 src/lagrangian/solidParticle/Make/options     |    2 +
 src/lagrangian/solidParticle/solidParticle.C  |   20 +-
 src/lagrangian/solidParticle/solidParticle.H  |   20 +-
 .../solidParticle/solidParticleCloud.H        |    2 +
 .../solidParticle/solidParticleCloudI.H       |    6 +
 src/lagrangian/solidParticle/solidParticleI.H |   11 +-
 .../meshRefinement/meshRefinementRefine.C     |    8 +-
 .../trackedParticle/ExactParticle.C           |  261 ----
 .../trackedParticle/ExactParticle.H           |  191 ---
 .../trackedParticle/trackedParticle.C         |   26 +-
 .../trackedParticle/trackedParticle.H         |   25 +-
 src/meshTools/Make/options                    |    6 +-
 src/meshTools/surfaceSets/surfaceSets.H       |    5 +-
 .../surfaceIntersection/surfaceIntersection.C |    2 -
 .../surfaceIntersectionFuncs.C                |    2 -
 .../surfaceFeatures/surfaceFeatures.C         |  196 ++-
 .../surfaceFeatures/surfaceFeatures.H         |   28 +-
 .../reconstruct/reconstruct/Make/options      |    2 +
 .../reconstructLagrangianPositions.C          |    7 +
 .../field/streamLine/streamLine.C             |   12 +-
 .../field/streamLine/streamLineParticle.C     |   18 +-
 .../field/streamLine/streamLineParticle.H     |   17 +-
 src/sampling/Make/options                     |    3 +-
 .../calculateMeshToMeshAddressing.C           |   54 +-
 .../meshToMesh/meshToMesh.H                   |    6 +-
 .../sampledTriSurfaceMesh.C                   |    1 -
 .../aachenBomb/constant/sprayProperties       |    2 +-
 .../iglooWithFridges/system/snappyHexMeshDict |   13 +-
 .../system/snappyHexMeshDict                  |   13 +-
 .../system/snappyHexMeshDict                  |   13 +-
 .../motorBike/system/snappyHexMeshDict        |   13 +-
 .../system/snappyHexMeshDict                  |   13 +-
 .../constant/coalCloud1Properties             |    2 +-
 .../filter/constant/reactingCloud1Properties  |    2 +-
 .../constant/reactingCloud1Properties         |    2 +-
 .../constant/reactingCloud1Properties         |    2 +-
 .../constant/reactingCloud1Properties         |    2 +-
 .../constant/reactingCloud1Properties         |    2 +-
 .../constant/reactingCloud1Properties         |    2 +-
 .../panel/constant/reactingCloud1Properties   |    2 +-
 .../constant/reactingCloud1Properties         |    2 +-
 .../0.org/pointDisplacement                   |   88 ++
 .../simpleHarmonicMotion/Allclean             |   11 +
 .../simpleHarmonicMotion/Allrun               |   19 +
 .../constant/dynamicMeshDict                  |   26 +
 .../constant/polyMesh/blockMeshDict           |   59 +
 .../constant/polyMesh/boundary}               |   31 +-
 .../simpleHarmonicMotion/extractData          |   42 +
 .../simpleHarmonicMotion/shm.gnuplot          |   76 ++
 .../simpleHarmonicMotion/system/controlDict   |   68 ++
 .../simpleHarmonicMotion/system/fvSchemes     |   50 +
 .../simpleHarmonicMotion/system/fvSolution    |   34 +
 .../simpleHarmonicMotion/system/topoSetDict}  |   35 +-
 .../cavitatingBullet/system/snappyHexMeshDict |   13 +-
 237 files changed, 6895 insertions(+), 2499 deletions(-)
 create mode 100644 applications/solvers/lagrangian/incompressibleUncoupledKinematicParcelDyMFoam/Make/files
 create mode 100644 applications/solvers/lagrangian/incompressibleUncoupledKinematicParcelDyMFoam/Make/options
 create mode 100644 applications/solvers/lagrangian/incompressibleUncoupledKinematicParcelDyMFoam/createFields.H
 create mode 100644 applications/solvers/lagrangian/incompressibleUncoupledKinematicParcelDyMFoam/incompressibleUncoupledKinematicParcelDyMFoam.C
 create mode 100644 src/OpenFOAM/meshes/polyMesh/polyMeshTetDecomposition/polyMeshTetDecomposition.C
 create mode 100644 src/OpenFOAM/meshes/polyMesh/polyMeshTetDecomposition/polyMeshTetDecomposition.H
 create mode 100644 src/OpenFOAM/meshes/polyMesh/polyMeshTetDecomposition/tetIndices.C
 create mode 100644 src/OpenFOAM/meshes/polyMesh/polyMeshTetDecomposition/tetIndices.H
 create mode 100644 src/OpenFOAM/meshes/polyMesh/polyMeshTetDecomposition/tetIndicesI.H
 delete mode 100644 src/mesh/autoMesh/autoHexMesh/trackedParticle/ExactParticle.C
 delete mode 100644 src/mesh/autoMesh/autoHexMesh/trackedParticle/ExactParticle.H
 create mode 100644 tutorials/mesh/moveDynamicMesh/simpleHarmonicMotion/0.org/pointDisplacement
 create mode 100755 tutorials/mesh/moveDynamicMesh/simpleHarmonicMotion/Allclean
 create mode 100755 tutorials/mesh/moveDynamicMesh/simpleHarmonicMotion/Allrun
 create mode 100644 tutorials/mesh/moveDynamicMesh/simpleHarmonicMotion/constant/dynamicMeshDict
 create mode 100644 tutorials/mesh/moveDynamicMesh/simpleHarmonicMotion/constant/polyMesh/blockMeshDict
 rename tutorials/{discreteMethods/molecularDynamics/mdEquilibrationFoam/periodicCubeWater/0/U => mesh/moveDynamicMesh/simpleHarmonicMotion/constant/polyMesh/boundary} (71%)
 create mode 100755 tutorials/mesh/moveDynamicMesh/simpleHarmonicMotion/extractData
 create mode 100644 tutorials/mesh/moveDynamicMesh/simpleHarmonicMotion/shm.gnuplot
 create mode 100644 tutorials/mesh/moveDynamicMesh/simpleHarmonicMotion/system/controlDict
 create mode 100644 tutorials/mesh/moveDynamicMesh/simpleHarmonicMotion/system/fvSchemes
 create mode 100644 tutorials/mesh/moveDynamicMesh/simpleHarmonicMotion/system/fvSolution
 rename tutorials/{discreteMethods/molecularDynamics/mdEquilibrationFoam/periodicCubeArgon/0/U => mesh/moveDynamicMesh/simpleHarmonicMotion/system/topoSetDict} (72%)

diff --git a/applications/solvers/combustion/dieselEngineFoam/Make/options b/applications/solvers/combustion/dieselEngineFoam/Make/options
index d0a572b86fe..9a452478199 100644
--- a/applications/solvers/combustion/dieselEngineFoam/Make/options
+++ b/applications/solvers/combustion/dieselEngineFoam/Make/options
@@ -2,6 +2,7 @@ EXE_INC = \
     -I../engineFoam \
     -I$(LIB_SRC)/turbulenceModels/compressible/turbulenceModel \
     -I$(LIB_SRC)/lagrangian/basic/lnInclude \
+    -I$(LIB_SRC)/meshTools/lnInclude \
     -I$(LIB_SRC)/lagrangian/dieselSpray/lnInclude \
     -I$(LIB_SRC)/thermophysicalModels/liquids/lnInclude \
     -I$(LIB_SRC)/thermophysicalModels/liquidMixture/lnInclude \
@@ -24,6 +25,7 @@ EXE_LIBS = \
     -lreactionThermophysicalModels \
     -lfiniteVolume \
     -llagrangian \
+    -lmeshTools \
     -ldieselSpray \
     -lliquids \
     -lliquidMixture \
diff --git a/applications/solvers/combustion/dieselFoam/Make/options b/applications/solvers/combustion/dieselFoam/Make/options
index 9096934c2dd..3b1786aa08b 100644
--- a/applications/solvers/combustion/dieselFoam/Make/options
+++ b/applications/solvers/combustion/dieselFoam/Make/options
@@ -3,6 +3,7 @@ EXE_INC = \
     -I$(LIB_SRC)/finiteVolume/lnInclude \
     -I$(LIB_SRC)/turbulenceModels/compressible/turbulenceModel \
     -I$(LIB_SRC)/lagrangian/basic/lnInclude \
+    -I$(LIB_SRC)/meshTools/lnInclude \
     -I$(LIB_SRC)/lagrangian/dieselSpray/lnInclude \
     -I$(LIB_SRC)/thermophysicalModels/liquids/lnInclude \
     -I$(LIB_SRC)/thermophysicalModels/liquidMixture/lnInclude \
@@ -21,6 +22,7 @@ EXE_LIBS = \
     -lcompressibleLESModels \
     -lreactionThermophysicalModels \
     -llagrangian \
+    -lmeshTools \
     -ldieselSpray \
     -lliquids \
     -lliquidMixture \
diff --git a/applications/solvers/discreteMethods/dsmc/dsmcFoam/Make/options b/applications/solvers/discreteMethods/dsmc/dsmcFoam/Make/options
index bc99834af6e..62520c19b0e 100644
--- a/applications/solvers/discreteMethods/dsmc/dsmcFoam/Make/options
+++ b/applications/solvers/discreteMethods/dsmc/dsmcFoam/Make/options
@@ -9,4 +9,3 @@ EXE_LIBS = \
     -lfiniteVolume \
     -llagrangian \
     -ldsmc
-
diff --git a/applications/solvers/lagrangian/incompressibleUncoupledKinematicParcelDyMFoam/Make/files b/applications/solvers/lagrangian/incompressibleUncoupledKinematicParcelDyMFoam/Make/files
new file mode 100644
index 00000000000..e233a549b84
--- /dev/null
+++ b/applications/solvers/lagrangian/incompressibleUncoupledKinematicParcelDyMFoam/Make/files
@@ -0,0 +1,3 @@
+incompressibleUncoupledKinematicParcelDyMFoam.C
+
+EXE = $(FOAM_APPBIN)/incompressibleUncoupledKinematicParcelDyMFoam
diff --git a/applications/solvers/lagrangian/incompressibleUncoupledKinematicParcelDyMFoam/Make/options b/applications/solvers/lagrangian/incompressibleUncoupledKinematicParcelDyMFoam/Make/options
new file mode 100644
index 00000000000..3c4206dc24a
--- /dev/null
+++ b/applications/solvers/lagrangian/incompressibleUncoupledKinematicParcelDyMFoam/Make/options
@@ -0,0 +1,33 @@
+EXE_INC = \
+    -I$(LIB_SRC)/lagrangian/basic/lnInclude \
+    -I$(LIB_SRC)/lagrangian/intermediate/lnInclude \
+    -I$(LIB_SRC)/thermophysicalModels/specie/lnInclude \
+    -I$(LIB_SRC)/thermophysicalModels/basic/lnInclude \
+    -I$(LIB_SRC)/thermophysicalModels/reactionThermo/lnInclude \
+    -I$(LIB_SRC)/thermophysicalModels/radiation/lnInclude \
+    -I$(LIB_SRC)/turbulenceModels/incompressible/turbulenceModel \
+    -I$(LIB_SRC)/transportModels \
+    -I$(LIB_SRC)/transportModels/incompressible/singlePhaseTransportModel \
+    -I$(LIB_SRC)/finiteVolume/lnInclude \
+    -I$(LIB_SRC)/meshTools/lnInclude \
+    -I$(LIB_SRC)/surfaceFilmModels/lnInclude \
+    -I$(LIB_SRC)/dynamicMesh/lnInclude \
+    -I$(LIB_SRC)/dynamicFvMesh/lnInclude
+
+
+EXE_LIBS = \
+    -llagrangian \
+    -llagrangianIntermediate \
+    -lthermophysicalFunctions \
+    -lbasicThermophysicalModels \
+    -lspecie \
+    -lradiation \
+    -lincompressibleRASModels \
+    -lincompressibleLESModels \
+    -lincompressibleTransportModels \
+    -lfiniteVolume \
+    -lmeshTools \
+    -lsurfaceFilmModels \
+    -ldynamicMesh \
+    -ldynamicFvMesh \
+    -ltopoChangerFvMesh
diff --git a/applications/solvers/lagrangian/incompressibleUncoupledKinematicParcelDyMFoam/createFields.H b/applications/solvers/lagrangian/incompressibleUncoupledKinematicParcelDyMFoam/createFields.H
new file mode 100644
index 00000000000..0ad057e2299
--- /dev/null
+++ b/applications/solvers/lagrangian/incompressibleUncoupledKinematicParcelDyMFoam/createFields.H
@@ -0,0 +1,147 @@
+    Info<< "\nReading transportProperties\n" << endl;
+
+    IOdictionary transportProperties
+    (
+        IOobject
+        (
+            "transportProperties",
+            runTime.constant(),
+            mesh,
+            IOobject::MUST_READ_IF_MODIFIED,
+            IOobject::NO_WRITE
+        )
+    );
+
+    dimensionedScalar rhoInfValue
+    (
+        transportProperties.lookup("rhoInf")
+    );
+
+    volScalarField rhoInf
+    (
+        IOobject
+        (
+            "rho",
+            runTime.timeName(),
+            mesh,
+            IOobject::NO_READ,
+            IOobject::AUTO_WRITE
+        ),
+        mesh,
+        rhoInfValue
+    );
+
+    Info<< "Reading field U\n" << endl;
+    volVectorField U
+    (
+        IOobject
+        (
+            "U",
+            runTime.timeName(),
+            mesh,
+            IOobject::MUST_READ,
+            IOobject::AUTO_WRITE
+        ),
+        mesh
+    );
+
+    #include "createPhi.H"
+
+    Info<< "Creating turbulence model\n" << endl;
+
+    singlePhaseTransportModel laminarTransport(U, phi);
+
+    const volScalarField nu = laminarTransport.nu();
+
+    autoPtr<incompressible::turbulenceModel> turbulence
+    (
+        incompressible::turbulenceModel::New(U, phi, laminarTransport)
+    );
+
+    volScalarField mu
+    (
+        IOobject
+        (
+            "mu",
+            runTime.timeName(),
+            mesh,
+            IOobject::NO_READ,
+            IOobject::AUTO_WRITE
+        ),
+        nu*rhoInfValue
+    );
+
+    word kinematicCloudName("kinematicCloud");
+    args.optionReadIfPresent("cloudName", kinematicCloudName);
+
+    Info<< "Constructing kinematicCloud " << kinematicCloudName << endl;
+    basicKinematicCloud kinematicCloud
+    (
+        kinematicCloudName,
+        rhoInf,
+        U,
+        mu,
+        g
+    );
+
+    IOobject Hheader
+    (
+        "H",
+        runTime.timeName(),
+        mesh,
+        IOobject::NO_READ
+    );
+
+    autoPtr<volVectorField> HPtr_;
+
+    if (Hheader.headerOk())
+    {
+        Info<< "\nReading field H\n" << endl;
+
+        HPtr_.reset
+        (
+            new volVectorField
+            (
+                IOobject
+                (
+                    "H",
+                    runTime.timeName(),
+                    mesh,
+                    IOobject::MUST_READ,
+                    IOobject::AUTO_WRITE
+                ),
+                mesh
+            )
+        );
+    }
+
+    IOobject HdotGradHheader
+    (
+        "HdotGradH",
+        runTime.timeName(),
+        mesh,
+        IOobject::NO_READ
+    );
+
+    autoPtr<volVectorField> HdotGradHPtr_;
+
+    if (HdotGradHheader.headerOk())
+    {
+        Info<< "Reading field HdotGradH" << endl;
+
+        HdotGradHPtr_.reset
+        (
+            new volVectorField
+            (
+                IOobject
+                (
+                    "HdotGradH",
+                    runTime.timeName(),
+                    mesh,
+                    IOobject::MUST_READ,
+                    IOobject::AUTO_WRITE
+                ),
+                mesh
+            )
+        );
+    }
diff --git a/applications/solvers/lagrangian/incompressibleUncoupledKinematicParcelDyMFoam/incompressibleUncoupledKinematicParcelDyMFoam.C b/applications/solvers/lagrangian/incompressibleUncoupledKinematicParcelDyMFoam/incompressibleUncoupledKinematicParcelDyMFoam.C
new file mode 100644
index 00000000000..f5fba006e02
--- /dev/null
+++ b/applications/solvers/lagrangian/incompressibleUncoupledKinematicParcelDyMFoam/incompressibleUncoupledKinematicParcelDyMFoam.C
@@ -0,0 +1,92 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2008-2010 OpenCFD Ltd.
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+License
+    This file is part of OpenFOAM.
+
+    OpenFOAM is free software: you can redistribute it and/or modify it
+    under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
+    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+    for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
+
+Application
+    uncoupledKinematicParcelFoam
+
+Description
+    Transient solver for the passive transport of a single kinematic
+    particle could.
+
+    Uses a pre-calculated velocity field to evolve the cloud.
+
+\*---------------------------------------------------------------------------*/
+
+#include "fvCFD.H"
+#include "dynamicFvMesh.H"
+#include "singlePhaseTransportModel.H"
+#include "turbulenceModel.H"
+#include "basicKinematicCloud.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+int main(int argc, char *argv[])
+{
+    argList::addOption
+    (
+        "cloudName",
+        "name",
+        "specify alternative cloud name. default is 'kinematicCloud'"
+    );
+
+    #include "setRootCase.H"
+    #include "createTime.H"
+    #   include "createDynamicFvMesh.H"
+
+    #include "readGravitationalAcceleration.H"
+    #include "createFields.H"
+
+    // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+    Info<< "\nStarting time loop\n" << endl;
+
+    while (runTime.loop())
+    {
+        Info<< "Time = " << runTime.timeName() << nl << endl;
+
+        mesh.update();
+
+        U.correctBoundaryConditions();
+
+        Info<< "Evolving " << kinematicCloud.name() << endl;
+
+        laminarTransport.correct();
+
+        mu = nu*rhoInfValue;
+
+        kinematicCloud.evolve();
+
+        runTime.write();
+
+        Info<< "ExecutionTime = " << runTime.elapsedCpuTime() << " s"
+            << "  ClockTime = " << runTime.elapsedClockTime() << " s"
+            << nl << endl;
+    }
+
+    Info<< "End\n" << endl;
+
+    return 0;
+}
+
+
+// ************************************************************************* //
diff --git a/applications/test/findCell-octree/findCell-octree.C b/applications/test/findCell-octree/findCell-octree.C
index 2fbb9c0703f..df8797eeb06 100644
--- a/applications/test/findCell-octree/findCell-octree.C
+++ b/applications/test/findCell-octree/findCell-octree.C
@@ -29,6 +29,8 @@ License
 #include "IStringStream.H"
 #include "octree.H"
 #include "octreeDataCell.H"
+#include "indexedOctree.H"
+#include "treeDataCell.H"
 #include "OFstream.H"
 
 using namespace Foam;
@@ -44,11 +46,13 @@ int main(int argc, char *argv[])
 #   include "createTime.H"
 #   include "createMesh.H"
 
+    label nReps = 100000;
+
     const point sample = args.argRead<point>(1);
 
-    treeBoundBox meshBb(mesh.points());
+    treeBoundBox meshBb(mesh.bounds());
 
-    // Calculate typical cell releated size to shift bb by.
+    // Calculate typical cell related size to shift bb by.
     scalar typDim = meshBb.avgDim()/(2.0*Foam::cbrt(scalar(mesh.nCells())));
 
     treeBoundBox shiftedBb
@@ -57,16 +61,13 @@ int main(int argc, char *argv[])
         meshBb.max() + vector(typDim, typDim, typDim)
     );
 
-
     Info<< "Mesh" << endl;
     Info<< "   bounding box           : " << meshBb << endl;
     Info<< "   bounding box (shifted) : " << shiftedBb << endl;
     Info<< "   typical dimension      : " << shiftedBb.typDim() << endl;
 
-
-    /*
-     * Now we have allBb and shiftedBb
-     */
+    Info<< "Initialised mesh in "
+        << runTime.cpuTimeIncrement() << " s" << endl;
 
     // Wrap indices and mesh information into helper object
     octreeDataCell shapes(mesh);
@@ -80,14 +81,52 @@ int main(int argc, char *argv[])
         10.0        // maximum ratio of cubes v.s. cells
     );
 
-    Info<< "Point:" << sample << " is in shape "
-        << oc.find(sample) << nl
-        << "Point:" << sample << " is in cell  "
-        << mesh.findCell(sample) << endl;
+    for (label i = 0; i < nReps - 1 ; i++)
+    {
+        oc.find(sample);
+    }
 
+    Info<< "Point:" << sample << " is in shape "
+        << oc.find(sample) << endl;
 
     oc.printStats(Info);
 
+    Info<< "Found in octree " << nReps << " times in "
+        << runTime.cpuTimeIncrement() << " s" << endl;
+
+    indexedOctree<treeDataCell> ioc
+    (
+        treeDataCell(true, mesh),
+        shiftedBb,
+        8,      // maxLevel
+        10,     // leafsize
+        3.0     // duplicity
+    );
+
+    for (label i = 0; i < nReps - 1 ; i++)
+    {
+        ioc.findInside(sample);
+    }
+
+    Info<< "Point:" << sample << " is in shape "
+        << ioc.findInside(sample)
+        << ", where the possible cells were:" << nl
+        << ioc.findIndices(sample)
+        << endl;
+
+    Info<< "Found in indexedOctree " << nReps << " times in "
+        << runTime.cpuTimeIncrement() << " s" << endl;
+
+    for (label i = 0; i < nReps - 1 ; i++)
+    {
+        mesh.findCell(sample);
+    }
+
+    Info<< "Point:" << sample << " is in cell  "
+        << mesh.findCell(sample) << endl;
+
+    Info<< "Found in mesh.findCell " << nReps << " times in "
+        << runTime.cpuTimeIncrement() << " s" << endl;
 
     Info<< "End\n" << endl;
 
diff --git a/applications/utilities/mesh/generation/snappyHexMesh/snappyHexMeshDict b/applications/utilities/mesh/generation/snappyHexMesh/snappyHexMeshDict
index 476dbe1afb4..a15aa483c4f 100644
--- a/applications/utilities/mesh/generation/snappyHexMesh/snappyHexMeshDict
+++ b/applications/utilities/mesh/generation/snappyHexMesh/snappyHexMeshDict
@@ -341,11 +341,14 @@ meshQualityControls
     //  Set to very negative number (e.g. -1E30) to disable.
     minVol 1e-13;
 
-    //- Minimum tet volume. Is absolute volume of the tet formed by the
-    //  face-centre decomposition triangle and the cell centre.
-    //  Set to a sensible fraction of the smallest cell volume expected.
-    //  Set to very negative number (e.g. -1E30) to disable.
-    minTetVol 1e-20;
+    //- Minimum quality of the tet formed by the face-centre
+    //  and variable base point minimum decomposition triangles and
+    //  the cell centre.  Set to very negative number (e.g. -1E30) to
+    //  disable.
+    //     <0 = inside out tet,
+    //      0 = flat tet
+    //      1 = regular tet
+    minTetQuality 1e-9;
 
     //- Minimum face area. Set to <0 to disable.
     minArea -1;
diff --git a/applications/utilities/mesh/manipulation/checkMesh/checkGeometry.C b/applications/utilities/mesh/manipulation/checkMesh/checkGeometry.C
index 57f892b8e44..82e0dedd82c 100644
--- a/applications/utilities/mesh/manipulation/checkMesh/checkGeometry.C
+++ b/applications/utilities/mesh/manipulation/checkMesh/checkGeometry.C
@@ -6,6 +6,7 @@
 #include "EdgeMap.H"
 #include "wedgePolyPatch.H"
 #include "unitConversion.H"
+#include "polyMeshTetDecomposition.H"
 
 
 // Find wedge with opposite orientation. Note: does not actually check that
@@ -83,7 +84,7 @@ bool Foam::checkWedges
             {
                 Info<< "    Wedge " << pp.name() << " with angle "
                     << radToDeg(wedgeAngle) << " degrees"
-                    << endl;                
+                    << endl;
             }
 
             // Find opposite
@@ -413,7 +414,6 @@ Foam::label Foam::checkGeometry(const polyMesh& mesh, const bool allGeometry)
         }
     }
 
-
     {
         faceSet faces(mesh, "wrongOrientedFaces", mesh.nFaces()/100 + 1);
         if (mesh.checkFacePyramids(true, -SMALL, &faces))
@@ -434,8 +434,8 @@ Foam::label Foam::checkGeometry(const polyMesh& mesh, const bool allGeometry)
     }
 
     {
-        faceSet faces(mesh, "wrongOrientedTriangleFaces", mesh.nFaces()/100+1);
-        if (mesh.checkFaceTets(true, 0, &faces))
+        faceSet faces(mesh, "skewFaces", mesh.nFaces()/100+1);
+        if (mesh.checkFaceSkewness(true, &faces))
         {
             noFailedChecks++;
 
@@ -444,17 +444,26 @@ Foam::label Foam::checkGeometry(const polyMesh& mesh, const bool allGeometry)
             if (nFaces > 0)
             {
                 Info<< "  <<Writing " << nFaces
-                    << " faces with incorrectly orientated triangles to set "
-                    << faces.name() << endl;
+                    << " skew faces to set " << faces.name() << endl;
                 faces.instance() = mesh.pointsInstance();
                 faces.write();
             }
         }
     }
 
+    if (allGeometry)
     {
-        faceSet faces(mesh, "skewFaces", mesh.nFaces()/100+1);
-        if (mesh.checkFaceSkewness(true, &faces))
+        faceSet faces(mesh, "lowQualityTetFaces", mesh.nFaces()/100+1);
+        if
+        (
+            polyMeshTetDecomposition::checkFaceTets
+            (
+                mesh,
+                polyMeshTetDecomposition::minTetQuality,
+                true,
+                &faces
+            )
+        )
         {
             noFailedChecks++;
 
@@ -463,7 +472,8 @@ Foam::label Foam::checkGeometry(const polyMesh& mesh, const bool allGeometry)
             if (nFaces > 0)
             {
                 Info<< "  <<Writing " << nFaces
-                    << " skew faces to set " << faces.name() << endl;
+                    << " faces with low quality or negative volume "
+                    << "decomposition tets to set " << faces.name() << endl;
                 faces.instance() = mesh.pointsInstance();
                 faces.write();
             }
diff --git a/applications/utilities/parallelProcessing/decomposePar/decomposePar.C b/applications/utilities/parallelProcessing/decomposePar/decomposePar.C
index 91a51e2ae57..0c72ca7ee72 100644
--- a/applications/utilities/parallelProcessing/decomposePar/decomposePar.C
+++ b/applications/utilities/parallelProcessing/decomposePar/decomposePar.C
@@ -558,7 +558,6 @@ int main(int argc, char *argv[])
                 lagrangianScalarFieldFields
             );
 
-
             lagrangianFieldDecomposer::readFields
             (
                 cloudI,
@@ -687,6 +686,19 @@ int main(int argc, char *argv[])
             )
         );
 
+        labelIOList faceProcAddressing
+        (
+            IOobject
+            (
+                "faceProcAddressing",
+                procMesh.facesInstance(),
+                procMesh.meshSubDir,
+                procMesh,
+                IOobject::MUST_READ,
+                IOobject::NO_WRITE
+            )
+        );
+
         labelIOList cellProcAddressing
         (
             IOobject
@@ -728,19 +740,6 @@ int main(int argc, char *argv[])
          || surfaceTensorFields.size()
         )
         {
-            labelIOList faceProcAddressing
-            (
-                IOobject
-                (
-                    "faceProcAddressing",
-                    procMesh.facesInstance(),
-                    procMesh.meshSubDir,
-                    procMesh,
-                    IOobject::MUST_READ,
-                    IOobject::NO_WRITE
-                )
-            );
-
             fvFieldDecomposer fieldDecomposer
             (
                 mesh,
@@ -814,6 +813,7 @@ int main(int argc, char *argv[])
                 (
                     mesh,
                     procMesh,
+                    faceProcAddressing,
                     cellProcAddressing,
                     cloudDirs[cloudI],
                     lagrangianPositions[cloudI],
diff --git a/applications/utilities/parallelProcessing/decomposePar/lagrangianFieldDecomposer.C b/applications/utilities/parallelProcessing/decomposePar/lagrangianFieldDecomposer.C
index 234565fb488..efe0c37ff6f 100644
--- a/applications/utilities/parallelProcessing/decomposePar/lagrangianFieldDecomposer.C
+++ b/applications/utilities/parallelProcessing/decomposePar/lagrangianFieldDecomposer.C
@@ -35,6 +35,7 @@ Foam::lagrangianFieldDecomposer::lagrangianFieldDecomposer
 (
     const polyMesh& mesh,
     const polyMesh& procMesh,
+    const labelList& faceProcAddressing,
     const labelList& cellProcAddressing,
     const word& cloudName,
     const Cloud<indexedParticle>& lagrangianPositions,
@@ -47,6 +48,14 @@ Foam::lagrangianFieldDecomposer::lagrangianFieldDecomposer
 {
     label pi = 0;
 
+    // faceProcAddressing not required currently
+    // labelList decodedProcFaceAddressing(faceProcAddressing.size());
+
+    // forAll(faceProcAddressing, i)
+    // {
+    //     decodedProcFaceAddressing[i] = mag(faceProcAddressing[i]) - 1;
+    // }
+
     forAll(cellProcAddressing, procCelli)
     {
         label celli = cellProcAddressing[procCelli];
@@ -60,6 +69,33 @@ Foam::lagrangianFieldDecomposer::lagrangianFieldDecomposer
                 const indexedParticle& ppi = *iter();
                 particleIndices_[pi++] = ppi.index();
 
+                // label mappedTetFace = findIndex
+                // (
+                //     decodedProcFaceAddressing,
+                //     ppi.tetFace()
+                // );
+
+                // if (mappedTetFace == -1)
+                // {
+                //     FatalErrorIn
+                //     (
+                //         "Foam::lagrangianFieldDecomposer"
+                //         "::lagrangianFieldDecomposer"
+                //         "("
+                //             "const polyMesh& mesh, "
+                //             "const polyMesh& procMesh, "
+                //             "const labelList& faceProcAddressing, "
+                //             "const labelList& cellProcAddressing, "
+                //             "const word& cloudName, "
+                //             "const Cloud<indexedParticle>& "
+                //             "lagrangianPositions, "
+                //             "const List<SLList<indexedParticle*>*>& "
+                //             "cellParticles"
+                //         ")"
+                //     )   << "Face lookup failure." << nl
+                //         << abort(FatalError);
+                // }
+
                 positions_.append
                 (
                     new passiveParticle
diff --git a/applications/utilities/parallelProcessing/decomposePar/lagrangianFieldDecomposer.H b/applications/utilities/parallelProcessing/decomposePar/lagrangianFieldDecomposer.H
index bc0d42c5ed8..dafd94c3430 100644
--- a/applications/utilities/parallelProcessing/decomposePar/lagrangianFieldDecomposer.H
+++ b/applications/utilities/parallelProcessing/decomposePar/lagrangianFieldDecomposer.H
@@ -84,6 +84,7 @@ public:
         (
             const polyMesh& mesh,
             const polyMesh& procMesh,
+            const labelList& faceProcAddressing,
             const labelList& cellProcAddressing,
             const word& cloudName,
             const Cloud<indexedParticle>& lagrangianPositions,
diff --git a/applications/utilities/parallelProcessing/decomposePar/lagrangianFieldDecomposerDecomposeFields.C b/applications/utilities/parallelProcessing/decomposePar/lagrangianFieldDecomposerDecomposeFields.C
index 00c025c953d..7e42907fd0d 100644
--- a/applications/utilities/parallelProcessing/decomposePar/lagrangianFieldDecomposerDecomposeFields.C
+++ b/applications/utilities/parallelProcessing/decomposePar/lagrangianFieldDecomposerDecomposeFields.C
@@ -51,7 +51,7 @@ void Foam::lagrangianFieldDecomposer::readFields
         )
     );
 
-    label lagrangianFieldi=0;
+    label lagrangianFieldi = 0;
     forAllIter(IOobjectList, lagrangianTypeObjects, iter)
     {
         lagrangianFields[cloudI].set
@@ -91,7 +91,7 @@ void Foam::lagrangianFieldDecomposer::readFieldFields
         )
     );
 
-    label lagrangianFieldi=0;
+    label lagrangianFieldi = 0;
 
     forAllIter(IOobjectList, lagrangianTypeObjectsA, iter)
     {
diff --git a/applications/utilities/postProcessing/dataConversion/foamToEnsight/Make/options b/applications/utilities/postProcessing/dataConversion/foamToEnsight/Make/options
index 01bd39cf760..5d00838fdcd 100644
--- a/applications/utilities/postProcessing/dataConversion/foamToEnsight/Make/options
+++ b/applications/utilities/postProcessing/dataConversion/foamToEnsight/Make/options
@@ -1,10 +1,12 @@
 EXE_INC = \
     /* -DFULLDEBUG -g -O0 */ \
     -I$(LIB_SRC)/finiteVolume/lnInclude \
+    -I$(LIB_SRC)/meshTools/lnInclude \
     -I$(LIB_SRC)/lagrangian/basic/lnInclude
 
 EXE_LIBS = \
     -lfiniteVolume \
+    -lmeshTools \
     -lgenericPatchFields \
     -llagrangian
 
diff --git a/applications/utilities/postProcessing/dataConversion/foamToEnsightParts/Make/options b/applications/utilities/postProcessing/dataConversion/foamToEnsightParts/Make/options
index 9243ffdb03d..40a58886bfc 100644
--- a/applications/utilities/postProcessing/dataConversion/foamToEnsightParts/Make/options
+++ b/applications/utilities/postProcessing/dataConversion/foamToEnsightParts/Make/options
@@ -1,10 +1,12 @@
 EXE_INC = \
     -I$(LIB_SRC)/finiteVolume/lnInclude \
+    -I$(LIB_SRC)/meshTools/lnInclude \
     -I$(LIB_SRC)/lagrangian/basic/lnInclude \
     -I$(LIB_SRC)/conversion/lnInclude
 
 EXE_LIBS = \
     -lfiniteVolume \
     -llagrangian \
+    -lmeshTools \
     -lgenericPatchFields \
     -lconversion
diff --git a/applications/utilities/postProcessing/dataConversion/foamToFieldview9/Make/options b/applications/utilities/postProcessing/dataConversion/foamToFieldview9/Make/options
index a61c912ba0c..0bc784e4c51 100644
--- a/applications/utilities/postProcessing/dataConversion/foamToFieldview9/Make/options
+++ b/applications/utilities/postProcessing/dataConversion/foamToFieldview9/Make/options
@@ -1,8 +1,10 @@
 EXE_INC = \
     -I$(LIB_SRC)/finiteVolume/lnInclude \
+    -I$(LIB_SRC)/meshTools/lnInclude \
     -I$(LIB_SRC)/lagrangian/basic/lnInclude
 
 EXE_LIBS = \
     -lfiniteVolume \
+    -lmeshTools \
     -lgenericPatchFields \
     -llagrangian
diff --git a/applications/utilities/postProcessing/dataConversion/foamToGMV/Make/options b/applications/utilities/postProcessing/dataConversion/foamToGMV/Make/options
index e09f6bdc7f1..5664dc4494e 100644
--- a/applications/utilities/postProcessing/dataConversion/foamToGMV/Make/options
+++ b/applications/utilities/postProcessing/dataConversion/foamToGMV/Make/options
@@ -1,6 +1,7 @@
 EXE_INC = \
     -I$(LIB_SRC)/finiteVolume/lnInclude \
     -I$(LIB_SRC)/sampling/lnInclude \
+    -I$(LIB_SRC)/meshTools/lnInclude \
     -I$(LIB_SRC)/turbulenceModels/incompressible/lnInclude \
     -I$(LIB_SRC)/transportModels/incompressible/lnInclude \
     -I$(LIB_SRC)/lagrangian/lnInclude \
@@ -14,5 +15,6 @@ EXE_INC = \
 
 EXE_LIBS = \
     -lfiniteVolume \
+    -lmeshTools \
     -lgenericPatchFields \
     -llagrangian
diff --git a/applications/utilities/postProcessing/graphics/ensightFoamReader/Make/options b/applications/utilities/postProcessing/graphics/ensightFoamReader/Make/options
index 8d21a9eae4b..0d76f0304cb 100644
--- a/applications/utilities/postProcessing/graphics/ensightFoamReader/Make/options
+++ b/applications/utilities/postProcessing/graphics/ensightFoamReader/Make/options
@@ -1,5 +1,6 @@
 EXE_INC = \
     -I$(LIB_SRC)/finiteVolume/lnInclude \
+    -I$(LIB_SRC)/meshTools/lnInclude \
     -I$(LIB_SRC)/browser/lnInclude \
     -I$(LIB_SRC)/sampling/lnInclude \
     -I$(LIB_SRC)/lagrangian/basic/lnInclude
@@ -7,6 +8,7 @@ EXE_INC = \
 LIB_LIBS = \
     -lOpenFOAM \
     -lfiniteVolume \
+    -lmeshTools \
     -lgenericPatchFields \
     -llagrangian \
     $(PROJECT_LIBS)
diff --git a/applications/utilities/postProcessing/lagrangian/particleTracks/Make/options b/applications/utilities/postProcessing/lagrangian/particleTracks/Make/options
index a61c912ba0c..0bc784e4c51 100644
--- a/applications/utilities/postProcessing/lagrangian/particleTracks/Make/options
+++ b/applications/utilities/postProcessing/lagrangian/particleTracks/Make/options
@@ -1,8 +1,10 @@
 EXE_INC = \
     -I$(LIB_SRC)/finiteVolume/lnInclude \
+    -I$(LIB_SRC)/meshTools/lnInclude \
     -I$(LIB_SRC)/lagrangian/basic/lnInclude
 
 EXE_LIBS = \
     -lfiniteVolume \
+    -lmeshTools \
     -lgenericPatchFields \
     -llagrangian
diff --git a/applications/utilities/preProcessing/dsmcInitialise/Make/options b/applications/utilities/preProcessing/dsmcInitialise/Make/options
index bc99834af6e..62520c19b0e 100644
--- a/applications/utilities/preProcessing/dsmcInitialise/Make/options
+++ b/applications/utilities/preProcessing/dsmcInitialise/Make/options
@@ -9,4 +9,3 @@ EXE_LIBS = \
     -lfiniteVolume \
     -llagrangian \
     -ldsmc
-
diff --git a/applications/utilities/preProcessing/mapFields/MapLagrangianFields.H b/applications/utilities/preProcessing/mapFields/MapLagrangianFields.H
index 3b9960408c4..87b3746484a 100644
--- a/applications/utilities/preProcessing/mapFields/MapLagrangianFields.H
+++ b/applications/utilities/preProcessing/mapFields/MapLagrangianFields.H
@@ -37,6 +37,7 @@ Description
 #include "GeometricField.H"
 #include "meshToMesh.H"
 #include "IOobjectList.H"
+#include "IOFieldField.H"
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
@@ -56,37 +57,118 @@ void MapLagrangianFields
 {
     const fvMesh& meshTarget = meshToMeshInterp.toMesh();
 
-    IOobjectList fields = objects.lookupClass(IOField<Type>::typeName);
-
-    forAllIter(IOobjectList, fields, fieldIter)
     {
-        Info<< "    mapping lagrangian field " << fieldIter()->name() << endl;
+        IOobjectList fields = objects.lookupClass(IOField<Type>::typeName);
+
+        forAllIter(IOobjectList, fields, fieldIter)
+        {
+            Info<< "    mapping lagrangian field "
+                << fieldIter()->name() << endl;
 
-        // Read field (does not need mesh)
-        IOField<Type> fieldSource(*fieldIter());
+            // Read field (does not need mesh)
+            IOField<Type> fieldSource(*fieldIter());
 
-        // Map
-        IOField<Type> fieldTarget
-        (
-            IOobject
+            // Map
+            IOField<Type> fieldTarget
             (
-                fieldIter()->name(),
-                meshTarget.time().timeName(),
-                cloud::prefix/cloudName,
-                meshTarget,
-                IOobject::NO_READ,
-                IOobject::NO_WRITE,
-                false
-            ),
-            addParticles.size()
-        );
-        forAll(addParticles, i)
+                IOobject
+                (
+                    fieldIter()->name(),
+                    meshTarget.time().timeName(),
+                    cloud::prefix/cloudName,
+                    meshTarget,
+                    IOobject::NO_READ,
+                    IOobject::NO_WRITE,
+                    false
+                ),
+                addParticles.size()
+            );
+
+            forAll(addParticles, i)
+            {
+                fieldTarget[i] = fieldSource[addParticles[i]];
+            }
+
+            // Write field
+            fieldTarget.write();
+        }
+    }
+
+    {
+        IOobjectList fieldFields =
+            objects.lookupClass(IOField<Field<Type> >::typeName);
+
+        forAllIter(IOobjectList, fieldFields, fieldIter)
         {
-            fieldTarget[i] = fieldSource[addParticles[i]];
+            Info<< "    mapping lagrangian fieldField "
+                << fieldIter()->name() << endl;
+
+            // Read field (does not need mesh)
+            IOField<Field<Type> > fieldSource(*fieldIter());
+
+            // Map - use IOFieldField to automatically write in
+            // compact form for binary format.
+            IOFieldField<Field<Type>, Type> fieldTarget
+            (
+                IOobject
+                (
+                    fieldIter()->name(),
+                    meshTarget.time().timeName(),
+                    cloud::prefix/cloudName,
+                    meshTarget,
+                    IOobject::NO_READ,
+                    IOobject::NO_WRITE,
+                    false
+                ),
+                addParticles.size()
+            );
+
+            forAll(addParticles, i)
+            {
+                fieldTarget[i] = fieldSource[addParticles[i]];
+            }
+
+            // Write field
+            fieldTarget.write();
         }
+    }
 
-        // Write field
-        fieldTarget.write();
+    {
+        IOobjectList fieldFields =
+            objects.lookupClass(IOFieldField<Field<Type>, Type>::typeName);
+
+        forAllIter(IOobjectList, fieldFields, fieldIter)
+        {
+            Info<< "    mapping lagrangian fieldField "
+                << fieldIter()->name() << endl;
+
+            // Read field (does not need mesh)
+            IOFieldField<Field<Type>, Type> fieldSource(*fieldIter());
+
+            // Map
+            IOFieldField<Field<Type>, Type> fieldTarget
+            (
+                IOobject
+                (
+                    fieldIter()->name(),
+                    meshTarget.time().timeName(),
+                    cloud::prefix/cloudName,
+                    meshTarget,
+                    IOobject::NO_READ,
+                    IOobject::NO_WRITE,
+                    false
+                ),
+                addParticles.size()
+            );
+
+            forAll(addParticles, i)
+            {
+                fieldTarget[i] = fieldSource[addParticles[i]];
+            }
+
+            // Write field
+            fieldTarget.write();
+        }
     }
 }
 
diff --git a/applications/utilities/preProcessing/mapFields/mapLagrangian.C b/applications/utilities/preProcessing/mapFields/mapLagrangian.C
index 72e402b934c..17f03ea3403 100644
--- a/applications/utilities/preProcessing/mapFields/mapLagrangian.C
+++ b/applications/utilities/preProcessing/mapFields/mapLagrangian.C
@@ -38,12 +38,13 @@ static const scalar perturbFactor = 1E-6;
 
 // Special version of findCell that generates a cell guaranteed to be
 // compatible with tracking.
-static label findCell(const meshSearch& meshSearcher, const point& pt)
+static label findCell(const Cloud<passiveParticle>& cloud, const point& pt)
 {
-    const polyMesh& mesh = meshSearcher.mesh();
+    label cellI = -1;
+    label tetFaceI = -1;
+    label tetPtI = -1;
 
-    // Use tracking to find cell containing pt
-    label cellI = meshSearcher.findCell(pt);
+    cloud.findCellFacePt(pt, cellI, tetFaceI, tetPtI);
 
     if (cellI >= 0)
     {
@@ -54,16 +55,24 @@ static label findCell(const meshSearch& meshSearcher, const point& pt)
         // See if particle on face by finding nearest face and shifting
         // particle.
 
+        const polyMesh& mesh = cloud.pMesh();
+
+        meshSearch meshSearcher(mesh, false);
+
         label faceI = meshSearcher.findNearestBoundaryFace(pt);
 
         if (faceI >= 0)
         {
             const point& cc = mesh.cellCentres()[mesh.faceOwner()[faceI]];
+
             const point perturbPt = (1-perturbFactor)*pt+perturbFactor*cc;
 
-            return meshSearcher.findCell(perturbPt);
+            cloud.findCellFacePt(perturbPt, cellI, tetFaceI, tetPtI);
+
+            return cellI;
         }
     }
+
     return -1;
 }
 
@@ -207,8 +216,6 @@ void mapLagrangian(const meshToMesh& meshToMeshInterp)
 
             if (unmappedSource.size())
             {
-                meshSearch targetSearcher(meshTarget, false);
-
                 sourceParticleI = 0;
 
                 forAllIter(Cloud<passiveParticle>, sourceParcels, iter)
@@ -216,7 +223,7 @@ void mapLagrangian(const meshToMesh& meshToMeshInterp)
                     if (unmappedSource.found(sourceParticleI))
                     {
                         label targetCell =
-                            findCell(targetSearcher, iter().position());
+                            findCell(targetParcels, iter().position());
 
                         if (targetCell >= 0)
                         {
diff --git a/applications/utilities/surface/surfaceSubset/surfaceSubset.C b/applications/utilities/surface/surfaceSubset/surfaceSubset.C
index 51255242ef9..6bc54d40add 100644
--- a/applications/utilities/surface/surfaceSubset/surfaceSubset.C
+++ b/applications/utilities/surface/surfaceSubset/surfaceSubset.C
@@ -35,7 +35,6 @@ Description
 #include "IOdictionary.H"
 #include "boundBox.H"
 #include "indexedOctree.H"
-#include "octree.H"
 #include "treeDataTriSurface.H"
 #include "Random.H"
 
diff --git a/etc/controlDict b/etc/controlDict
index 3d5aff59304..691e9701243 100644
--- a/etc/controlDict
+++ b/etc/controlDict
@@ -780,6 +780,7 @@ DebugSwitches
     syringePressure     0;
     tensorAverageField  0;
     tensorField         0;
+    tetDecomposedPolyMesh 0;
     thermoCloud         0;
     thermophysicalFunction 0;
     time                0;
diff --git a/src/Allwmake b/src/Allwmake
index 33966ddb97b..43458cfc621 100755
--- a/src/Allwmake
+++ b/src/Allwmake
@@ -20,8 +20,6 @@ Pstream/Allwmake
 OSspecific/$WM_OSTYPE/Allwmake
 wmake libso OpenFOAM
 
-wmake libso lagrangian/basic
-
 wmake libso fileFormats
 wmake libso edgeMesh
 wmake libso surfMesh
@@ -33,6 +31,7 @@ parallel/decompose/AllwmakeLnInclude
 dummyThirdParty/Allwmake
 
 wmake libso meshTools
+wmake libso lagrangian/basic
 wmake libso finiteVolume
 wmake libso genericPatchFields
 
diff --git a/src/OpenFOAM/Make/files b/src/OpenFOAM/Make/files
index e9103f8020e..599d30fa094 100644
--- a/src/OpenFOAM/Make/files
+++ b/src/OpenFOAM/Make/files
@@ -365,6 +365,8 @@ $(globalMeshData)/globalPoints.C
 $(globalMeshData)/globalIndex.C
 
 $(polyMesh)/syncTools/syncTools.C
+$(polyMesh)/polyMeshTetDecomposition/polyMeshTetDecomposition.C
+$(polyMesh)/polyMeshTetDecomposition/tetIndices.C
 
 zone = $(polyMesh)/zones/zone
 $(zone)/zone.C
diff --git a/src/OpenFOAM/meshes/polyMesh/polyMesh.C b/src/OpenFOAM/meshes/polyMesh/polyMesh.C
index 2cbde73f8b3..668320e1d6d 100644
--- a/src/OpenFOAM/meshes/polyMesh/polyMesh.C
+++ b/src/OpenFOAM/meshes/polyMesh/polyMesh.C
@@ -33,6 +33,7 @@ License
 #include "processorPolyPatch.H"
 #include "OSspecific.H"
 #include "demandDrivenData.H"
+#include "polyMeshTetDecomposition.H"
 
 #include "pointMesh.H"
 
@@ -204,6 +205,7 @@ Foam::polyMesh::polyMesh(const IOobject& io)
     bounds_(points_),
     geometricD_(Vector<label>::zero),
     solutionD_(Vector<label>::zero),
+    tetBasePtIsPtr_(NULL),
     pointZones_
     (
         IOobject
@@ -392,6 +394,7 @@ Foam::polyMesh::polyMesh
     bounds_(points_, syncPar),
     geometricD_(Vector<label>::zero),
     solutionD_(Vector<label>::zero),
+    tetBasePtIsPtr_(NULL),
     pointZones_
     (
         IOobject
@@ -548,6 +551,7 @@ Foam::polyMesh::polyMesh
     bounds_(points_, syncPar),
     geometricD_(Vector<label>::zero),
     solutionD_(Vector<label>::zero),
+    tetBasePtIsPtr_(NULL),
     pointZones_
     (
         IOobject
@@ -847,6 +851,28 @@ Foam::label Foam::polyMesh::nSolutionD() const
 }
 
 
+const Foam::labelList& Foam::polyMesh::tetBasePtIs() const
+{
+    if (!tetBasePtIsPtr_)
+    {
+        if (debug)
+        {
+            WarningIn("const labelList& polyMesh::tetBasePtIs() const")
+                << "Tet base point indices not available.  "
+                << "Forcing storage of base points."
+                << endl;
+        }
+
+        tetBasePtIsPtr_ = new labelList
+        (
+            polyMeshTetDecomposition::findFaceBasePts(*this)
+        );
+    }
+
+    return *tetBasePtIsPtr_;
+}
+
+
 // Add boundary patches. Constructor helper
 void Foam::polyMesh::addPatches
 (
diff --git a/src/OpenFOAM/meshes/polyMesh/polyMesh.H b/src/OpenFOAM/meshes/polyMesh/polyMesh.H
index ec57faa9fbd..a100a358e15 100644
--- a/src/OpenFOAM/meshes/polyMesh/polyMesh.H
+++ b/src/OpenFOAM/meshes/polyMesh/polyMesh.H
@@ -62,6 +62,7 @@ namespace Foam
 
 class globalMeshData;
 class mapPolyMesh;
+class polyMeshTetDecomposition;
 
 /*---------------------------------------------------------------------------*\
                           Class polyMesh Declaration
@@ -127,6 +128,9 @@ private:
             //  defined according to the presence of empty patches
             mutable Vector<label> solutionD_;
 
+            //- Base point for face decomposition into tets
+            mutable labelList* tetBasePtIsPtr_;
+
 
         // Zoning information
 
@@ -356,6 +360,9 @@ public:
             //- Return the number of valid solved-for dimensions in the mesh
             label nSolutionD() const;
 
+            //- Return the tetBasePtIs
+            const labelList& tetBasePtIs() const;
+
             //- Return point zone mesh
             const pointZoneMesh& pointZones() const
             {
diff --git a/src/OpenFOAM/meshes/polyMesh/polyMeshClear.C b/src/OpenFOAM/meshes/polyMesh/polyMeshClear.C
index 187d3c128fa..56c2f2428cd 100644
--- a/src/OpenFOAM/meshes/polyMesh/polyMeshClear.C
+++ b/src/OpenFOAM/meshes/polyMesh/polyMeshClear.C
@@ -71,6 +71,9 @@ void Foam::polyMesh::clearGeom()
     geometricD_ = Vector<label>::zero;
     solutionD_ = Vector<label>::zero;
 
+    // Remove the stored tet base points
+    deleteDemandDrivenData(tetBasePtIsPtr_);
+
     pointMesh::Delete(*this);
 }
 
diff --git a/src/OpenFOAM/meshes/polyMesh/polyMeshFromShapeMesh.C b/src/OpenFOAM/meshes/polyMesh/polyMeshFromShapeMesh.C
index 0f35692b0ce..73434502f45 100644
--- a/src/OpenFOAM/meshes/polyMesh/polyMeshFromShapeMesh.C
+++ b/src/OpenFOAM/meshes/polyMesh/polyMeshFromShapeMesh.C
@@ -503,6 +503,7 @@ Foam::polyMesh::polyMesh
     bounds_(points_, syncPar),
     geometricD_(Vector<label>::zero),
     solutionD_(Vector<label>::zero),
+    tetBasePtIsPtr_(NULL),
     pointZones_
     (
         IOobject
@@ -775,6 +776,7 @@ Foam::polyMesh::polyMesh
     bounds_(points_, syncPar),
     geometricD_(Vector<label>::zero),
     solutionD_(Vector<label>::zero),
+    tetBasePtIsPtr_(NULL),
     pointZones_
     (
         IOobject
diff --git a/src/OpenFOAM/meshes/polyMesh/polyMeshTetDecomposition/polyMeshTetDecomposition.C b/src/OpenFOAM/meshes/polyMesh/polyMeshTetDecomposition/polyMeshTetDecomposition.C
new file mode 100644
index 00000000000..23e7a3c52e2
--- /dev/null
+++ b/src/OpenFOAM/meshes/polyMesh/polyMeshTetDecomposition/polyMeshTetDecomposition.C
@@ -0,0 +1,627 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2010-2010 OpenCFD Ltd.
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+License
+    This file is part of OpenFOAM.
+
+    OpenFOAM is free software: you can redistribute it and/or modify it
+    under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
+    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+    for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
+
+\*---------------------------------------------------------------------------*/
+
+#include "polyMeshTetDecomposition.H"
+
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
+
+const Foam::scalar Foam::polyMeshTetDecomposition::minTetQuality = 1e-9;
+
+
+// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
+
+Foam::label Foam::polyMeshTetDecomposition::findSharedBasePoint
+(
+    const polyMesh& mesh,
+    label fI,
+    const point& nCc,
+    scalar tol,
+    bool report
+)
+{
+    const faceList& pFaces = mesh.faces();
+    const pointField& pPts = mesh.points();
+    const vectorField& pC = mesh.cellCentres();
+    const labelList& pOwner = mesh.faceOwner();
+
+    const face& f = pFaces[fI];
+
+    label oCI = pOwner[fI];
+
+    const point& oCc = pC[oCI];
+
+    forAll(f, faceBasePtI)
+    {
+        scalar thisBaseMinTetQuality = VGREAT;
+
+        const point& tetBasePt = pPts[f[faceBasePtI]];
+
+        for (label tetPtI = 1; tetPtI < f.size() - 1; tetPtI++)
+        {
+            label facePtI = (tetPtI + faceBasePtI) % f.size();
+            label otherFacePtI = f.fcIndex(facePtI);
+
+            List<scalar> tetQualities(2, 0.0);
+
+            {
+                // owner cell tet
+                label ptAI = f[facePtI];
+                label ptBI = f[otherFacePtI];
+
+                const point& pA = pPts[ptAI];
+                const point& pB = pPts[ptBI];
+
+                tetPointRef tet(oCc, tetBasePt, pA, pB);
+
+                tetQualities[0] = tet.quality();
+            }
+
+            {
+                // neighbour cell tet
+                label ptAI = f[otherFacePtI];
+                label ptBI = f[facePtI];
+
+                const point& pA = pPts[ptAI];
+                const point& pB = pPts[ptBI];
+
+                tetPointRef tet(nCc, tetBasePt, pA, pB);
+
+                tetQualities[1] = tet.quality();
+            }
+
+            if (min(tetQualities) < thisBaseMinTetQuality)
+            {
+                thisBaseMinTetQuality = min(tetQualities);
+            }
+        }
+
+        if (thisBaseMinTetQuality > tol)
+        {
+            return faceBasePtI;
+        }
+
+    }
+
+    // If a base point hasn't triggered a return by now, then there is
+    // non that can produce a good decomposition
+    return -1;
+}
+
+
+Foam::label Foam::polyMeshTetDecomposition::findSharedBasePoint
+(
+    const polyMesh& mesh,
+    label fI,
+    scalar tol,
+    bool report
+)
+{
+    return findSharedBasePoint
+    (
+        mesh,
+        fI,
+        mesh.cellCentres()[mesh.faceNeighbour()[fI]],
+        tol,
+        report
+    );
+}
+
+
+Foam::label Foam::polyMeshTetDecomposition::findBasePoint
+(
+    const polyMesh& mesh,
+    label fI,
+    scalar tol,
+    bool report
+)
+{
+    const faceList& pFaces = mesh.faces();
+    const pointField& pPts = mesh.points();
+    const vectorField& pC = mesh.cellCentres();
+    const labelList& pOwner = mesh.faceOwner();
+
+    const face& f = pFaces[fI];
+
+    label cI = pOwner[fI];
+
+    bool own = (pOwner[fI] == cI);
+
+    const point& cC = pC[cI];
+
+    forAll(f, faceBasePtI)
+    {
+        scalar thisBaseMinTetQuality = VGREAT;
+
+        const point& tetBasePt = pPts[f[faceBasePtI]];
+
+        for (label tetPtI = 1; tetPtI < f.size() - 1; tetPtI++)
+        {
+            label facePtI = (tetPtI + faceBasePtI) % f.size();
+            label otherFacePtI = f.fcIndex(facePtI);
+
+            label ptAI = -1;
+            label ptBI = -1;
+
+            if (own)
+            {
+                ptAI = f[facePtI];
+                ptBI = f[otherFacePtI];
+            }
+            else
+            {
+                ptAI = f[otherFacePtI];
+                ptBI = f[facePtI];
+            }
+
+            const point& pA = pPts[ptAI];
+            const point& pB = pPts[ptBI];
+
+            tetPointRef tet(cC, tetBasePt, pA, pB);
+
+            scalar tetQuality = tet.quality();
+
+            if (tetQuality < thisBaseMinTetQuality)
+            {
+                thisBaseMinTetQuality = tetQuality;
+            }
+        }
+
+        if (thisBaseMinTetQuality > tol)
+        {
+            return faceBasePtI;
+        }
+    }
+
+    // If a base point hasn't triggered a return by now, then there is
+    // non that can produce a good decomposition
+    return -1;
+}
+
+
+Foam::labelList Foam::polyMeshTetDecomposition::findFaceBasePts
+(
+    const polyMesh& mesh,
+    scalar tol,
+    bool report
+)
+{
+    const labelList& pOwner = mesh.faceOwner();
+
+    // Find a suitable base point for each face, considering both
+    // cells for interface faces or those on coupled patches
+
+    labelList tetBasePtIs(mesh.nFaces(), -1);
+
+    label nInternalFaces = mesh.nInternalFaces();
+
+    for (label fI = 0; fI < nInternalFaces; fI++)
+    {
+        tetBasePtIs[fI] = findSharedBasePoint(mesh, fI, tol, report);
+    }
+
+    pointField neighbourCellCentres(mesh.nFaces() - nInternalFaces);
+
+    for(label faceI = nInternalFaces; faceI < mesh.nFaces(); faceI++)
+    {
+        neighbourCellCentres[faceI - nInternalFaces] =
+            mesh.cellCentres()[pOwner[faceI]];
+    }
+
+    syncTools::swapBoundaryFacePositions(mesh, neighbourCellCentres);
+
+    const polyBoundaryMesh& patches = mesh.boundaryMesh();
+
+    SubList<label> boundaryFaceTetBasePtIs
+    (
+        tetBasePtIs,
+        mesh.nFaces() - nInternalFaces,
+        nInternalFaces
+    );
+
+    for
+    (
+        label fI = nInternalFaces, bFI = 0;
+        fI < mesh.nFaces();
+        fI++, bFI++
+    )
+    {
+        label patchI =
+            mesh.boundaryMesh().patchID()[bFI];
+
+        if (patches[patchI].coupled())
+        {
+            const coupledPolyPatch& pp =
+                refCast<const coupledPolyPatch>(patches[patchI]);
+
+            if (pp.owner())
+            {
+                boundaryFaceTetBasePtIs[bFI] = findSharedBasePoint
+                (
+                    mesh,
+                    fI,
+                    neighbourCellCentres[bFI],
+                    tol,
+                    report
+                );
+            }
+            else
+            {
+                // Assign -2, to distinguish from a failed base point
+                // find, which returns -1.
+                boundaryFaceTetBasePtIs[bFI] = -2;
+            }
+        }
+        else
+        {
+            boundaryFaceTetBasePtIs[bFI] = findBasePoint
+            (
+                mesh,
+                fI,
+                tol,
+                report
+            );
+        }
+    }
+
+    // maxEqOp will replace the -2 values on the neighbour patches
+    // with the result from the owner base point find.
+
+    syncTools::syncBoundaryFaceList
+    (
+        mesh,
+        boundaryFaceTetBasePtIs,
+        maxEqOp<label>()
+    );
+
+    for
+    (
+        label fI = nInternalFaces, bFI = 0;
+        fI < mesh.nFaces();
+        fI++, bFI++
+    )
+    {
+        label& bFTetBasePtI = boundaryFaceTetBasePtIs[bFI];
+
+        if (bFTetBasePtI == -2)
+        {
+            FatalErrorIn
+            (
+                "Foam::labelList"
+                "Foam::polyMeshTetDecomposition::findFaceBasePts"
+                "("
+                    "const polyMesh& mesh, "
+                    "scalar tol, "
+                    "bool report"
+                ")"
+            )
+                << "Coupled face base point exchange failure for face "
+                << fI
+                << abort(FatalError);
+        }
+
+        if (bFTetBasePtI < 1)
+        {
+            // If the base point is -1, it should be left as such to
+            // indicate a problem, if it is 0, then no action is required.
+
+            continue;
+        }
+
+        label patchI = mesh.boundaryMesh().patchID()[bFI];
+
+        if (patches[patchI].coupled())
+        {
+            const coupledPolyPatch& pp =
+                refCast<const coupledPolyPatch>(patches[patchI]);
+
+            // Calculated base points on coupled faces are those of
+            // the owner patch face. They need to be reindexed to for
+            // the non-owner face, which has the opposite order.
+
+            // So, for fPtI_o != 0, fPtI_n = f.size() - fPtI_o
+
+            // i.e.:
+
+            // owner coupledPolyPatch face
+            // face    (a b c d e f)
+            // fPtI     0 1 2 3 4 5
+            //            +
+            //              #
+
+            // neighbour coupledPolyPatch face
+            // face    (a f e d c b)
+            // fPtI     0 1 2 3 4 5
+            //                    +
+            //                  #
+            // +: 6 - 1 = 5
+            // #: 6 - 2 = 4
+
+            if (!pp.owner())
+            {
+                bFTetBasePtI = mesh.faces()[fI].size() - bFTetBasePtI;
+            }
+        }
+    }
+
+    return tetBasePtIs;
+}
+
+
+bool Foam::polyMeshTetDecomposition::checkFaceTets
+(
+    const polyMesh& mesh,
+    scalar tol,
+    const bool report,
+    labelHashSet* setPtr
+)
+{
+    const labelList& own = mesh.faceOwner();
+    const labelList& nei = mesh.faceNeighbour();
+    const polyBoundaryMesh& patches = mesh.boundaryMesh();
+
+    const vectorField& cc = mesh.cellCentres();
+    const vectorField& fc = mesh.faceCentres();
+
+    // Calculate coupled cell centre
+    pointField neiCc(mesh.nFaces() - mesh.nInternalFaces());
+
+    for (label faceI = mesh.nInternalFaces(); faceI < mesh.nFaces(); faceI++)
+    {
+        neiCc[faceI - mesh.nInternalFaces()] = cc[own[faceI]];
+    }
+
+    syncTools::swapBoundaryFacePositions(mesh, neiCc);
+
+    const faceList& fcs = mesh.faces();
+
+    const pointField& p = mesh.points();
+
+    label nErrorTets = 0;
+
+    forAll(fcs, faceI)
+    {
+        const face& f = fcs[faceI];
+
+        forAll(f, fPtI)
+        {
+            scalar tetQual = tetPointRef
+            (
+                p[f[fPtI]],
+                p[f.nextLabel(fPtI)],
+                fc[faceI],
+                cc[own[faceI]]
+            ).quality();
+
+            if (tetQual > -tol)
+            {
+                if (setPtr)
+                {
+                    setPtr->insert(faceI);
+                }
+
+                nErrorTets++;
+                break;              // no need to check other tets
+            }
+        }
+
+        if (mesh.isInternalFace(faceI))
+        {
+            // Create the neighbour tet - it will have positive volume
+            const face& f = fcs[faceI];
+
+            forAll(f, fPtI)
+            {
+                scalar tetQual = tetPointRef
+                (
+                    p[f[fPtI]],
+                    p[f.nextLabel(fPtI)],
+                    fc[faceI],
+                    cc[nei[faceI]]
+                ).quality();
+
+                if (tetQual < tol)
+                {
+                    if (setPtr)
+                    {
+                        setPtr->insert(faceI);
+                    }
+
+                    nErrorTets++;
+                    break;
+                }
+            }
+
+            if (findSharedBasePoint(mesh, faceI, tol, report) == -1)
+            {
+                if (setPtr)
+                {
+                    setPtr->insert(faceI);
+                }
+
+                nErrorTets++;
+            }
+        }
+        else
+        {
+            label patchI = patches.patchID()[faceI - mesh.nInternalFaces()];
+
+            if (patches[patchI].coupled())
+            {
+                if
+                (
+                    findSharedBasePoint
+                    (
+                        mesh,
+                        faceI,
+                        neiCc[faceI - mesh.nInternalFaces()],
+                        tol,
+                        report
+                    ) == -1
+                )
+                {
+                    if (setPtr)
+                    {
+                        setPtr->insert(faceI);
+                    }
+
+                    nErrorTets++;
+                }
+            }
+            else
+            {
+                if (findBasePoint(mesh, faceI, tol, report) == -1)
+                {
+                    if (setPtr)
+                    {
+                        setPtr->insert(faceI);
+                    }
+
+                    nErrorTets++;
+                }
+            }
+        }
+    }
+
+    reduce(nErrorTets, sumOp<label>());
+
+    if (nErrorTets > 0)
+    {
+        if (report)
+        {
+            Info<< " ***Error in face tets: "
+                << nErrorTets << " faces with low quality or negative volume "
+                << "decomposition tets." << endl;
+        }
+
+        return true;
+    }
+    else
+    {
+        if (report)
+        {
+            Info<< "    Face tets OK." << endl;
+        }
+
+        return false;
+    }
+}
+
+
+Foam::List<Foam::tetIndices> Foam::polyMeshTetDecomposition::faceTetIndices
+(
+    const polyMesh& mesh,
+    label fI,
+    label cI
+)
+{
+    const faceList& pFaces = mesh.faces();
+    const labelList& pOwner = mesh.faceOwner();
+
+    const face& f = pFaces[fI];
+
+    label nTets = f.size() - 2;
+
+    List<tetIndices> faceTets(nTets);
+
+    bool own = (pOwner[fI] == cI);
+
+    label tetBasePtI = mesh.tetBasePtIs()[fI];
+
+    if (tetBasePtI == -1)
+    {
+        FatalErrorIn
+        (
+            "Foam::List<Foam::FixedList<Foam::label, 4> >"
+            "Foam::Cloud<ParticleType>::"
+            "faceTetIndices(label fI, label cI) const"
+        )
+        << "No base point for face " << fI << ", " << f
+            << ", produces a valid tet decomposition."
+            << abort(FatalError);
+    }
+
+    for (label tetPtI = 1; tetPtI < f.size() - 1; tetPtI++)
+    {
+        tetIndices& faceTetIs = faceTets[tetPtI - 1];
+
+        label facePtI = (tetPtI + tetBasePtI) % f.size();
+        label otherFacePtI = f.fcIndex(facePtI);
+
+        faceTetIs.cell() = cI;
+
+        faceTetIs.face() = fI;
+
+        faceTetIs.faceBasePt() = tetBasePtI;
+
+        if (own)
+        {
+            faceTetIs.facePtA() = facePtI;
+            faceTetIs.facePtB() = otherFacePtI;
+        }
+        else
+        {
+            faceTetIs.facePtA() = otherFacePtI;
+            faceTetIs.facePtB() = facePtI;
+        }
+
+        faceTetIs.tetPt() = tetPtI;
+    }
+
+    return faceTets;
+}
+
+
+Foam::List<Foam::tetIndices> Foam::polyMeshTetDecomposition::cellTetIndices
+(
+    const polyMesh& mesh,
+    label cI
+)
+{
+    const faceList& pFaces = mesh.faces();
+    const cellList& pCells = mesh.cells();
+
+    const cell& thisCell = pCells[cI];
+
+    label nTets = 0;
+
+    forAll(thisCell, cFI)
+    {
+        nTets += pFaces[thisCell[cFI]].size() - 2;
+    }
+
+    DynamicList<tetIndices> cellTets(nTets);
+
+    forAll(thisCell, cFI)
+    {
+        label fI = thisCell[cFI];
+
+        cellTets.append(faceTetIndices(mesh, fI, cI));
+    }
+
+    return cellTets;
+}
+
+
+// ************************************************************************* //
diff --git a/src/OpenFOAM/meshes/polyMesh/polyMeshTetDecomposition/polyMeshTetDecomposition.H b/src/OpenFOAM/meshes/polyMesh/polyMeshTetDecomposition/polyMeshTetDecomposition.H
new file mode 100644
index 00000000000..cf2ea5e2971
--- /dev/null
+++ b/src/OpenFOAM/meshes/polyMesh/polyMeshTetDecomposition/polyMeshTetDecomposition.H
@@ -0,0 +1,151 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2010-2010 OpenCFD Ltd.
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+License
+    This file is part of OpenFOAM.
+
+    OpenFOAM is free software: you can redistribute it and/or modify it
+    under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
+    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+    for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
+
+Class
+    Foam::polyMeshTetDecomposition
+
+Description
+    Tools for performing the minimum decomposition of faces of the
+    mesh into triangles so that the cells may be tet decomposed.
+    Includes functions for finding variable face starting (base)
+    points on each face to avoid the decomposition of cells into tets
+    that have negative or zero volume.
+
+SourceFiles
+    polyMeshTetDecomposition.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef polyMeshTetDecomposition_H
+#define polyMeshTetDecomposition_H
+
+#include "polyMesh.H"
+#include "coupledPolyPatch.H"
+#include "syncTools.H"
+#include "tetPointRef.H"
+#include "tetIndices.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+/*---------------------------------------------------------------------------*\
+                Class polyMeshTetDecomposition Declaration
+\*---------------------------------------------------------------------------*/
+
+class polyMeshTetDecomposition
+{
+
+public:
+
+    // Static data members
+
+        //- Minimum tetrahedron quality
+        static const scalar minTetQuality;
+
+
+    // Member Functions
+
+        //- Find the first suitable base point to use for a minimum
+        //  triangle decomposition of the face, suiting owner and
+        //  neighbour cells.  Finds the first base point on the face
+        //  whose worst quality tet from either cell is better than
+        //  tolerance.  Neighbour cell centre supplied.  For coupled
+        //  patches.
+        static label findSharedBasePoint
+        (
+            const polyMesh& mesh,
+            label fI,
+            const point& nCc,
+            scalar tol,
+            bool report = false
+        );
+
+        //- As for findSharedBasePoint, but using neighbour cell
+        //  centre from the mesh.  For internal faces.
+        static label findSharedBasePoint
+        (
+            const polyMesh& mesh,
+            label fI,
+            scalar tol,
+            bool report = false
+        );
+
+        //- Find the base point to use for a minimum triangle
+        //  decomposition of the face, using only the owner
+        //  information.  For non-coupled boundary faces.
+        static label findBasePoint
+        (
+            const polyMesh& mesh,
+            label fI,
+            scalar tol,
+            bool report = false
+        );
+
+        //- Find a suitable base point for each face for decomposition
+        //  into tets
+        static labelList findFaceBasePts
+        (
+            const polyMesh& mesh,
+            scalar tol = minTetQuality,
+            bool report = false
+        );
+
+        //- Check face-decomposition tet volume
+        static bool checkFaceTets
+        (
+            const polyMesh& mesh,
+            scalar tol = minTetQuality,
+            const bool report = false,
+            labelHashSet* setPtr = NULL
+        );
+
+        //- Return the tet decomposition of the given face, with
+        //  respect to the given cell
+        static List<tetIndices> faceTetIndices
+        (
+            const polyMesh& mesh,
+            label fI,
+            label cI
+        );
+
+        //- Return the tet decomposition of the given cell, see
+        //  findFacePt for the meaning of the indices
+        static List<tetIndices> cellTetIndices
+        (
+            const polyMesh& mesh,
+            label cI
+        );
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/OpenFOAM/meshes/polyMesh/polyMeshTetDecomposition/tetIndices.C b/src/OpenFOAM/meshes/polyMesh/polyMeshTetDecomposition/tetIndices.C
new file mode 100644
index 00000000000..163fccf49f3
--- /dev/null
+++ b/src/OpenFOAM/meshes/polyMesh/polyMeshTetDecomposition/tetIndices.C
@@ -0,0 +1,148 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2010-2010 OpenCFD Ltd.
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+License
+    This file is part of OpenFOAM.
+
+    OpenFOAM is free software: you can redistribute it and/or modify it
+    under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
+    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+    for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
+
+\*---------------------------------------------------------------------------*/
+
+#include "tetIndices.H"
+
+// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
+
+Foam::tetIndices::tetIndices()
+:
+    cellI_(-1),
+    faceI_(-1),
+    faceBasePtI_(-1),
+    facePtAI_(-1),
+    facePtBI_(-1),
+    tetPtI_(-1)
+{}
+
+
+Foam::tetIndices::tetIndices
+(
+    label cellI,
+    label faceI,
+    label faceBasePtI,
+    label facePtAI,
+    label facePtBI,
+    label tetPtI
+)
+:
+    cellI_(cellI),
+    faceI_(faceI),
+    faceBasePtI_(faceBasePtI),
+    facePtAI_(facePtAI),
+    facePtBI_(facePtBI),
+    tetPtI_(tetPtI)
+{}
+
+
+Foam::tetIndices::tetIndices
+(
+    label cellI,
+    label faceI,
+    label tetPtI,
+    const polyMesh& mesh
+)
+:
+    cellI_(cellI),
+    faceI_(faceI),
+    faceBasePtI_(-1),
+    facePtAI_(-1),
+    facePtBI_(-1),
+    tetPtI_(tetPtI)
+{
+    const faceList& pFaces = mesh.faces();
+    const labelList& pOwner = mesh.faceOwner();
+
+    const Foam::face& f = pFaces[faceI_];
+
+    bool own = (pOwner[faceI_] == cellI_);
+
+    faceBasePtI_ = mesh.tetBasePtIs()[faceI_];
+
+    label facePtI = (tetPtI_ + faceBasePtI_) % f.size();
+    label otherFacePtI = f.fcIndex(facePtI);
+
+    if (own)
+    {
+        facePtAI_ = facePtI;
+        facePtBI_ = otherFacePtI;
+    }
+    else
+    {
+        facePtAI_ = otherFacePtI;
+        facePtBI_ = facePtI;
+    }
+}
+
+
+// * * * * * * * * * * * * * * * * Destructor  * * * * * * * * * * * * * * * //
+
+Foam::tetIndices::~tetIndices()
+{}
+
+
+// * * * * * * * * * * * * * * * IOstream Operators  * * * * * * * * * * * * //
+
+Foam::Istream& Foam::operator>>(Istream& is, tetIndices& tI)
+{
+    is  >> tI.cell()
+        >> tI.face()
+        >> tI.faceBasePt()
+        >> tI.facePtA()
+        >> tI.facePtB()
+        >> tI.tetPt();
+
+    // Check state of Istream
+    is.check
+    (
+        "Foam::Istream& Foam::operator>>(Foam::Istream&, Foam::tetIndices&)"
+    );
+
+    return is;
+}
+
+
+Foam::Ostream& Foam::operator<<(Ostream& os, const tetIndices& tI)
+{
+    os  << tI.cell() << token::SPACE
+        << tI.face() << token::SPACE
+        << tI.faceBasePt() << token::SPACE
+        << tI.facePtA() << token::SPACE
+        << tI.facePtB() << token::SPACE
+        << tI.tetPt() << token::SPACE
+        << endl;
+
+    // Check state of Ostream
+    os.check
+    (
+        "Foam::Ostream& Foam::operator<<(Foam::Ostream&, "
+        "const Foam::tetIndices&)"
+    );
+
+    return os;
+}
+
+
+// ************************************************************************* //
diff --git a/src/OpenFOAM/meshes/polyMesh/polyMeshTetDecomposition/tetIndices.H b/src/OpenFOAM/meshes/polyMesh/polyMeshTetDecomposition/tetIndices.H
new file mode 100644
index 00000000000..3e5ee425d6e
--- /dev/null
+++ b/src/OpenFOAM/meshes/polyMesh/polyMeshTetDecomposition/tetIndices.H
@@ -0,0 +1,201 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2010-2010 OpenCFD Ltd.
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+License
+    This file is part of OpenFOAM.
+
+    OpenFOAM is free software: you can redistribute it and/or modify it
+    under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
+    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+    for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
+
+Class
+    Foam::tetIndices
+
+Description
+    Storage and named access for the indices of a tet which is part of
+    the decomposition of a cell.
+
+SourceFiles
+    tetIndicesI.H
+    tetIndices.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef tetIndices_H
+#define tetIndices_H
+
+#include "label.H"
+#include "tetPointRef.H"
+#include "triPointRef.H"
+#include "polyMesh.H"
+#include "face.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+/*---------------------------------------------------------------------------*\
+                         Class tetIndices Declaration
+\*---------------------------------------------------------------------------*/
+
+class tetIndices
+{
+    // Private data
+
+    //- cell that this is a decomposed tet of
+    label cellI_;
+
+    //- face that holds this decomposed tet
+    label faceI_;
+
+    //- base point on the face
+    label faceBasePtI_;
+
+    //- point on the face such that the right-hand circulation
+    //      {faceBasePtI_, facePtIA_, facePtBI_}
+    //  forms a triangle that points out of the tet
+    label facePtAI_;
+
+    //- point on the face such that the right-hand circulation
+    //      {faceBasePtI_, facePtIA_, facePtBI_}
+    //  forms a triangle that points out of the tet
+    label facePtBI_;
+
+    //- point on the face, *relative to the base point*, which
+    //  characterises this tet on the face.
+    label tetPtI_;
+
+
+public:
+
+    // Constructors
+
+        //- Construct null
+        tetIndices();
+
+        //- Construct from components
+        tetIndices
+        (
+            label cellI,
+            label faceI,
+            label faceBasePtI,
+            label facePtAI,
+            label facePtBI,
+            label tetPtI
+        );
+
+        //- Construct from cell, face, pt description of tet
+        tetIndices
+        (
+            label cellI,
+            label faceI,
+            label tetPtI,
+            const polyMesh& mesh
+        );
+
+
+    //- Destructor
+    ~tetIndices();
+
+
+    // Member Functions
+
+        // Access
+
+            //- Return the cell
+            inline label cell() const;
+
+            //- Return the face
+            inline label face() const;
+
+            //- Return the face base point
+            inline label faceBasePt() const;
+
+            //- Return face point A
+            inline label facePtA() const;
+
+            //- Return face point B
+            inline label facePtB() const;
+
+            //- Return the characterising tetPtI
+            inline label tetPt() const;
+
+            //- Return the geometry corresponding to this tet from the
+            //  supplied mesh
+            inline tetPointRef tet(const polyMesh& mesh) const;
+
+            //- Return the geometry corresponding to this tet from the
+            //  supplied mesh using the old positions
+            inline tetPointRef oldTet(const polyMesh& mesh) const;
+
+            //- Return the geometry corresponding to the tri on the
+            //  mesh face for this tet from the supplied mesh
+            inline triPointRef faceTri(const polyMesh& mesh) const;
+
+            //- Return the geometry corresponding to the tri on the
+            //  mesh face for this tet from the supplied mesh using
+            //  the old position
+            inline triPointRef oldFaceTri(const polyMesh& mesh) const;
+
+
+        // Edit
+
+            //- Return non-const access to the cell
+            inline label& cell();
+
+            //- Return non-const access to the face
+            inline label& face();
+
+            //- Return non-const access to the face base point
+            inline label& faceBasePt();
+
+            //- Return non-const access to face point A
+            inline label& facePtA();
+
+            //- Return non-const access to face point B
+            inline label& facePtB();
+
+            //- Return non-const access to the characterising tetPtI
+            inline label& tetPt();
+
+
+    // Member Operators
+
+        inline bool operator==(const tetIndices&) const;
+        inline bool operator!=(const tetIndices&) const;
+
+
+    // IOstream Operators
+
+        friend Istream& operator>>(Istream&, tetIndices&);
+        friend Ostream& operator<<(Ostream&, const tetIndices&);
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#include "tetIndicesI.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/OpenFOAM/meshes/polyMesh/polyMeshTetDecomposition/tetIndicesI.H b/src/OpenFOAM/meshes/polyMesh/polyMeshTetDecomposition/tetIndicesI.H
new file mode 100644
index 00000000000..6b8a7871a04
--- /dev/null
+++ b/src/OpenFOAM/meshes/polyMesh/polyMeshTetDecomposition/tetIndicesI.H
@@ -0,0 +1,202 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2010-2010 OpenCFD Ltd.
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+License
+    This file is part of OpenFOAM.
+
+    OpenFOAM is free software: you can redistribute it and/or modify it
+    under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
+    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+    for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
+
+\*---------------------------------------------------------------------------*/
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+Foam::label Foam::tetIndices::cell() const
+{
+    return cellI_;
+}
+
+
+Foam::label Foam::tetIndices::face() const
+{
+    return faceI_;
+}
+
+
+Foam::label Foam::tetIndices::faceBasePt() const
+{
+    return faceBasePtI_;
+}
+
+
+Foam::label Foam::tetIndices::facePtA() const
+{
+    return facePtAI_;
+}
+
+
+Foam::label Foam::tetIndices::facePtB() const
+{
+    return facePtBI_;
+}
+
+
+Foam::label Foam::tetIndices::tetPt() const
+{
+    return tetPtI_;
+}
+
+
+Foam::tetPointRef Foam::tetIndices::tet(const polyMesh& mesh) const
+{
+    const pointField& pPts = mesh.points();
+    const faceList& pFaces = mesh.faces();
+    const vectorField& pC = mesh.cellCentres();
+
+    const Foam::face& f = pFaces[faceI_];
+
+    return tetPointRef
+    (
+        pC[cellI_],
+        pPts[f[faceBasePtI_]],
+        pPts[f[facePtAI_]],
+        pPts[f[facePtBI_]]
+    );
+}
+
+
+Foam::tetPointRef Foam::tetIndices::oldTet(const polyMesh& mesh) const
+{
+    const pointField& oldPPts = mesh.oldPoints();
+    const faceList& pFaces = mesh.faces();
+
+    // We need to reconstruct the old Cc from oldPoints (it isn't
+    // stored)
+    point oldC = mesh.cells()[cellI_].centre
+    (
+        oldPPts,
+        pFaces
+    );
+
+    const Foam::face& f = pFaces[faceI_];
+
+    return tetPointRef
+    (
+        oldC,
+        oldPPts[f[faceBasePtI_]],
+        oldPPts[f[facePtAI_]],
+        oldPPts[f[facePtBI_]]
+    );
+}
+
+
+Foam::triPointRef Foam::tetIndices::faceTri(const polyMesh& mesh) const
+{
+    const pointField& pPts = mesh.points();
+    const faceList& pFaces = mesh.faces();
+
+    const Foam::face& f = pFaces[faceI_];
+
+    return triPointRef
+    (
+        pPts[f[faceBasePtI_]],
+        pPts[f[facePtAI_]],
+        pPts[f[facePtBI_]]
+    );
+}
+
+
+Foam::triPointRef Foam::tetIndices::oldFaceTri(const polyMesh& mesh) const
+{
+    const pointField& oldPPts = mesh.oldPoints();
+    const faceList& pFaces = mesh.faces();
+
+    const Foam::face& f = pFaces[faceI_];
+
+    return triPointRef
+    (
+        oldPPts[f[faceBasePtI_]],
+        oldPPts[f[facePtAI_]],
+        oldPPts[f[facePtBI_]]
+    );
+}
+
+
+Foam::label& Foam::tetIndices::cell()
+{
+    return cellI_;
+}
+
+
+Foam::label& Foam::tetIndices::face()
+{
+    return faceI_;
+}
+
+
+Foam::label& Foam::tetIndices::faceBasePt()
+{
+    return faceBasePtI_;
+}
+
+
+Foam::label& Foam::tetIndices::facePtA()
+{
+    return facePtAI_;
+}
+
+
+Foam::label& Foam::tetIndices::facePtB()
+{
+    return facePtBI_;
+}
+
+
+Foam::label& Foam::tetIndices::tetPt()
+{
+    return tetPtI_;
+}
+
+
+// * * * * * * * * * * * * * * * Member Operators  * * * * * * * * * * * * * //
+
+inline bool Foam::tetIndices::operator==(const Foam::tetIndices& rhs) const
+{
+    return
+    (
+        cell() == rhs.cell()
+     && face() == rhs.face()
+     && faceBasePt() == rhs.faceBasePt()
+     && facePtA() == rhs.facePtA()
+     && facePtB() == rhs.facePtB()
+     && tetPt() == rhs.tetPt()
+    );
+}
+
+
+inline bool Foam::tetIndices::operator!=(const Foam::tetIndices& rhs) const
+{
+    return !(*this == rhs);
+}
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+
+// ************************************************************************* //
diff --git a/src/OpenFOAM/meshes/polyMesh/polyPatches/constraint/processor/processorPolyPatch.H b/src/OpenFOAM/meshes/polyMesh/polyPatches/constraint/processor/processorPolyPatch.H
index 53aba7fd17f..bda2eb19d6c 100644
--- a/src/OpenFOAM/meshes/polyMesh/polyPatches/constraint/processor/processorPolyPatch.H
+++ b/src/OpenFOAM/meshes/polyMesh/polyPatches/constraint/processor/processorPolyPatch.H
@@ -229,6 +229,19 @@ public:
 
     // Member functions
 
+        //- Return true only if this is a parallel run
+        virtual bool coupled() const
+        {
+            if (Pstream::parRun())
+            {
+                return true;
+            }
+            else
+            {
+                return false;
+            }
+        }
+
         //- Return processor number
         int myProcNo() const
         {
diff --git a/src/OpenFOAM/meshes/polyMesh/syncTools/syncToolsTemplates.C b/src/OpenFOAM/meshes/polyMesh/syncTools/syncToolsTemplates.C
index 5d35b85f758..539aa1b6c6e 100644
--- a/src/OpenFOAM/meshes/polyMesh/syncTools/syncToolsTemplates.C
+++ b/src/OpenFOAM/meshes/polyMesh/syncTools/syncToolsTemplates.C
@@ -1223,7 +1223,7 @@ void Foam::syncTools::syncBoundaryFaceList
                 label patchStart = procPatch.start()-mesh.nInternalFaces();
 
                 UOPstream toNbr(procPatch.neighbProcNo(), pBufs);
-                toNbr << 
+                toNbr <<
                     SubField<T>
                     (
                         faceValues,
@@ -1423,7 +1423,7 @@ void Foam::syncTools::syncFaceList
                     cop(t, val1);
                     faceValues[meshFace0] = t;
 
-                    cop(val1, val0);                
+                    cop(val1, val0);
                     faceValues[meshFace1] = val1;
                 }
             }
@@ -1683,7 +1683,7 @@ void Foam::syncTools::syncEdgeList
                 const processorPolyPatch& procPatch =
                     refCast<const processorPolyPatch>(patches[patchI]);
 
-                // Receive from neighbour. 
+                // Receive from neighbour.
                 List<unsigned int> nbrPatchInfo(procPatch.nEdges());
 
                 {
diff --git a/src/OpenFOAM/meshes/primitiveMesh/primitiveMesh.H b/src/OpenFOAM/meshes/primitiveMesh/primitiveMesh.H
index efa78e3b303..61c3da9bb9f 100644
--- a/src/OpenFOAM/meshes/primitiveMesh/primitiveMesh.H
+++ b/src/OpenFOAM/meshes/primitiveMesh/primitiveMesh.H
@@ -66,6 +66,7 @@ SourceFiles
 #include "HashSet.H"
 #include "Map.H"
 #include "EdgeMap.H"
+#include "boundBox.H"
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
@@ -577,14 +578,6 @@ public:
                     labelHashSet* setPtr = NULL
                 ) const;
 
-                //- Check face-decomposition tet volume
-                bool checkFaceTets
-                (
-                    const bool report = false,
-                    const scalar minTetVol = 0,
-                    labelHashSet* setPtr = NULL
-                ) const;
-
                 //- Check face skewness
                 bool checkFaceSkewness
                 (
diff --git a/src/OpenFOAM/meshes/primitiveMesh/primitiveMeshCheck/primitiveMeshCheck.C b/src/OpenFOAM/meshes/primitiveMesh/primitiveMeshCheck/primitiveMeshCheck.C
index 04ef5b21bb4..17b5bfd78eb 100644
--- a/src/OpenFOAM/meshes/primitiveMesh/primitiveMeshCheck/primitiveMeshCheck.C
+++ b/src/OpenFOAM/meshes/primitiveMesh/primitiveMeshCheck/primitiveMeshCheck.C
@@ -618,114 +618,6 @@ bool Foam::primitiveMesh::checkFacePyramids
 }
 
 
-bool Foam::primitiveMesh::checkFaceTets
-(
-    const bool report,
-    const scalar minTetVol,
-    labelHashSet* setPtr
-) const
-{
-    if (debug)
-    {
-        Info<< "bool primitiveMesh::checkFaceTets("
-            << "const bool, const scalar, labelHashSet*) const: "
-            << "checking face orientation" << endl;
-    }
-
-    // check whether face area vector points to the cell with higher label
-    const vectorField& cc = cellCentres();
-    const vectorField& fc = faceCentres();
-
-    const labelList& own = faceOwner();
-    const labelList& nei = faceNeighbour();
-
-    const faceList& fcs = faces();
-
-    const pointField& p = points();
-
-    label nErrorPyrs = 0;
-
-    forAll(fcs, faceI)
-    {
-        // Create the owner tets - they will have negative volume
-        const face& f = fcs[faceI];
-
-        forAll(f, fp)
-        {
-            scalar tetVol = tetPointRef
-            (
-                p[f[fp]],
-                p[f.nextLabel(fp)],
-                fc[faceI],
-                cc[own[faceI]]
-            ).mag();
-
-            if (tetVol > -minTetVol)
-            {
-                if (setPtr)
-                {
-                    setPtr->insert(faceI);
-                }
-
-                nErrorPyrs++;
-                break;              // no need to check other tets
-            }
-        }
-
-        if (isInternalFace(faceI))
-        {
-            // Create the neighbour tet - it will have positive volume
-            const face& f = fcs[faceI];
-
-            forAll(f, fp)
-            {
-                scalar tetVol = tetPointRef
-                (
-                    p[f[fp]],
-                    p[f.nextLabel(fp)],
-                    fc[faceI],
-                    cc[nei[faceI]]
-                ).mag();
-
-                if (tetVol < minTetVol)
-                {
-                    if (setPtr)
-                    {
-                        setPtr->insert(faceI);
-                    }
-
-                    nErrorPyrs++;
-                    break;
-                }
-            }
-        }
-    }
-
-    reduce(nErrorPyrs, sumOp<label>());
-
-    if (nErrorPyrs > 0)
-    {
-        if (debug || report)
-        {
-            Info<< " ***Error in face tets: "
-                << nErrorPyrs << " faces have incorrectly oriented face"
-                << " decomposition triangles." << endl;
-        }
-
-        return true;
-    }
-    else
-    {
-        if (debug || report)
-        {
-            Info<< "    Face tets OK." << endl;
-        }
-
-        return false;
-    }
-}
-
-
 bool Foam::primitiveMesh::checkFaceSkewness
 (
     const bool report,
diff --git a/src/OpenFOAM/meshes/primitiveMesh/primitiveMeshFindCell.C b/src/OpenFOAM/meshes/primitiveMesh/primitiveMeshFindCell.C
index 6918235551d..fecbc3c6fbd 100644
--- a/src/OpenFOAM/meshes/primitiveMesh/primitiveMeshFindCell.C
+++ b/src/OpenFOAM/meshes/primitiveMesh/primitiveMeshFindCell.C
@@ -32,32 +32,14 @@ License
 // Is the point in the cell bounding box
 bool Foam::primitiveMesh::pointInCellBB(const point& p, label celli) const
 {
-    const pointField& points = this->points();
-    const faceList& f = faces();
-    const vectorField& centres = cellCentres();
-    const cellList& cf = cells();
-
-    labelList cellVertices = cf[celli].labels(f);
-
-    vector bbmax = -GREAT*vector::one;
-    vector bbmin = GREAT*vector::one;
-
-    forAll(cellVertices, vertexI)
-    {
-        bbmax = max(bbmax, points[cellVertices[vertexI]]);
-        bbmin = min(bbmin, points[cellVertices[vertexI]]);
-    }
-
-    scalar distance = mag(centres[celli] - p);
-
-    if ((distance - mag(bbmax - bbmin)) < SMALL)
-    {
-        return true;
-    }
-    else
-    {
-        return false;
-    }
+    return boundBox
+    (
+        cells()[celli].points
+        (
+            faces(),
+            points()
+        )
+    ).contains(p);
 }
 
 
diff --git a/src/OpenFOAM/meshes/primitiveShapes/tetrahedron/tetrahedron.C b/src/OpenFOAM/meshes/primitiveShapes/tetrahedron/tetrahedron.C
index 2c801c1db9f..54f8168b605 100644
--- a/src/OpenFOAM/meshes/primitiveShapes/tetrahedron/tetrahedron.C
+++ b/src/OpenFOAM/meshes/primitiveShapes/tetrahedron/tetrahedron.C
@@ -30,7 +30,6 @@ Description
 #include "triPointRef.H"
 #include "scalarField.H"
 
-
 // * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
 
 // (Probably very inefficient) minimum containment sphere calculation.
diff --git a/src/OpenFOAM/meshes/primitiveShapes/tetrahedron/tetrahedron.H b/src/OpenFOAM/meshes/primitiveShapes/tetrahedron/tetrahedron.H
index fe036de2d23..f80ac49d406 100644
--- a/src/OpenFOAM/meshes/primitiveShapes/tetrahedron/tetrahedron.H
+++ b/src/OpenFOAM/meshes/primitiveShapes/tetrahedron/tetrahedron.H
@@ -42,6 +42,7 @@ SourceFiles
 #include "point.H"
 #include "primitiveFieldsFwd.H"
 #include "pointHit.H"
+#include "Random.H"
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
@@ -133,16 +134,42 @@ public:
 
             inline vector Sd() const;
 
+            //- Return centre (centroid)
+            inline Point centre() const;
 
             //- Return volume
             inline scalar mag() const;
 
             //- Return circum-centre
-            inline vector circumCentre() const;
+            inline Point circumCentre() const;
 
             //- Return circum-radius
             inline scalar circumRadius() const;
 
+            //- Return quality: Ratio of tetrahedron and circum-sphere
+            //  volume, scaled so that a regular tetrahedron has a
+            //  quality of 1
+            inline scalar quality() const;
+
+            //- Return a random point in the tetrahedron from a
+            //  uniform distribution
+            inline Point randomPoint(Random& rndGen) const;
+
+            //- Calculate the barycentric coordinates of the given
+            //  point, in the same order as a, b, c, d.  Returns the
+            //  determinant of the solution.
+            inline scalar barycentric
+            (
+                const point& pt,
+                List<scalar>& bary
+            ) const;
+
+            //- Return nearest point to p on tetrahedron
+            inline pointHit nearestPoint
+            (
+                const point& p
+            ) const;
+
             //- Return (min)containment sphere, i.e. the smallest sphere with
             //  all points inside. Returns pointHit with:
             //  - hit         : if sphere is equal to circumsphere
diff --git a/src/OpenFOAM/meshes/primitiveShapes/tetrahedron/tetrahedronI.H b/src/OpenFOAM/meshes/primitiveShapes/tetrahedron/tetrahedronI.H
index 5fab6fa5381..a96d9f577fc 100644
--- a/src/OpenFOAM/meshes/primitiveShapes/tetrahedron/tetrahedronI.H
+++ b/src/OpenFOAM/meshes/primitiveShapes/tetrahedron/tetrahedronI.H
@@ -125,6 +125,13 @@ inline vector tetrahedron<Point, PointRef>::Sd() const
 }
 
 
+template<class Point, class PointRef>
+inline Point tetrahedron<Point, PointRef>::centre() const
+{
+    return 0.25*(a_ + b_ + c_ + d_);
+}
+
+
 template<class Point, class PointRef>
 inline scalar tetrahedron<Point, PointRef>::mag() const
 {
@@ -133,19 +140,19 @@ inline scalar tetrahedron<Point, PointRef>::mag() const
 
 
 template<class Point, class PointRef>
-inline vector tetrahedron<Point, PointRef>::circumCentre() const
+inline Point tetrahedron<Point, PointRef>::circumCentre() const
 {
     vector a = b_ - a_;
     vector b = c_ - a_;
     vector c = d_ - a_;
 
-    scalar lamda = magSqr(c) - (a&c);
-    scalar mu = magSqr(b) - (a&b);
+    scalar lambda = magSqr(c) - (a & c);
+    scalar mu = magSqr(b) - (a & b);
 
-    vector ba = b^a;
-    vector ca = c^a;
+    vector ba = b ^ a;
+    vector ca = c ^ a;
 
-    return a_ + 0.5*(a + (lamda*ba - mu*ca)/(c&ba));
+    return a_ + 0.5*(a + (lambda*ba - mu*ca)/((c & ba) + ROOTVSMALL));
 }
 
 
@@ -156,6 +163,196 @@ inline scalar tetrahedron<Point, PointRef>::circumRadius() const
 }
 
 
+template<class Point, class PointRef>
+inline scalar tetrahedron<Point, PointRef>::quality() const
+{
+    // Note: 8/(9*sqrt(3)) = 0.5132002393
+
+    return mag()/(0.5132002393*pow3(min(circumRadius(), GREAT)) + ROOTVSMALL);
+}
+
+
+template<class Point, class PointRef>
+inline Point tetrahedron<Point, PointRef>::randomPoint(Random& rndGen) const
+{
+    // Adapted from
+    // http://vcg.isti.cnr.it/activities/geometryegraphics/pointintetraedro.html
+
+    scalar s = rndGen.scalar01();
+    scalar t = rndGen.scalar01();
+    scalar u = rndGen.scalar01();
+
+    if (s + t > 1.0)
+    {
+        s = 1.0 - s;
+        t = 1.0 - t;
+    }
+
+    if (t + u > 1.0)
+    {
+        scalar tmp = u;
+        u = 1.0 - s - t;
+        t = 1.0 - tmp;
+    }
+    else if (s + t + u > 1.0)
+    {
+        scalar tmp = u;
+        u = s + t + u - 1.0;
+        s = 1.0 - t - tmp;
+    }
+
+    return (1 - s - t - u)*a_ + s*b_ + t*c_ + u*d_;
+}
+
+
+template<class Point, class PointRef>
+scalar tetrahedron<Point, PointRef>::barycentric
+(
+    const point& pt,
+    List<scalar>& bary
+) const
+{
+    // From:
+    // http://en.wikipedia.org/wiki/Barycentric_coordinate_system_(mathematics)
+
+    vector e0(a_ - d_);
+    vector e1(b_ - d_);
+    vector e2(c_ - d_);
+
+    tensor t
+    (
+        e0.x(), e1.x(), e2.x(),
+        e0.y(), e1.y(), e2.y(),
+        e0.z(), e1.z(), e2.z()
+    );
+
+    scalar detT = det(t);
+
+    if (Foam::mag(detT) < SMALL)
+    {
+        WarningIn
+        (
+            "List<scalar> tetrahedron<Point, PointRef>::barycentric"
+            "("
+                "const point& pt"
+            ") const"
+        )
+            << "Degenerate triangle - returning 1/4 barycentric coordinates."
+            << endl;
+
+        bary = List<scalar>(4, 0.25);
+
+        return detT;
+    }
+
+    vector res = inv(t, detT) & (pt - d_);
+
+    bary.setSize(4);
+
+    bary[0] = res.x();
+    bary[1] = res.y();
+    bary[2] = res.z();
+    bary[3] = (1.0 - res.x() - res.y() - res.z());
+
+    return detT;
+}
+
+
+template<class Point, class PointRef>
+inline pointHit tetrahedron<Point, PointRef>::nearestPoint
+(
+    const point& p
+) const
+{
+    // Adapted from:
+    // Real-time collision detection, Christer Ericson, 2005, p142-144
+
+    // Assuming initially that the point is inside all of the
+    // halfspaces, so closest to itself.
+
+    point closestPt = p;
+
+    scalar minOutsideDistance = VGREAT;
+
+    bool inside = true;
+
+    if (((p - b_) & Sa()) >= 0)
+    {
+        // p is outside halfspace plane of tri
+        pointHit info = triangle<Point, PointRef>(b_, c_, d_).nearestPoint(p);
+
+        inside = false;
+
+        if (info.distance() < minOutsideDistance)
+        {
+            closestPt = info.rawPoint();
+
+            minOutsideDistance = info.distance();
+        }
+    }
+
+    if (((p - a_) & Sb()) >= 0)
+    {
+        // p is outside halfspace plane of tri
+        pointHit info = triangle<Point, PointRef>(a_, d_, c_).nearestPoint(p);
+
+        inside = false;
+
+        if (info.distance() < minOutsideDistance)
+        {
+            closestPt = info.rawPoint();
+
+            minOutsideDistance = info.distance();
+        }
+    }
+
+    if (((p - a_) & Sc()) >= 0)
+    {
+        // p is outside halfspace plane of tri
+        pointHit info = triangle<Point, PointRef>(a_, b_, d_).nearestPoint(p);
+
+        inside = false;
+
+        if (info.distance() < minOutsideDistance)
+        {
+            closestPt = info.rawPoint();
+
+            minOutsideDistance = info.distance();
+        }
+    }
+
+    if (((p - a_) & Sd()) >= 0)
+    {
+        // p is outside halfspace plane of tri
+        pointHit info = triangle<Point, PointRef>(a_, c_, b_).nearestPoint(p);
+
+        inside = false;
+
+        if (info.distance() < minOutsideDistance)
+        {
+            closestPt = info.rawPoint();
+
+            minOutsideDistance = info.distance();
+        }
+    }
+
+    // If the point is inside, then the distance to the closest point
+    // is zero
+    if (inside)
+    {
+        minOutsideDistance = 0;
+    }
+
+    return pointHit
+    (
+        inside,
+        closestPt,
+        minOutsideDistance,
+        !inside
+    );
+}
+
+
 // * * * * * * * * * * * * * * * Ostream Operator  * * * * * * * * * * * * * //
 
 template<class point, class pointRef>
diff --git a/src/OpenFOAM/meshes/primitiveShapes/triangle/triangle.H b/src/OpenFOAM/meshes/primitiveShapes/triangle/triangle.H
index a5c246a3607..64d9318306f 100644
--- a/src/OpenFOAM/meshes/primitiveShapes/triangle/triangle.H
+++ b/src/OpenFOAM/meshes/primitiveShapes/triangle/triangle.H
@@ -39,7 +39,7 @@ SourceFiles
 #include "vector.H"
 #include "tensor.H"
 #include "pointHit.H"
-
+#include "Random.H"
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
@@ -141,12 +141,14 @@ public:
             inline vector normal() const;
 
             //- Return circum-centre
-            inline vector circumCentre() const;
+            inline Point circumCentre() const;
 
             //- Return circum-radius
             inline scalar circumRadius() const;
 
-            //- Return quality: Ratio triangle and circum-circle area
+            //- Return quality: Ratio of triangle and circum-circle
+            //  area, scaled so that an equilateral triangle has a
+            //  quality of 1
             inline scalar quality() const;
 
             //- Return swept-volume
@@ -160,6 +162,19 @@ public:
                 scalar density = 1.0
             ) const;
 
+            //- Return a random point on the triangle from a uniform
+            //  distribution
+            inline Point randomPoint(Random& rndGen) const;
+
+            //- Calculate the barycentric coordinates of the given
+            //  point, in the same order as a, b, c.  Returns the
+            //  determinant of the solution.
+            inline scalar barycentric
+            (
+                const point& pt,
+                List<scalar>& bary
+            ) const;
+
             //- Return point intersection with a ray.
             //  For a hit, the distance is signed. Positive number
             //  represents the point in front of triangle.
diff --git a/src/OpenFOAM/meshes/primitiveShapes/triangle/triangleI.H b/src/OpenFOAM/meshes/primitiveShapes/triangle/triangleI.H
index 3587ac8d907..10267a923f7 100644
--- a/src/OpenFOAM/meshes/primitiveShapes/triangle/triangleI.H
+++ b/src/OpenFOAM/meshes/primitiveShapes/triangle/triangleI.H
@@ -259,7 +259,7 @@ inline vector triangle<Point, PointRef>::normal() const
 
 
 template<class Point, class PointRef>
-inline vector triangle<Point, PointRef>::circumCentre() const
+inline Point triangle<Point, PointRef>::circumCentre() const
 {
     scalar d1 = (c_ - a_)&(b_ - a_);
     scalar d2 = -(c_ - b_)&(b_ - a_);
@@ -303,12 +303,14 @@ inline scalar triangle<Point, PointRef>::circumRadius() const
 template<class Point, class PointRef>
 inline scalar triangle<Point, PointRef>::quality() const
 {
+    // Note: 3*sqr(3)/(4*pi) = 0.4134966716
+
     return
         mag()
       / (
             constant::mathematical::pi
            *Foam::sqr(circumRadius())
-           *0.413497
+           *0.4134966716
           + VSMALL
         );
 }
@@ -366,6 +368,72 @@ inline tensor triangle<Point, PointRef>::inertia
    *density;
 }
 
+
+template<class Point, class PointRef>
+inline Point triangle<Point, PointRef>::randomPoint(Random& rndGen) const
+{
+    // Generating Random Points in Triangles
+    // by Greg Turk
+    // from "Graphics Gems", Academic Press, 1990
+    // http://tog.acm.org/GraphicsGems/gems/TriPoints.c
+
+    scalar s = rndGen.scalar01();
+
+    scalar t = sqrt(rndGen.scalar01());
+
+    return (1 - t)*a_ + (1 - s)*t*b_ + s*t*c_;
+}
+
+
+template<class Point, class PointRef>
+scalar triangle<Point, PointRef>::barycentric
+(
+    const point& pt,
+    List<scalar>& bary
+) const
+{
+    // From:
+    // Real-time collision detection, Christer Ericson, 2005, p47-48
+
+    vector v0 = b_ - a_;
+    vector v1 = c_ - a_;
+    vector v2 = pt - a_;
+
+    scalar d00 = v0 & v0;
+    scalar d01 = v0 & v1;
+    scalar d11 = v1 & v1;
+    scalar d20 = v2 & v0;
+    scalar d21 = v2 & v1;
+
+    scalar denom = d00*d11 - d01*d01;
+
+    if (Foam::mag(denom) < SMALL)
+    {
+        WarningIn
+        (
+            "List<scalar> triangle<Point, PointRef>::barycentric"
+            "("
+                "const point& pt"
+            ") const"
+        )
+            << "Degenerate triangle - returning 1/3 barycentric coordinates."
+            << endl;
+
+        bary = List<scalar>(3, 1.0/3.0);
+
+        return denom;
+    }
+
+    bary.setSize(3);
+
+    bary[0] = (d11*d20 - d01*d21)/denom;
+    bary[1] = (d00*d21 - d01*d20)/denom;
+    bary[2] = 1.0 - bary[0] - bary[1];
+
+    return denom;
+}
+
+
 template<class Point, class PointRef>
 inline pointHit triangle<Point, PointRef>::ray
 (
@@ -775,4 +843,3 @@ inline Ostream& operator<<(Ostream& os, const triangle<Point, PointRef>& t)
 } // End namespace Foam
 
 // ************************************************************************* //
-
diff --git a/src/OpenFOAM/primitives/Tensor/TensorI.H b/src/OpenFOAM/primitives/Tensor/TensorI.H
index 8ca4f601693..11860b5c4c9 100644
--- a/src/OpenFOAM/primitives/Tensor/TensorI.H
+++ b/src/OpenFOAM/primitives/Tensor/TensorI.H
@@ -481,7 +481,7 @@ inline Tensor<Cmpt> cof(const Tensor<Cmpt>& t)
 }
 
 
-//- Return the inverse of a tensor give the determinant
+//- Return the inverse of a tensor given the determinant
 template <class Cmpt>
 inline Tensor<Cmpt> inv(const Tensor<Cmpt>& t, const Cmpt dett)
 {
diff --git a/src/dynamicMesh/motionSmoother/motionSmootherCheck.C b/src/dynamicMesh/motionSmoother/motionSmootherCheck.C
index 4823c3a429d..5df1ca1dbcf 100644
--- a/src/dynamicMesh/motionSmoother/motionSmootherCheck.C
+++ b/src/dynamicMesh/motionSmoother/motionSmootherCheck.C
@@ -68,9 +68,9 @@ bool Foam::motionSmoother::checkMesh
     (
         readScalar(dict.lookup("minVol", true))
     );
-    const scalar minTetVol
+    const scalar minTetQuality
     (
-        readScalar(dict.lookup("minTetVol", true))
+        readScalar(dict.lookup("minTetQuality", true))
     );
     const scalar maxConcave
     (
@@ -160,12 +160,12 @@ bool Foam::motionSmoother::checkMesh
         nWrongFaces = nNewWrongFaces;
     }
 
-    if (minTetVol > -GREAT)
+    if (minTetQuality > -GREAT)
     {
         polyMeshGeometry::checkFaceTets
         (
             report,
-            minTetVol,
+            minTetQuality,
             mesh,
             mesh.cellCentres(),
             mesh.faceCentres(),
@@ -177,8 +177,8 @@ bool Foam::motionSmoother::checkMesh
 
         label nNewWrongFaces = returnReduce(wrongFaces.size(), sumOp<label>());
 
-        Info<< "    faces with face-decomposition tet volume < "
-            << setw(5) << minTetVol << "        : "
+        Info<< "    faces with face-decomposition tet quality < "
+            << setw(5) << minTetQuality << "      : "
             << nNewWrongFaces-nWrongFaces << endl;
 
         nWrongFaces = nNewWrongFaces;
@@ -443,9 +443,9 @@ bool Foam::motionSmoother::checkMesh
     (
         readScalar(dict.lookup("minVol", true))
     );
-    const scalar minTetVol
+    const scalar minTetQuality
     (
-        readScalar(dict.lookup("minTetVol", true))
+        readScalar(dict.lookup("minTetQuality", true))
     );
     const scalar maxConcave
     (
@@ -531,12 +531,12 @@ bool Foam::motionSmoother::checkMesh
         nWrongFaces = nNewWrongFaces;
     }
 
-    if (minTetVol > -GREAT)
+    if (minTetQuality > -GREAT)
     {
         meshGeom.checkFaceTets
         (
             report,
-            minTetVol,
+            minTetQuality,
             meshGeom.mesh().points(),
             checkFaces,
             baffles,
@@ -545,8 +545,8 @@ bool Foam::motionSmoother::checkMesh
 
         label nNewWrongFaces = returnReduce(wrongFaces.size(), sumOp<label>());
 
-        Info<< "    faces with face-decomposition tet volume < "
-            << setw(5) << minTetVol << "                 : "
+        Info<< "    faces with face-decomposition tet quality < "
+            << setw(5) << minTetQuality << "                : "
             << nNewWrongFaces-nWrongFaces << endl;
 
         nWrongFaces = nNewWrongFaces;
diff --git a/src/dynamicMesh/motionSmoother/polyMeshGeometry/polyMeshGeometry.C b/src/dynamicMesh/motionSmoother/polyMeshGeometry/polyMeshGeometry.C
index fb9ccb9631f..4df209498a8 100644
--- a/src/dynamicMesh/motionSmoother/polyMeshGeometry/polyMeshGeometry.C
+++ b/src/dynamicMesh/motionSmoother/polyMeshGeometry/polyMeshGeometry.C
@@ -24,6 +24,7 @@ License
 \*---------------------------------------------------------------------------*/
 
 #include "polyMeshGeometry.H"
+#include "polyMeshTetDecomposition.H"
 #include "pyramidPointFaceRef.H"
 #include "tetPointRef.H"
 #include "syncTools.H"
@@ -313,7 +314,7 @@ bool Foam::polyMeshGeometry::checkFaceTet
 (
     const polyMesh& mesh,
     const bool report,
-    const scalar minTetVol,
+    const scalar minTetQuality,
     const pointField& p,
     const label faceI,
     const point& fc,    // face centre
@@ -326,15 +327,15 @@ bool Foam::polyMeshGeometry::checkFaceTet
 
     forAll(f, fp)
     {
-        scalar tetVol = tetPointRef
+        scalar tetQual = tetPointRef
         (
             p[f[fp]],
             p[f.nextLabel(fp)],
             fc,
             cc
-        ).mag();
+        ).quality();
 
-        if (tetVol < minTetVol)
+        if (tetQual < minTetQuality)
         {
             if (report)
             {
@@ -345,7 +346,7 @@ bool Foam::polyMeshGeometry::checkFaceTet
                     << "face " << faceI
                     << " has a triangle that points the wrong way."
                      << endl
-                    << "Tet volume: " << tetVol
+                    << "Tet quality: " << tetQual
                     << " Face " << faceI
                     << endl;
             }
@@ -423,16 +424,15 @@ bool Foam::polyMeshGeometry::checkFaceDotProduct
     // Severe nonorthogonality threshold
     const scalar severeNonorthogonalityThreshold = ::cos(degToRad(orthWarn));
 
-
     // Calculate coupled cell centre
-    pointField neiCc(mesh.nFaces()-mesh.nInternalFaces());
+    pointField neiCc(mesh.nFaces() - mesh.nInternalFaces());
 
     for (label faceI = mesh.nInternalFaces(); faceI < mesh.nFaces(); faceI++)
     {
         neiCc[faceI-mesh.nInternalFaces()] = cellCentres[own[faceI]];
     }
-    syncTools::swapBoundaryFacePositions(mesh, neiCc);
 
+    syncTools::swapBoundaryFacePositions(mesh, neiCc);
 
     scalar minDDotS = GREAT;
 
@@ -754,7 +754,7 @@ bool Foam::polyMeshGeometry::checkFacePyramids
                 "polyMeshGeometry::checkFacePyramids("
                 "const bool, const scalar, const pointField&"
                 ", const labelList&, labelHashSet*)"
-            )   << "Error in face pyramids: faces pointing the wrong way!"
+            )   << "Error in face pyramids: faces pointing the wrong way."
                 << endl;
         }
 
@@ -775,7 +775,7 @@ bool Foam::polyMeshGeometry::checkFacePyramids
 bool Foam::polyMeshGeometry::checkFaceTets
 (
     const bool report,
-    const scalar minTetVol,
+    const scalar minTetQuality,
     const polyMesh& mesh,
     const vectorField& cellCentres,
     const vectorField& faceCentres,
@@ -785,11 +785,23 @@ bool Foam::polyMeshGeometry::checkFaceTets
     labelHashSet* setPtr
 )
 {
-    // check whether face area vector points to the cell with higher label
+    // check whether decomposing each cell into tets results in
+    // positive volume, non-flat tets
     const labelList& own = mesh.faceOwner();
     const labelList& nei = mesh.faceNeighbour();
+    const polyBoundaryMesh& patches = mesh.boundaryMesh();
 
-    label nErrorPyrs = 0;
+    // Calculate coupled cell centre
+    pointField neiCc(mesh.nFaces() - mesh.nInternalFaces());
+
+    for (label faceI = mesh.nInternalFaces(); faceI < mesh.nFaces(); faceI++)
+    {
+        neiCc[faceI - mesh.nInternalFaces()] = cellCentres[own[faceI]];
+    }
+
+    syncTools::swapBoundaryFacePositions(mesh, neiCc);
+
+    label nErrorTets = 0;
 
     forAll(checkFaces, i)
     {
@@ -801,36 +813,104 @@ bool Foam::polyMeshGeometry::checkFaceTets
         (
             mesh,
             report,
-            minTetVol,
+            minTetQuality,
             p,
             faceI,
             cellCentres[own[faceI]],    // face centre
-            faceCentres[faceI],    // cell centre
+            faceCentres[faceI],         // cell centre
             setPtr
         );
 
         if (tetError)
         {
-            nErrorPyrs++;
+            nErrorTets++;
         }
 
         if (mesh.isInternalFace(faceI))
         {
-            // Create the neighbour pyramid - it will have positive volume
+            // Create the neighbour tets - they will have positive volume
             bool tetError = checkFaceTet
             (
                 mesh,
                 report,
-                minTetVol,
+                minTetQuality,
                 p,
                 faceI,
-                faceCentres[faceI],    // face centre
+                faceCentres[faceI],         // face centre
                 cellCentres[nei[faceI]],    // cell centre
                 setPtr
             );
+
             if (tetError)
             {
-                nErrorPyrs++;
+                nErrorTets++;
+            }
+
+            if
+            (
+                polyMeshTetDecomposition::findSharedBasePoint
+                (
+                    mesh,
+                    faceI,
+                    minTetQuality,
+                    report
+                ) == -1
+            )
+            {
+                if (setPtr)
+                {
+                    setPtr->insert(faceI);
+                }
+
+                nErrorTets++;
+            }
+        }
+        else
+        {
+            label patchI = patches.whichPatch(faceI);
+
+            if (patches[patchI].coupled())
+            {
+                if
+                (
+                    polyMeshTetDecomposition::findSharedBasePoint
+                    (
+                        mesh,
+                        faceI,
+                        neiCc[faceI - mesh.nInternalFaces()],
+                        minTetQuality,
+                        report
+                    ) == -1
+                )
+                {
+                    if (setPtr)
+                    {
+                        setPtr->insert(faceI);
+                    }
+
+                    nErrorTets++;
+                }
+            }
+            else
+            {
+                if
+                (
+                    polyMeshTetDecomposition::findBasePoint
+                    (
+                        mesh,
+                        faceI,
+                        minTetQuality,
+                        report
+                    ) == -1
+                )
+                {
+                    if (setPtr)
+                    {
+                        setPtr->insert(faceI);
+                    }
+
+                    nErrorTets++;
+                }
             }
         }
     }
@@ -844,7 +924,7 @@ bool Foam::polyMeshGeometry::checkFaceTets
         (
             mesh,
             report,
-            minTetVol,
+            minTetQuality,
             p,
             face0,
             cellCentres[own[face0]],    // face centre
@@ -854,15 +934,15 @@ bool Foam::polyMeshGeometry::checkFaceTets
 
         if (tetError)
         {
-            nErrorPyrs++;
+            nErrorTets++;
         }
 
-        // Create the neighbour pyramid - it will have positive volume
+        // Create the neighbour tets - they will have positive volume
         tetError = checkFaceTet
         (
             mesh,
             report,
-            minTetVol,
+            minTetQuality,
             p,
             face0,
             faceCentres[face0],         // face centre
@@ -872,13 +952,33 @@ bool Foam::polyMeshGeometry::checkFaceTets
 
         if (tetError)
         {
-            nErrorPyrs++;
+            nErrorTets++;
+        }
+
+        if
+        (
+            polyMeshTetDecomposition::findSharedBasePoint
+            (
+                mesh,
+                face0,
+                cellCentres[own[face1]],
+                minTetQuality,
+                report
+            ) == -1
+        )
+        {
+            if (setPtr)
+            {
+                setPtr->insert(face0);
+            }
+
+            nErrorTets++;
         }
     }
 
-    reduce(nErrorPyrs, sumOp<label>());
+    reduce(nErrorTets, sumOp<label>());
 
-    if (nErrorPyrs > 0)
+    if (nErrorTets > 0)
     {
         if (report)
         {
@@ -887,7 +987,7 @@ bool Foam::polyMeshGeometry::checkFaceTets
                 "polyMeshGeometry::checkFaceTets("
                 "const bool, const scalar, const pointField&, const pointField&"
                 ", const labelList&, labelHashSet*)"
-            )   << "Error in face pyramids: faces pointing the wrong way!"
+            )   << "Error in face decomposition: negative tets."
                 << endl;
         }
 
@@ -2135,7 +2235,7 @@ bool Foam::polyMeshGeometry::checkFacePyramids
 bool Foam::polyMeshGeometry::checkFaceTets
 (
     const bool report,
-    const scalar minTetVol,
+    const scalar minTetQuality,
     const pointField& p,
     const labelList& checkFaces,
     const List<labelPair>& baffles,
@@ -2145,7 +2245,7 @@ bool Foam::polyMeshGeometry::checkFaceTets
     return checkFaceTets
     (
         report,
-        minTetVol,
+        minTetQuality,
         mesh_,
         cellCentres_,
         faceCentres_,
diff --git a/src/dynamicMesh/motionSmoother/polyMeshGeometry/polyMeshGeometry.H b/src/dynamicMesh/motionSmoother/polyMeshGeometry/polyMeshGeometry.H
index 4a8178b8f60..71f61447b53 100644
--- a/src/dynamicMesh/motionSmoother/polyMeshGeometry/polyMeshGeometry.H
+++ b/src/dynamicMesh/motionSmoother/polyMeshGeometry/polyMeshGeometry.H
@@ -113,7 +113,7 @@ class polyMeshGeometry
         (
             const polyMesh&,
             const bool report,
-            const scalar minTetVol,
+            const scalar minTetQuality,
             const pointField& p,
             const label faceI,
             const point& fc,    // face centre
@@ -121,6 +121,7 @@ class polyMeshGeometry
             labelHashSet* setPtr
         );
 
+
 public:
 
     ClassName("polyMeshGeometry");
@@ -350,7 +351,7 @@ public:
             bool checkFaceTets
             (
                 const bool report,
-                const scalar minTetVol,
+                const scalar minTetQuality,
                 const pointField& p,
                 const labelList& checkFaces,
                 const List<labelPair>& baffles,
diff --git a/src/finiteVolume/interpolation/interpolation/interpolation/interpolation.H b/src/finiteVolume/interpolation/interpolation/interpolation/interpolation.H
index ef200b354a8..e6df2154a4f 100644
--- a/src/finiteVolume/interpolation/interpolation/interpolation/interpolation.H
+++ b/src/finiteVolume/interpolation/interpolation/interpolation/interpolation.H
@@ -38,6 +38,7 @@ Description
 #include "typeInfo.H"
 #include "autoPtr.H"
 #include "runTimeSelectionTables.H"
+#include "tetIndices.H"
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
@@ -130,9 +131,23 @@ public:
         virtual Type interpolate
         (
             const vector& position,
-            const label nCell,
-            const label facei = -1
+            const label cellI,
+            const label faceI = -1
         ) const = 0;
+
+        //- Interpolate field to the given point in the tetrahedron
+        //  defined by the given indices.  Calls interpolate function
+        //  above here execpt where overridden by derived
+        //  interpolation types.
+        virtual Type interpolate
+        (
+            const vector& position,
+            const tetIndices& tetIs,
+            const label faceI = -1
+        ) const
+        {
+            return interpolate(position, tetIs.cell(), faceI);
+        }
 };
 
 
diff --git a/src/finiteVolume/interpolation/interpolation/interpolationCell/interpolationCell.C b/src/finiteVolume/interpolation/interpolation/interpolationCell/interpolationCell.C
index fbf1bfa14ac..760fd2ff633 100644
--- a/src/finiteVolume/interpolation/interpolation/interpolationCell/interpolationCell.C
+++ b/src/finiteVolume/interpolation/interpolation/interpolationCell/interpolationCell.C
@@ -49,11 +49,11 @@ template<class Type>
 Type interpolationCell<Type>::interpolate
 (
     const vector&,
-    const label celli,
+    const label cellI,
     const label
 ) const
 {
-    return this->psi_[celli];
+    return this->psi_[cellI];
 }
 
 
diff --git a/src/finiteVolume/interpolation/interpolation/interpolationCell/interpolationCell.H b/src/finiteVolume/interpolation/interpolation/interpolationCell/interpolationCell.H
index 3c852da8679..fa9461805b1 100644
--- a/src/finiteVolume/interpolation/interpolation/interpolationCell/interpolationCell.H
+++ b/src/finiteVolume/interpolation/interpolation/interpolationCell/interpolationCell.H
@@ -72,8 +72,8 @@ public:
         Type interpolate
         (
             const vector& position,
-            const label nCell,
-            const label facei = -1
+            const label cellI,
+            const label faceI = -1
         ) const;
 };
 
diff --git a/src/finiteVolume/interpolation/interpolation/interpolationCellPoint/cellPointWeight/cellPointWeight.C b/src/finiteVolume/interpolation/interpolation/interpolationCellPoint/cellPointWeight/cellPointWeight.C
index 52ce6ba21e6..99b10c10b54 100644
--- a/src/finiteVolume/interpolation/interpolation/interpolationCellPoint/cellPointWeight/cellPointWeight.C
+++ b/src/finiteVolume/interpolation/interpolation/interpolationCellPoint/cellPointWeight/cellPointWeight.C
@@ -25,10 +25,13 @@ License
 
 #include "cellPointWeight.H"
 #include "polyMesh.H"
+#include "tetPointRef.H"
+#include "polyMeshTetDecomposition.H"
 
 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
 
 int Foam::cellPointWeight::debug(debug::debugSwitch("cellPointWeight", 0));
+
 Foam::scalar Foam::cellPointWeight::tol(SMALL);
 
 // * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * * //
@@ -37,148 +40,101 @@ void Foam::cellPointWeight::findTetrahedron
 (
     const polyMesh& mesh,
     const vector& position,
-    const label cellIndex
+    const label cellI
 )
 {
     if (debug)
     {
-        Pout<< "\nFoam::cellPointWeight::findTetrahedron" << nl
+        Pout<< nl << "Foam::cellPointWeight::findTetrahedron" << nl
             << "position = " << position << nl
-            << "cellIndex = " << cellIndex << endl;
+            << "cellI = " << cellI << endl;
     }
 
-    // Initialise closest triangle variables
-    scalar minUVWClose = VGREAT;
-    label pointIClose = 0;
-    label faceClose = 0;
+    List<tetIndices> cellTets = polyMeshTetDecomposition::cellTetIndices
+    (
+        mesh,
+        cellI
+    );
 
-    const vector& P0 = mesh.cellCentres()[cellIndex];
-    const labelList& cellFaces = mesh.cells()[cellIndex];
-    const scalar cellVolume = mesh.cellVolumes()[cellIndex];
+    const faceList& pFaces = mesh.faces();
+    const scalar cellVolume = mesh.cellVolumes()[cellI];
 
-    // Find the tet that the point occupies
-    forAll(cellFaces, faceI)
+    forAll(cellTets, tetI)
     {
-        // Decompose each face into triangles, making a tet when
-        // augmented by the cell centre
-        const labelList& facePoints = mesh.faces()[cellFaces[faceI]];
+        const tetIndices& tetIs = cellTets[tetI];
+
+        const face& f = pFaces[tetIs.face()];
+
+        // Barycentric coordinates of the position
+        scalar det = tetIs.tet(mesh).barycentric(position, weights_);
 
-        label pointI = 1;
-        while ((pointI + 1) < facePoints.size())
+        if (mag(det/cellVolume) > tol)
         {
-            // Cartesian co-ordinates of the triangle vertices
-            const vector& P1 = mesh.points()[facePoints[0]];
-            const vector& P2 = mesh.points()[facePoints[pointI]];
-            const vector& P3 = mesh.points()[facePoints[pointI + 1]];
+            const scalar& u = weights_[0];
+            const scalar& v = weights_[1];
+            const scalar& w = weights_[2];
+
+            if
+            (
+                (u + tol > 0)
+             && (v + tol > 0)
+             && (w + tol > 0)
+             && (u + v + w < 1 + tol)
+            )
+            {
+                faceVertices_[0] = f[tetIs.faceBasePt()];
+                faceVertices_[1] = f[tetIs.facePtA()];;
+                faceVertices_[2] = f[tetIs.facePtB()];;
 
-            // Edge vectors
-            const vector e1 = P1 - P0;
-            const vector e2 = P2 - P0;
-            const vector e3 = P3 - P0;
+                return;
+            }
+        }
+    }
 
-            // Solve for interpolation weighting factors
+    // A suitable point in a tetrahedron was not found, find the
+    // nearest.
 
-            // Source term
-            const vector rhs = position - P0;
+    scalar minNearDist = VGREAT;
 
-            // Determinant of coefficients matrix
-            // Note: if det(A) = 0 the tet is degenerate
-            const scalar detA =
-                e1.x()*e2.y()*e3.z() + e2.x()*e3.y()*e1.z()
-              + e3.x()*e1.y()*e2.z() - e1.x()*e3.y()*e2.z()
-              - e2.x()*e1.y()*e3.z() - e3.x()*e2.y()*e1.z();
+    label nearestTetI = -1;
 
-            if (mag(detA/cellVolume) > tol)
-            {
-                // Solve using Cramers' rule
-                const scalar u =
-                (
-                   rhs.x()*e2.y()*e3.z() + e2.x()*e3.y()*rhs.z()
-                  + e3.x()*rhs.y()*e2.z() - rhs.x()*e3.y()*e2.z()
-                  - e2.x()*rhs.y()*e3.z() - e3.x()*e2.y()*rhs.z()
-                )/detA;
-
-                const scalar v =
-                (
-                    e1.x()*rhs.y()*e3.z() + rhs.x()*e3.y()*e1.z()
-                  + e3.x()*e1.y()*rhs.z() - e1.x()*e3.y()*rhs.z()
-                  - rhs.x()*e1.y()*e3.z() - e3.x()*rhs.y()*e1.z()
-                )/detA;
-
-                const scalar w =
-                (
-                    e1.x()*e2.y()*rhs.z() + e2.x()*rhs.y()*e1.z()
-                  + rhs.x()*e1.y()*e2.z() - e1.x()*rhs.y()*e2.z()
-                  - e2.x()*e1.y()*rhs.z() - rhs.x()*e2.y()*e1.z()
-                )/detA;
-
-                // Check if point is in tet
-                // value = 0 indicates position lies on a tet face
-                if
-                (
-                   (u + tol > 0) && (v + tol > 0) && (w + tol > 0)
-                && (u + v + w < 1 + tol)
-                )
-                {
-                    faceVertices_[0] = facePoints[0];
-                    faceVertices_[1] = facePoints[pointI];
-                    faceVertices_[2] = facePoints[pointI + 1];
-
-                    weights_[0] = u;
-                    weights_[1] = v;
-                    weights_[2] = w;
-                    weights_[3] = 1.0 - (u + v + w);
-
-                    return;
-                }
-                else
-                {
-                    scalar minU = mag(u);
-                    scalar minV = mag(v);
-                    scalar minW = mag(w);
-                    if (minU > 1.0)
-                    {
-                        minU -= 1.0;
-                    }
-                    if (minV > 1.0)
-                    {
-                        minV -= 1.0;
-                    }
-                    if (minW > 1.0)
-                    {
-                        minW -= 1.0;
-                    }
-                    const scalar minUVW = mag(minU + minV + minW);
-
-                    if (minUVW < minUVWClose)
-                    {
-                        minUVWClose = minUVW;
-                        pointIClose = pointI;
-                        faceClose = faceI;
-                    }
-                }
-            }
+    forAll(cellTets, tetI)
+    {
+        const tetIndices& tetIs = cellTets[tetI];
+
+        scalar nearDist = tetIs.tet(mesh).nearestPoint(position).distance();
 
-            pointI++;
+        if (nearDist < minNearDist)
+        {
+            minNearDist = nearDist;
+
+            nearestTetI = tetI;
         }
     }
 
     if (debug)
     {
         Pout<< "cellPointWeight::findTetrahedron" << nl
-            << "    Tetrahedron search failed; using closest tet values to "
-            << "point " << nl << "    cell: " << cellIndex << nl << endl;
+            << "    Tetrahedron search failed; using closest tet to point "
+            << position << nl
+            << "    cell: "
+            << cellI << nl
+            << endl;
     }
 
-    const labelList& facePointsClose = mesh.faces()[cellFaces[faceClose]];
-    faceVertices_[0] = facePointsClose[0];
-    faceVertices_[1] = facePointsClose[pointIClose];
-    faceVertices_[2] = facePointsClose[pointIClose + 1];
 
-    weights_[0] = 0.25;
-    weights_[1] = 0.25;
-    weights_[2] = 0.25;
-    weights_[3] = 0.25;
+    const tetIndices& tetIs = cellTets[nearestTetI];
+
+    const face& f = pFaces[tetIs.face()];
+
+    // Barycentric coordinates of the position, ignoring if the
+    // determinant is suitable.  If not, the return from barycentric
+    // to weights_ is safe.
+    tetIs.tet(mesh).barycentric(position, weights_);
+
+    faceVertices_[0] = f[tetIs.faceBasePt()];
+    faceVertices_[1] = f[tetIs.facePtA()];
+    faceVertices_[2] = f[tetIs.facePtB()];
 }
 
 
@@ -186,119 +142,112 @@ void Foam::cellPointWeight::findTriangle
 (
     const polyMesh& mesh,
     const vector& position,
-    const label faceIndex
+    const label faceI
 )
 {
     if (debug)
     {
         Pout<< "\nbool Foam::cellPointWeight::findTriangle" << nl
             << "position = " << position << nl
-            << "faceIndex = " << faceIndex << endl;
+            << "faceI = " << faceI << endl;
     }
 
-    // Initialise closest triangle variables
-    scalar minUVClose = VGREAT;
-    label pointIClose = 0;
+    List<tetIndices> faceTets = polyMeshTetDecomposition::faceTetIndices
+    (
+        mesh,
+        mesh.faceOwner()[faceI],
+        faceI
+    );
 
-    // Decompose each face into triangles, making a tet when
-    // augmented by the cell centre
-    const labelList& facePoints = mesh.faces()[faceIndex];
+    const scalar faceAreaSqr = magSqr(mesh.faceAreas()[faceI]);
 
-    const scalar faceArea2 = magSqr(mesh.faceAreas()[faceIndex]);
+    const face& f =  mesh.faces()[faceI];
 
-    label pointI = 1;
-    while ((pointI + 1) < facePoints.size())
+    forAll(faceTets, tetI)
     {
-        // Cartesian co-ordinates of the triangle vertices
-        const vector& P1 = mesh.points()[facePoints[0]];
-        const vector& P2 = mesh.points()[facePoints[pointI]];
-        const vector& P3 = mesh.points()[facePoints[pointI + 1]];
-
-        // Direction vectors
-        vector v1 = position - P1;
-        const vector v2 = P2 - P1;
-        const vector v3 = P3 - P1;
-
-        // Plane normal
-        vector n = v2 ^ v3;
-        n /= mag(n);
-
-        // Remove any offset to plane
-        v1 -= (n & v1)*v1;
-
-        // Helper variables
-        const scalar d12 = v1 & v2;
-        const scalar d13 = v1 & v3;
-        const scalar d22 = v2 & v2;
-        const scalar d23 = v2 & v3;
-        const scalar d33 = v3 & v3;
-
-        // Determinant of coefficients matrix
-        // Note: if det(A) = 0 the triangle is degenerate
-        const scalar detA = d22*d33 - d23*d23;
-
-        if (0.25*detA/faceArea2 > tol)
-        {
-            // Solve using Cramers' rule
-            const scalar u = (d12*d33 - d23*d13)/detA;
-            const scalar v = (d22*d13 - d12*d23)/detA;
+        const tetIndices& tetIs = faceTets[tetI];
+
+        List<scalar> triWeights(3);
 
-            // Check if point is in triangle
-            if ((u + tol > 0) && (v + tol > 0) && (u + v < 1 + tol))
+        // Barycentric coordinates of the position
+        scalar det = tetIs.faceTri(mesh).barycentric(position, triWeights);
+
+        if (0.25*mag(det)/faceAreaSqr > tol)
+        {
+            const scalar& u = triWeights[0];
+            const scalar& v = triWeights[1];
+
+            if
+            (
+                (u + tol > 0)
+             && (v + tol > 0)
+             && (u + v < 1 + tol)
+            )
             {
-                // Indices of the cell vertices making up the triangle
-                faceVertices_[0] = facePoints[0];
-                faceVertices_[1] = facePoints[pointI];
-                faceVertices_[2] = facePoints[pointI + 1];
+                // Weight[0] is for the cell centre.
+                weights_[0] = 0;
+                weights_[1] = triWeights[0];
+                weights_[2] = triWeights[1];
+                weights_[3] = triWeights[2];
 
-                weights_[0] = u;
-                weights_[1] = v;
-                weights_[2] = 1.0 - (u + v);
-                weights_[3] = 0.0;
+                faceVertices_[0] = f[tetIs.faceBasePt()];
+                faceVertices_[1] = f[tetIs.facePtA()];;
+                faceVertices_[2] = f[tetIs.facePtB()];;
 
                 return;
             }
-            else
-            {
-                scalar minU = mag(u);
-                scalar minV = mag(v);
-                if (minU > 1.0)
-                {
-                    minU -= 1.0;
-                }
-                if (minV > 1.0)
-                {
-                    minV -= 1.0;
-                }
-                const scalar minUV = mag(minU + minV);
-
-                if (minUV < minUVClose)
-                {
-                    minUVClose = minUV;
-                    pointIClose = pointI;
-                }
-            }
         }
+    }
+
+    // A suitable point in a triangle was not found, find the nearest.
+
+    scalar minNearDist = VGREAT;
+
+    label nearestTetI = -1;
+
+    forAll(faceTets, tetI)
+    {
+        const tetIndices& tetIs = faceTets[tetI];
+
+        scalar nearDist = tetIs.faceTri(mesh).nearestPoint(position).distance();
 
-        pointI++;
+        if (nearDist < minNearDist)
+        {
+            minNearDist = nearDist;
+
+            nearestTetI = tetI;
+        }
     }
 
     if (debug)
     {
-        Pout<< "Foam::cellPointWeight::findTriangle"
-            << "Triangle search failed; using closest triangle to point" << nl
-            << "    cell face: " << faceIndex << nl << endl;
+        Pout<< "cellPointWeight::findTriangle" << nl
+            << "    Triangle search failed; using closest tri to point "
+            << position << nl
+            << "    face: "
+            << faceI << nl
+            << endl;
     }
 
-    // Indices of the cell vertices making up the triangle
-    faceVertices_[0] = facePoints[0];
-    faceVertices_[1] = facePoints[pointIClose];
-    faceVertices_[2] = facePoints[pointIClose + 1];
+    const tetIndices& tetIs = faceTets[nearestTetI];
+
+    // Barycentric coordinates of the position, ignoring if the
+    // determinant is suitable.  If not, the return from barycentric
+    // to triWeights is safe.
+
+    List<scalar> triWeights(3);
+
+    tetIs.faceTri(mesh).barycentric(position, triWeights);
+
+    // Weight[0] is for the cell centre.
+    weights_[0] = 0;
+    weights_[1] = triWeights[0];
+    weights_[2] = triWeights[1];
+    weights_[3] = triWeights[2];
 
-    weights_[0] = 1.0/3.0;
-    weights_[1] = 1.0/3.0;
-    weights_[2] = 1.0/3.0;
-    weights_[3] = 0.0;
+    faceVertices_[0] = f[tetIs.faceBasePt()];
+    faceVertices_[1] = f[tetIs.facePtA()];
+    faceVertices_[2] = f[tetIs.facePtB()];
 }
 
 
@@ -308,21 +257,23 @@ Foam::cellPointWeight::cellPointWeight
 (
     const polyMesh& mesh,
     const vector& position,
-    const label cellIndex,
-    const label faceIndex
+    const label cellI,
+    const label faceI
 )
 :
-    cellIndex_(cellIndex)
+    cellI_(cellI),
+    weights_(4),
+    faceVertices_(3)
 {
-    if (faceIndex < 0)
+    if (faceI < 0)
     {
         // Face data not supplied
-        findTetrahedron(mesh, position, cellIndex);
+        findTetrahedron(mesh, position, cellI);
     }
     else
     {
         // Face data supplied
-        findTriangle(mesh, position, faceIndex);
+        findTriangle(mesh, position, faceI);
     }
 }
 
diff --git a/src/finiteVolume/interpolation/interpolation/interpolationCellPoint/cellPointWeight/cellPointWeight.H b/src/finiteVolume/interpolation/interpolation/interpolationCellPoint/cellPointWeight/cellPointWeight.H
index bb3647ac309..3042b926b1a 100644
--- a/src/finiteVolume/interpolation/interpolation/interpolationCellPoint/cellPointWeight/cellPointWeight.H
+++ b/src/finiteVolume/interpolation/interpolation/interpolationCellPoint/cellPointWeight/cellPointWeight.H
@@ -55,13 +55,13 @@ protected:
     // Protected data
 
        //- Cell index
-       const label cellIndex_;
+       const label cellI_;
 
        //- Weights applied to tet vertices
-       FixedList<scalar, 4> weights_;
+       List<scalar> weights_;
 
        //- Face vertex indices
-       FixedList<label, 3> faceVertices_;
+       List<label> faceVertices_;
 
 
     // Protected Member Functions
@@ -70,14 +70,14 @@ protected:
         (
             const polyMesh& mesh,
             const vector& position,
-            const label cellIndex
+            const label cellI
         );
 
         void findTriangle
         (
             const polyMesh& mesh,
             const vector& position,
-            const label faceIndex
+            const label faceI
         );
 
 
@@ -98,8 +98,8 @@ public:
         (
             const polyMesh& mesh,
             const vector& position,
-            const label nCell,
-            const label facei = -1
+            const label cellI,
+            const label faceI = -1
         );
 
 
@@ -108,17 +108,17 @@ public:
         //- Cell index
         inline label cell() const
         {
-            return cellIndex_;
+            return cellI_;
         }
 
         //- interpolation weights
-        inline const FixedList<scalar, 4>& weights() const
+        inline const List<scalar>& weights() const
         {
             return weights_;
         }
 
         //- interpolation addressing for points on face
-        inline const FixedList<label, 3>& faceVertices() const
+        inline const List<label>& faceVertices() const
         {
             return faceVertices_;
         }
diff --git a/src/finiteVolume/interpolation/interpolation/interpolationCellPoint/interpolationCellPoint.H b/src/finiteVolume/interpolation/interpolation/interpolationCellPoint/interpolationCellPoint.H
index e3424f3c4ce..657d8168ac1 100644
--- a/src/finiteVolume/interpolation/interpolation/interpolationCellPoint/interpolationCellPoint.H
+++ b/src/finiteVolume/interpolation/interpolation/interpolationCellPoint/interpolationCellPoint.H
@@ -25,7 +25,7 @@ Class
     Foam::interpolationCellPoint
 
 Description
-    Given cell centre values and point (vertex) values decompose into 
+    Given cell centre values and point (vertex) values decompose into
     tetrahedra and linear interpolate within them.
 
 \*---------------------------------------------------------------------------*/
@@ -82,8 +82,17 @@ public:
         inline Type interpolate
         (
             const vector& position,
-            const label nCell,
-            const label facei = -1
+            const label cellI,
+            const label faceI = -1
+        ) const;
+
+        //- Interpolate field to the given point in the tetrahedron
+        //  defined by the given indices.
+        inline Type interpolate
+        (
+            const vector& position,
+            const tetIndices& tetIs,
+            const label faceI = -1
         ) const;
 };
 
diff --git a/src/finiteVolume/interpolation/interpolation/interpolationCellPoint/interpolationCellPointI.H b/src/finiteVolume/interpolation/interpolation/interpolationCellPoint/interpolationCellPointI.H
index 16c555224ce..35171984cf6 100644
--- a/src/finiteVolume/interpolation/interpolation/interpolationCellPoint/interpolationCellPointI.H
+++ b/src/finiteVolume/interpolation/interpolation/interpolationCellPoint/interpolationCellPointI.H
@@ -31,13 +31,13 @@ inline Type Foam::interpolationCellPoint<Type>::interpolate
     const cellPointWeight& cpw
 ) const
 {
-    const FixedList<scalar, 4>& weights = cpw.weights();
-    const FixedList<label, 3>& faceVertices = cpw.faceVertices();
+    const List<scalar>& weights = cpw.weights();
+    const List<label>& faceVertices = cpw.faceVertices();
 
-    Type t = psip_[faceVertices[0]]*weights[0];
-    t += psip_[faceVertices[1]]*weights[1];
-    t += psip_[faceVertices[2]]*weights[2];
-    t += this->psi_[cpw.cell()]*weights[3];
+    Type t = this->psi_[cpw.cell()]*weights[0];
+    t += psip_[faceVertices[0]]*weights[1];
+    t += psip_[faceVertices[1]]*weights[2];
+    t += psip_[faceVertices[2]]*weights[3];
 
     return t;
 }
@@ -47,11 +47,66 @@ template<class Type>
 inline Type Foam::interpolationCellPoint<Type>::interpolate
 (
     const vector& position,
-    const label celli,
-    const label facei
+    const label cellI,
+    const label faceI
 ) const
 {
-    return interpolate(cellPointWeight(this->pMesh_, position, celli, facei));
+    return interpolate(cellPointWeight(this->pMesh_, position, cellI, faceI));
+}
+
+
+template<class Type>
+inline Type Foam::interpolationCellPoint<Type>::interpolate
+(
+    const vector& position,
+    const tetIndices& tetIs,
+    const label faceI
+) const
+{
+    // Assumes that the position is consistent with the supplied
+    // tetIndices.  Does not pay attention to whether or not faceI is
+    // supplied or not - the result will be essentially the same.
+    // Performs a consistency check, however.
+
+    if (faceI >= 0)
+    {
+        if (faceI != tetIs.face())
+        {
+            FatalErrorIn
+            (
+                "inline Type Foam::interpolationCellPoint<Type>::interpolate"
+                "("
+                    "const vector& position, "
+                    "const tetIndices& tetIs, "
+                    "const label faceI"
+                ") const"
+            )
+                << "specified face " << faceI << " inconsistent with the face "
+                << "stored by tetIndices: " << tetIs.face()
+                << exit(FatalError);
+        }
+    }
+
+    List<scalar> weights;
+
+    tetIs.tet(this->pMesh_).barycentric(position, weights);
+
+    const faceList& pFaces = this->pMesh_.faces();
+
+    const face& f = pFaces[tetIs.face()];
+
+    // Order of weights is the same as that of the vertices of the tet, i.e.
+    // cellCentre, faceBasePt, facePtA, facePtB.
+
+    Type t = this->psi_[tetIs.cell()]*weights[0];
+
+    t += psip_[f[tetIs.faceBasePt()]]*weights[1];
+
+    t += psip_[f[tetIs.facePtA()]]*weights[2];
+
+    t += psip_[f[tetIs.facePtB()]]*weights[3];
+
+    return t;
 }
 
 
diff --git a/src/finiteVolume/interpolation/interpolation/interpolationCellPointFace/interpolationCellPointFace.C b/src/finiteVolume/interpolation/interpolation/interpolationCellPointFace/interpolationCellPointFace.C
index e075cb90143..c583a6555c2 100644
--- a/src/finiteVolume/interpolation/interpolation/interpolationCellPointFace/interpolationCellPointFace.C
+++ b/src/finiteVolume/interpolation/interpolation/interpolationCellPointFace/interpolationCellPointFace.C
@@ -56,8 +56,8 @@ template<class Type>
 Type interpolationCellPointFace<Type>::interpolate
 (
     const vector& position,
-    const label nCell,
-    const label facei
+    const label cellI,
+    const label faceI
 ) const
 {
     Type ts[4];
@@ -68,10 +68,10 @@ Type interpolationCellPointFace<Type>::interpolate
     Type t = pTraits<Type>::zero;
 
     // only use face information when the position is on a face
-    if (facei < 0)
+    if (faceI < 0)
     {
-        const vector& cellCentre = this->pMesh_.cellCentres()[nCell];
-        const labelList& cellFaces = this->pMesh_.cells()[nCell];
+        const vector& cellCentre = this->pMesh_.cellCentres()[cellI];
+        const labelList& cellFaces = this->pMesh_.cells()[cellI];
 
         vector projection = position - cellCentre;
         tetPoints[3] = cellCentre;
@@ -85,9 +85,9 @@ Type interpolationCellPointFace<Type>::interpolate
         label closestFace = -1;
         scalar minDistance = GREAT;
 
-        forAll(cellFaces, facei)
+        forAll(cellFaces, faceI)
         {
-            label nFace = cellFaces[facei];
+            label nFace = cellFaces[faceI];
 
             vector normal = this->pMeshFaceAreas_[nFace];
             normal /= mag(normal);
@@ -160,10 +160,10 @@ Type interpolationCellPointFace<Type>::interpolate
         {
             minDistance = GREAT;
 
-            label facei = 0;
-            while (facei < cellFaces.size() && !foundTet)
+            label faceI = 0;
+            while (faceI < cellFaces.size() && !foundTet)
             {
-                label nFace = cellFaces[facei];
+                label nFace = cellFaces[faceI];
                 if (nFace < this->pMeshFaceAreas_.size())
                 {
                     foundTet = findTet
@@ -179,7 +179,7 @@ Type interpolationCellPointFace<Type>::interpolate
                         minDistance
                     );
                 }
-                facei++;
+                faceI++;
             }
         }
 
@@ -217,16 +217,16 @@ Type interpolationCellPointFace<Type>::interpolate
             }
             else
             {
-                label patchi =
+                label patchI =
                     this->pMesh_.boundaryMesh().whichPatch(closestFace);
 
                 // If the boundary patch is not empty use the face value
                 // else use the cell value
-                if (this->psi_.boundaryField()[patchi].size())
+                if (this->psi_.boundaryField()[patchI].size())
                 {
-                    ts[2] = this->psi_.boundaryField()[patchi]
+                    ts[2] = this->psi_.boundaryField()[patchI]
                     [
-                        this->pMesh_.boundaryMesh()[patchi].whichFace
+                        this->pMesh_.boundaryMesh()[patchI].whichFace
                         (
                             closestFace
                         )
@@ -234,11 +234,11 @@ Type interpolationCellPointFace<Type>::interpolate
                 }
                 else
                 {
-                    ts[2] = this->psi_[nCell];
+                    ts[2] = this->psi_[cellI];
                 }
             }
 
-            ts[3] = this->psi_[nCell];
+            ts[3] = this->psi_[cellI];
 
             for (label n=0; n<4; n++)
             {
@@ -251,9 +251,9 @@ Type interpolationCellPointFace<Type>::interpolate
         else
         {
             Info<< "interpolationCellPointFace<Type>::interpolate"
-                << "(const vector&, const label nCell) const : "
+                << "(const vector&, const label cellI) const : "
                 << "search failed; using closest cellFace value" << endl
-                << "cell number " << nCell << tab
+                << "cell number " << cellI << tab
                 << "position " << position << endl;
 
             if (closestFace < psis_.size())
@@ -262,16 +262,16 @@ Type interpolationCellPointFace<Type>::interpolate
             }
             else
             {
-                label patchi =
+                label patchI =
                     this->pMesh_.boundaryMesh().whichPatch(closestFace);
 
                 // If the boundary patch is not empty use the face value
                 // else use the cell value
-                if (this->psi_.boundaryField()[patchi].size())
+                if (this->psi_.boundaryField()[patchI].size())
                 {
-                    t = this->psi_.boundaryField()[patchi]
+                    t = this->psi_.boundaryField()[patchI]
                     [
-                        this->pMesh_.boundaryMesh()[patchi].whichFace
+                        this->pMesh_.boundaryMesh()[patchI].whichFace
                         (
                             closestFace
                         )
@@ -279,7 +279,7 @@ Type interpolationCellPointFace<Type>::interpolate
                 }
                 else
                 {
-                    t = this->psi_[nCell];
+                    t = this->psi_[cellI];
                 }
             }
         }
@@ -289,7 +289,7 @@ Type interpolationCellPointFace<Type>::interpolate
         bool foundTriangle = findTriangle
         (
             position,
-            facei,
+            faceI,
             tetPointLabels,
             phi
         );
@@ -304,48 +304,48 @@ Type interpolationCellPointFace<Type>::interpolate
             }
 
             // ... and the face value
-            if (facei < psis_.size())
+            if (faceI < psis_.size())
             {
-                t += phi[2]*psis_[facei];
+                t += phi[2]*psis_[faceI];
             }
             else
             {
-                label patchi = this->pMesh_.boundaryMesh().whichPatch(facei);
+                label patchI = this->pMesh_.boundaryMesh().whichPatch(faceI);
 
                 // If the boundary patch is not empty use the face value
                 // else use the cell value
-                if (this->psi_.boundaryField()[patchi].size())
+                if (this->psi_.boundaryField()[patchI].size())
                 {
-                    t += phi[2]*this->psi_.boundaryField()[patchi]
-                        [this->pMesh_.boundaryMesh()[patchi].whichFace(facei)];
+                    t += phi[2]*this->psi_.boundaryField()[patchI]
+                        [this->pMesh_.boundaryMesh()[patchI].whichFace(faceI)];
                 }
                 else
                 {
-                    t += phi[2]*this->psi_[nCell];
+                    t += phi[2]*this->psi_[cellI];
                 }
             }
         }
         else
         {
             // use face value only
-            if (facei < psis_.size())
+            if (faceI < psis_.size())
             {
-                t = psis_[facei];
+                t = psis_[faceI];
             }
             else
             {
-                label patchi = this->pMesh_.boundaryMesh().whichPatch(facei);
+                label patchI = this->pMesh_.boundaryMesh().whichPatch(faceI);
 
                 // If the boundary patch is not empty use the face value
                 // else use the cell value
-                if (this->psi_.boundaryField()[patchi].size())
+                if (this->psi_.boundaryField()[patchI].size())
                 {
-                    t = this->psi_.boundaryField()[patchi]
-                        [this->pMesh_.boundaryMesh()[patchi].whichFace(facei)];
+                    t = this->psi_.boundaryField()[patchI]
+                        [this->pMesh_.boundaryMesh()[patchI].whichFace(faceI)];
                 }
                 else
                 {
-                    t = this->psi_[nCell];
+                    t = this->psi_[cellI];
                 }
             }
         }
diff --git a/src/finiteVolume/interpolation/interpolation/interpolationCellPointFace/interpolationCellPointFace.H b/src/finiteVolume/interpolation/interpolation/interpolationCellPointFace/interpolationCellPointFace.H
index e32b2ad68bb..fb2f9954f81 100644
--- a/src/finiteVolume/interpolation/interpolation/interpolationCellPointFace/interpolationCellPointFace.H
+++ b/src/finiteVolume/interpolation/interpolation/interpolationCellPointFace/interpolationCellPointFace.H
@@ -99,8 +99,8 @@ public:
         Type interpolate
         (
             const vector& position,
-            const label nCell,
-            const label facei = -1
+            const label cellI,
+            const label faceI = -1
         ) const;
 };
 
diff --git a/src/finiteVolume/interpolation/interpolation/interpolationCellPointWallModified/cellPointWeightWallModified/cellPointWeightWallModified.C b/src/finiteVolume/interpolation/interpolation/interpolationCellPointWallModified/cellPointWeightWallModified/cellPointWeightWallModified.C
index 5448de2dafe..8716ade026b 100644
--- a/src/finiteVolume/interpolation/interpolation/interpolationCellPointWallModified/cellPointWeightWallModified/cellPointWeightWallModified.C
+++ b/src/finiteVolume/interpolation/interpolation/interpolationCellPointWallModified/cellPointWeightWallModified/cellPointWeightWallModified.C
@@ -24,9 +24,6 @@ License
 \*---------------------------------------------------------------------------*/
 
 #include "cellPointWeightWallModified.H"
-#include "wallPolyPatch.H"
-#include "polyMesh.H"
-#include "polyBoundaryMesh.H"
 
 // * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
 
@@ -34,36 +31,30 @@ Foam::cellPointWeightWallModified::cellPointWeightWallModified
 (
     const polyMesh& mesh,
     const vector& position,
-    const label cellIndex,
-    const label faceIndex
+    const label cellI,
+    const label faceI
 )
 :
-    cellPointWeight(mesh, position, cellIndex, faceIndex)
+    cellPointWeight(mesh, position, cellI, faceI)
 {
-    if (faceIndex < 0)
-    {
-        findTetrahedron(mesh, position, cellIndex);
-    }
-    else
+    // findTetrahedron or findTriangle will already have been called
+    // by the cellPointWeight constructor
+
+    if (faceI >= 0)
     {
         const polyBoundaryMesh& bm = mesh.boundaryMesh();
-        label patchI = bm.whichPatch(faceIndex);
+        label patchI = bm.whichPatch(faceI);
         if (patchI != -1)
         {
             if (isA<wallPolyPatch>(bm[patchI]))
             {
                 // Apply cell centre value wall faces
-                weights_[0] = 0.0;
+                weights_[0] = 1.0;
                 weights_[1] = 0.0;
                 weights_[2] = 0.0;
-                weights_[3] = 1.0;
+                weights_[3] = 0.0;
             }
         }
-        else
-        {
-            // Interpolate
-            findTriangle(mesh, position, faceIndex);
-        }
     }
 }
 
diff --git a/src/finiteVolume/interpolation/interpolation/interpolationCellPointWallModified/cellPointWeightWallModified/cellPointWeightWallModified.H b/src/finiteVolume/interpolation/interpolation/interpolationCellPointWallModified/cellPointWeightWallModified/cellPointWeightWallModified.H
index db4e35575d8..0e12a145284 100644
--- a/src/finiteVolume/interpolation/interpolation/interpolationCellPointWallModified/cellPointWeightWallModified/cellPointWeightWallModified.H
+++ b/src/finiteVolume/interpolation/interpolation/interpolationCellPointWallModified/cellPointWeightWallModified/cellPointWeightWallModified.H
@@ -36,6 +36,9 @@ SourceFiles
 #define cellPointWeightWallModified_H
 
 #include "cellPointWeight.H"
+#include "wallPolyPatch.H"
+#include "polyMesh.H"
+#include "polyBoundaryMesh.H"
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
@@ -61,8 +64,8 @@ public:
         (
             const polyMesh& mesh,
             const vector& position,
-            const label nCell,
-            const label facei = -1
+            const label cellI,
+            const label faceI = -1
         );
 };
 
diff --git a/src/finiteVolume/interpolation/interpolation/interpolationCellPointWallModified/interpolationCellPointWallModified.H b/src/finiteVolume/interpolation/interpolation/interpolationCellPointWallModified/interpolationCellPointWallModified.H
index d7b35c9a383..8bef893c58e 100644
--- a/src/finiteVolume/interpolation/interpolation/interpolationCellPointWallModified/interpolationCellPointWallModified.H
+++ b/src/finiteVolume/interpolation/interpolation/interpolationCellPointWallModified/interpolationCellPointWallModified.H
@@ -74,8 +74,17 @@ public:
         inline Type interpolate
         (
             const vector& position,
-            const label nCell,
-            const label facei = -1
+            const label cellI,
+            const label faceI = -1
+        ) const;
+
+        //- Interpolate field to the given point in the tetrahedron
+        //  defined by the given indices.
+        inline Type interpolate
+        (
+            const vector& position,
+            const tetIndices& tetIs,
+            const label faceI = -1
         ) const;
 };
 
diff --git a/src/finiteVolume/interpolation/interpolation/interpolationCellPointWallModified/interpolationCellPointWallModifiedI.H b/src/finiteVolume/interpolation/interpolation/interpolationCellPointWallModified/interpolationCellPointWallModifiedI.H
index d0284f0b0f5..d0ed41eef73 100644
--- a/src/finiteVolume/interpolation/interpolation/interpolationCellPointWallModified/interpolationCellPointWallModifiedI.H
+++ b/src/finiteVolume/interpolation/interpolation/interpolationCellPointWallModified/interpolationCellPointWallModifiedI.H
@@ -31,13 +31,13 @@ inline Type Foam::interpolationCellPointWallModified<Type>::interpolate
     const cellPointWeightWallModified& cpw
 ) const
 {
-    const FixedList<scalar, 4>& weights = cpw.weights();
-    const FixedList<label, 3>& faceVertices = cpw.faceVertices();
+    const List<scalar>& weights = cpw.weights();
+    const List<label>& faceVertices = cpw.faceVertices();
 
-    Type t = this->psip_[faceVertices[0]]*weights[0];
-    t += this->psip_[faceVertices[1]]*weights[1];
-    t += this->psip_[faceVertices[2]]*weights[2];
-    t += this->psi_[cpw.cell()]*weights[3];
+    Type t = this->psi_[cpw.cell()]*weights[0];
+    t += this->psip_[faceVertices[0]]*weights[1];
+    t += this->psip_[faceVertices[1]]*weights[2];
+    t += this->psip_[faceVertices[2]]*weights[3];
 
     return t;
 }
@@ -47,21 +47,73 @@ template<class Type>
 inline Type Foam::interpolationCellPointWallModified<Type>::interpolate
 (
     const vector& position,
-    const label celli,
-    const label facei
+    const label cellI,
+    const label faceI
 ) const
 {
-    return
-        interpolate
+    return interpolate
+    (
+        cellPointWeightWallModified
         (
-            cellPointWeightWallModified
+            this->pMesh_,
+            position,
+            cellI,
+            faceI
+        )
+    );
+}
+
+
+template<class Type>
+inline Type Foam::interpolationCellPointWallModified<Type>::interpolate
+(
+    const vector& position,
+    const tetIndices& tetIs,
+    const label faceI
+) const
+{
+    if (faceI >= 0)
+    {
+        if (faceI != tetIs.face())
+        {
+            FatalErrorIn
             (
-                this->pMesh_,
-                position,
-                celli,
-                facei
+                "inline Type "
+                "Foam::interpolationCellPointWallModifie<Type>::interpolate"
+                "("
+                    "const vector& position, "
+                    "const tetIndices& tetIs, "
+                    "const label faceI"
+                ") const"
             )
-        );
+                << "specified face " << faceI << " inconsistent with the face "
+                << "stored by tetIndices: " << tetIs.face()
+                << exit(FatalError);
+        }
+
+        const polyBoundaryMesh& bm = this->pMesh_.boundaryMesh();
+        label patchI = bm.whichPatch(faceI);
+
+        if (patchI != -1)
+        {
+            if (isA<wallPolyPatch>(bm[patchI]))
+            {
+                Type t = this->psi_[tetIs.cell()];
+
+                return t;
+            }
+        }
+    }
+
+    // If the wall face selection did not return, then use the normal
+    // interpolate method
+
+    return interpolationCellPoint<Type>::interpolate
+    (
+        position,
+        tetIs,
+        faceI
+    );
 }
 
 
diff --git a/src/finiteVolume/interpolation/interpolation/interpolationPoint/interpolationPoint.H b/src/finiteVolume/interpolation/interpolation/interpolationPoint/interpolationPoint.H
index a3dc106eb02..46f102187fc 100644
--- a/src/finiteVolume/interpolation/interpolation/interpolationPoint/interpolationPoint.H
+++ b/src/finiteVolume/interpolation/interpolation/interpolationPoint/interpolationPoint.H
@@ -81,8 +81,8 @@ public:
         inline Type interpolate
         (
             const vector& position,
-            const label nCell,
-            const label facei = -1
+            const label cellI,
+            const label faceI = -1
         ) const;
 };
 
diff --git a/src/finiteVolume/interpolation/interpolation/interpolationPoint/interpolationPointI.H b/src/finiteVolume/interpolation/interpolation/interpolationPoint/interpolationPointI.H
index e297e569fca..624bb9f5627 100644
--- a/src/finiteVolume/interpolation/interpolation/interpolationPoint/interpolationPointI.H
+++ b/src/finiteVolume/interpolation/interpolation/interpolationPoint/interpolationPointI.H
@@ -39,13 +39,13 @@ template<class Type>
 inline Type Foam::interpolationPoint<Type>::interpolate
 (
     const vector& position,
-    const label celli,
-    const label facei
+    const label cellI,
+    const label faceI
 ) const
 {
     return interpolate
     (
-        pointMVCWeight(this->pMesh_, position, celli, facei)
+        pointMVCWeight(this->pMesh_, position, cellI, faceI)
     );
 }
 
diff --git a/src/finiteVolume/interpolation/interpolation/interpolationPoint/pointMVCWeight.H b/src/finiteVolume/interpolation/interpolation/interpolationPoint/pointMVCWeight.H
index a1cfd4507b0..9cd2bec95b3 100644
--- a/src/finiteVolume/interpolation/interpolation/interpolationPoint/pointMVCWeight.H
+++ b/src/finiteVolume/interpolation/interpolation/interpolationPoint/pointMVCWeight.H
@@ -119,8 +119,8 @@ public:
         (
             const polyMesh& mesh,
             const vector& position,
-            const label nCell,
-            const label facei = -1
+            const label cellI,
+            const label faceI = -1
         );
 
 
diff --git a/src/lagrangian/basic/Cloud/Cloud.C b/src/lagrangian/basic/Cloud/Cloud.C
index 8f817df7fb1..2b39e286f26 100644
--- a/src/lagrangian/basic/Cloud/Cloud.C
+++ b/src/lagrangian/basic/Cloud/Cloud.C
@@ -30,6 +30,41 @@ License
 #include "mapPolyMesh.H"
 #include "Time.H"
 #include "OFstream.H"
+#include "wallPolyPatch.H"
+
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
+
+template<class ParticleType>
+const Foam::scalar Foam::Cloud<ParticleType>::trackingCorrectionTol = 1e-5;
+
+
+// * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * * //
+
+template<class ParticleType>
+void Foam::Cloud<ParticleType>::calcCellWallFaces() const
+{
+    cellWallFacesPtr_.reset(new PackedBoolList(pMesh().nCells(), false));
+
+    PackedBoolList& cellWallFaces = cellWallFacesPtr_();
+
+    const polyBoundaryMesh& patches = pMesh().boundaryMesh();
+
+    forAll(patches, patchI)
+    {
+        if (isA<wallPolyPatch>(patches[patchI]))
+        {
+            const polyPatch& patch = patches[patchI];
+
+            const labelList& pFaceCells = patch.faceCells();
+
+            forAll(pFaceCells, pFCI)
+            {
+                cellWallFaces[pFaceCells[pFCI]] = true;
+            }
+        }
+    }
+}
+
 
 // * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
 
@@ -43,7 +78,11 @@ Foam::Cloud<ParticleType>::Cloud
     cloud(pMesh),
     IDLList<ParticleType>(),
     polyMesh_(pMesh),
-    particleCount_(0)
+    particleCount_(0),
+    labels_(),
+    cellTree_(),
+    nTrackingRescues_(),
+    cellWallFacesPtr_()
 {
     IDLList<ParticleType>::operator=(particles);
 }
@@ -60,7 +99,11 @@ Foam::Cloud<ParticleType>::Cloud
     cloud(pMesh, cloudName),
     IDLList<ParticleType>(),
     polyMesh_(pMesh),
-    particleCount_(0)
+    particleCount_(0),
+    labels_(),
+    cellTree_(),
+    nTrackingRescues_(),
+    cellWallFacesPtr_()
 {
     IDLList<ParticleType>::operator=(particles);
 }
@@ -68,6 +111,250 @@ Foam::Cloud<ParticleType>::Cloud
 
 // * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
 
+template<class ParticleType>
+void Foam::Cloud<ParticleType>::findCellFacePt
+(
+    const point& pt,
+    label& cellI,
+    label& tetFaceI,
+    label& tetPtI
+) const
+{
+    cellI = -1;
+    tetFaceI = -1;
+    tetPtI = -1;
+
+    const indexedOctree<treeDataCell>& tree = cellTree();
+
+    // Find nearest cell to the point
+
+    pointIndexHit info = tree.findNearest(pt, sqr(GREAT));
+
+    if (info.hit())
+    {
+        label nearestCellI = tree.shapes().cellLabels()[info.index()];
+
+        // Check the nearest cell to see if the point is inside.
+        findFacePt(nearestCellI, pt, tetFaceI, tetPtI);
+
+        if (tetFaceI != -1)
+        {
+            // Point was in the nearest cell
+
+            cellI = nearestCellI;
+
+            return;
+        }
+        else
+        {
+            // Check the other possible cells that the point may be in
+
+            labelList testCells = tree.findIndices(pt);
+
+            forAll(testCells, pCI)
+            {
+                label testCellI = tree.shapes().cellLabels()[testCells[pCI]];
+
+                if (testCellI == nearestCellI)
+                {
+                    // Don't retest the nearest cell
+
+                    continue;
+                }
+
+                // Check the test cell to see if the point is inside.
+                findFacePt(testCellI, pt, tetFaceI, tetPtI);
+
+                if (tetFaceI != -1)
+                {
+                    // Point was in the test cell
+
+                    cellI = testCellI;
+
+                    return;
+                }
+            }
+        }
+    }
+    else
+    {
+        FatalErrorIn
+        (
+            "void Foam::Cloud<ParticleType>::findCellFacePt"
+            "("
+                "const point& pt, "
+                "label& cellI, "
+                "label& tetFaceI, "
+                "label& tetPtI"
+            ") const"
+        )   << "Did not find nearest cell in search tree."
+            << abort(FatalError);
+    }
+}
+
+
+template<class ParticleType>
+void Foam::Cloud<ParticleType>::findFacePt
+(
+    label cellI,
+    const point& pt,
+    label& tetFaceI,
+    label& tetPtI
+) const
+{
+    tetFaceI = -1;
+    tetPtI = -1;
+
+    List<tetIndices> cellTets = polyMeshTetDecomposition::cellTetIndices
+    (
+        polyMesh_,
+        cellI
+    );
+
+    forAll(cellTets, tetI)
+    {
+        const tetIndices& cellTetIs = cellTets[tetI];
+
+        if (inTet(pt, cellTetIs.tet(polyMesh_)))
+        {
+            tetFaceI = cellTetIs.face();
+            tetPtI = cellTetIs.tetPt();
+
+            return;
+        }
+    }
+}
+
+
+template<class ParticleType>
+bool Foam::Cloud<ParticleType>::inTet
+(
+    const point& pt,
+    const tetPointRef& tet
+) const
+{
+    // For robustness, assuming that the point is in the tet unless
+    // "definitively" shown otherwise by obtaining a positive dot
+    // product greater than a tolerance of SMALL.
+
+    // The tet is defined: tet(Cc, tetBasePt, pA, pB) where the normal
+    // vectors and base points for the half-space planes are:
+    // area[0] = tet.Sa();
+    // area[1] = tet.Sb();
+    // area[2] = tet.Sc();
+    // area[3] = tet.Sd();
+    // planeBase[0] = tetBasePt = tet.b()
+    // planeBase[1] = ptA       = tet.c()
+    // planeBase[2] = tetBasePt = tet.b()
+    // planeBase[3] = tetBasePt = tet.b()
+
+    vector n = vector::zero;
+
+    {
+        // 0, a
+        const point& basePt = tet.b();
+
+        n = tet.Sa();
+        n /= (mag(n) + VSMALL);
+
+        if (((pt - basePt) & n) > SMALL)
+        {
+            return false;
+        }
+    }
+
+    {
+        // 1, b
+        const point& basePt = tet.c();
+
+        n = tet.Sb();
+        n /= (mag(n) + VSMALL);
+
+        if (((pt - basePt) & n) > SMALL)
+        {
+            return false;
+        }
+    }
+
+    {
+        // 2, c
+        const point& basePt = tet.b();
+
+        n = tet.Sc();
+        n /= (mag(n) + VSMALL);
+
+        if (((pt - basePt) & n) > SMALL)
+        {
+            return false;
+        }
+    }
+
+    {
+        // 3, d
+        const point& basePt = tet.b();
+
+        n = tet.Sd();
+        n /= (mag(n) + VSMALL);
+
+        if (((pt - basePt) & n) > SMALL)
+        {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+
+template<class ParticleType>
+const Foam::indexedOctree<Foam::treeDataCell>&
+Foam::Cloud<ParticleType>::cellTree() const
+{
+    if (cellTree_.empty())
+    {
+        treeBoundBox overallBb(polyMesh_.points());
+
+        Random rndGen(261782);
+
+        overallBb = overallBb.extend(rndGen, 1E-4);
+        overallBb.min() -= point(ROOTVSMALL, ROOTVSMALL, ROOTVSMALL);
+        overallBb.max() += point(ROOTVSMALL, ROOTVSMALL, ROOTVSMALL);
+
+        cellTree_.reset
+        (
+            new indexedOctree<treeDataCell>
+            (
+                treeDataCell
+                (
+                    false,      // not cache bb
+                    polyMesh_
+                ),
+                overallBb,
+                8,              // maxLevel
+                10,             // leafsize
+                3.0             // duplicity
+            )
+        );
+    }
+
+    return cellTree_();
+}
+
+
+template<class ParticleType>
+const Foam::PackedBoolList& Foam::Cloud<ParticleType>::cellHasWallFaces()
+const
+{
+    if (!cellWallFacesPtr_.valid())
+    {
+        calcCellWallFaces();
+    }
+
+    return cellWallFacesPtr_();
+}
+
+
+
 template<class ParticleType>
 Foam::label Foam::Cloud<ParticleType>::getNewParticleID() const
 {
@@ -131,6 +418,9 @@ void Foam::Cloud<ParticleType>::move(TrackingData& td)
         pIter().stepFraction() = 0;
     }
 
+    // Reset nTrackingRescues
+    nTrackingRescues_ = 0;
+
     // While there are particles to transfer
     while (true)
     {
@@ -162,29 +452,29 @@ void Foam::Cloud<ParticleType>::move(TrackingData& td)
             {
                 // If we are running in parallel and the particle is on a
                 // boundary face
-                if (Pstream::parRun() && p.facei_ >= pMesh().nInternalFaces())
+                if (Pstream::parRun() && p.faceI_ >= pMesh().nInternalFaces())
                 {
-                    label patchi = pbm.whichPatch(p.facei_);
+                    label patchI = pbm.whichPatch(p.faceI_);
 
                     // ... and the face is on a processor patch
                     // prepare it for transfer
-                    if (procPatchIndices[patchi] != -1)
+                    if (procPatchIndices[patchI] != -1)
                     {
                         label n = neighbourProcIndices
                         [
                             refCast<const processorPolyPatch>
                             (
-                                pbm[patchi]
+                                pbm[patchI]
                             ).neighbProcNo()
                         ];
 
-                        p.prepareForParallelTransfer(patchi, td);
+                        p.prepareForParallelTransfer(patchI, td);
 
                         particleTransferLists[n].append(this->remove(&p));
 
                         patchIndexTransferLists[n].append
                         (
-                            procPatchNeighbours[patchi]
+                            procPatchNeighbours[patchI]
                         );
                     }
                 }
@@ -270,15 +560,22 @@ void Foam::Cloud<ParticleType>::move(TrackingData& td)
                 {
                     ParticleType& newp = newpIter();
 
-                    label patchi = procPatches[receivePatchIndex[pI++]];
+                    label patchI = procPatches[receivePatchIndex[pI++]];
 
-                    newp.correctAfterParallelTransfer(patchi, td);
+                    newp.correctAfterParallelTransfer(patchI, td);
 
                     addParticle(newParticles.remove(&newp));
                 }
             }
         }
     }
+
+    reduce(nTrackingRescues_, sumOp<label>());
+
+    if (nTrackingRescues_ > 0)
+    {
+        Info<< nTrackingRescues_ << " tracking rescue corrections" << endl;
+    }
 }
 
 
@@ -294,24 +591,30 @@ void Foam::Cloud<ParticleType>::autoMap(const mapPolyMesh& mapper)
     const labelList& reverseCellMap = mapper.reverseCellMap();
     const labelList& reverseFaceMap = mapper.reverseFaceMap();
 
+    // Reset stored data that relies on the mesh
+    cellTree_.clear();
+    cellWallFacesPtr_.clear();
+
     forAllIter(typename Cloud<ParticleType>, *this, pIter)
     {
-        if (reverseCellMap[pIter().celli_] >= 0)
+        if (reverseCellMap[pIter().cellI_] >= 0)
         {
-            pIter().celli_ = reverseCellMap[pIter().celli_];
+            pIter().cellI_ = reverseCellMap[pIter().cellI_];
 
-            if (pIter().facei_ >= 0 && reverseFaceMap[pIter().facei_] >= 0)
+            if (pIter().faceI_ >= 0 && reverseFaceMap[pIter().faceI_] >= 0)
             {
-                pIter().facei_ = reverseFaceMap[pIter().facei_];
+                pIter().faceI_ = reverseFaceMap[pIter().faceI_];
             }
             else
             {
-                pIter().facei_ = -1;
+                pIter().faceI_ = -1;
             }
+
+            pIter().initCellFacePt();
         }
         else
         {
-            label trackStartCell = mapper.mergedCell(pIter().celli_);
+            label trackStartCell = mapper.mergedCell(pIter().cellI_);
 
             if (trackStartCell < 0)
             {
@@ -319,9 +622,14 @@ void Foam::Cloud<ParticleType>::autoMap(const mapPolyMesh& mapper)
             }
 
             vector p = pIter().position();
+
             const_cast<vector&>(pIter().position()) =
                 polyMesh_.cellCentres()[trackStartCell];
+
             pIter().stepFraction() = 0;
+
+            pIter().initCellFacePt();
+
             pIter().track(p);
         }
     }
diff --git a/src/lagrangian/basic/Cloud/Cloud.H b/src/lagrangian/basic/Cloud/Cloud.H
index 25cf3fce33d..0c9b32ebac5 100644
--- a/src/lagrangian/basic/Cloud/Cloud.H
+++ b/src/lagrangian/basic/Cloud/Cloud.H
@@ -40,6 +40,11 @@ SourceFiles
 #include "IOField.H"
 #include "IOFieldField.H"
 #include "polyMesh.H"
+#include "indexedOctree.H"
+#include "treeDataCell.H"
+#include "tetPointRef.H"
+#include "polyMeshTetDecomposition.H"
+#include "PackedBoolList.H"
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
@@ -78,15 +83,28 @@ class Cloud
         //- Overall count of particles ever created. Never decreases.
         mutable label particleCount_;
 
-        //- Temporary storage for addressing. Used in findFaces.
+        //- Temporary storage for addressing. Used in findTris.
         mutable DynamicList<label> labels_;
 
+        //- Search tree to allow spatial tet searching
+        mutable autoPtr<indexedOctree<treeDataCell> > cellTree_;
+
+        //- Count of how many tracking rescue corrections have been
+        //  applied
+        mutable label nTrackingRescues_;
+
+        //- Does the cell have wall faces
+        mutable autoPtr<PackedBoolList> cellWallFacesPtr_;
+
 
     // Private Member Functions
 
         //- Initialise cloud on IO constructor
         void initCloud(const bool checkClass);
 
+        //- Find all cells which have wall faces
+        void calcCellWallFaces() const;
+
         //- Read cloud properties dictionary
         void readCloudUniformProperties();
 
@@ -115,6 +133,10 @@ public:
         //- Name of cloud properties dictionary
         static word cloudPropertiesName;
 
+        //- Fraction of distance to tet centre to move a particle to
+        // 'rescue' it from a tracking problem
+        static const scalar trackingCorrectionTol;
+
 
     // Constructors
 
@@ -163,27 +185,27 @@ public:
             }
 
             //- Is this global face an internal face?
-            bool internalFace(const label facei) const
+            bool internalFace(const label faceI) const
             {
-                return polyMesh_.isInternalFace(facei);
+                return polyMesh_.isInternalFace(faceI);
             }
 
             //- Is this global face a boundary face?
-            bool boundaryFace(const label facei) const
+            bool boundaryFace(const label faceI) const
             {
-                return !internalFace(facei);
+                return !internalFace(faceI);
             }
 
             //- Which patch is this global face on
-            label facePatch(const label facei) const
+            label facePatch(const label faceI) const
             {
-                return polyMesh_.boundaryMesh().whichPatch(facei);
+                return polyMesh_.boundaryMesh().whichPatch(faceI);
             }
 
             //- Which face of this patch is this global face
-            label patchFace(const label patchi, const label facei) const
+            label patchFace(const label patchI, const label faceI) const
             {
-                return polyMesh_.boundaryMesh()[patchi].whichFace(facei);
+                return polyMesh_.boundaryMesh()[patchI].whichFace(faceI);
             }
 
             label size() const
@@ -191,6 +213,61 @@ public:
                 return IDLList<ParticleType>::size();
             };
 
+            //- Find the cell, tetFaceI and tetPtI for the given
+            //  position
+            void findCellFacePt
+            (
+                const point& pt,
+                label& cellI,
+                label& tetFaceI,
+                label& tetPtI
+            ) const;
+
+            //- Find the tetFaceI and tetPtI for the given position in
+            //  the supplied cell, tetFaceI and tetPtI = -1 if not
+            //  found
+            void findFacePt
+            (
+                label cellI,
+                const point& pt,
+                label& tetFaceI,
+                label& tetPtI
+            ) const;
+
+            //- Test if the given position is inside the give tet
+            bool inTet
+            (
+                const point& pt,
+                const tetPointRef& tet
+            ) const;
+
+            //- Build (if necessary) and return the cell search tree
+            const indexedOctree<treeDataCell>& cellTree() const;
+
+            //- Return nTrackingRescues
+            label nTrackingRescues() const
+            {
+                return nTrackingRescues_;
+            }
+
+            //- Increment the nTrackingRescues counter
+            void trackingRescue() const
+            {
+                nTrackingRescues_++;
+            }
+
+            //- Whether each cell has any wall faces (demand driven data)
+            const PackedBoolList& cellHasWallFaces() const;
+
+            //- Switch to specify if particles of the cloud can return
+            //  non-zero wall distance values.  By default, assume
+            //  that they can't (default for wallImpactDistance in
+            //  Particle is 0.0).
+            virtual bool hasWallImpactDistance() const
+            {
+                return false;
+            }
+
 
             // Iterators
 
diff --git a/src/lagrangian/basic/Cloud/CloudIO.C b/src/lagrangian/basic/Cloud/CloudIO.C
index 388fba1c574..c12d12d6a51 100644
--- a/src/lagrangian/basic/Cloud/CloudIO.C
+++ b/src/lagrangian/basic/Cloud/CloudIO.C
@@ -126,6 +126,13 @@ void Foam::Cloud<ParticleType>::initCloud(const bool checkClass)
             << "    " << ioP.path() << nl
             << "    assuming the initial cloud contains 0 particles." << endl;
     }
+
+    forAllIter(typename Cloud<ParticleType>, *this, pIter)
+    {
+        ParticleType& p = pIter();
+
+        p.initCellFacePt();
+    }
 }
 
 
@@ -140,7 +147,11 @@ Foam::Cloud<ParticleType>::Cloud
 :
     cloud(pMesh),
     polyMesh_(pMesh),
-    particleCount_(0)
+    particleCount_(0),
+    labels_(),
+    cellTree_(),
+    nTrackingRescues_(),
+    cellWallFacesPtr_()
 {
     initCloud(checkClass);
 }
@@ -156,7 +167,11 @@ Foam::Cloud<ParticleType>::Cloud
 :
     cloud(pMesh, cloudName),
     polyMesh_(pMesh),
-    particleCount_(0)
+    particleCount_(0),
+    labels_(),
+    cellTree_(),
+    nTrackingRescues_(),
+    cellWallFacesPtr_()
 {
     initCloud(checkClass);
 }
diff --git a/src/lagrangian/basic/Make/options b/src/lagrangian/basic/Make/options
index e69de29bb2d..4b2f0a059fb 100644
--- a/src/lagrangian/basic/Make/options
+++ b/src/lagrangian/basic/Make/options
@@ -0,0 +1,5 @@
+EXE_INC = \
+    -I$(LIB_SRC)/meshTools/lnInclude
+
+LIB_LIBS = \
+    -lmeshTools
diff --git a/src/lagrangian/basic/Particle/Particle.C b/src/lagrangian/basic/Particle/Particle.C
index 99c00f07e55..9ad51cdeaff 100644
--- a/src/lagrangian/basic/Particle/Particle.C
+++ b/src/lagrangian/basic/Particle/Particle.C
@@ -29,73 +29,20 @@ License
 #include "symmetryPolyPatch.H"
 #include "cyclicPolyPatch.H"
 #include "processorPolyPatch.H"
-#include "wallPolyPatch.H"
 #include "transform.H"
 
 // * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
 
-template<class ParticleType>
-void Foam::Particle<ParticleType>::findFaces
-(
-    const vector& position,
-    DynamicList<label>& faceList
-) const
-{
-    const polyMesh& mesh = cloud_.polyMesh_;
-    const labelList& faces = mesh.cells()[celli_];
-    const vector& C = mesh.cellCentres()[celli_];
-
-    faceList.clear();
-    forAll(faces, i)
-    {
-        label facei = faces[i];
-        scalar lam = lambda(C, position, facei);
-
-        if ((lam > 0) && (lam < 1.0))
-        {
-            faceList.append(facei);
-        }
-    }
-}
-
-
-template<class ParticleType>
-void Foam::Particle<ParticleType>::findFaces
-(
-    const vector& position,
-    const label celli,
-    const scalar stepFraction,
-    DynamicList<label>& faceList
-) const
-{
-    const polyMesh& mesh = cloud_.pMesh();
-    const labelList& faces = mesh.cells()[celli];
-    const vector& C = mesh.cellCentres()[celli];
-
-    faceList.clear();
-    forAll(faces, i)
-    {
-        label facei = faces[i];
-        scalar lam = lambda(C, position, facei, stepFraction);
-
-        if ((lam > 0) && (lam < 1.0))
-        {
-            faceList.append(facei);
-        }
-    }
-}
-
-
 template<class ParticleType>
 template<class TrackData>
 void Foam::Particle<ParticleType>::prepareForParallelTransfer
 (
-    const label patchi,
+    const label patchI,
     TrackData& td
 )
 {
     // Convert the face index to be local to the processor patch
-    facei_ = patchFace(patchi, facei_);
+    faceI_ = patchFace(patchI, faceI_);
 }
 
 
@@ -103,15 +50,15 @@ template<class ParticleType>
 template<class TrackData>
 void Foam::Particle<ParticleType>::correctAfterParallelTransfer
 (
-    const label patchi,
+    const label patchI,
     TrackData& td
 )
 {
     const processorPolyPatch& ppp =
         refCast<const processorPolyPatch>
-        (cloud_.pMesh().boundaryMesh()[patchi]);
+        (cloud_.pMesh().boundaryMesh()[patchI]);
 
-    celli_ = ppp.faceCells()[facei_];
+    cellI_ = ppp.faceCells()[faceI_];
 
     if (!ppp.parallel())
     {
@@ -123,7 +70,7 @@ void Foam::Particle<ParticleType>::correctAfterParallelTransfer
         }
         else
         {
-            const tensor& T = ppp.forwardT()[facei_];
+            const tensor& T = ppp.forwardT()[faceI_];
             transformPosition(T);
             static_cast<ParticleType&>(*this).transformProperties(T);
         }
@@ -140,23 +87,48 @@ void Foam::Particle<ParticleType>::correctAfterParallelTransfer
         }
         else
         {
-            position_ -= ppp.separation()[facei_];
+            position_ -= ppp.separation()[faceI_];
             static_cast<ParticleType&>(*this).transformProperties
             (
-                -ppp.separation()[facei_]
+                -ppp.separation()[faceI_]
             );
         }
     }
 
+    tetFaceI_ = faceI_ + ppp.start();
+
+    // Faces either side of a coupled patch have matched base indices,
+    // tetPtI is specified relative to the base point, already and
+    // opposite circulation directions by design, so if the vertices
+    // are:
+    // source:
+    // face    (a b c d e f)
+    // fPtI     0 1 2 3 4 5
+    //            +
+    // destination:
+    // face    (a f e d c b)
+    // fPtI     0 1 2 3 4 5
+    //                  +
+    // where a is the base point of the face are matching , and we
+    // have fPtI = 1 on the source processor face, i.e. vertex b, then
+    // this because of the face circulation direction change, vertex c
+    // is the characterising point on the destination processor face,
+    // giving the destination fPtI as:
+    //     fPtI_d = f.size() - 1 - fPtI_s = 6 - 1 - 1 = 4
+    // This relationship can be verified for other points and sizes of
+    // face.
+
+    tetPtI_ = cloud_.polyMesh_.faces()[tetFaceI_].size() - 1 - tetPtI_;
+
     // Reset the face index for the next tracking operation
     if (stepFraction_ > (1.0 - SMALL))
     {
         stepFraction_ = 1.0;
-        facei_ = -1;
+        faceI_ = -1;
     }
     else
     {
-        facei_ += ppp.start();
+        faceI_ += ppp.start();
     }
 }
 
@@ -168,27 +140,55 @@ Foam::Particle<ParticleType>::Particle
 (
     const Cloud<ParticleType>& cloud,
     const vector& position,
-    const label celli
+    const label cellI,
+    const label tetFaceI,
+    const label tetPtI
 )
 :
     cloud_(cloud),
     position_(position),
-    celli_(celli),
-    facei_(-1),
+    cellI_(cellI),
+    faceI_(-1),
     stepFraction_(0.0),
+    tetFaceI_(tetFaceI),
+    tetPtI_(tetPtI),
     origProc_(Pstream::myProcNo()),
     origId_(cloud_.getNewParticleID())
 {}
 
 
+template<class ParticleType>
+Foam::Particle<ParticleType>::Particle
+(
+    const Cloud<ParticleType>& cloud,
+    const vector& position,
+    const label cellI
+)
+:
+    cloud_(cloud),
+    position_(position),
+    cellI_(cellI),
+    faceI_(-1),
+    stepFraction_(0.0),
+    tetFaceI_(-1),
+    tetPtI_(-1),
+    origProc_(Pstream::myProcNo()),
+    origId_(cloud_.getNewParticleID())
+{
+    initCellFacePt();
+}
+
+
 template<class ParticleType>
 Foam::Particle<ParticleType>::Particle(const Particle<ParticleType>& p)
 :
     cloud_(p.cloud_),
     position_(p.position_),
-    celli_(p.celli_),
-    facei_(p.facei_),
+    cellI_(p.cellI_),
+    faceI_(p.faceI_),
     stepFraction_(p.stepFraction_),
+    tetFaceI_(p.tetFaceI_),
+    tetPtI_(p.tetPtI_),
     origProc_(p.origProc_),
     origId_(p.origId_)
 {}
@@ -204,7 +204,7 @@ Foam::label Foam::Particle<ParticleType>::track
     TrackData& td
 )
 {
-    facei_ = -1;
+    faceI_ = -1;
 
     // Tracks to endPosition or stop on boundary
     while (!onBoundary() && stepFraction_ < 1.0 - SMALL)
@@ -212,7 +212,7 @@ Foam::label Foam::Particle<ParticleType>::track
         stepFraction_ += trackToFace(endPosition, td)*(1.0 - stepFraction_);
     }
 
-    return facei_;
+    return faceI_;
 }
 
 
@@ -224,6 +224,7 @@ Foam::label Foam::Particle<ParticleType>::track(const vector& endPosition)
     return track(endPosition, dummyTd);
 }
 
+
 template<class ParticleType>
 template<class TrackData>
 Foam::scalar Foam::Particle<ParticleType>::trackToFace
@@ -234,175 +235,414 @@ Foam::scalar Foam::Particle<ParticleType>::trackToFace
 {
     const polyMesh& mesh = cloud_.polyMesh_;
 
-    DynamicList<label>& faces = cloud_.labels_;
-    findFaces(endPosition, faces);
+    const faceList& pFaces = mesh.faces();
+    const pointField& pPts = mesh.points();
+    const vectorField& pC = mesh.cellCentres();
+
+    faceI_ = -1;
 
-    facei_ = -1;
     scalar trackFraction = 0.0;
 
-    if (faces.empty())  // inside cell
+    // Minimum tetrahedron decomposition of each cell of the mesh into
+    // using the cell centre, base point on face, and further two
+    // points on the face.  For each face of n points, there are n - 2
+    // tets generated.
+
+    // The points for each tet are organised to match those used in the
+    // tetrahedron class, supplying them in the order:
+    //     Cc, basePt, pA, pB
+    // where:
+    //   + Cc is the cell centre;
+    //   + basePt is the base point on the face;
+    //   + pA and pB are the remaining points on the face, such that
+    //     the circulation, {basePt, pA, pB} produces a positive
+    //     normal by the right-hand rule.  pA and pB are chosen from
+    //     tetPtI_ do accomplish this depending if the cell owns the
+    //     face, tetPtI_ is the vertex that characterises the tet, and
+    //     is the first vertex on the tet when circulating around the
+    //     face. Therefore, the same tetPtI represents the same face
+    //     triangle for both the owner and neighbour cell.
+    //
+    // Each tet has its four triangles represented in the same order:
+    // 0) tri joining a tet to the tet across the face in next cell.
+    //    This is the triangle opposite Cc.
+    // 1) tri joining a tet to the tet that is in the same cell, but
+    //    belongs to the face that shares the edge of the current face
+    //    that doesn't contain basePt.  This is the triangle opposite
+    //    basePt.
+
+    // 2) tri joining a tet to the tet that is in the same cell, but
+    //    belongs to the face that shares the tet-edge (basePt - pB).
+    //    This may be on the same face, or a different one.  This is
+    //    the triangle opposite basePt.  This is the triangle opposite
+    //    pA.
+
+    // 4) tri joining a tet to the tet that is in the same cell, but
+    //    belongs to the face that shares the tet-edge (basePt - pA).
+    //    This may be on the same face, or a different one.  This is
+    //    the triangle opposite basePt.  This is the triangle opposite
+    //    pA.
+
+    // Which tri (0..3) of the tet has been crossed
+    label triI = -1;
+
+    // Determine which face was actually crossed.  lambdaMin < SMALL
+    // is considered a trigger for a tracking correction towards the
+    // current tet centre.
+    scalar lambdaMin = VGREAT;
+
+    DynamicList<label>& tris = cloud_.labels_;
+
+    // Tet indices that will be set by hitWallFaces if a wall face is
+    // to be hit, or are set when any wall tri of a tet is hit.
+    // Carries the description of the tet on which the cell face has
+    // been hit.  For the case of being set in hitWallFaces, this may
+    // be a different tet to the one that the particle occupies.
+    tetIndices faceHitTetIs;
+
+    do
     {
-        trackFraction = 1.0;
-        position_ = endPosition;
-    }
-    else // hit face
-    {
-        scalar lambdaMin = GREAT;
+        if (triI != -1)
+        {
+            // Change tet ownership because a tri face has been crossed
+            tetNeighbour(triI);
+        }
+
+        const Foam::face& f = pFaces[tetFaceI_];
+
+        bool own = (mesh.faceOwner()[tetFaceI_] == cellI_);
+
+        label tetBasePtI = mesh.tetBasePtIs()[tetFaceI_];
+
+        label basePtI = f[tetBasePtI];
 
-        if (faces.size() == 1)
+        label facePtI = (tetPtI_ + tetBasePtI) % f.size();
+        label otherFacePtI = f.fcIndex(facePtI);
+
+        label fPtAI = -1;
+        label fPtBI = -1;
+
+        if (own)
         {
-            lambdaMin = lambda(position_, endPosition, faces[0], stepFraction_);
-            facei_ = faces[0];
+            fPtAI = facePtI;
+            fPtBI = otherFacePtI;
         }
         else
         {
-            // If the particle has to cross more than one cell to reach the
-            // endPosition, we check which way to go.
-            // If one of the faces is a boundary face and the particle is
-            // outside, we choose the boundary face.
-            // The particle is outside if one of the lambda's is > 1 or < 0
-            forAll(faces, i)
+            fPtAI = otherFacePtI;
+            fPtBI = facePtI;
+        }
+
+        tetPointRef tet
+        (
+            pC[cellI_],
+            pPts[basePtI],
+            pPts[f[fPtAI]],
+            pPts[f[fPtBI]]
+        );
+
+        if (lambdaMin < SMALL)
+        {
+            // Apply tracking correction towards tet centre
+
+            position_ +=
+                Cloud<ParticleType>::trackingCorrectionTol
+               *(tet.centre() - position_);
+
+            cloud_.trackingRescue();
+
+            return trackFraction;
+        }
+
+        if (triI != -1 && mesh.moving())
+        {
+            // Mesh motion requires stepFraction to be correct for
+            // each tracking portion, so trackToFace must return after
+            // every lambda calculation.
+            return trackFraction;
+        }
+
+        FixedList<vector, 4> tetAreas;
+
+        tetAreas[0] = tet.Sa();
+        tetAreas[1] = tet.Sb();
+        tetAreas[2] = tet.Sc();
+        tetAreas[3] = tet.Sd();
+
+        FixedList<label, 4> tetPlaneBasePtIs;
+
+        tetPlaneBasePtIs[0] = basePtI;
+        tetPlaneBasePtIs[1] = f[fPtAI];
+        tetPlaneBasePtIs[2] = basePtI;
+        tetPlaneBasePtIs[3] = basePtI;
+
+        findTris(endPosition, tris, tet, tetAreas, tetPlaneBasePtIs);
+
+        // Reset variables for new track
+        triI = -1;
+        lambdaMin = VGREAT;
+
+        // Sets a value for lambdaMin and faceI_ if a wall face is hit
+        // by the track.
+        hitWallFaces(position_, endPosition, lambdaMin, faceHitTetIs);
+
+        // Did not hit any tet tri faces, and no wall face has been
+        // found to hit.
+        if (tris.empty() && faceI_ < 0)
+        {
+            position_ = endPosition;
+
+            return 1.0;
+        }
+        else
+        {
+            // Loop over all found tris and see if any of them find a
+            // lambda value smaller than that found for a wall face.
+            forAll(tris, i)
             {
-                scalar lam =
-                    lambda(position_, endPosition, faces[i], stepFraction_);
+                label tI = tris[i];
+
+                scalar lam = tetLambda
+                (
+                    position_,
+                    endPosition,
+                    triI,
+                    tetAreas[tI],
+                    tetPlaneBasePtIs[tI],
+                    cellI_,
+                    tetFaceI_,
+                    tetPtI_
+                );
 
                 if (lam < lambdaMin)
                 {
                     lambdaMin = lam;
-                    facei_ = faces[i];
+
+                    triI = tI;
                 }
             }
         }
 
-        bool internalFace = cloud_.internalFace(facei_);
+        if (triI == 0)
+        {
+            // This must be a cell face crossing
+            faceI_ = tetFaceI_;
 
-        // For warped faces the particle can be 'outside' the cell.
-        // This will yield a lambda larger than 1, or smaller than 0
-        // For values < 0, the particle travels away from the cell
-        // and we don't move the particle, only change cell.
-        // For values larger than 1, we move the particle to endPosition only.
-        if (lambdaMin > 0.0)
+            // Set the faceHitTetIs to those for the current tet in case a
+            // wall interaction is required with the cell face
+            faceHitTetIs = tetIndices
+            (
+                cellI_,
+                tetFaceI_,
+                tetBasePtI,
+                fPtAI,
+                fPtBI,
+                tetPtI_
+            );
+        }
+        else if (triI > 0)
+        {
+            // A tri was found to be crossed before a wall face was hit (if any)
+            faceI_ = -1;
+        }
+
+        // The particle can be 'outside' the tet.  This will yield a
+        // lambda larger than 1, or smaller than 0.  For values < 0,
+        // the particle travels away from the tet and we don't move
+        // the particle, only change tet/cell.  For values larger than
+        // 1, we move the particle to endPosition before the tet/cell
+        // change.
+        if (lambdaMin > SMALL)
         {
             if (lambdaMin <= 1.0)
             {
-                trackFraction = lambdaMin;
-                position_ += trackFraction*(endPosition - position_);
+                trackFraction += lambdaMin*(1 - trackFraction);
+
+                position_ += lambdaMin*(endPosition - position_);
             }
             else
             {
                 trackFraction = 1.0;
+
                 position_ = endPosition;
             }
         }
-        else if (static_cast<ParticleType&>(*this).softImpact())
+        else
         {
-            // Soft-sphere particles can travel outside the domain
-            // but we don't use lambda since this the particle
-            // is going away from face
-            trackFraction = 1.0;
-            position_ = endPosition;
+            // Set lambdaMin to zero to force a towards-tet-centre
+            // correction.
+            lambdaMin = 0.0;
         }
 
-        // change cell
-        if (internalFace) // Internal face
+    } while (faceI_ < 0);
+
+    if (cloud_.internalFace(faceI_))
+    {
+        // Change tet ownership because a tri face has been crossed,
+        // in general this is:
+        //     tetNeighbour(triI);
+        // but triI must be 0;
+        // No modifications are required for triI = 0, no call required to
+        //     tetNeighbour(0);
+
+        if (cellI_ == mesh.faceOwner()[faceI_])
+        {
+            cellI_ = mesh.faceNeighbour()[faceI_];
+        }
+        else if (cellI_ == mesh.faceNeighbour()[faceI_])
         {
-            if (celli_ == mesh.faceOwner()[facei_])
+            cellI_ = mesh.faceOwner()[faceI_];
+        }
+        else
+        {
+            FatalErrorIn
+            (
+                "Particle::trackToFace(const vector&, TrackData&)"
+            )   << "addressing failure" << nl
+                << abort(FatalError);
+        }
+    }
+    else
+    {
+        ParticleType& p = static_cast<ParticleType&>(*this);
+
+        label origFaceI = faceI_;
+        label patchI = patch(faceI_);
+
+        // No action taken for tetPtI_ for tetFaceI_ here, handled by
+        // patch interaction call or later during processor transfer.
+
+        if
+        (
+            !p.hitPatch
+            (
+                mesh.boundaryMesh()[patchI],
+                td,
+                patchI,
+                trackFraction,
+                faceHitTetIs
+            )
+        )
+        {
+            // Did patch interaction model switch patches?
+            if (faceI_ != origFaceI)
             {
-                celli_ = mesh.faceNeighbour()[facei_];
+                patchI = patch(faceI_);
             }
-            else if (celli_ == mesh.faceNeighbour()[facei_])
+
+            const polyPatch& patch = mesh.boundaryMesh()[patchI];
+
+            if (isA<wedgePolyPatch>(patch))
             {
-                celli_ = mesh.faceOwner()[facei_];
+                p.hitWedgePatch
+                (
+                    static_cast<const wedgePolyPatch&>(patch), td
+                );
             }
-            else
+            else if (isA<symmetryPolyPatch>(patch))
             {
-                FatalErrorIn
+                p.hitSymmetryPatch
                 (
-                    "Particle::trackToFace(const vector&, TrackData&)"
-                )<< "addressing failure" << nl
-                 << abort(FatalError);
+                    static_cast<const symmetryPolyPatch&>(patch), td
+                );
+            }
+            else if (isA<cyclicPolyPatch>(patch))
+            {
+                p.hitCyclicPatch
+                (
+                    static_cast<const cyclicPolyPatch&>(patch), td
+                );
+            }
+            else if (isA<processorPolyPatch>(patch))
+            {
+                p.hitProcessorPatch
+                (
+                    static_cast<const processorPolyPatch&>(patch), td
+                );
+            }
+            else if (isA<wallPolyPatch>(patch))
+            {
+                p.hitWallPatch
+                (
+                    static_cast<const wallPolyPatch&>(patch), td, faceHitTetIs
+                );
+            }
+            else
+            {
+                p.hitPatch(patch, td);
             }
         }
-        else
+    }
+
+    if (lambdaMin < SMALL)
+    {
+        // Apply tracking correction towards tet centre.
+        // Generate current tet to find centre to apply correction.
+
+        tetPointRef tet = currentTet();
+
+        position_ +=
+            Cloud<ParticleType>::trackingCorrectionTol
+           *(tet.centre() - position_);
+
+        if
+        (
+            cloud_.hasWallImpactDistance()
+            && !cloud_.internalFace(faceHitTetIs.face())
+            && cloud_.cellHasWallFaces()[faceHitTetIs.cell()]
+        )
         {
-            ParticleType& p = static_cast<ParticleType&>(*this);
+            const polyBoundaryMesh& patches = mesh.boundaryMesh();
 
-            // Soft-sphere algorithm ignores the boundary
-            if (p.softImpact())
-            {
-                trackFraction = 1.0;
-                position_ = endPosition;
-            }
+            label fI = faceHitTetIs.face();
 
-            label origFacei = facei_;
-            label patchi = patch(facei_);
+            label patchI = patches.patchID()[fI - mesh.nInternalFaces()];
 
-            if (!p.hitPatch(mesh.boundaryMesh()[patchi], td, patchi))
+            if (isA<wallPolyPatch>(patches[patchI]))
             {
-                // Did patch interaction model switch patches?
-                if (facei_ != origFacei)
-                {
-                    patchi = patch(facei_);
-                }
-                const polyPatch& patch = mesh.boundaryMesh()[patchi];
-
-                if (isA<wedgePolyPatch>(patch))
-                {
-                    p.hitWedgePatch
-                    (
-                        static_cast<const wedgePolyPatch&>(patch), td
-                    );
-                }
-                else if (isA<symmetryPolyPatch>(patch))
-                {
-                    p.hitSymmetryPatch
-                    (
-                        static_cast<const symmetryPolyPatch&>(patch), td
+                // In the case of collision with a wall where there is
+                // a non-zero wallImpactDistance, it is possible for
+                // there to be a tracking correction required to bring
+                // the particle into the domain, but the position of
+                // the particle is further from the wall than the tet
+                // centre, in which case the normal correction can be
+                // counter-productive, i.e. pushes the particle
+                // further out of the domain.  In this case it is the
+                // position that hit the wall that is in need of a
+                // rescue correction.
+
+                triPointRef wallTri = faceHitTetIs.faceTri(mesh);
+
+                tetPointRef wallTet = faceHitTetIs.tet(mesh);
+
+                vector nHat = wallTri.normal();
+                nHat /= mag(nHat);
+
+                const ParticleType& p = static_cast<const ParticleType&>(*this);
+
+                scalar r = p.wallImpactDistance(nHat);
+
+                // Removing (approximately) the wallTri normal
+                // component of the existing correction, to avoid the
+                // situation where the existing correction in the wall
+                // normal direction is larger towards the wall than
+                // the new correction is away from it.
+                position_ +=
+                    Cloud<ParticleType>::trackingCorrectionTol
+                   *(
+                        (wallTet.centre() - (position_ + r*nHat))
+                      - (nHat & (tet.centre() - position_))*nHat
                     );
-                }
-                else if (isA<cyclicPolyPatch>(patch))
-                {
-                    p.hitCyclicPatch
-                    (
-                        static_cast<const cyclicPolyPatch&>(patch), td
-                    );
-                }
-                else if (isA<processorPolyPatch>(patch))
-                {
-                    p.hitProcessorPatch
-                    (
-                        static_cast<const processorPolyPatch&>(patch), td
-                    );
-                }
-                else if (isA<wallPolyPatch>(patch))
-                {
-                    p.hitWallPatch
-                    (
-                        static_cast<const wallPolyPatch&>(patch), td
-                    );
-                }
-                else
-                {
-                    p.hitPatch(patch, td);
-                }
             }
         }
-    }
 
-    // If the trackFraction = 0 something went wrong.
-    // Either the particle is flipping back and forth across a face perhaps
-    // due to velocity interpolation errors or it is in a "hole" in the mesh
-    // caused by face warpage.
-    // In both cases resolve the positional ambiguity by moving the particle
-    // slightly towards the cell-centre.
-    if (trackFraction < SMALL)
-    {
-        position_ += 1.0e-3*(mesh.cellCentres()[celli_] - position_);
+        cloud_.trackingRescue();
     }
 
     return trackFraction;
 }
 
+
 template<class ParticleType>
 Foam::scalar Foam::Particle<ParticleType>::trackToFace
 (
@@ -413,6 +653,7 @@ Foam::scalar Foam::Particle<ParticleType>::trackToFace
     return trackToFace(endPosition, dummyTd);
 }
 
+
 template<class ParticleType>
 void Foam::Particle<ParticleType>::transformPosition(const tensor& T)
 {
@@ -436,7 +677,9 @@ bool Foam::Particle<ParticleType>::hitPatch
 (
     const polyPatch&,
     TrackData&,
-    const label
+    const label,
+    const scalar,
+    const tetIndices&
 )
 {
     return false;
@@ -451,7 +694,17 @@ void Foam::Particle<ParticleType>::hitWedgePatch
     TrackData&
 )
 {
-    vector nf = wpp.faceAreas()[wpp.whichFace(facei_)];
+    FatalErrorIn
+    (
+        "void Foam::Particle<ParticleType>::hitWedgePatch"
+        "("
+            "const wedgePolyPatch& wpp, "
+            "TrackData&"
+        ")"
+    )   << "Hitting a wedge patch should not be possible."
+        << abort(FatalError);
+
+    vector nf = normal();
     nf /= mag(nf);
 
     static_cast<ParticleType&>(*this).transformProperties(I - 2.0*nf*nf);
@@ -466,7 +719,7 @@ void Foam::Particle<ParticleType>::hitSymmetryPatch
     TrackData&
 )
 {
-    vector nf = spp.faceAreas()[spp.whichFace(facei_)];
+    vector nf = normal();
     nf /= mag(nf);
 
     static_cast<ParticleType&>(*this).transformProperties(I - 2.0*nf*nf);
@@ -481,11 +734,16 @@ void Foam::Particle<ParticleType>::hitCyclicPatch
     TrackData&
 )
 {
-//    label patchFacei_ = cpp.whichFace(facei_);
+    // label patchFaceI_ = cpp.whichFace(faceI_);
+
+    faceI_ = cpp.transformGlobalFace(faceI_);
+
+    cellI_ = cloud_.polyMesh_.faceOwner()[faceI_];
 
-    facei_ = cpp.transformGlobalFace(facei_);
+    tetFaceI_ = faceI_;
 
-    celli_ = cloud_.polyMesh_.faceOwner()[facei_];
+    // See note in correctAfterParallelTransfer for tetPtI_ addressing.
+    tetPtI_ = cloud_.polyMesh_.faces()[tetFaceI_].size() - 1 - tetPtI_;
 
     // Now the particle is on the receiving side
 
@@ -522,7 +780,8 @@ template<class TrackData>
 void Foam::Particle<ParticleType>::hitWallPatch
 (
     const wallPolyPatch& spp,
-    TrackData&
+    TrackData&,
+    const tetIndices&
 )
 {}
 
@@ -549,7 +808,7 @@ bool Foam::operator==
     return
     (
         pA.origProc() == pB.origProc()
-        && pA.origId() == pB.origId()
+     && pA.origId() == pB.origId()
     );
 }
 
diff --git a/src/lagrangian/basic/Particle/Particle.H b/src/lagrangian/basic/Particle/Particle.H
index 39c21de0767..8dbdebd4e1b 100644
--- a/src/lagrangian/basic/Particle/Particle.H
+++ b/src/lagrangian/basic/Particle/Particle.H
@@ -38,6 +38,10 @@ Description
 #include "faceList.H"
 #include "typeInfo.H"
 #include "OFstream.H"
+#include "tetPointRef.H"
+#include "FixedList.H"
+#include "polyMeshTetDecomposition.H"
+#include "wallPolyPatch.H"
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
@@ -125,14 +129,23 @@ protected:
         vector position_;
 
         //- Index of the cell it is in
-        label celli_;
+        label cellI_;
 
         //- Face index if the particle is on a face otherwise -1
-        label facei_;
+        label faceI_;
 
         //- Fraction of time-step completed
         scalar stepFraction_;
 
+        //- Index of the face that owns the decomposed tet that the
+        //  particle is in
+        label tetFaceI_;
+
+        //- Index of the point on the face that defines the decomposed
+        //  tet that the particle is in.  Relative to the face base
+        //  point.
+        label tetPtI_;
+
         //- Originating processor id
         label origProc_;
 
@@ -142,54 +155,82 @@ protected:
 
     // Private Member Functions
 
-        //- Return the 'lambda' value for the position, p, on the face,
-        // where, p = from + lamda*(to - from)
-        // for non-static meshes
-        inline scalar lambda
+        //- Find the tet tri faces between position and tet centre
+        inline void findTris
         (
-            const vector& from,
-            const vector& to,
-            const label facei,
-            const scalar stepFraction
+            const vector& position,
+            DynamicList<label>& faceList,
+            const tetPointRef& tet,
+            const FixedList<vector, 4>& tetAreas,
+            const FixedList<label, 4>& tetPlaneBasePtIs
         ) const;
 
-        //- Return the 'lambda' value for the position, p, on the face,
-        // where, p = from + lamda*(to - from)
-        // for static meshes
-        inline scalar lambda
+        //- Find the lambda value for the line to-from across the
+        //  given tri face, where p = from + lambda*(to - from)
+        inline scalar tetLambda
         (
             const vector& from,
             const vector& to,
-            const label facei
+            const label triI,
+            const vector& tetArea,
+            const label tetPlaneBasePtI,
+            const label cellI,
+            const label tetFaceI,
+            const label tetPtI
         ) const;
 
-        //- Find the faces between position and cell centre
-        void findFaces
+         //- Find the lambda value for a moving tri face
+         inline scalar movingTetLambda
+         (
+             const vector& from,
+             const vector& to,
+             const label triI,
+             const vector& tetArea,
+             const label tetPlaneBasePtI,
+             const label cellI,
+             const label tetFaceI,
+             const label tetPtI
+         ) const;
+
+        //- Modify the tet owner data by crossing triI
+        inline void tetNeighbour(label triI);
+
+        //- Cross the from the given face across the given edge of the
+        //  given cell to find the resulting face and tetPtI
+        inline void crossEdgeConnectedFace
         (
-            const vector& position,
-            DynamicList<label>& faceList
-        ) const;
+            const label& cellI,
+            label& tetFaceI,
+            label& tetPtI,
+            const edge& e
+        );
 
-        //- Find the faces between position and cell centre
-        void findFaces
+        //- Hit wall faces in the current cell if the
+        //- wallImpactDistance is non-zero.  They may not be in
+        //- different tets to the current.
+        inline void hitWallFaces
         (
-            const vector& position,
-            const label celli,
-            const scalar stepFraction,
-            DynamicList<label>& faceList
-        ) const;
+            const vector& from,
+            const vector& to,
+            scalar& lambdaMin,
+            tetIndices& closestTetIs
+        );
 
 
     // Patch interactions
 
-        //- Overridable function to handle the particle hitting a patch
-        //  Executed before other patch-hitting functions
+        //- Overridable function to handle the particle hitting a
+        //  patch.  Executed before other patch-hitting functions.
+        //  trackFraction is passed in to allow mesh motion to
+        //  interpolate in time to the correct face state.
         template<class TrackData>
         bool hitPatch
         (
             const polyPatch&,
             TrackData& td,
-            const label patchI
+            const label patchI,
+            const scalar trackFraction,
+            const tetIndices& tetIs
         );
 
         //- Overridable function to handle the particle hitting a wedgePatch
@@ -231,7 +272,8 @@ protected:
         void hitWallPatch
         (
             const wallPolyPatch&,
-            TrackData& td
+            TrackData& td,
+            const tetIndices& tetIs
         );
 
         //- Overridable function to handle the particle hitting a
@@ -264,12 +306,12 @@ protected:
         //- Convert global addressing to the processor patch
         //  local equivalents
         template<class TrackData>
-        void prepareForParallelTransfer(const label patchi, TrackData& td);
+        void prepareForParallelTransfer(const label patchI, TrackData& td);
 
         //- Convert processor patch addressing to the global equivalents
-        //  and set the celli to the face-neighbour
+        //  and set the cellI to the face-neighbour
         template<class TrackData>
-        void correctAfterParallelTransfer(const label patchi, TrackData& td);
+        void correctAfterParallelTransfer(const label patchI, TrackData& td);
 
 
 public:
@@ -293,7 +335,18 @@ public:
         (
             const Cloud<ParticleType>&,
             const vector& position,
-            const label celli
+            const label cellI,
+            const label tetFaceI,
+            const label tetPtI
+        );
+
+        //- Construct from components, tetFaceI_ and tetPtI_ are not
+        //  supplied so they will be deduced by a search
+        Particle
+        (
+            const Cloud<ParticleType>&,
+            const vector& position,
+            const label cellI
         );
 
         //- Construct from Istream
@@ -355,17 +408,6 @@ public:
 
         // Access
 
-            //- Return true if particle is in cell
-            inline bool inCell() const;
-
-            //- Return true if position is in cell i
-            inline bool inCell
-            (
-                const vector& position,
-                const label celli,
-                const scalar stepFraction
-            ) const;
-
             //- Return current particle position
             inline const vector& position() const;
 
@@ -378,6 +420,35 @@ public:
             //- Return current cell particle is in
             inline label cell() const;
 
+            //- Return current tet face particle is in
+            inline label& tetFace();
+
+            //- Return current tet face particle is in
+            inline label tetFace() const;
+
+            //- Return current tet face particle is in
+            inline label& tetPt();
+
+            //- Return current tet face particle is in
+            inline label tetPt() const;
+
+            //- Return the indices of the current tet that the
+            //  particle occupies.
+            inline tetIndices currentTetIndices() const;
+
+            //- Return the geometry of the current tet that the
+            //  particle occupies.
+            inline tetPointRef currentTet() const;
+
+            //- Return the normal of the tri on tetFaceI_ for the
+            //  current tet.
+            inline vector normal() const;
+
+            //- Return the normal of the tri on tetFaceI_ for the
+            //  current tet at the start of the timestep, i.e. based
+            //  on oldPoints
+            inline vector oldNormal() const;
+
             //- Return current face particle is on otherwise -1
             inline label& face();
 
@@ -396,17 +467,21 @@ public:
 
         // Check
 
+            //- Check the stored cell value (setting if necessary) and
+            //  initialise the tetFace and tetPt values
+            inline void initCellFacePt();
+
             //- Is the particle on the boundary/(or outside the domain)?
             inline bool onBoundary() const;
 
             //- Which patch is particle on
-            inline label patch(const label facei) const;
+            inline label patch(const label faceI) const;
 
             //- Which face of this patch is this particle on
             inline label patchFace
             (
-                const label patchi,
-                const label facei
+                const label patchI,
+                const label faceI
             ) const;
 
             //- The nearest distance to a wall that
diff --git a/src/lagrangian/basic/Particle/ParticleI.H b/src/lagrangian/basic/Particle/ParticleI.H
index 43fd1d38065..35d658d1153 100644
--- a/src/lagrangian/basic/Particle/ParticleI.H
+++ b/src/lagrangian/basic/Particle/ParticleI.H
@@ -24,243 +24,776 @@ License
 \*---------------------------------------------------------------------------*/
 
 #include "polyMesh.H"
-#include "wallPolyPatch.H"
 
 // * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
 
 template<class ParticleType>
-inline Foam::scalar Foam::Particle<ParticleType>::lambda
+inline void Foam::Particle<ParticleType>::findTris
+(
+    const vector& position,
+    DynamicList<label>& faceList,
+    const tetPointRef& tet,
+    const FixedList<vector, 4>& tetAreas,
+    const FixedList<label, 4>& tetPlaneBasePtIs
+) const
+{
+    faceList.clear();
+
+    const point Ct = tet.centre();
+
+    for (label i = 0; i < 4; i++)
+    {
+        scalar lambda = tetLambda
+        (
+            Ct,
+            position,
+            i,
+            tetAreas[i],
+            tetPlaneBasePtIs[i],
+            cellI_,
+            tetFaceI_,
+            tetPtI_
+        );
+
+        if ((lambda > 0.0) && (lambda < 1.0))
+        {
+            faceList.append(i);
+        }
+    }
+}
+
+
+template<class ParticleType>
+inline Foam::scalar Foam::Particle<ParticleType>::tetLambda
+(
+    const vector& from,
+    const vector& to,
+    const label triI,
+    const vector& n,
+    const label tetPlaneBasePtI,
+    const label cellI,
+    const label tetFaceI,
+    const label tetPtI
+) const
+{
+    const polyMesh& mesh = cloud_.polyMesh_;
+    const pointField& pPts = mesh.points();
+
+    if (mesh.moving())
+    {
+        return movingTetLambda
+        (
+            from,
+            to,
+            triI,
+            n,
+            tetPlaneBasePtI,
+            cellI,
+            tetFaceI,
+            tetPtI
+        );
+    }
+
+    const point& base = pPts[tetPlaneBasePtI];
+
+    scalar lambdaNumerator = (base - from) & n;
+    scalar lambdaDenominator = (to - from) & n;
+
+    if (mag(lambdaDenominator) < SMALL)
+    {
+        if (mag(lambdaNumerator) < SMALL)
+        {
+            // Track starts on the face, and is potentially
+            // parallel to it.  +-SMALL)/+-SMALL is not a good
+            // comparison, return 0.0, in anticipation of tet
+            // centre correction.
+
+            return 0.0;
+        }
+        else
+        {
+            if (mag((to - from)) < SMALL)
+            {
+                // Zero length track, not along the face, face
+                // cannot be crossed.
+
+                return GREAT;
+            }
+            else
+            {
+                // Trajectory is non-zero and parallel to face
+
+                lambdaDenominator = sign(lambdaDenominator)*SMALL;
+            }
+        }
+    }
+
+    return lambdaNumerator/lambdaDenominator;
+}
+
+
+
+template<class ParticleType>
+inline Foam::scalar Foam::Particle<ParticleType>::movingTetLambda
 (
     const vector& from,
     const vector& to,
-    const label facei,
-    const scalar stepFraction
+    const label triI,
+    const vector& n,
+    const label tetPlaneBasePtI,
+    const label cellI,
+    const label tetFaceI,
+    const label tetPtI
 ) const
 {
     const polyMesh& mesh = cloud_.polyMesh_;
-    bool movingMesh = mesh.moving();
+    const pointField& pPts = mesh.points();
+    const pointField& oldPPts = mesh.oldPoints();
+
+    // Base point of plane at end of motion
+    const point& b = pPts[tetPlaneBasePtI];
+
+    // n: Normal of plane at end of motion
+
+    // Base point of plane at start of timestep
+    const point& b00 = oldPPts[tetPlaneBasePtI];
+
+    // Base point of plane at start of tracking portion (cast forward by
+    // stepFraction)
+    point b0 = b00 + stepFraction_*(b - b00);
+
+    // Normal of plane at start of tracking portion
+    vector n0 = vector::zero;
 
-    if (movingMesh)
     {
-        vector Sf = mesh.faceAreas()[facei];
-        Sf /= mag(Sf);
-        vector Cf = mesh.faceCentres()[facei];
+        tetIndices tetIs(cellI, tetFaceI, tetPtI, mesh);
+
+        // tet at timestep start
+        tetPointRef tet00 = tetIs.oldTet(mesh);
 
-        // patch interaction
-        if (!cloud_.internalFace(facei))
+        // tet at timestep end
+        tetPointRef tet = tetIs.tet(mesh);
+
+        point tet0PtA = tet00.a() + stepFraction_*(tet.a() - tet00.a());
+        point tet0PtB = tet00.b() + stepFraction_*(tet.b() - tet00.b());
+        point tet0PtC = tet00.c() + stepFraction_*(tet.c() - tet00.c());
+        point tet0PtD = tet00.d() + stepFraction_*(tet.d() - tet00.d());
+
+        // Tracking portion start tet (cast forward by stepFraction)
+        tetPointRef tet0(tet0PtA, tet0PtB, tet0PtC, tet0PtD);
+
+        switch (triI)
         {
-            label patchi = patch(facei);
-            const polyPatch& patch = mesh.boundaryMesh()[patchi];
+            case 0:
+            {
+                n0 = tet0.Sa();
+                break;
+            }
+            case 1:
+            {
+                n0 = tet0.Sb();
+                break;
+            }
+            case 2:
+            {
+                n0 = tet0.Sc();
+                break;
+            }
+            case 3:
+            {
+                n0 = tet0.Sd();
+                break;
+            }
+            default:
+            {
+                break;
+            }
+        }
+    }
+
+    if (mag(n0) < SMALL)
+    {
+        // If the old normal is zero (for example in layer addition)
+        // then use the current normal;
 
-            // move reference point for wall
-            if (isA<wallPolyPatch>(patch))
+        n0 = n;
+    }
+
+    scalar lambdaNumerator = 0;
+    scalar lambdaDenominator = 0;
+
+    vector dP = to - from;
+    vector dN = n - n0;
+    vector dB = b - b0;
+    vector dS = from - b0;
+
+    if (mag(dN) > SMALL)
+    {
+        scalar a = (dP - dB) & dN;
+        scalar b = ((dP - dB) & n0) + (dS & dN);
+        scalar c = dS & n0;
+
+        if (mag(a) > SMALL)
+        {
+
+            // Solve quadratic for lambda
+            scalar discriminant = sqr(b) - 4.0*a*c;
+
+            if (discriminant < 0)
+            {
+                // Imaginary roots only - face not crossed
+                return GREAT;
+            }
+            else
             {
-                const vector& C = mesh.cellCentres()[celli_];
-                scalar CCf = mag((C - Cf) & Sf);
-                // check if distance between cell centre and face centre
-                // is larger than wallImpactDistance
-                const ParticleType& p =
-                    static_cast<const ParticleType&>(*this);
-                if (CCf > p.wallImpactDistance(Sf))
+                // Numerical Recipes in C,
+                // Second Edition (1992),
+                // Section 5.6.
+                // q = -0.5*(b + sgn(b)*sqrt(b^2 - 4ac))
+                // x1 = q/a
+                // x2 = c/q
+
+                scalar q = -0.5*(b + sign(b)*Foam::sqrt(discriminant));
+
+                if (mag(q) < VSMALL)
                 {
-                    Cf -=p.wallImpactDistance(Sf)*Sf;
+                    // If q is zero, then l1 = q/a is the required
+                    // value of lambda, and is zero.
+
+                    return 0.0;
+                }
+
+                scalar l1 = q/a;
+                scalar l2 = c/q;
+
+                // There will be two roots, a big one and a little
+                // one, choose the little one.
+
+                if (mag(l1) < mag(l2))
+                {
+                    return l1;
+                }
+                else
+                {
+                    return l2;
                 }
             }
         }
+        {
+            // When a is zero, solve the first order polynomial
 
-        // for a moving mesh we need to reconstruct the old
-        // Sf and Cf from oldPoints (they aren't stored)
-
-        const vectorField& oldPoints = mesh.oldPoints();
+            lambdaNumerator = -c;
+            lambdaDenominator = b;
+        }
+    }
+    else
+    {
+        // when n = n0 is zero, there is no plane rotation, solve the
+        // first order polynomial
 
-        vector Cf00 = mesh.faces()[facei].centre(oldPoints);
-        vector Cf0 = Cf00 + stepFraction*(Cf - Cf00);
+        lambdaNumerator = -(dS & n0);
+        lambdaDenominator = ((dP - dB) & n0);
 
-        vector Sf00 = mesh.faces()[facei].normal(oldPoints);
+    }
 
-        // for layer addition Sf00 = vector::zero and we use Sf
-        if (mag(Sf00) > SMALL)
+    if (mag(lambdaDenominator) < SMALL)
+    {
+        if (mag(lambdaNumerator) < SMALL)
         {
-            Sf00 /= mag(Sf00);
+            // Track starts on the face, and is potentially
+            // parallel to it.  +-SMALL)/+-SMALL is not a good
+            // comparison, return 0.0, in anticipation of tet
+            // centre correction.
+
+            return 0.0;
         }
         else
         {
-            Sf00 = Sf;
+            if (mag((to - from)) < SMALL)
+            {
+                // Zero length track, not along the face, face
+                // cannot be crossed.
+
+                return GREAT;
+            }
+            else
+            {
+                // Trajectory is non-zero and parallel to face
+
+                lambdaDenominator = sign(lambdaDenominator)*SMALL;
+            }
         }
+    }
+
+    return lambdaNumerator/lambdaDenominator;
+}
+
+
+template<class ParticleType>
+inline void Foam::Particle<ParticleType>::tetNeighbour(label triI)
+{
+    const polyMesh& mesh = cloud_.polyMesh_;
 
-        scalar magSfDiff = mag(Sf - Sf00);
+    const labelList& pOwner = mesh.faceOwner();
+    const faceList& pFaces = mesh.faces();
 
-        // check if the face is rotating
-        if (magSfDiff > SMALL)
+    bool own = (pOwner[tetFaceI_] == cellI_);
+
+    const Foam::face& f = pFaces[tetFaceI_];
+
+    label tetBasePtI = mesh.tetBasePtIs()[tetFaceI_];
+
+    label facePtI = (tetPtI_ + tetBasePtI) % f.size();
+    label otherFacePtI = f.fcIndex(facePtI);
+
+    if (tetBasePtI == -1)
+    {
+        FatalErrorIn
+        (
+            "inline void Foam::Particle<ParticleType>::tetNeighbour(label triI)"
+        )
+            << "No base point for face " << tetFaceI_ << ", " << f
+            << ", produces a valid tet decomposition."
+            << abort(FatalError);
+    }
+
+    switch (triI)
+    {
+        case 0:
         {
-            vector Sf0 = Sf00 + stepFraction*(Sf - Sf00);
-
-            // find center of rotation
-            vector omega = Sf0 ^ Sf;
-            scalar magOmega = mag(omega);
-            omega /= magOmega + SMALL;
-            vector n0 = omega ^ Sf0;
-            scalar lam = ((Cf - Cf0) & Sf)/(n0 & Sf);
-            vector r0 = Cf0 + lam*n0;
-
-            // solve '(p - r0) & Sfp = 0', where
-            // p = from + lambda*(to - from)
-            // Sfp = Sf0 + lambda*(Sf - Sf0)
-            // which results in the quadratic eq.
-            // a*lambda^2 + b*lambda + c = 0
-            vector alpha = from - r0;
-            vector beta = to - from;
-            scalar a = beta & (Sf - Sf0);
-            scalar b = (alpha & (Sf - Sf0)) + (beta & Sf0);
-            scalar c = alpha & Sf0;
-
-            if (mag(a) > SMALL)
-            {
-                // solve the second order polynomial
-                scalar ap = b/a;
-                scalar bp = c/a;
-                scalar cp = ap*ap - 4.0*bp;
+            // Crossing this triangle changes tet to that in the
+            // neighbour cell over tetFaceI
+
+            // Modification of cellI_ will happen by other indexing,
+            // tetFaceI_ and tetPtI don't change.
 
-                if (cp < 0.0)
+            break;
+        }
+        case 1:
+        {
+            crossEdgeConnectedFace
+            (
+                cellI_,
+                tetFaceI_,
+                tetPtI_,
+                Foam::edge(f[facePtI], f[otherFacePtI])
+            );
+
+            break;
+        }
+        case 2:
+        {
+            if (own)
+            {
+                if (tetPtI_ < f.size() - 2)
                 {
-                    // imaginary roots only
-                    return GREAT;
+                    tetPtI_ = f.fcIndex(tetPtI_);
                 }
                 else
                 {
-                    scalar l1 = -0.5*(ap - ::sqrt(cp));
-                    scalar l2 = -0.5*(ap + ::sqrt(cp));
-
-                    // one root is around 0-1, while
-                    // the other is very large in mag
-                    if (mag(l1) < mag(l2))
-                    {
-                        return l1;
-                    }
-                    else
-                    {
-                        return l2;
-                    }
+                    crossEdgeConnectedFace
+                    (
+                        cellI_,
+                        tetFaceI_,
+                        tetPtI_,
+                        Foam::edge(f[tetBasePtI], f[otherFacePtI])
+                    );
                 }
             }
             else
             {
-                // when a==0, solve the first order polynomial
-                return (-c/b);
+                if (tetPtI_ > 1)
+                {
+                    tetPtI_ = f.rcIndex(tetPtI_);
+                }
+                else
+                {
+                    crossEdgeConnectedFace
+                    (
+                        cellI_,
+                        tetFaceI_,
+                        tetPtI_,
+                        Foam::edge(f[tetBasePtI], f[facePtI])
+                    );
+                }
             }
+
+            break;
         }
-        else // no rotation
+        case 3:
         {
-            vector alpha = from - Cf0;
-            vector beta = to - from - (Cf - Cf0);
-            scalar lambdaNominator = alpha & Sf;
-            scalar lambdaDenominator = beta & Sf;
-
-            // check if trajectory is parallel to face
-            if (mag(lambdaDenominator) < SMALL)
+            if (own)
+            {
+                if (tetPtI_ > 1)
+                {
+                    tetPtI_ = f.rcIndex(tetPtI_);
+                }
+                else
+                {
+                    crossEdgeConnectedFace
+                    (
+                        cellI_,
+                        tetFaceI_,
+                        tetPtI_,
+                        Foam::edge(f[tetBasePtI], f[facePtI])
+                    );
+                }
+            }
+            else
             {
-                if (lambdaDenominator < 0.0)
+                if (tetPtI_ < f.size() - 2)
                 {
-                    lambdaDenominator = -SMALL;
+                    tetPtI_ = f.fcIndex(tetPtI_);
                 }
                 else
                 {
-                    lambdaDenominator = SMALL;
+                    crossEdgeConnectedFace
+                    (
+                        cellI_,
+                        tetFaceI_,
+                        tetPtI_,
+                        Foam::edge(f[tetBasePtI], f[otherFacePtI])
+                    );
                 }
             }
 
-            return (-lambdaNominator/lambdaDenominator);
+            break;
+        }
+        default:
+        {
+            FatalErrorIn
+            (
+                "inline void "
+                "Foam::Particle<ParticleType>::tetNeighbour(label triI)"
+            )
+                << "Tet tri face index error, can only be 0..3, supplied "
+                << triI << nl
+                << abort(FatalError);
+
+            break;
         }
-    }
-    else
-    {
-        // mesh is static and stepFraction is not needed
-        return lambda(from, to, facei);
     }
 }
 
-
 template<class ParticleType>
-inline Foam::scalar Foam::Particle<ParticleType>::lambda
+inline void Foam::Particle<ParticleType>::crossEdgeConnectedFace
 (
-    const vector& from,
-    const vector& to,
-    const label facei
-) const
+    const label& cellI,
+    label& tetFaceI,
+    label& tetPtI,
+    const edge& e
+)
 {
     const polyMesh& mesh = cloud_.polyMesh_;
 
-    vector Sf = mesh.faceAreas()[facei];
-    Sf /= mag(Sf);
-    vector Cf = mesh.faceCentres()[facei];
+    const faceList& pFaces = mesh.faces();
+    const cellList& pCells = mesh.cells();
+
+    const Foam::face& f = pFaces[tetFaceI];
+
+    const Foam::cell& thisCell = pCells[cellI];
 
-    // patch interaction
-    if (!cloud_.internalFace(facei))
+    forAll(thisCell, cFI)
     {
-        label patchi = patch(facei);
-        const polyPatch& patch = mesh.boundaryMesh()[patchi];
+        // Loop over all other faces of this cell and
+        // find the one that shares this edge
 
-        // move reference point for wall
-        if (isA<wallPolyPatch>(patch))
+        label fI = thisCell[cFI];
+
+        if (tetFaceI == fI)
         {
-            const vector& C = mesh.cellCentres()[celli_];
-            scalar CCf = mag((C - Cf) & Sf);
-            // check if distance between cell centre and face centre
-            // is larger than wallImpactDistance
-            const ParticleType& p = static_cast<const ParticleType&>(*this);
-            if (CCf > p.wallImpactDistance(Sf))
-            {
-                Cf -=p.wallImpactDistance(Sf)*Sf;
-            }
+            continue;
         }
-    }
 
-    scalar lambdaNominator = (Cf - from) & Sf;
-    scalar lambdaDenominator = (to - from) & Sf;
+        const Foam::face& otherFace = pFaces[fI];
 
-    // check if trajectory is parallel to face
-    if (mag(lambdaDenominator) < SMALL)
-    {
-        if (lambdaDenominator < 0.0)
+        label edDir = otherFace.edgeDirection(e);
+
+        if (edDir == 0)
         {
-            lambdaDenominator = -SMALL;
+            continue;
         }
         else
         {
-            lambdaDenominator = SMALL;
-        }
-    }
+            //Found edge on other face
+            tetFaceI = fI;
 
-    return lambdaNominator/lambdaDenominator;
-}
+            label eIndex = -1;
 
+            if (edDir == 1)
+            {
+                // Edge is in the forward circulation of this face, so
+                // work with the start point of the edge
 
-template<class ParticleType>
-inline bool Foam::Particle<ParticleType>::inCell() const
-{
-    DynamicList<label>& faces = cloud_.labels_;
-    findFaces(position_, faces);
+                eIndex = findIndex(otherFace, e.start());
+            }
+            else
+            {
+                // edDir == -1, so the edge is in the reverse
+                // circulation of this face, so work with the end
+                // point of the edge
 
-    return (!faces.size());
+                eIndex = findIndex(otherFace, e.end());
+            }
+
+            label tetBasePtI = mesh.tetBasePtIs()[fI];
+
+            if (tetBasePtI == -1)
+            {
+                FatalErrorIn
+                (
+                    "inline void "
+                    "Foam::Particle<ParticleType>::crossEdgeConnectedFace"
+                    "("
+                        "const label& cellI,"
+                        "label& tetFaceI,"
+                        "label& tetPtI,"
+                        "const edge& e"
+                    ")"
+                )
+                    << "No base point for face " << fI << ", " << f
+                    << ", produces a decomposition that has a minimum "
+                    << "volume greater than tolerance."
+                    << abort(FatalError);
+            }
+
+            // Find eIndex relative to the base point on new face
+            eIndex -= tetBasePtI;
+
+            if (neg(eIndex))
+            {
+                eIndex = (eIndex + otherFace.size()) % otherFace.size();
+            }
+
+            if (eIndex == 0)
+            {
+                // The point is the base point, so this is first tet
+                // in the face circulation
+
+                tetPtI = 1;
+            }
+            else if (eIndex == otherFace.size() - 1)
+            {
+                // The point is the last before the base point, so
+                // this is the last tet in the face circulation
+
+                tetPtI = otherFace.size() - 2;
+            }
+            else
+            {
+                tetPtI = eIndex;
+            }
+
+            break;
+        }
+    }
 }
 
 
 template<class ParticleType>
-inline bool Foam::Particle<ParticleType>::inCell
+inline void Foam::Particle<ParticleType>::hitWallFaces
 (
-    const vector& position,
-    const label celli,
-    const scalar stepFraction
-) const
+    const vector& from,
+    const vector& to,
+    scalar& lambdaMin,
+    tetIndices& closestTetIs
+)
 {
-    DynamicList<label>& faces = cloud_.labels_;
-    findFaces(position, celli, stepFraction, faces);
+    if (!(cloud_.hasWallImpactDistance() && cloud_.cellHasWallFaces()[cellI_]))
+    {
+        return;
+    }
+
+    const polyMesh& mesh = cloud_.polyMesh_;
+    const faceList& pFaces = mesh.faces();
+
+    const ParticleType& p = static_cast<const ParticleType&>(*this);
 
-    return (!faces.size());
+    const Foam::cell& thisCell = mesh.cells()[cellI_];
+
+    const polyBoundaryMesh& patches = mesh.boundaryMesh();
+
+    forAll(thisCell, cFI)
+    {
+        label fI = thisCell[cFI];
+
+        if (cloud_.internalFace(fI))
+        {
+            continue;
+        }
+
+        label patchI = patches.patchID()[fI - mesh.nInternalFaces()];
+
+        if (isA<wallPolyPatch>(patches[patchI]))
+        {
+            // Get the decomposition of this wall face
+
+            const List<tetIndices>& faceTetIs =
+                polyMeshTetDecomposition::faceTetIndices(mesh, fI, cellI_);
+
+            const Foam::face& f = pFaces[fI];
+
+            forAll(faceTetIs, tI)
+            {
+                const tetIndices& tetIs = faceTetIs[tI];
+
+                triPointRef tri = tetIs.faceTri(mesh);
+
+                vector n = tri.normal();
+
+                vector nHat = n/mag(n);
+
+                // Radius of particle with respect to this wall face
+                // triangle.  Assuming that the wallImpactDistance
+                // does not change as the particle or the mesh moves
+                // forward in time.
+                scalar r = p.wallImpactDistance(nHat);
+
+                vector toPlusRNHat = to + r*nHat;
+
+                // triI = 0 because it is the cell face tri of the tet
+                // we are concerned with.
+                scalar tetClambda = tetLambda
+                (
+                    tetIs.tet(mesh).centre(),
+                    toPlusRNHat,
+                    0,
+                    n,
+                    f[tetIs.faceBasePt()],
+                    cellI_,
+                    fI,
+                    tetIs.tetPt()
+                );
+
+                if ((tetClambda <= 0.0) || (tetClambda >= 1.0))
+                {
+                    // toPlusRNHat is not on the outside of the plane of
+                    // the wall face tri, the tri cannot be hit.
+                    continue;
+                }
+
+                // Check if the actual trajectory of the near-tri
+                // points intersects the triangle.
+
+                vector fromPlusRNHat = from + r*nHat;
+
+                // triI = 0 because it is the cell face tri of the tet
+                // we are concerned with.
+                scalar lambda = tetLambda
+                (
+                    fromPlusRNHat,
+                    toPlusRNHat,
+                    0,
+                    n,
+                    f[tetIs.faceBasePt()],
+                    cellI_,
+                    fI,
+                    tetIs.tetPt()
+                );
+
+                pointHit hitInfo(vector::zero);
+
+                if (mesh.moving())
+                {
+                    // For a moving mesh, the position of wall
+                    // triangle needs to be moved in time to be
+                    // consistent with the moment defined by the
+                    // current value of stepFraction and the value of
+                    // lambda just calculated.
+
+                    // Total fraction thought the timestep of the
+                    // motion, including stepFraction before the
+                    // current tracking step and the current
+                    // lambda
+                    // i.e.
+                    // let s = stepFraction, l = lambda
+                    // Motion of x in time:
+                    // |-----------------|---------|---------|
+                    // x00               x0        xi        x
+                    //
+                    // where xi is the correct value of x at the required
+                    // tracking instant.
+                    //
+                    // x0 = x00 + s*(x - x00) = s*x + (1 - s)*x00
+                    //
+                    // i.e. the motion covered by previous tracking portions
+                    // within this timestep, and
+                    //
+                    // xi = x0 + l*(x - x0)
+                    //    = l*x + (1 - l)*x0
+                    //    = l*x + (1 - l)*(s*x + (1 - s)*x00)
+                    //    = (s + l - s*l)*x + (1 - (s + l - s*l))*x00
+                    //
+                    // let m = (s + l - s*l)
+                    //
+                    // xi = m*x + (1 - m)*x00 = x00 + m*(x - x00);
+                    //
+                    // In the same form as before.
+
+                    // Clip lambda to 0.0-1.0 to ensure that sensible
+                    // positions are used for triangle intersections.
+                    scalar lam = max(0.0, min(1.0, lambda));
+
+                    scalar m = stepFraction_ + lam - (stepFraction_*lam);
+
+                    triPointRef tri00 = tetIs.oldFaceTri(mesh);
+
+                    // Use SMALL positive tolerance to make the triangle
+                    // slightly "fat" to improve robustness.  Intersection
+                    // is calculated as the ray (from + r*nHat) -> (to +
+                    // r*nHat).
+
+                    point tPtA = tri00.a() + m*(tri.a() - tri00.a());
+                    point tPtB = tri00.b() + m*(tri.b() - tri00.b());
+                    point tPtC = tri00.c() + m*(tri.c() - tri00.c());
+
+                    triPointRef t(tPtA, tPtB, tPtC);
+
+                    // The point fromPlusRNHat + m*(to - from) is on the
+                    // plane of the triangle.  Determine the
+                    // intersection with this triangle by testing if
+                    // this point is inside or outside of the triangle.
+                    hitInfo = t.intersection
+                    (
+                        fromPlusRNHat + m*(to - from),
+                        t.normal(),
+                        intersection::FULL_RAY,
+                        SMALL
+                    );
+                }
+                else
+                {
+                    // Use SMALL positive tolerance to make the triangle
+                    // slightly "fat" to improve robustness.  Intersection
+                    // is calculated as the ray (from + r*nHat) -> (to +
+                    // r*nHat).
+                    hitInfo = tri.intersection
+                    (
+                        fromPlusRNHat,
+                        (to - from),
+                        intersection::FULL_RAY,
+                        SMALL
+                    );
+                }
+
+                if (hitInfo.hit())
+                {
+                    if (lambda < lambdaMin)
+                    {
+                        lambdaMin = lambda;
+
+                        faceI_ = fI;
+
+                        closestTetIs = tetIs;
+                    }
+                }
+            }
+        }
+    }
 }
 
 
-// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
 
 template<class ParticleType>
 inline Foam::Particle<ParticleType>::trackData::trackData
@@ -272,6 +805,8 @@ inline Foam::Particle<ParticleType>::trackData::trackData
 {}
 
 
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
 template<class ParticleType>
 inline Foam::Cloud<ParticleType>&
 Foam::Particle<ParticleType>::trackData::cloud()
@@ -280,8 +815,6 @@ Foam::Particle<ParticleType>::trackData::cloud()
 }
 
 
-// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
-
 template<class ParticleType>
 inline const Foam::Cloud<ParticleType>&
 Foam::Particle<ParticleType>::cloud() const
@@ -307,35 +840,256 @@ inline Foam::vector& Foam::Particle<ParticleType>::position()
 template<class ParticleType>
 inline Foam::label Foam::Particle<ParticleType>::cell() const
 {
-    return celli_;
+    return cellI_;
 }
 
 
 template<class ParticleType>
 inline Foam::label& Foam::Particle<ParticleType>::cell()
 {
-    return celli_;
+    return cellI_;
+}
+
+
+template<class ParticleType>
+inline Foam::label Foam::Particle<ParticleType>::tetFace() const
+{
+    return tetFaceI_;
+}
+
+
+template<class ParticleType>
+inline Foam::label& Foam::Particle<ParticleType>::tetFace()
+{
+    return tetFaceI_;
+}
+
+
+template<class ParticleType>
+inline Foam::label Foam::Particle<ParticleType>::tetPt() const
+{
+    return tetPtI_;
+}
+
+
+template<class ParticleType>
+inline Foam::label& Foam::Particle<ParticleType>::tetPt()
+{
+    return tetPtI_;
+}
+
+
+template<class ParticleType>
+inline Foam::tetIndices Foam::Particle<ParticleType>::currentTetIndices() const
+{
+    return tetIndices(cellI_, tetFaceI_, tetPtI_, cloud_.polyMesh_);
+}
+
+
+template<class ParticleType>
+inline Foam::tetPointRef Foam::Particle<ParticleType>::currentTet() const
+{
+    return currentTetIndices().tet(cloud_.polyMesh_);
+}
+
+
+template<class ParticleType>
+inline Foam::vector Foam::Particle<ParticleType>::normal() const
+{
+    return currentTetIndices().faceTri(cloud_.polyMesh_).normal();
+}
+
+
+template<class ParticleType>
+inline Foam::vector
+Foam::Particle<ParticleType>::oldNormal() const
+{
+    return currentTetIndices().oldFaceTri(cloud_.polyMesh_).normal();
 }
 
 
 template<class ParticleType>
 inline Foam::label Foam::Particle<ParticleType>::face() const
 {
-    return facei_;
+    return faceI_;
 }
 
 
 template<class ParticleType>
 inline Foam::label& Foam::Particle<ParticleType>::face()
 {
-    return facei_;
+    return faceI_;
+}
+
+
+template<class ParticleType>
+inline void Foam::Particle<ParticleType>::initCellFacePt()
+{
+    if (cellI_ == -1)
+    {
+        cloud_.findCellFacePt
+        (
+            position_,
+            cellI_,
+            tetFaceI_,
+            tetPtI_
+        );
+
+        if (cellI_ == -1)
+        {
+            FatalErrorIn
+            (
+                "Foam::Particle<ParticleType>::Particle"
+                "("
+                    "void Foam::Particle<ParticleType>::initCellFacePt()"
+                ")"
+            )   << "cell, tetFace and tetPt search failure at position "
+                << position_ << nl
+                << abort(FatalError);
+        }
+    }
+    else
+    {
+        cloud_.findFacePt(cellI_, position_, tetFaceI_, tetPtI_);
+
+        if (tetFaceI_ == -1 || tetPtI_ == -1)
+        {
+            label oldCellI = cellI_;
+
+            cloud_.findCellFacePt
+            (
+                position_,
+                cellI_,
+                tetFaceI_,
+                tetPtI_
+            );
+
+            if (cellI_ == -1 || tetFaceI_ == -1 || tetPtI_ == -1)
+            {
+                // The particle has entered this function with a cell
+                // number, but hasn't been able to find a cell to
+                // occupy.
+
+                if(!cloud_.polyMesh_.pointInCellBB(position_, oldCellI))
+                {
+                    // If the position is not inside the bound-box of
+                    // the cell that it thought it should be in, then
+                    // this is considered an error.
+
+                    FatalErrorIn
+                    (
+                        "Foam::Particle<ParticleType>::Particle"
+                        "("
+                            "void "
+                            "Foam::Particle<ParticleType>::initCellFacePt()"
+                        ")"
+                    )   << "cell, tetFace and tetPt search failure at position "
+                        << position_ << nl
+                        << abort(FatalError);
+                }
+
+                // The position is in the bound-box of the cell.  This
+                // situation may arise because the face decomposition
+                // of the cell is not the same as when the particle
+                // acquired the cell index.  For example, it has been
+                // read into a mesh that has made a different face
+                // base-point decision for a boundary face and now
+                // this particle is in a position that is not in the
+                // mesh.  Gradually move the particle towards the
+                // centre of the cell that it thought that it was in.
+
+                cellI_ = oldCellI;
+
+                point newPosition = position_;
+
+                const point& cC = cloud_.polyMesh_.cellCentres()[cellI_];
+
+                label trap(1.0/Cloud<ParticleType>::trackingCorrectionTol + 1);
+
+                label iterNo = 0;
+
+                do
+                {
+                    newPosition +=
+                        Cloud<ParticleType>::trackingCorrectionTol
+                       *(cC - position_);
+
+                    cloud_.findFacePt
+                    (
+                        cellI_,
+                        newPosition,
+                        tetFaceI_,
+                        tetPtI_
+                    );
+
+                    iterNo++;
+
+                } while (tetFaceI_ < 0  && iterNo <= trap);
+
+                if(tetFaceI_ == -1)
+                {
+                    FatalErrorIn
+                    (
+                        "Foam::Particle<ParticleType>::Particle"
+                        "("
+                        "void "
+                        "Foam::Particle<ParticleType>::initCellFacePt()"
+                        ")"
+                    )   << "cell, tetFace and tetPt search failure at position "
+                        << position_ << nl
+                        << abort(FatalError);
+                }
+
+                WarningIn
+                (
+                    "Foam::Particle<ParticleType>::Particle"
+                    "("
+                        "void Foam::Particle<ParticleType>::initCellFacePt()"
+                    ")"
+                )
+                    << "Particle moved from " << position_
+                    << " to " << newPosition
+                    << " in cell " << cellI_
+                    << " tetFace " << tetFaceI_
+                    << " tetPt " << tetPtI_ << nl
+                    << "    (A fraction of "
+                    << 1.0 - mag(cC - newPosition)/mag(cC - position_)
+                    << " of the distance to the cell centre)"
+                    << " because a decomposition tetFace and tetPt "
+                    << "could not be found."
+                    << endl;
+
+                position_ = newPosition;
+            }
+
+            if (cellI_ != oldCellI)
+            {
+                WarningIn
+                (
+                    "Foam::Particle<ParticleType>::Particle"
+                    "("
+                        "void Foam::Particle<ParticleType>::initCellFacePt()"
+                    ")"
+                )
+                    << "Particle at position " << position_
+                    << " searched for a cell, tetFace and tetPt." << nl
+                    << "    Found"
+                    << " cell " << cellI_
+                    << " tetFace " << tetFaceI_
+                    << " tetPt " << tetPtI_ << nl
+                    << "    This is a different cell to that which was supplied"
+                    << " (" << oldCellI << ")." << nl
+                    << endl;
+            }
+        }
+    }
 }
 
 
 template<class ParticleType>
 inline bool Foam::Particle<ParticleType>::onBoundary() const
 {
-    return facei_ != -1 && facei_ >= cloud_.pMesh().nInternalFaces();
+    return faceI_ != -1 && faceI_ >= cloud_.pMesh().nInternalFaces();
 }
 
 
@@ -384,20 +1138,20 @@ inline Foam::scalar Foam::Particle<ParticleType>::currentTime() const
 
 
 template<class ParticleType>
-inline Foam::label Foam::Particle<ParticleType>::patch(const label facei) const
+inline Foam::label Foam::Particle<ParticleType>::patch(const label faceI) const
 {
-    return cloud_.facePatch(facei);
+    return cloud_.facePatch(faceI);
 }
 
 
 template<class ParticleType>
 inline Foam::label Foam::Particle<ParticleType>::patchFace
 (
-    const label patchi,
-    const label facei
+    const label patchI,
+    const label faceI
 ) const
 {
-    return cloud_.patchFace(patchi, facei);
+    return cloud_.patchFace(patchI, faceI);
 }
 
 
@@ -412,7 +1166,7 @@ Foam::Particle<ParticleType>::wallImpactDistance(const vector&) const
 template<class ParticleType>
 inline Foam::label Foam::Particle<ParticleType>::faceInterpolation() const
 {
-    return facei_;
+    return faceI_;
 }
 
 
diff --git a/src/lagrangian/basic/Particle/ParticleIO.C b/src/lagrangian/basic/Particle/ParticleIO.C
index 3212c1f4861..18d17064598 100644
--- a/src/lagrangian/basic/Particle/ParticleIO.C
+++ b/src/lagrangian/basic/Particle/ParticleIO.C
@@ -31,7 +31,8 @@ License
 
 template<class ParticleType>
 Foam::string Foam::Particle<ParticleType>::propHeader =
-    "(Px Py Pz) cellI origProc origId";
+    "(Px Py Pz) cellI tetFaceI tetPtI origProc origId";
+
 
 // * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
 
@@ -44,20 +45,24 @@ Foam::Particle<ParticleType>::Particle
 )
 :
     cloud_(cloud),
-    facei_(-1),
+    position_(),
+    cellI_(-1),
+    faceI_(-1),
     stepFraction_(0.0),
+    tetFaceI_(-1),
+    tetPtI_(-1),
     origProc_(Pstream::myProcNo()),
     origId_(-1)
 {
-
     // readFields : read additional data. Should be consistent with writeFields.
 
     if (is.format() == IOstream::ASCII)
     {
-        is >> position_ >> celli_;
+        is  >> position_ >> cellI_;
+
         if (readFields)
         {
-            is >> origProc_ >> origId_;
+            is  >> tetFaceI_ >> tetPtI_ >> origProc_ >> origId_;
         }
     }
     else
@@ -69,9 +74,11 @@ Foam::Particle<ParticleType>::Particle
             (
                 reinterpret_cast<char*>(&position_),
                 sizeof(position_)
-              + sizeof(celli_)
-              + sizeof(facei_)
+              + sizeof(cellI_)
+              + sizeof(faceI_)
               + sizeof(stepFraction_)
+              + sizeof(tetFaceI_)
+              + sizeof(tetPtI_)
               + sizeof(origProc_)
               + sizeof(origId_)
             );
@@ -82,18 +89,13 @@ Foam::Particle<ParticleType>::Particle
             (
                 reinterpret_cast<char*>(&position_),
                 sizeof(position_)
-              + sizeof(celli_)
-              + sizeof(facei_)
+              + sizeof(cellI_)
+              + sizeof(faceI_)
               + sizeof(stepFraction_)
             );
         }
     }
 
-    if (celli_ == -1)
-    {
-        celli_ = cloud_.pMesh().findCell(position_);
-    }
-
     // Check state of Istream
     is.check("Particle<ParticleType>::Particle(Istream&)");
 }
@@ -176,29 +178,33 @@ void Foam::Particle<ParticleType>::write(Ostream& os, bool writeFields) const
         if (writeFields)
         {
             // Write the additional entries
-            os << position_
-               << token::SPACE << celli_
-               << token::SPACE << origProc_
-               << token::SPACE << origId_;
+            os  << position_
+                << token::SPACE << cellI_
+                << token::SPACE << tetFaceI_
+                << token::SPACE << tetPtI_
+                << token::SPACE << origProc_
+                << token::SPACE << origId_;
         }
         else
         {
-            os << position_
-               << token::SPACE << celli_;
+            os  << position_
+                << token::SPACE << cellI_;
         }
     }
     else
     {
-        // In binary write both celli_ and facei_, needed for parallel transfer
+        // In binary write both cellI_ and faceI_, needed for parallel transfer
         if (writeFields)
         {
             os.write
             (
                 reinterpret_cast<const char*>(&position_),
                 sizeof(position_)
-              + sizeof(celli_)
-              + sizeof(facei_)
+              + sizeof(cellI_)
+              + sizeof(faceI_)
               + sizeof(stepFraction_)
+              + sizeof(tetFaceI_)
+              + sizeof(tetPtI_)
               + sizeof(origProc_)
               + sizeof(origId_)
             );
@@ -209,8 +215,8 @@ void Foam::Particle<ParticleType>::write(Ostream& os, bool writeFields) const
             (
                 reinterpret_cast<const char*>(&position_),
                 sizeof(position_)
-              + sizeof(celli_)
-              + sizeof(facei_)
+              + sizeof(cellI_)
+              + sizeof(faceI_)
               + sizeof(stepFraction_)
             );
         }
diff --git a/src/lagrangian/basic/indexedParticle/indexedParticle.H b/src/lagrangian/basic/indexedParticle/indexedParticle.H
index 9f725d5f550..6c940d0b70b 100644
--- a/src/lagrangian/basic/indexedParticle/indexedParticle.H
+++ b/src/lagrangian/basic/indexedParticle/indexedParticle.H
@@ -67,11 +67,26 @@ public:
         (
             const Cloud<indexedParticle>& c,
             const vector& position,
-            const label celli,
+            const label cellI,
+            const label tetFaceI,
+            const label tetPtI,
             const label index = 0
         )
         :
-            Particle<indexedParticle>(c, position, celli),
+            Particle<indexedParticle>(c, position, cellI, tetFaceI, tetPtI),
+            index_(index)
+        {}
+
+        //- Construct from components, with searching for tetFace and tetPt
+        indexedParticle
+        (
+            const Cloud<indexedParticle>& c,
+            const vector& position,
+            const label cellI,
+            const label index = 0
+        )
+        :
+            Particle<indexedParticle>(c, position, cellI),
             index_(index)
         {}
 
diff --git a/src/lagrangian/basic/passiveParticle/passiveParticle.H b/src/lagrangian/basic/passiveParticle/passiveParticle.H
index fec00ea922f..49de4eba667 100644
--- a/src/lagrangian/basic/passiveParticle/passiveParticle.H
+++ b/src/lagrangian/basic/passiveParticle/passiveParticle.H
@@ -60,10 +60,23 @@ public:
         (
             const Cloud<passiveParticle>& c,
             const vector& position,
-            const label celli
+            const label cellI,
+            const label tetFaceI,
+            const label tetPtI
         )
         :
-            Particle<passiveParticle>(c, position, celli)
+            Particle<passiveParticle>(c, position, cellI, tetFaceI, tetPtI)
+        {}
+
+        //- Construct from components, with searching for tetFace and tetPt
+        passiveParticle
+        (
+            const Cloud<passiveParticle>& c,
+            const vector& position,
+            const label cellI
+        )
+        :
+            Particle<passiveParticle>(c, position, cellI)
         {}
 
         //- Construct from Istream
diff --git a/src/lagrangian/coalCombustion/coalParcel/coalParcel.C b/src/lagrangian/coalCombustion/coalParcel/coalParcel.C
index 21086b996cd..a6500c8388e 100644
--- a/src/lagrangian/coalCombustion/coalParcel/coalParcel.C
+++ b/src/lagrangian/coalCombustion/coalParcel/coalParcel.C
@@ -31,10 +31,19 @@ Foam::coalParcel::coalParcel
 (
     ReactingMultiphaseCloud<coalParcel>& owner,
     const vector& position,
-    const label cellI
+    const label cellI,
+    const label tetFaceI,
+    const label tetPtI
 )
 :
-    ReactingMultiphaseParcel<coalParcel>(owner, position, cellI)
+    ReactingMultiphaseParcel<coalParcel>
+    (
+        owner,
+        position,
+        cellI,
+        tetFaceI,
+        tetPtI
+    )
 {}
 
 
@@ -43,6 +52,8 @@ Foam::coalParcel::coalParcel
     ReactingMultiphaseCloud<coalParcel>& owner,
     const vector& position,
     const label cellI,
+    const label tetFaceI,
+    const label tetPtI,
     const label typeId,
     const scalar nParticle0,
     const scalar d0,
@@ -62,6 +73,8 @@ Foam::coalParcel::coalParcel
         owner,
         position,
         cellI,
+        tetFaceI,
+        tetPtI,
         typeId,
         nParticle0,
         d0,
diff --git a/src/lagrangian/coalCombustion/coalParcel/coalParcel.H b/src/lagrangian/coalCombustion/coalParcel/coalParcel.H
index 2904ccbbcfd..81f6447320c 100644
--- a/src/lagrangian/coalCombustion/coalParcel/coalParcel.H
+++ b/src/lagrangian/coalCombustion/coalParcel/coalParcel.H
@@ -63,7 +63,9 @@ public:
         (
              ReactingMultiphaseCloud<coalParcel>& owner,
              const vector& position,
-             const label cellI
+             const label cellI,
+             const label tetFaceI,
+             const label tetPtI
         );
 
         //- Construct from components
@@ -72,6 +74,8 @@ public:
              ReactingMultiphaseCloud<coalParcel>& owner,
              const vector& position,
              const label cellI,
+             const label tetFaceI,
+             const label tetPtI,
              const label typeId,
              const scalar nParticle0,
              const scalar d0,
diff --git a/src/lagrangian/dieselSpray/Make/options b/src/lagrangian/dieselSpray/Make/options
index 454c4f15bfa..40cb013fbb7 100644
--- a/src/lagrangian/dieselSpray/Make/options
+++ b/src/lagrangian/dieselSpray/Make/options
@@ -1,5 +1,6 @@
 EXE_INC = \
     -I$(LIB_SRC)/finiteVolume/lnInclude \
+    -I$(LIB_SRC)/meshTools/lnInclude \
     -I$(LIB_SRC)/lagrangian/basic/lnInclude \
     -I$(LIB_SRC)/turbulenceModels \
     -I$(LIB_SRC)/turbulenceModels/compressible/turbulenceModel \
@@ -16,6 +17,7 @@ EXE_INC = \
 
 LIB_LIBS = \
     -llagrangian \
+    -lmeshTools \
     -lfiniteVolume \
     -lcompressibleRASModels \
     -lcompressibleLESModels \
diff --git a/src/lagrangian/dieselSpray/parcel/boundaryTreatment.H b/src/lagrangian/dieselSpray/parcel/boundaryTreatment.H
index 4420773172b..a2e03dc676f 100644
--- a/src/lagrangian/dieselSpray/parcel/boundaryTreatment.H
+++ b/src/lagrangian/dieselSpray/parcel/boundaryTreatment.H
@@ -15,12 +15,11 @@ if (isA<wallPolyPatch>(pbMesh[patch(face())]))
 else if (isA<wedgePolyPatch>(pbMesh[patch(face())]))
 {
     // check if parcel is trying to move out of the domain
-    label patchi = patch(face());
-    label patchFacei = patchFace(patchi, face());
-    const polyPatch& patch = mesh.boundaryMesh()[patchi];
-    vector nf = patch.faceAreas()[patchFacei];
+
+    vector nf = normal();
 
     scalar Un = U() & nf;
+
     if (Un > 0)
     {
         scalar Un2 = U() & n();
@@ -30,12 +29,11 @@ else if (isA<wedgePolyPatch>(pbMesh[patch(face())]))
 else if (isA<symmetryPolyPatch>(pbMesh[patch(face())]))
 {
     // check if parcel is trying to move out of the domain
-    label patchi = patch(face());
-    label patchFacei = patchFace(patchi, face());
-    const polyPatch& patch = mesh.boundaryMesh()[patchi];
-    vector nf = patch.faceAreas()[patchFacei];
+
+    vector nf = normal();
 
     scalar Un = U() & nf;
+
     if (Un > 0)
     {
         if (sDB.twoD())
diff --git a/src/lagrangian/dieselSpray/parcel/parcel.C b/src/lagrangian/dieselSpray/parcel/parcel.C
index 4b0b1ef620c..50837a923d4 100644
--- a/src/lagrangian/dieselSpray/parcel/parcel.C
+++ b/src/lagrangian/dieselSpray/parcel/parcel.C
@@ -50,6 +50,8 @@ Foam::parcel::parcel
     const Cloud<parcel>& cloud,
     const vector& position,
     const label cellI,
+    const label tetFaceI,
+    const label tetPtI,
     const vector& n,
     const scalar d,
     const scalar T,
@@ -67,7 +69,7 @@ Foam::parcel::parcel
     const List<word>& liquidNames
 )
 :
-    Particle<parcel>(cloud, position, cellI),
+    Particle<parcel>(cloud, position, cellI, tetFaceI, tetPtI),
     liquidComponents_
     (
         liquidNames
@@ -103,12 +105,13 @@ bool Foam::parcel::move(spray& sDB)
     label Nf = fuels.components().size();
     label Ns = sDB.composition().Y().size();
 
+    tetIndices tetIs = this->currentTetIndices();
+
     // Calculate the interpolated gas properties at the position of the parcel
-    vector Up = sDB.UInterpolator().interpolate(position(), cell())
-        + Uturb();
-    scalar rhog = sDB.rhoInterpolator().interpolate(position(), cell());
-    scalar pg = sDB.pInterpolator().interpolate(position(), cell());
-    scalar Tg = sDB.TInterpolator().interpolate(position(), cell());
+    vector Up = sDB.UInterpolator().interpolate(position(), tetIs) + Uturb();
+    scalar rhog = sDB.rhoInterpolator().interpolate(position(), tetIs);
+    scalar pg = sDB.pInterpolator().interpolate(position(), tetIs);
+    scalar Tg = sDB.TInterpolator().interpolate(position(), tetIs);
 
     scalarField Yfg(Nf, 0.0);
 
@@ -119,8 +122,8 @@ bool Foam::parcel::move(spray& sDB)
         if (sDB.isLiquidFuel()[i])
         {
             label j = sDB.gasToLiquidIndex()[i];
-            scalar Yicelli = Yi[cell()];
-            Yfg[j] = Yicelli;
+            scalar YicellI = Yi[cell()];
+            Yfg[j] = YicellI;
         }
         cpMixture += Yi[cell()]*sDB.gasProperties()[i].Cp(Tg);
     }
@@ -199,8 +202,8 @@ bool Foam::parcel::move(spray& sDB)
 
         // remember which cell the parcel is in
         // since this will change if a face is hit
-        label celli = cell();
-        scalar p = sDB.p()[celli];
+        label cellI = cell();
+        scalar p = sDB.p()[cellI];
 
         // track parcel to face, or end of trajectory
         if (keepParcel)
@@ -259,7 +262,7 @@ bool Foam::parcel::move(spray& sDB)
         (
             dt,
             sDB,
-            celli,
+            cellI,
             face()
         );
 
@@ -283,11 +286,11 @@ bool Foam::parcel::move(spray& sDB)
         // Update the Spray Source Terms
         forAll(nMass, i)
         {
-            sDB.srhos()[i][celli] += oMass[i] - nMass[i];
+            sDB.srhos()[i][cellI] += oMass[i] - nMass[i];
         }
-        sDB.sms()[celli] += oMom - nMom;
+        sDB.sms()[cellI] += oMom - nMom;
 
-        sDB.shs()[celli] += oTotMass*(oH + oPE) - m()*(nH + nPE);
+        sDB.shs()[cellI] += oTotMass*(oH + oPE) - m()*(nH + nPE);
 
         // Remove evaporated mass from stripped mass
         ms() -= ms()*(oTotMass-m())/oTotMass;
@@ -300,11 +303,11 @@ bool Foam::parcel::move(spray& sDB)
             // ... and add the removed 'stuff' to the gas
             forAll(nMass, i)
             {
-                sDB.srhos()[i][celli] += nMass[i];
+                sDB.srhos()[i][cellI] += nMass[i];
             }
 
-            sDB.sms()[celli] += nMom;
-            sDB.shs()[celli] += m()*(nH + nPE);
+            sDB.sms()[cellI] += nMom;
+            sDB.shs()[cellI] += m()*(nH + nPE);
         }
 
         if (onBoundary() && keepParcel)
@@ -327,8 +330,8 @@ void Foam::parcel::updateParcelProperties
 (
     const scalar dt,
     spray& sDB,
-    const label celli,
-    const label facei
+    const label cellI,
+    const label faceI
 )
 {
     const liquidMixture& fuels = sDB.fuels();
@@ -340,34 +343,34 @@ void Foam::parcel::updateParcelProperties
     scalar W = 0.0;
     for (label i=0; i<Ns; i++)
     {
-        W += sDB.composition().Y()[i][celli]/sDB.gasProperties()[i].W();
+        W += sDB.composition().Y()[i][cellI]/sDB.gasProperties()[i].W();
 
     }
     W = 1.0/W;
 
     // Calculate the interpolated gas properties at the position of the parcel
-    vector Up = sDB.UInterpolator().interpolate(position(), celli, facei)
+    vector Up = sDB.UInterpolator().interpolate(position(), cellI, faceI)
         + Uturb();
-    scalar rhog = sDB.rhoInterpolator().interpolate(position(), celli, facei);
-    scalar pg = sDB.pInterpolator().interpolate(position(), celli, facei);
-    scalar Tg0 = sDB.TInterpolator().interpolate(position(), celli, facei);
+    scalar rhog = sDB.rhoInterpolator().interpolate(position(), cellI, faceI);
+    scalar pg = sDB.pInterpolator().interpolate(position(), cellI, faceI);
+    scalar Tg0 = sDB.TInterpolator().interpolate(position(), cellI, faceI);
 
     // correct the gaseous temperature for evaporated fuel
     scalar cpMix = 0.0;
     for (label i=0; i<Ns; i++)
     {
-        cpMix += sDB.composition().Y()[i][celli]
+        cpMix += sDB.composition().Y()[i][cellI]
                 *sDB.gasProperties()[i].Cp(T());
     }
-    scalar cellV            = sDB.mesh().V()[celli];
-    scalar rho              = sDB.rho()[celli];
+    scalar cellV            = sDB.mesh().V()[cellI];
+    scalar rho              = sDB.rho()[cellI];
     scalar cellMass         = rho*cellV;
-    scalar dh               = sDB.shs()[celli];
+    scalar dh               = sDB.shs()[cellI];
     scalarField addedMass(Nf, 0.0);
 
     forAll(addedMass, i)
     {
-        addedMass[i] += sDB.srhos()[i][celli]*cellV;
+        addedMass[i] += sDB.srhos()[i][cellI]*cellV;
     }
 
     scalar Tg  = Tg0 + dh/(cpMix*cellMass);
@@ -378,7 +381,7 @@ void Foam::parcel::updateParcelProperties
     {
         label j = sDB.liquidToGasIndex()[i];
         const volScalarField& Yj = sDB.composition().Y()[j];
-        scalar Yfg0 = Yj[celli];
+        scalar Yfg0 = Yj[cellI];
         Yfg[i] = (Yfg0*cellMass + addedMass[i])/(addedMass[i] + cellMass);
     }
 
@@ -389,7 +392,7 @@ void Foam::parcel::updateParcelProperties
 
     setRelaxationTimes
     (
-        celli,
+        cellI,
         tauMomentum,
         tauEvaporation,
         tauHeatTransfer,
@@ -492,14 +495,14 @@ void Foam::parcel::updateParcelProperties
             {
                 label j = sDB.liquidToGasIndex()[i];
                 const volScalarField& Yj = sDB.composition().Y()[j];
-                scalar Yfg0 = Yj[celli];
+                scalar Yfg0 = Yj[cellI];
                 Yfg[i] = (Yfg0*cellMass + addedMass[i] + dm)
                         /(addedMass[i] + cellMass + dm);
             }
 
             setRelaxationTimes
             (
-                celli,
+                cellI,
                 tauMomentum,
                 tauEvaporation,
                 tauHeatTransfer,
@@ -530,7 +533,7 @@ void Foam::parcel::updateParcelProperties
             }
             else
             {
-                scalar Y = sDB.composition().Y()[i][celli];
+                scalar Y = sDB.composition().Y()[i][cellI];
                 cpMix += Y*sDB.gasProperties()[i].Cp(Taverage);
             }
         }
diff --git a/src/lagrangian/dieselSpray/parcel/parcel.H b/src/lagrangian/dieselSpray/parcel/parcel.H
index c96abd9155c..90d46e0231e 100644
--- a/src/lagrangian/dieselSpray/parcel/parcel.H
+++ b/src/lagrangian/dieselSpray/parcel/parcel.H
@@ -113,7 +113,7 @@ class parcel
         //- Set the relaxation times
         void setRelaxationTimes
         (
-            label celli,
+            label cellI,
             scalar& tauMomentum,
             scalarField& tauEvaporation,
             scalar& tauHeatTransfer,
@@ -133,8 +133,8 @@ class parcel
         (
             const scalar dt,
             spray& sprayData,
-            const label celli,
-            const label facei
+            const label cellI,
+            const label faceI
         );
 
 
@@ -150,7 +150,9 @@ public:
         (
             const Cloud<parcel>& cloud,
             const vector& position,
-            const label celli,
+            const label cellI,
+            const label tetFaceI,
+            const label tetPtI,
             const vector& n,
             const scalar d,
             const scalar T,
diff --git a/src/lagrangian/dieselSpray/parcel/setRelaxationTimes.C b/src/lagrangian/dieselSpray/parcel/setRelaxationTimes.C
index 7565a747102..0b4b305f0cd 100644
--- a/src/lagrangian/dieselSpray/parcel/setRelaxationTimes.C
+++ b/src/lagrangian/dieselSpray/parcel/setRelaxationTimes.C
@@ -36,7 +36,7 @@ License
 
 void Foam::parcel::setRelaxationTimes
 (
-    label celli,
+    label cellI,
     scalar& tauMomentum,
     scalarField& tauEvaporation,
     scalar& tauHeatTransfer,
@@ -70,7 +70,7 @@ void Foam::parcel::setRelaxationTimes
 
     for (label i=0; i<Ns; i++)
     {
-        scalar Y = sDB.composition().Y()[i][celli];
+        scalar Y = sDB.composition().Y()[i][cellI];
         W += Y/sDB.gasProperties()[i].W();
         // Using mass-fractions to average...
         kMixture += Y*sDB.gasProperties()[i].kappa(Tf);
@@ -87,7 +87,7 @@ void Foam::parcel::setRelaxationTimes
     for (label i=0; i<Nf; i++)
     {
         label j = sDB.liquidToGasIndex()[i];
-        scalar Y = sDB.composition().Y()[j][celli];
+        scalar Y = sDB.composition().Y()[j][cellI];
         scalar Wi = sDB.gasProperties()[j].W();
         Yf[i] = Y;
         Xf[i] = Y*W/Wi;
@@ -264,10 +264,10 @@ void Foam::parcel::setRelaxationTimes
                 forAll(sDB.gasProperties(), k)
                 {
                     vapourSurfaceEnthalpy +=
-                        sDB.composition().Y()[k][celli]
+                        sDB.composition().Y()[k][cellI]
                        *sDB.gasProperties()[k].H(tBoilingSurface);
                     vapourFarEnthalpy +=
-                        sDB.composition().Y()[k][celli]
+                        sDB.composition().Y()[k][cellI]
                        *sDB.gasProperties()[k].H(temperature);
                 }
 
diff --git a/src/lagrangian/dieselSpray/spray/findInjectorCell.H b/src/lagrangian/dieselSpray/spray/findInjectorCell.H
index ff4eea7904a..e9e8a7bfa80 100644
--- a/src/lagrangian/dieselSpray/spray/findInjectorCell.H
+++ b/src/lagrangian/dieselSpray/spray/findInjectorCell.H
@@ -22,7 +22,14 @@ reduce(foundCell, orOp<bool>());
 if (!foundCell)
 {
     injectionPosition = it->position(n);
-    injectorCell = mesh_.findCell(injectionPosition);
+
+    findCellFacePt
+    (
+        injectionPosition,
+        injectorCell,
+        injectorTetFaceI,
+        injectorTetPtI
+    );
 
     if (injectorCell >= 0)
     {
diff --git a/src/lagrangian/dieselSpray/spray/spray.C b/src/lagrangian/dieselSpray/spray/spray.C
index 6ff9319f9a5..81047c68094 100644
--- a/src/lagrangian/dieselSpray/spray/spray.C
+++ b/src/lagrangian/dieselSpray/spray/spray.C
@@ -241,16 +241,16 @@ Foam::spray::spray
     label n=0;
 
     // check for the type of boundary condition
-    forAll(bMesh, patchi)
+    forAll(bMesh, patchI)
     {
-        if (isA<symmetryPolyPatch>(bMesh[patchi]))
+        if (isA<symmetryPolyPatch>(bMesh[patchI]))
         {
             symPlaneExist = true;
         }
-        else if (isA<wedgePolyPatch>(bMesh[patchi]))
+        else if (isA<wedgePolyPatch>(bMesh[patchI]))
         {
             wedgeExist = true;
-            patches[n++] = patchi;
+            patches[n++] = patchI;
         }
     }
 
diff --git a/src/lagrangian/dieselSpray/spray/sprayFunctions.C b/src/lagrangian/dieselSpray/spray/sprayFunctions.C
index 2a7de13c54c..9a1d0669428 100644
--- a/src/lagrangian/dieselSpray/spray/sprayFunctions.C
+++ b/src/lagrangian/dieselSpray/spray/sprayFunctions.C
@@ -149,9 +149,9 @@ Foam::scalar Foam::spray::liquidTotalEnthalpy() const
 
     forAllConstIter(spray, *this, iter)
     {
-        label celli = iter().cell();
+        label cellI = iter().cell();
         scalar T = iter().T();
-        scalar pc = p()[celli];
+        scalar pc = p()[cellI];
         scalar rho = fuels().rho(pc, T, iter().X());
         scalar hlat = fuels().hl(pc, T, iter().X());
         scalar hg = 0.0;
@@ -351,8 +351,8 @@ Foam::scalar Foam::spray::smd() const
 
     forAllConstIter(spray, *this, iter)
     {
-        label celli = iter().cell();
-        scalar Pc = p()[celli];
+        label cellI = iter().cell();
+        scalar Pc = p()[cellI];
         scalar T = iter().T();
         scalar rho = fuels_->rho(Pc, T, iter().X());
 
diff --git a/src/lagrangian/dieselSpray/spray/sprayInject.C b/src/lagrangian/dieselSpray/spray/sprayInject.C
index f27769dfb6d..06dbc9b7c61 100644
--- a/src/lagrangian/dieselSpray/spray/sprayInject.C
+++ b/src/lagrangian/dieselSpray/spray/sprayInject.C
@@ -107,7 +107,17 @@ void Foam::spray::inject()
                     scalar deviation = breakup().y0();
                     scalar ddev = breakup().yDot0();
 
-                    label injectorCell = mesh_.findCell(injectionPosition);
+                    label injectorCell = -1;
+                    label injectorTetFaceI = -1;
+                    label injectorTetPtI = -1;
+
+                    findCellFacePt
+                    (
+                        injectionPosition,
+                        injectorCell,
+                        injectorTetFaceI,
+                        injectorTetPtI
+                    );
 
 #                   include "findInjectorCell.H"
 
@@ -122,6 +132,8 @@ void Foam::spray::inject()
                             *this,
                             injectionPosition,
                             injectorCell,
+                            injectorTetFaceI,
+                            injectorTetPtI,
                             normal,
                             diameter,
                             it->T(toi),
diff --git a/src/lagrangian/dieselSpray/spray/sprayOps.C b/src/lagrangian/dieselSpray/spray/sprayOps.C
index 6210c019672..7904771fa14 100644
--- a/src/lagrangian/dieselSpray/spray/sprayOps.C
+++ b/src/lagrangian/dieselSpray/spray/sprayOps.C
@@ -88,7 +88,7 @@ void Foam::spray::breakupLoop()
         vector velocity = UInterpolator().interpolate
         (
             elmnt().position(),
-            elmnt().cell()
+            elmnt().currentTetIndices()
         );
 
         // liquidCore < 0.5 indicates discrete drops
@@ -122,7 +122,7 @@ void Foam::spray::atomizationLoop()
         vector velocity = UInterpolator().interpolate
         (
             elmnt().position(),
-            elmnt().cell()
+            elmnt().currentTetIndices()
         );
 
         // liquidCore > 0.5 indicates a liquid core
diff --git a/src/lagrangian/dieselSpray/spraySubModels/breakupModel/SHF/SHF.C b/src/lagrangian/dieselSpray/spraySubModels/breakupModel/SHF/SHF.C
index 12a3a6771b9..4824d253f85 100644
--- a/src/lagrangian/dieselSpray/spraySubModels/breakupModel/SHF/SHF.C
+++ b/src/lagrangian/dieselSpray/spraySubModels/breakupModel/SHF/SHF.C
@@ -102,14 +102,14 @@ void Foam::SHF::breakupParcel
     const liquidMixture& fuels
 ) const
 {
-    label celli = p.cell();
+    label cellI = p.cell();
     scalar T = p.T();
-    scalar pc = spray_.p()[celli];
+    scalar pc = spray_.p()[cellI];
 
     scalar sigma = fuels.sigma(pc, T, p.X());
     scalar rhoLiquid = fuels.rho(pc, T, p.X());
     scalar muLiquid = fuels.mu(pc, T, p.X());
-    scalar rhoGas = spray_.rho()[celli];
+    scalar rhoGas = spray_.rho()[cellI];
 
     scalar weGas = p.We(vel, rhoGas, sigma);
     scalar weLiquid = p.We(vel, rhoLiquid, sigma);
@@ -237,6 +237,8 @@ void Foam::SHF::breakupParcel
                     spray_,
                     p.position(),
                     p.cell(),
+                    p.tetFace(),
+                    p.tetPt(),
                     p.n(),
                     d,
                     p.T(),
diff --git a/src/lagrangian/dieselSpray/spraySubModels/breakupModel/reitzKHRT/reitzKHRT.C b/src/lagrangian/dieselSpray/spraySubModels/breakupModel/reitzKHRT/reitzKHRT.C
index 921af9c5858..5c03fedba98 100644
--- a/src/lagrangian/dieselSpray/spraySubModels/breakupModel/reitzKHRT/reitzKHRT.C
+++ b/src/lagrangian/dieselSpray/spraySubModels/breakupModel/reitzKHRT/reitzKHRT.C
@@ -78,15 +78,15 @@ void Foam::reitzKHRT::breakupParcel
     const liquidMixture& fuels
 ) const
 {
-    label celli = p.cell();
+    label cellI = p.cell();
     scalar T = p.T();
     scalar r = 0.5*p.d();
-    scalar pc = spray_.p()[celli];
+    scalar pc = spray_.p()[cellI];
 
     scalar sigma = fuels.sigma(pc, T, p.X());
     scalar rhoLiquid = fuels.rho(pc, T, p.X());
     scalar muLiquid = fuels.mu(pc, T, p.X());
-    scalar rhoGas = spray_.rho()[celli];
+    scalar rhoGas = spray_.rho()[cellI];
     scalar Np = p.N(rhoLiquid);
     scalar semiMass = Np*pow3(p.d());
 
@@ -198,6 +198,8 @@ void Foam::reitzKHRT::breakupParcel
                         spray_,
                         p.position(),
                         p.cell(),
+                        p.tetFace(),
+                        p.tetPt(),
                         p.n(),
                         dc,
                         p.T(),
diff --git a/src/lagrangian/dieselSpray/spraySubModels/wallModel/reflectParcel/reflectParcel.C b/src/lagrangian/dieselSpray/spraySubModels/wallModel/reflectParcel/reflectParcel.C
index e776b78f490..162484d5f23 100644
--- a/src/lagrangian/dieselSpray/spraySubModels/wallModel/reflectParcel/reflectParcel.C
+++ b/src/lagrangian/dieselSpray/spraySubModels/wallModel/reflectParcel/reflectParcel.C
@@ -70,18 +70,18 @@ Foam::reflectParcel::~reflectParcel()
 bool Foam::reflectParcel::wallTreatment
 (
     parcel& p,
-    const label globalFacei
+    const label globalFaceI
 ) const
 {
-    label patchi = p.patch(globalFacei);
-    label facei = p.patchFace(patchi, globalFacei);
+    label patchI = p.patch(globalFaceI);
+    label faceI = p.patchFace(patchI, globalFaceI);
 
     const polyMesh& mesh = spray_.mesh();
 
-    if (isA<wallPolyPatch>(mesh_.boundaryMesh()[patchi]))
+    if (isA<wallPolyPatch>(mesh_.boundaryMesh()[patchI]))
     {
         // wallNormal defined to point outwards of domain
-        vector Sf = mesh_.Sf().boundaryField()[patchi][facei];
+        vector Sf = mesh_.Sf().boundaryField()[patchI][faceI];
         Sf /= mag(Sf);
 
         if (!mesh.moving())
@@ -97,17 +97,17 @@ bool Foam::reflectParcel::wallTreatment
         else
         {
             // moving mesh
-            vector Ub1 = U_.boundaryField()[patchi][facei];
-            vector Ub0 = U_.oldTime().boundaryField()[patchi][facei];
+            vector Ub1 = U_.boundaryField()[patchI][faceI];
+            vector Ub0 = U_.oldTime().boundaryField()[patchI][faceI];
 
             scalar dt = spray_.runTime().deltaTValue();
             const vectorField& oldPoints = mesh.oldPoints();
 
-            const vector& Cf1 = mesh.faceCentres()[globalFacei];
+            const vector& Cf1 = mesh.faceCentres()[globalFaceI];
 
-            vector Cf0 = mesh.faces()[globalFacei].centre(oldPoints);
+            vector Cf0 = mesh.faces()[globalFaceI].centre(oldPoints);
             vector Cf = Cf0 + p.stepFraction()*(Cf1 - Cf0);
-            vector Sf0 = mesh.faces()[globalFacei].normal(oldPoints);
+            vector Sf0 = mesh.faces()[globalFaceI].normal(oldPoints);
 
             // for layer addition Sf0 = vector::zero and we use Sf
             if (mag(Sf0) > SMALL)
@@ -157,10 +157,10 @@ bool Foam::reflectParcel::wallTreatment
                 {
                     Info<< "reflectParcel:: v = " << v
                         << ", Ub = " << Ub
-                        << ", facei = " << facei
-                        << ", patchi = " << patchi
-                        << ", globalFacei = " << globalFacei
-                        << ", name = " << mesh_.boundaryMesh()[patchi].name()
+                        << ", faceI = " << faceI
+                        << ", patchI = " << patchI
+                        << ", globalFaceI = " << globalFaceI
+                        << ", name = " << mesh_.boundaryMesh()[patchI].name()
                         << endl;
                 }
             */
@@ -175,7 +175,7 @@ bool Foam::reflectParcel::wallTreatment
     else
     {
         FatalErrorIn("bool reflectParcel::wallTreatment(parcel& parcel) const")
-            << " parcel has hit a boundary " << mesh_.boundary()[patchi].type()
+            << " parcel has hit a boundary " << mesh_.boundary()[patchI].type()
             << " which not yet has been implemented." << nl
             << abort(FatalError);
     }
diff --git a/src/lagrangian/dieselSpray/spraySubModels/wallModel/reflectParcel/reflectParcel.H b/src/lagrangian/dieselSpray/spraySubModels/wallModel/reflectParcel/reflectParcel.H
index be19fafe24b..0316e1d819e 100644
--- a/src/lagrangian/dieselSpray/spraySubModels/wallModel/reflectParcel/reflectParcel.H
+++ b/src/lagrangian/dieselSpray/spraySubModels/wallModel/reflectParcel/reflectParcel.H
@@ -87,7 +87,7 @@ public:
 
         //- Return true if parcel is to be kept, and false if it is to be
         // removed
-        bool wallTreatment(parcel& parcel, const label facei) const;
+        bool wallTreatment(parcel& parcel, const label faceI) const;
 };
 
 
diff --git a/src/lagrangian/dieselSpray/spraySubModels/wallModel/removeParcel/removeParcel.C b/src/lagrangian/dieselSpray/spraySubModels/wallModel/removeParcel/removeParcel.C
index b62ba09778d..f4f74742de9 100644
--- a/src/lagrangian/dieselSpray/spraySubModels/wallModel/removeParcel/removeParcel.C
+++ b/src/lagrangian/dieselSpray/spraySubModels/wallModel/removeParcel/removeParcel.C
@@ -65,7 +65,7 @@ Foam::removeParcel::~removeParcel()
 bool Foam::removeParcel::wallTreatment
 (
     parcel&,
-    const label facei
+    const label faceI
 ) const
 {
     return false;
diff --git a/src/lagrangian/dieselSpray/spraySubModels/wallModel/removeParcel/removeParcel.H b/src/lagrangian/dieselSpray/spraySubModels/wallModel/removeParcel/removeParcel.H
index f07761847a9..a25b04b565d 100644
--- a/src/lagrangian/dieselSpray/spraySubModels/wallModel/removeParcel/removeParcel.H
+++ b/src/lagrangian/dieselSpray/spraySubModels/wallModel/removeParcel/removeParcel.H
@@ -73,7 +73,7 @@ public:
 
         //- Return true if parcel is to be kept, and false if it is to be
         // removed
-        bool wallTreatment(parcel& parcel, const label facei) const;
+        bool wallTreatment(parcel& parcel, const label faceI) const;
 };
 
 
diff --git a/src/lagrangian/dieselSpray/spraySubModels/wallModel/wallModel/wallModel.H b/src/lagrangian/dieselSpray/spraySubModels/wallModel/wallModel/wallModel.H
index 598c6803d12..a17e4c078fb 100644
--- a/src/lagrangian/dieselSpray/spraySubModels/wallModel/wallModel/wallModel.H
+++ b/src/lagrangian/dieselSpray/spraySubModels/wallModel/wallModel/wallModel.H
@@ -118,7 +118,7 @@ public:
         virtual bool wallTreatment
         (
             parcel&,
-            const label facei
+            const label faceI
         ) const = 0;
 };
 
diff --git a/src/lagrangian/dsmc/clouds/Templates/DsmcCloud/DsmcCloud.C b/src/lagrangian/dsmc/clouds/Templates/DsmcCloud/DsmcCloud.C
index 80e0dea37d5..8b92319bcb5 100644
--- a/src/lagrangian/dsmc/clouds/Templates/DsmcCloud/DsmcCloud.C
+++ b/src/lagrangian/dsmc/clouds/Templates/DsmcCloud/DsmcCloud.C
@@ -109,161 +109,86 @@ void Foam::DsmcCloud<ParcelType>::initialise
 
     numberDensities /= nParticle_;
 
-    forAll(mesh_.cells(), cell)
+    forAll(mesh_.cells(), cellI)
     {
-        const vector& cC = mesh_.cellCentres()[cell];
-        const labelList& cellFaces = mesh_.cells()[cell];
-        const scalar cV = mesh_.cellVolumes()[cell];
-
-        label nTets = 0;
+        List<tetIndices> cellTets = polyMeshTetDecomposition::cellTetIndices
+        (
+            mesh_,
+            cellI
+        );
 
-        // Each face is split into nEdges (or nVertices) - 2 tets.
-        forAll(cellFaces, face)
+        forAll(cellTets, tetI)
         {
-            nTets += mesh_.faces()[cellFaces[face]].size() - 2;
-        }
-
-        // Calculate the cumulative tet volumes circulating around the cell and
-        // record the vertex labels of each.
-        scalarList cTetVFracs(nTets, 0.0);
+            const tetIndices& cellTetIs = cellTets[tetI];
 
-        List<labelList> tetPtIs(nTets, labelList(3,-1));
+            tetPointRef tet = cellTetIs.tet(mesh_);
 
-        // Keep track of which tet this is.
-        label tet = 0;
+            scalar tetVolume = tet.mag();
 
-        forAll(cellFaces, face)
-        {
-            const labelList& facePoints = mesh_.faces()[cellFaces[face]];
-
-            label pointI = 1;
-            while ((pointI + 1) < facePoints.size())
+            forAll(molecules, i)
             {
+                const word& moleculeName(molecules[i]);
 
-                const vector& pA = mesh_.points()[facePoints[0]];
-                const vector& pB = mesh_.points()[facePoints[pointI]];
-                const vector& pC = mesh_.points()[facePoints[pointI + 1]];
-
-                cTetVFracs[tet] =
-                    mag(((pA - cC) ^ (pB - cC)) & (pC - cC))/(cV*6.0)
-                  + cTetVFracs[max((tet - 1),0)];
-
-                tetPtIs[tet][0] = facePoints[0];
-                tetPtIs[tet][1] = facePoints[pointI];
-                tetPtIs[tet][2] = facePoints[pointI + 1];
-
-                pointI++;
-                tet++;
-            }
-        }
-
-        // Force the last volume fraction value to 1.0 to avoid any
-        // rounding/non-flat face errors giving a value < 1.0
-        cTetVFracs[nTets - 1] = 1.0;
-
-        forAll(molecules, i)
-        {
-            const word& moleculeName(molecules[i]);
-
-            label typeId(findIndex(typeIdList_, moleculeName));
+                label typeId(findIndex(typeIdList_, moleculeName));
 
-            if (typeId == -1)
-            {
-                FatalErrorIn("Foam::DsmcCloud<ParcelType>::initialise")
-                << "typeId " << moleculeName << "not defined." << nl
-                    << abort(FatalError);
-            }
+                if (typeId == -1)
+                {
+                    FatalErrorIn("Foam::DsmcCloud<ParcelType>::initialise")
+                        << "typeId " << moleculeName << "not defined." << nl
+                        << abort(FatalError);
+                }
 
-            const typename ParcelType::constantProperties& cP =
+                const typename ParcelType::constantProperties& cP =
                 constProps(typeId);
 
-            scalar numberDensity = numberDensities[i];
-
-            // Calculate the number of particles required
-            scalar particlesRequired = numberDensity*mesh_.cellVolumes()[cell];
-
-            // Only integer numbers of particles can be inserted
-            label nParticlesToInsert = label(particlesRequired);
+                scalar numberDensity = numberDensities[i];
 
-            // Add another particle with a probability proportional to the
-            // remainder of taking the integer part of particlesRequired
-            if ((particlesRequired - nParticlesToInsert) > rndGen_.scalar01())
-            {
-                nParticlesToInsert++;
-            }
+                // Calculate the number of particles required
+                scalar particlesRequired = numberDensity*tetVolume;
 
-            for (label pI = 0; pI < nParticlesToInsert; pI++)
-            {
-                // Choose a random point in a generic tetrahedron
+                // Only integer numbers of particles can be inserted
+                label nParticlesToInsert = label(particlesRequired);
 
-                scalar s = rndGen_.scalar01();
-                scalar t = rndGen_.scalar01();
-                scalar u = rndGen_.scalar01();
-
-                if (s + t > 1.0)
+                // Add another particle with a probability proportional to the
+                // remainder of taking the integer part of particlesRequired
+                if
+                (
+                    (particlesRequired - nParticlesToInsert)
+                  > rndGen_.scalar01()
+                )
                 {
-                    s = 1.0 - s;
-                    t = 1.0 - t;
+                    nParticlesToInsert++;
                 }
 
-                if (t + u > 1.0)
-                {
-                    scalar tmp = u;
-                    u = 1.0 - s - t;
-                    t = 1.0 - tmp;
-                }
-                else if (s + t + u > 1.0)
+                for (label pI = 0; pI < nParticlesToInsert; pI++)
                 {
-                    scalar tmp = u;
-                    u = s + t + u - 1.0;
-                    s = 1.0 - t - tmp;
-                }
+                    point p = tet.randomPoint(rndGen_);
 
-                // Choose a tetrahedron to insert in, based on their relative
-                // volumes
-                scalar tetSelection = rndGen_.scalar01();
+                    vector U = equipartitionLinearVelocity
+                    (
+                        temperature,
+                        cP.mass()
+                    );
 
-                // Selected tetrahedron
-                label sTet = -1;
+                    scalar Ei = equipartitionInternalEnergy
+                    (
+                        temperature,
+                        cP.internalDegreesOfFreedom()
+                    );
 
-                forAll(cTetVFracs, tet)
-                {
-                    sTet = tet;
+                    U += velocity;
 
-                    if (cTetVFracs[tet] >= tetSelection)
-                    {
-                        break;
-                    }
+                    addNewParcel
+                    (
+                        p,
+                        U,
+                        Ei,
+                        cellI,
+                        cellTetIs.face(),
+                        cellTetIs.tetPt(),
+                        typeId
+                    );
                 }
-
-                vector p =
-                    (1 - s - t - u)*cC
-                  + s*mesh_.points()[tetPtIs[sTet][0]]
-                  + t*mesh_.points()[tetPtIs[sTet][1]]
-                  + u*mesh_.points()[tetPtIs[sTet][2]];
-
-                vector U = equipartitionLinearVelocity
-                (
-                    temperature,
-                    cP.mass()
-                );
-
-                scalar Ei = equipartitionInternalEnergy
-                (
-                    temperature,
-                    cP.internalDegreesOfFreedom()
-                );
-
-                U += velocity;
-
-                addNewParcel
-                (
-                    p,
-                    U,
-                    Ei,
-                    cell,
-                    typeId
-                );
             }
         }
     }
@@ -306,9 +231,9 @@ void Foam::DsmcCloud<ParcelType>::collisions()
 
     label collisions = 0;
 
-    forAll(cellOccupancy_, celli)
+    forAll(cellOccupancy_, cellI)
     {
-        const DynamicList<ParcelType*>& cellParcels(cellOccupancy_[celli]);
+        const DynamicList<ParcelType*>& cellParcels(cellOccupancy_[cellI]);
 
         label nC(cellParcels.size());
 
@@ -327,13 +252,13 @@ void Foam::DsmcCloud<ParcelType>::collisions()
             // Inverse addressing specifying which subCell a parcel is in
             List<label> whichSubCell(cellParcels.size());
 
-            const point& cC = mesh_.cellCentres()[celli];
+            const point& cC = mesh_.cellCentres()[cellI];
 
             forAll(cellParcels, i)
             {
-                ParcelType* p = cellParcels[i];
+                const ParcelType& p = *cellParcels[i];
 
-                vector relPos = p->position() - cC;
+                vector relPos = p.position() - cC;
 
                 label subCell =
                     pos(relPos.x()) + 2*pos(relPos.y()) + 4*pos(relPos.z());
@@ -345,16 +270,16 @@ void Foam::DsmcCloud<ParcelType>::collisions()
 
             // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-            scalar sigmaTcRMax = sigmaTcRMax_[celli];
+            scalar sigmaTcRMax = sigmaTcRMax_[cellI];
 
             scalar selectedPairs =
-                collisionSelectionRemainder_[celli]
+                collisionSelectionRemainder_[cellI]
               + 0.5*nC*(nC - 1)*nParticle_*sigmaTcRMax*deltaT
-               /mesh_.cellVolumes()[celli];
+               /mesh_.cellVolumes()[cellI];
 
             label nCandidates(selectedPairs);
 
-            collisionSelectionRemainder_[celli] = selectedPairs - nCandidates;
+            collisionSelectionRemainder_[cellI] = selectedPairs - nCandidates;
 
             collisionCandidates += nCandidates;
 
@@ -415,39 +340,30 @@ void Foam::DsmcCloud<ParcelType>::collisions()
 
                 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-                ParcelType* parcelP = cellParcels[candidateP];
-                ParcelType* parcelQ = cellParcels[candidateQ];
-
-                label typeIdP = parcelP->typeId();
-                label typeIdQ = parcelQ->typeId();
+                ParcelType& parcelP = *cellParcels[candidateP];
+                ParcelType& parcelQ = *cellParcels[candidateQ];
 
                 scalar sigmaTcR = binaryCollision().sigmaTcR
                 (
-                    typeIdP,
-                    typeIdQ,
-                    parcelP->U(),
-                    parcelQ->U()
+                    parcelP,
+                    parcelQ
                 );
 
                 // Update the maximum value of sigmaTcR stored, but use the
                 // initial value in the acceptance-rejection criteria because
                 // the number of collision candidates selected was based on this
 
-                if (sigmaTcR > sigmaTcRMax_[celli])
+                if (sigmaTcR > sigmaTcRMax_[cellI])
                 {
-                    sigmaTcRMax_[celli] = sigmaTcR;
+                    sigmaTcRMax_[cellI] = sigmaTcR;
                 }
 
                 if ((sigmaTcR/sigmaTcRMax) > rndGen_.scalar01())
                 {
                     binaryCollision().collide
                     (
-                        typeIdP,
-                        typeIdQ,
-                        parcelP->U(),
-                        parcelQ->U(),
-                        parcelP->Ei(),
-                        parcelQ->Ei()
+                        parcelP,
+                        parcelQ
                     );
 
                     collisions++;
@@ -577,7 +493,9 @@ void Foam::DsmcCloud<ParcelType>::addNewParcel
     const vector& position,
     const vector& U,
     const scalar Ei,
-    const label cellId,
+    const label cellI,
+    const label tetFaceI,
+    const label tetPtI,
     const label typeId
 )
 {
@@ -587,7 +505,9 @@ void Foam::DsmcCloud<ParcelType>::addNewParcel
         position,
         U,
         Ei,
-        cellId,
+        cellI,
+        tetFaceI,
+        tetPtI,
         typeId
     );
 
diff --git a/src/lagrangian/dsmc/clouds/Templates/DsmcCloud/DsmcCloud.H b/src/lagrangian/dsmc/clouds/Templates/DsmcCloud/DsmcCloud.H
index aba26e7bf83..1df0dee9748 100644
--- a/src/lagrangian/dsmc/clouds/Templates/DsmcCloud/DsmcCloud.H
+++ b/src/lagrangian/dsmc/clouds/Templates/DsmcCloud/DsmcCloud.H
@@ -214,6 +214,10 @@ public:
     virtual ~DsmcCloud();
 
 
+    //- Type of parcel the cloud was instantiated for
+    typedef ParcelType parcelType;
+
+
     // Member Functions
 
         // Access
@@ -452,7 +456,9 @@ public:
                 const vector& position,
                 const vector& U,
                 const scalar Ei,
-                const label cellId,
+                const label cellI,
+                const label tetFaceI,
+                const label tetPtI,
                 const label typeId
             );
 
diff --git a/src/lagrangian/dsmc/parcels/Templates/DsmcParcel/DsmcParcel.C b/src/lagrangian/dsmc/parcels/Templates/DsmcParcel/DsmcParcel.C
index a8883630924..a6a6949122c 100644
--- a/src/lagrangian/dsmc/parcels/Templates/DsmcParcel/DsmcParcel.C
+++ b/src/lagrangian/dsmc/parcels/Templates/DsmcParcel/DsmcParcel.C
@@ -92,7 +92,9 @@ bool Foam::DsmcParcel<ParcelType>::hitPatch
 (
     const polyPatch&,
     TrackData& td,
-    const label patchI
+    const label,
+    const scalar,
+    const tetIndices&
 )
 {
     return false;
@@ -125,7 +127,8 @@ template<class TrackData>
 void Foam::DsmcParcel<ParcelType>::hitWallPatch
 (
     const wallPolyPatch& wpp,
-    TrackData& td
+    TrackData& td,
+    const tetIndices& tetIs
 )
 {
     label wppIndex = wpp.index();
@@ -171,11 +174,8 @@ void Foam::DsmcParcel<ParcelType>::hitWallPatch
 
     td.cloud().wallInteraction().correct
     (
-        wpp,
-        this->face(),
-        U_,
-        Ei_,
-        typeId_
+        static_cast<ParcelType&>(*this),
+        wpp
     );
 
     U_dot_nw = U_ & nw;
@@ -219,7 +219,8 @@ template<class ParcelType>
 void Foam::DsmcParcel<ParcelType>::hitWallPatch
 (
     const wallPolyPatch&,
-    int&
+    int&,
+    const tetIndices&
 )
 {}
 
diff --git a/src/lagrangian/dsmc/parcels/Templates/DsmcParcel/DsmcParcel.H b/src/lagrangian/dsmc/parcels/Templates/DsmcParcel/DsmcParcel.H
index 8a644652d0c..8d09a91e8d7 100644
--- a/src/lagrangian/dsmc/parcels/Templates/DsmcParcel/DsmcParcel.H
+++ b/src/lagrangian/dsmc/parcels/Templates/DsmcParcel/DsmcParcel.H
@@ -185,7 +185,9 @@ public:
             const vector& position,
             const vector& U,
             const scalar Ei,
-            const label celli,
+            const label cellI,
+            const label tetFaceI,
+            const label tetPtI,
             const label typeId
         );
 
@@ -244,7 +246,9 @@ public:
             (
                 const polyPatch&,
                 TrackData& td,
-                const label patchI
+                const label patchI,
+                const scalar trackFraction,
+                const tetIndices& tetIs
             );
 
             //- Overridable function to handle the particle hitting a
@@ -269,7 +273,8 @@ public:
             void hitWallPatch
             (
                 const wallPolyPatch&,
-                TrackData& td
+                TrackData& td,
+                const tetIndices&
             );
 
             //- Overridable function to handle the particle hitting a wallPatch
@@ -277,7 +282,8 @@ public:
             void hitWallPatch
             (
                 const wallPolyPatch&,
-                int&
+                int&,
+                const tetIndices&
             );
 
             //- Overridable function to handle the particle hitting a polyPatch
@@ -352,4 +358,3 @@ public:
 #endif
 
 // ************************************************************************* //
-
diff --git a/src/lagrangian/dsmc/parcels/Templates/DsmcParcel/DsmcParcelI.H b/src/lagrangian/dsmc/parcels/Templates/DsmcParcel/DsmcParcelI.H
index 9f6d661ec6b..5bfc61b44b0 100644
--- a/src/lagrangian/dsmc/parcels/Templates/DsmcParcel/DsmcParcelI.H
+++ b/src/lagrangian/dsmc/parcels/Templates/DsmcParcel/DsmcParcelI.H
@@ -69,11 +69,13 @@ inline Foam::DsmcParcel<ParcelType>::DsmcParcel
     const vector& position,
     const vector& U,
     const scalar Ei,
-    const label celli,
+    const label cellI,
+    const label tetFaceI,
+    const label tetPtI,
     const label typeId
 )
 :
-    Particle<ParcelType>(owner, position, celli),
+    Particle<ParcelType>(owner, position, cellI, tetFaceI, tetPtI),
     U_(U),
     Ei_(Ei),
     typeId_(typeId)
diff --git a/src/lagrangian/dsmc/parcels/derived/dsmcParcel/dsmcParcel.C b/src/lagrangian/dsmc/parcels/derived/dsmcParcel/dsmcParcel.C
index b46bb2452fc..91fa156c639 100644
--- a/src/lagrangian/dsmc/parcels/derived/dsmcParcel/dsmcParcel.C
+++ b/src/lagrangian/dsmc/parcels/derived/dsmcParcel/dsmcParcel.C
@@ -43,7 +43,9 @@ Foam::dsmcParcel::dsmcParcel
     const vector& position,
     const vector& U,
     const scalar Ei,
-    const label celli,
+    const label cellI,
+    const label tetFaceI,
+    const label tetPtI,
     const label typeId
 )
 :
@@ -53,7 +55,9 @@ Foam::dsmcParcel::dsmcParcel
         position,
         U,
         Ei,
-        celli,
+        cellI,
+        tetFaceI,
+        tetPtI,
         typeId
     )
 {}
diff --git a/src/lagrangian/dsmc/parcels/derived/dsmcParcel/dsmcParcel.H b/src/lagrangian/dsmc/parcels/derived/dsmcParcel/dsmcParcel.H
index 8cc4bf11849..6286e06c60e 100644
--- a/src/lagrangian/dsmc/parcels/derived/dsmcParcel/dsmcParcel.H
+++ b/src/lagrangian/dsmc/parcels/derived/dsmcParcel/dsmcParcel.H
@@ -66,7 +66,9 @@ public:
             const vector& position,
             const vector& U,
             const scalar Ei,
-            const label celli,
+            const label cellI,
+            const label tetFaceI,
+            const label tetPtI,
             const label typeId
         );
 
diff --git a/src/lagrangian/dsmc/submodels/BinaryCollisionModel/BinaryCollisionModel/BinaryCollisionModel.H b/src/lagrangian/dsmc/submodels/BinaryCollisionModel/BinaryCollisionModel/BinaryCollisionModel.H
index 0136cd5f6a0..bd0ce0fa315 100644
--- a/src/lagrangian/dsmc/submodels/BinaryCollisionModel/BinaryCollisionModel/BinaryCollisionModel.H
+++ b/src/lagrangian/dsmc/submodels/BinaryCollisionModel/BinaryCollisionModel/BinaryCollisionModel.H
@@ -132,21 +132,15 @@ public:
         //- Return the collision cross section * relative velocity product
         virtual scalar sigmaTcR
         (
-            label typeIdP,
-            label typeIdQ,
-            const vector& UP,
-            const vector& UQ
+            const typename CloudType::parcelType& pP,
+            const typename CloudType::parcelType& pQ
         ) const = 0;
 
         //- Apply collision
         virtual void collide
         (
-            label typeIdP,
-            label typeIdQ,
-            vector& UP,
-            vector& UQ,
-            scalar& EiP,
-            scalar& EiQ
+            typename CloudType::parcelType& pP,
+            typename CloudType::parcelType& pQ
         ) = 0;
 };
 
diff --git a/src/lagrangian/dsmc/submodels/BinaryCollisionModel/LarsenBorgnakkeVariableHardSphere/LarsenBorgnakkeVariableHardSphere.C b/src/lagrangian/dsmc/submodels/BinaryCollisionModel/LarsenBorgnakkeVariableHardSphere/LarsenBorgnakkeVariableHardSphere.C
index 85f0eb12ec7..5e067847b5d 100644
--- a/src/lagrangian/dsmc/submodels/BinaryCollisionModel/LarsenBorgnakkeVariableHardSphere/LarsenBorgnakkeVariableHardSphere.C
+++ b/src/lagrangian/dsmc/submodels/BinaryCollisionModel/LarsenBorgnakkeVariableHardSphere/LarsenBorgnakkeVariableHardSphere.C
@@ -128,14 +128,15 @@ bool Foam::LarsenBorgnakkeVariableHardSphere<CloudType>::active() const
 template <class CloudType>
 Foam::scalar Foam::LarsenBorgnakkeVariableHardSphere<CloudType>::sigmaTcR
 (
-    label typeIdP,
-    label typeIdQ,
-    const vector& UP,
-    const vector& UQ
+    const typename CloudType::parcelType& pP,
+    const typename CloudType::parcelType& pQ
 ) const
 {
     const CloudType& cloud(this->owner());
 
+    label typeIdP = pP.typeId();
+    label typeIdQ = pQ.typeId();
+
     scalar dPQ =
         0.5
        *(
@@ -150,7 +151,7 @@ Foam::scalar Foam::LarsenBorgnakkeVariableHardSphere<CloudType>::sigmaTcR
           + cloud.constProps(typeIdQ).omega()
         );
 
-    scalar cR = mag(UP - UQ);
+    scalar cR = mag(pP.U() - pQ.U());
 
     if (cR < VSMALL)
     {
@@ -176,16 +177,19 @@ Foam::scalar Foam::LarsenBorgnakkeVariableHardSphere<CloudType>::sigmaTcR
 template <class CloudType>
 void Foam::LarsenBorgnakkeVariableHardSphere<CloudType>::collide
 (
-    label typeIdP,
-    label typeIdQ,
-    vector& UP,
-    vector& UQ,
-    scalar& EiP,
-    scalar& EiQ
+    typename CloudType::parcelType& pP,
+    typename CloudType::parcelType& pQ
 )
 {
     CloudType& cloud(this->owner());
 
+    label typeIdP = pP.typeId();
+    label typeIdQ = pQ.typeId();
+    vector& UP = pP.U();
+    vector& UQ = pQ.U();
+    scalar& EiP = pP.Ei();
+    scalar& EiQ = pQ.Ei();
+
     Random& rndGen(cloud.rndGen());
 
     scalar inverseCollisionNumber = 1/relaxationCollisionNumber_;
diff --git a/src/lagrangian/dsmc/submodels/BinaryCollisionModel/LarsenBorgnakkeVariableHardSphere/LarsenBorgnakkeVariableHardSphere.H b/src/lagrangian/dsmc/submodels/BinaryCollisionModel/LarsenBorgnakkeVariableHardSphere/LarsenBorgnakkeVariableHardSphere.H
index 3f742a78ae6..fa4f232a02b 100644
--- a/src/lagrangian/dsmc/submodels/BinaryCollisionModel/LarsenBorgnakkeVariableHardSphere/LarsenBorgnakkeVariableHardSphere.H
+++ b/src/lagrangian/dsmc/submodels/BinaryCollisionModel/LarsenBorgnakkeVariableHardSphere/LarsenBorgnakkeVariableHardSphere.H
@@ -95,21 +95,15 @@ public:
         //- Return the collision cross section * relative velocity product
         virtual scalar sigmaTcR
         (
-            label typeIdP,
-            label typeIdQ,
-            const vector& UP,
-            const vector& UQ
+            const typename CloudType::parcelType& pP,
+            const typename CloudType::parcelType& pQ
         ) const;
 
         //- Apply collision
         virtual void collide
         (
-            label typeIdP,
-            label typeIdQ,
-            vector& UP,
-            vector& UQ,
-            scalar& EiP,
-            scalar& EiQ
+            typename CloudType::parcelType& pP,
+            typename CloudType::parcelType& pQ
         );
 };
 
diff --git a/src/lagrangian/dsmc/submodels/BinaryCollisionModel/NoBinaryCollision/NoBinaryCollision.C b/src/lagrangian/dsmc/submodels/BinaryCollisionModel/NoBinaryCollision/NoBinaryCollision.C
index 5ff98896383..67a1b4443ea 100644
--- a/src/lagrangian/dsmc/submodels/BinaryCollisionModel/NoBinaryCollision/NoBinaryCollision.C
+++ b/src/lagrangian/dsmc/submodels/BinaryCollisionModel/NoBinaryCollision/NoBinaryCollision.C
@@ -60,10 +60,8 @@ bool Foam::NoBinaryCollision<CloudType>::active() const
 template <class CloudType>
 Foam::scalar Foam::NoBinaryCollision<CloudType>::sigmaTcR
 (
-    label typeIdP,
-    label typeIdQ,
-    const vector& UP,
-    const vector& UQ
+    const typename CloudType::parcelType& pP,
+    const typename CloudType::parcelType& pQ
 ) const
 {
     FatalErrorIn
@@ -89,12 +87,8 @@ Foam::scalar Foam::NoBinaryCollision<CloudType>::sigmaTcR
 template <class CloudType>
 void Foam::NoBinaryCollision<CloudType>::collide
 (
-    label typeIdP,
-    label typeIdQ,
-    vector& UP,
-    vector& UQ,
-    scalar& EiP,
-    scalar& EiQ
+    typename CloudType::parcelType& pP,
+    typename CloudType::parcelType& pQ
 )
 {}
 
diff --git a/src/lagrangian/dsmc/submodels/BinaryCollisionModel/NoBinaryCollision/NoBinaryCollision.H b/src/lagrangian/dsmc/submodels/BinaryCollisionModel/NoBinaryCollision/NoBinaryCollision.H
index b48cedb316e..9ceea73e303 100644
--- a/src/lagrangian/dsmc/submodels/BinaryCollisionModel/NoBinaryCollision/NoBinaryCollision.H
+++ b/src/lagrangian/dsmc/submodels/BinaryCollisionModel/NoBinaryCollision/NoBinaryCollision.H
@@ -74,21 +74,15 @@ public:
         //- Return the collision cross section * relative velocity product
         virtual scalar sigmaTcR
         (
-            label typeIdP,
-            label typeIdQ,
-            const vector& UP,
-            const vector& UQ
+            const typename CloudType::parcelType& pP,
+            const typename CloudType::parcelType& pQ
         ) const;
 
         //- Apply collision
         virtual void collide
         (
-            label typeIdP,
-            label typeIdQ,
-            vector& UP,
-            vector& UQ,
-            scalar& EiP,
-            scalar& EiQ
+            typename CloudType::parcelType& pP,
+            typename CloudType::parcelType& pQ
         );
 };
 
diff --git a/src/lagrangian/dsmc/submodels/BinaryCollisionModel/VariableHardSphere/VariableHardSphere.C b/src/lagrangian/dsmc/submodels/BinaryCollisionModel/VariableHardSphere/VariableHardSphere.C
index 8f7d13dd7de..450e1218ed0 100644
--- a/src/lagrangian/dsmc/submodels/BinaryCollisionModel/VariableHardSphere/VariableHardSphere.C
+++ b/src/lagrangian/dsmc/submodels/BinaryCollisionModel/VariableHardSphere/VariableHardSphere.C
@@ -61,14 +61,15 @@ bool Foam::VariableHardSphere<CloudType>::active() const
 template <class CloudType>
 Foam::scalar Foam::VariableHardSphere<CloudType>::sigmaTcR
 (
-    label typeIdP,
-    label typeIdQ,
-    const vector& UP,
-    const vector& UQ
+    const typename CloudType::parcelType& pP,
+    const typename CloudType::parcelType& pQ
 ) const
 {
     const CloudType& cloud(this->owner());
 
+    label typeIdP = pP.typeId();
+    label typeIdQ = pQ.typeId();
+
     scalar dPQ =
         0.5
        *(
@@ -83,7 +84,7 @@ Foam::scalar Foam::VariableHardSphere<CloudType>::sigmaTcR
           + cloud.constProps(typeIdQ).omega()
         );
 
-    scalar cR = mag(UP - UQ);
+    scalar cR = mag(pP.U() - pQ.U());
 
     if (cR < VSMALL)
     {
@@ -109,16 +110,17 @@ Foam::scalar Foam::VariableHardSphere<CloudType>::sigmaTcR
 template <class CloudType>
 void Foam::VariableHardSphere<CloudType>::collide
 (
-    label typeIdP,
-    label typeIdQ,
-    vector& UP,
-    vector& UQ,
-    scalar& EiP,
-    scalar& EiQ
+    typename CloudType::parcelType& pP,
+    typename CloudType::parcelType& pQ
 )
 {
     CloudType& cloud(this->owner());
 
+    label typeIdP = pP.typeId();
+    label typeIdQ = pQ.typeId();
+    vector& UP = pP.U();
+    vector& UQ = pQ.U();
+
     Random& rndGen(cloud.rndGen());
 
     scalar mP = cloud.constProps(typeIdP).mass();
diff --git a/src/lagrangian/dsmc/submodels/BinaryCollisionModel/VariableHardSphere/VariableHardSphere.H b/src/lagrangian/dsmc/submodels/BinaryCollisionModel/VariableHardSphere/VariableHardSphere.H
index bf986189c0b..2540107f7b1 100644
--- a/src/lagrangian/dsmc/submodels/BinaryCollisionModel/VariableHardSphere/VariableHardSphere.H
+++ b/src/lagrangian/dsmc/submodels/BinaryCollisionModel/VariableHardSphere/VariableHardSphere.H
@@ -80,21 +80,15 @@ public:
         //- Return the collision cross section * relative velocity product
         virtual scalar sigmaTcR
         (
-            label typeIdP,
-            label typeIdQ,
-            const vector& UP,
-            const vector& UQ
+            const typename CloudType::parcelType& pP,
+            const typename CloudType::parcelType& pQ
         ) const;
 
         //- Apply collision
         virtual void collide
         (
-            label typeIdP,
-            label typeIdQ,
-            vector& UP,
-            vector& UQ,
-            scalar& EiP,
-            scalar& EiQ
+            typename CloudType::parcelType& pP,
+            typename CloudType::parcelType& pQ
         );
 };
 
diff --git a/src/lagrangian/dsmc/submodels/InflowBoundaryModel/FreeStream/FreeStream.C b/src/lagrangian/dsmc/submodels/InflowBoundaryModel/FreeStream/FreeStream.C
index b8818aa9cdf..df835e735d0 100644
--- a/src/lagrangian/dsmc/submodels/InflowBoundaryModel/FreeStream/FreeStream.C
+++ b/src/lagrangian/dsmc/submodels/InflowBoundaryModel/FreeStream/FreeStream.C
@@ -25,6 +25,8 @@ License
 
 #include "FreeStream.H"
 #include "constants.H"
+#include "triPointRef.H"
+#include "tetIndices.H"
 
 using namespace Foam::constant::mathematical;
 
@@ -181,13 +183,13 @@ void Foam::FreeStream<CloudType>::inflow()
                 )
             );
 
-            // Dotting boundary velocity with the face unit normal (which points
-            // out of the domain, so it must be negated), dividing by the most
-            // probable speed to form molecularSpeedRatio * cosTheta
+            // Dotting boundary velocity with the face unit normal
+            // (which points out of the domain, so it must be
+            // negated), dividing by the most probable speed to form
+            // molecularSpeedRatio * cosTheta
 
             scalarField sCosTheta =
-                boundaryU[patchI]
-              & -patch.faceAreas()/mag(patch.faceAreas())
+                (boundaryU[patchI] & -patch.faceAreas()/mag(patch.faceAreas()))
                /mostProbableSpeed;
 
             // From Bird eqn 4.22
@@ -201,50 +203,56 @@ void Foam::FreeStream<CloudType>::inflow()
                /(2.0*sqrtPi);
         }
 
-        forAll(patch, f)
+        forAll(patch, pFI)
         {
             // Loop over all faces as the outer loop to avoid calculating
             // geometrical properties multiple times.
 
-            labelList faceVertices = patch[f];
+            const face& f = patch[pFI];
 
-            label nVertices = faceVertices.size();
+            label globalFaceIndex = pFI + patch.start();
 
-            label globalFaceIndex = f + patch.start();
+            label cellI = mesh.faceOwner()[globalFaceIndex];
 
-            label cell = mesh.faceOwner()[globalFaceIndex];
+            const vector& fC = patch.faceCentres()[pFI];
 
-            const vector& fC = patch.faceCentres()[f];
+            scalar fA = mag(patch.faceAreas()[pFI]);
 
-            scalar fA = mag(patch.faceAreas()[f]);
+            List<tetIndices> faceTets = polyMeshTetDecomposition::faceTetIndices
+            (
+                mesh,
+                globalFaceIndex,
+                cellI
+            );
 
             // Cumulative triangle area fractions
-            List<scalar> cTriAFracs(nVertices);
-            cTriAFracs[0] = 0.0;
+            List<scalar> cTriAFracs(faceTets.size(), 0.0);
 
-            for (label v = 0; v < nVertices - 1; v++)
+            scalar previousCummulativeSum = 0.0;
+
+            forAll(faceTets, triI)
             {
-                const point& vA = mesh.points()[faceVertices[v]];
+                const tetIndices& faceTetIs = faceTets[triI];
 
-                const point& vB = mesh.points()[faceVertices[(v + 1)]];
+                cTriAFracs[triI] =
+                    faceTetIs.faceTri(mesh).mag()/fA
+                  + previousCummulativeSum;
 
-                cTriAFracs[v] =
-                    0.5*mag((vA - fC)^(vB - fC))/fA
-                  + cTriAFracs[max((v - 1), 0)];
+                previousCummulativeSum = cTriAFracs[triI];
             }
 
             // Force the last area fraction value to 1.0 to avoid any
             // rounding/non-flat face errors giving a value < 1.0
-            cTriAFracs[nVertices - 1] = 1.0;
+            cTriAFracs.last() = 1.0;
 
             // Normal unit vector *negative* so normal is pointing into the
             // domain
-            vector n = patch.faceAreas()[f];
+            vector n = patch.faceAreas()[pFI];
             n /= -mag(n);
 
             // Wall tangential unit vector. Use the direction between the
             // face centre and the first vertex in the list
-            vector t1 = fC - (mesh.points()[faceVertices[0]]);
+            vector t1 = fC - (mesh.points()[f[0]]);
             t1 /= mag(t1);
 
             // Other tangential unit vector.  Rescaling in case face is not
@@ -252,13 +260,13 @@ void Foam::FreeStream<CloudType>::inflow()
             vector t2 = n^t1;
             t2 /= mag(t2);
 
-            scalar faceTemperature = boundaryT[patchI][f];
+            scalar faceTemperature = boundaryT[patchI][pFI];
 
-            const vector& faceVelocity = boundaryU[patchI][f];
+            const vector& faceVelocity = boundaryU[patchI][pFI];
 
             forAll(pFA, i)
             {
-                scalar& faceAccumulator = pFA[i][f];
+                scalar& faceAccumulator = pFA[i][pFI];
 
                 // Number of whole particles to insert
                 label nI = max(label(faceAccumulator), 0);
@@ -284,34 +292,23 @@ void Foam::FreeStream<CloudType>::inflow()
                     scalar triSelection = rndGen.scalar01();
 
                     // Selected triangle
-                    label sTri = -1;
+                    label selectedTriI = -1;
 
-                    forAll(cTriAFracs, tri)
+                    forAll(cTriAFracs, triI)
                     {
-                        sTri = tri;
+                        selectedTriI = triI;
 
-                        if (cTriAFracs[tri] >= triSelection)
+                        if (cTriAFracs[triI] >= triSelection)
                         {
                             break;
                         }
                     }
 
-                    // Randomly distribute the points on the triangle, using the
-                    // method from:
-                    // Generating Random Points in Triangles
-                    // by Greg Turk
-                    // from "Graphics Gems", Academic Press, 1990
-                    // http://tog.acm.org/GraphicsGems/gems/TriPoints.c
-
-                    const point& A = fC;
-                    const point& B = mesh.points()[faceVertices[sTri]];
-                    const point& C =
-                    mesh.points()[faceVertices[(sTri + 1) % nVertices]];
+                    // Randomly distribute the points on the triangle.
 
-                    scalar s = rndGen.scalar01();
-                    scalar t = sqrt(rndGen.scalar01());
+                    const tetIndices& faceTetIs = faceTets[selectedTriI];
 
-                    point p = (1 - t)*A + (1 - s)*t*B + s*t*C;
+                    point p = faceTetIs.faceTri(mesh).randomPoint(rndGen);
 
                     // Velocity generation
 
@@ -393,7 +390,9 @@ void Foam::FreeStream<CloudType>::inflow()
                         p,
                         U,
                         Ei,
-                        cell,
+                        cellI,
+                        globalFaceIndex,
+                        faceTetIs.tetPt(),
                         typeId
                     );
 
diff --git a/src/lagrangian/dsmc/submodels/WallInteractionModel/MaxwellianThermal/MaxwellianThermal.C b/src/lagrangian/dsmc/submodels/WallInteractionModel/MaxwellianThermal/MaxwellianThermal.C
index 467e5fad4fe..c05e6ae2013 100644
--- a/src/lagrangian/dsmc/submodels/WallInteractionModel/MaxwellianThermal/MaxwellianThermal.C
+++ b/src/lagrangian/dsmc/submodels/WallInteractionModel/MaxwellianThermal/MaxwellianThermal.C
@@ -53,20 +53,21 @@ Foam::MaxwellianThermal<CloudType>::~MaxwellianThermal()
 template <class CloudType>
 void Foam::MaxwellianThermal<CloudType>::correct
 (
-    const wallPolyPatch& wpp,
-    const label faceId,
-    vector& U,
-    scalar& Ei,
-    label typeId
+    typename CloudType::parcelType& p,
+    const wallPolyPatch& wpp
 )
 {
-    label wppIndex = wpp.index();
+    vector& U = p.U();
+
+    scalar& Ei = p.Ei();
 
-    label wppLocalFace = wpp.whichFace(faceId);
+    label typeId = p.typeId();
+
+    label wppIndex = wpp.index();
 
-    vector nw = wpp.faceAreas()[wppLocalFace];
+    label wppLocalFace = wpp.whichFace(p.face());
 
-    // Normal unit vector
+    vector nw = p.normal();
     nw /= mag(nw);
 
     // Normal velocity magnitude
diff --git a/src/lagrangian/dsmc/submodels/WallInteractionModel/MaxwellianThermal/MaxwellianThermal.H b/src/lagrangian/dsmc/submodels/WallInteractionModel/MaxwellianThermal/MaxwellianThermal.H
index f42382b7da0..2ed030bbe74 100644
--- a/src/lagrangian/dsmc/submodels/WallInteractionModel/MaxwellianThermal/MaxwellianThermal.H
+++ b/src/lagrangian/dsmc/submodels/WallInteractionModel/MaxwellianThermal/MaxwellianThermal.H
@@ -25,8 +25,9 @@ Class
     Foam::MaxwellianThermal
 
 Description
-    Wall interaction setting microscopic velocity to a random one drawn from a
-    Maxwellian distribution corresponding to a specified temperature
+    Wall interaction setting microscopic velocity to a random one
+    drawn from a Maxwellian distribution corresponding to a specified
+    temperature
 
 \*---------------------------------------------------------------------------*/
 
@@ -73,11 +74,8 @@ public:
         //- Apply wall correction
         virtual void correct
         (
-            const wallPolyPatch& wpp,
-            const label faceId,
-            vector& U,
-            scalar& Ei,
-            label typeId
+            typename CloudType::parcelType& p,
+            const wallPolyPatch& wpp
         );
 };
 
diff --git a/src/lagrangian/dsmc/submodels/WallInteractionModel/MixedDiffuseSpecular/MixedDiffuseSpecular.C b/src/lagrangian/dsmc/submodels/WallInteractionModel/MixedDiffuseSpecular/MixedDiffuseSpecular.C
index 91c103a4aa4..9614c81cff8 100644
--- a/src/lagrangian/dsmc/submodels/WallInteractionModel/MixedDiffuseSpecular/MixedDiffuseSpecular.C
+++ b/src/lagrangian/dsmc/submodels/WallInteractionModel/MixedDiffuseSpecular/MixedDiffuseSpecular.C
@@ -51,20 +51,21 @@ Foam::MixedDiffuseSpecular<CloudType>::~MixedDiffuseSpecular()
 template <class CloudType>
 void Foam::MixedDiffuseSpecular<CloudType>::correct
 (
-    const wallPolyPatch& wpp,
-    const label faceId,
-    vector& U,
-    scalar& Ei,
-    label typeId
+    typename CloudType::parcelType& p,
+    const wallPolyPatch& wpp
 )
 {
-    label wppIndex = wpp.index();
+    vector& U = p.U();
+
+    scalar& Ei = p.Ei();
 
-    label wppLocalFace = wpp.whichFace(faceId);
+    label typeId = p.typeId();
+
+    label wppIndex = wpp.index();
 
-    vector nw = wpp.faceAreas()[wppLocalFace];
+    label wppLocalFace = wpp.whichFace(p.face());
 
-    // Normal unit vector
+    vector nw = p.normal();
     nw /= mag(nw);
 
     // Normal velocity magnitude
diff --git a/src/lagrangian/dsmc/submodels/WallInteractionModel/MixedDiffuseSpecular/MixedDiffuseSpecular.H b/src/lagrangian/dsmc/submodels/WallInteractionModel/MixedDiffuseSpecular/MixedDiffuseSpecular.H
index f3745dbc907..3a375d5c7a6 100644
--- a/src/lagrangian/dsmc/submodels/WallInteractionModel/MixedDiffuseSpecular/MixedDiffuseSpecular.H
+++ b/src/lagrangian/dsmc/submodels/WallInteractionModel/MixedDiffuseSpecular/MixedDiffuseSpecular.H
@@ -25,8 +25,11 @@ Class
     Foam::MixedDiffuseSpecular
 
 Description
-    Wall interaction setting microscopic velocity to a random one drawn from a
-    Maxwellian distribution corresponding to a specified temperature
+    Wall interaction setting microscopic velocity to a random one
+    drawn from a Maxwellian distribution corresponding to a specified
+    temperature for a specified fraction of collisions, and reversing
+    the wall-normal component of the particle velocity for the
+    remainder.
 
 \*---------------------------------------------------------------------------*/
 
@@ -79,11 +82,8 @@ public:
         //- Apply wall correction
         virtual void correct
         (
-            const wallPolyPatch& wpp,
-            const label faceId,
-            vector& U,
-            scalar& Ei,
-            label typeId
+            typename CloudType::parcelType& p,
+            const wallPolyPatch& wpp
         );
 };
 
diff --git a/src/lagrangian/dsmc/submodels/WallInteractionModel/SpecularReflection/SpecularReflection.C b/src/lagrangian/dsmc/submodels/WallInteractionModel/SpecularReflection/SpecularReflection.C
index b690a0f87e8..e15a416dd67 100644
--- a/src/lagrangian/dsmc/submodels/WallInteractionModel/SpecularReflection/SpecularReflection.C
+++ b/src/lagrangian/dsmc/submodels/WallInteractionModel/SpecularReflection/SpecularReflection.C
@@ -52,14 +52,13 @@ Foam::SpecularReflection<CloudType>::~SpecularReflection()
 template <class CloudType>
 void Foam::SpecularReflection<CloudType>::correct
 (
-    const wallPolyPatch& wpp,
-    const label faceId,
-    vector& U,
-    scalar& Ei,
-    label typeId
+    typename CloudType::parcelType& p,
+    const wallPolyPatch& wpp
 )
 {
-    vector nw = wpp.faceAreas()[wpp.whichFace(faceId)];
+    vector& U = p.U();
+
+    vector nw = p.normal();
     nw /= mag(nw);
 
     scalar U_dot_nw = U & nw;
diff --git a/src/lagrangian/dsmc/submodels/WallInteractionModel/SpecularReflection/SpecularReflection.H b/src/lagrangian/dsmc/submodels/WallInteractionModel/SpecularReflection/SpecularReflection.H
index 1c47640fe38..68bf2da6891 100644
--- a/src/lagrangian/dsmc/submodels/WallInteractionModel/SpecularReflection/SpecularReflection.H
+++ b/src/lagrangian/dsmc/submodels/WallInteractionModel/SpecularReflection/SpecularReflection.H
@@ -72,11 +72,8 @@ public:
         //- Apply wall correction
         virtual void correct
         (
-            const wallPolyPatch& wpp,
-            const label faceId,
-            vector& U,
-            scalar& Ei,
-            label typeId
+            typename CloudType::parcelType& p,
+            const wallPolyPatch& wpp
         );
 };
 
diff --git a/src/lagrangian/dsmc/submodels/WallInteractionModel/WallInteractionModel/WallInteractionModel.H b/src/lagrangian/dsmc/submodels/WallInteractionModel/WallInteractionModel/WallInteractionModel.H
index dfed430ff76..e0af2025a93 100644
--- a/src/lagrangian/dsmc/submodels/WallInteractionModel/WallInteractionModel/WallInteractionModel.H
+++ b/src/lagrangian/dsmc/submodels/WallInteractionModel/WallInteractionModel/WallInteractionModel.H
@@ -129,11 +129,8 @@ public:
         //- Apply wall correction
         virtual void correct
         (
-            const wallPolyPatch& wpp,
-            const label faceId,
-            vector& U,
-            scalar& Ei,
-            label typeId
+            typename CloudType::parcelType& p,
+            const wallPolyPatch& wpp
         ) = 0;
 };
 
diff --git a/src/lagrangian/intermediate/Make/options b/src/lagrangian/intermediate/Make/options
index 5625914e28d..7a208454538 100644
--- a/src/lagrangian/intermediate/Make/options
+++ b/src/lagrangian/intermediate/Make/options
@@ -17,7 +17,8 @@ EXE_INC = \
     -I$(LIB_SRC)/turbulenceModels/compressible/RAS/lnInclude \
     -I$(LIB_SRC)/turbulenceModels/LES/LESdeltas/lnInclude \
     -I$(LIB_SRC)/turbulenceModels/compressible/LES/lnInclude \
-    -I$(LIB_SRC)/surfaceFilmModels/lnInclude
+    -I$(LIB_SRC)/surfaceFilmModels/lnInclude \
+    -I$(LIB_SRC)/dynamicFvMesh/lnInclude
 
 LIB_LIBS = \
     -lfiniteVolume \
@@ -37,4 +38,6 @@ LIB_LIBS = \
     -lODE \
     -lcompressibleRASModels \
     -lcompressibleLESModels \
+    -ldynamicFvMesh \
     -lsurfaceFilmModels
+
diff --git a/src/lagrangian/intermediate/clouds/Templates/KinematicCloud/KinematicCloud.H b/src/lagrangian/intermediate/clouds/Templates/KinematicCloud/KinematicCloud.H
index c09988c76d7..5d36cb575e9 100644
--- a/src/lagrangian/intermediate/clouds/Templates/KinematicCloud/KinematicCloud.H
+++ b/src/lagrangian/intermediate/clouds/Templates/KinematicCloud/KinematicCloud.H
@@ -247,6 +247,13 @@ public:
 
         // Access
 
+            //- If the collision model controls the wall interaction,
+            //  then the wall impact distance should be zero.
+            //  Otherwise, it should be allowed to be the value from
+            //  the Parcel.
+            inline bool hasWallImpactDistance() const;
+
+
             // References to the mesh and databases
 
                 //- Return refernce to the mesh
diff --git a/src/lagrangian/intermediate/clouds/Templates/KinematicCloud/KinematicCloudI.H b/src/lagrangian/intermediate/clouds/Templates/KinematicCloud/KinematicCloudI.H
index ed65e9809ba..9f9b441f43d 100644
--- a/src/lagrangian/intermediate/clouds/Templates/KinematicCloud/KinematicCloudI.H
+++ b/src/lagrangian/intermediate/clouds/Templates/KinematicCloud/KinematicCloudI.H
@@ -27,6 +27,13 @@ License
 
 // * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
 
+template<class ParcelType>
+inline bool Foam::KinematicCloud<ParcelType>::hasWallImpactDistance() const
+{
+    return !collision().controlsWallInteraction();
+}
+
+
 template<class ParcelType>
 inline Foam::label Foam::KinematicCloud<ParcelType>::parcelTypeId() const
 {
diff --git a/src/lagrangian/intermediate/parcels/Templates/KinematicParcel/KinematicParcel.C b/src/lagrangian/intermediate/parcels/Templates/KinematicParcel/KinematicParcel.C
index 66e4523fcb2..26acc7d9e6e 100644
--- a/src/lagrangian/intermediate/parcels/Templates/KinematicParcel/KinematicParcel.C
+++ b/src/lagrangian/intermediate/parcels/Templates/KinematicParcel/KinematicParcel.C
@@ -36,7 +36,10 @@ void Foam::KinematicParcel<ParcelType>::setCellValues
     const label cellI
 )
 {
-    rhoc_ = td.rhoInterp().interpolate(this->position(), cellI);
+    tetIndices tetIs = this->currentTetIndices();
+
+    rhoc_ = td.rhoInterp().interpolate(this->position(), tetIs);
+
     if (rhoc_ < td.constProps().rhoMin())
     {
         WarningIn
@@ -53,9 +56,9 @@ void Foam::KinematicParcel<ParcelType>::setCellValues
         rhoc_ = td.constProps().rhoMin();
     }
 
-    Uc_ = td.UInterp().interpolate(this->position(), cellI);
+    Uc_ = td.UInterp().interpolate(this->position(), tetIs);
 
-    muc_ = td.muInterp().interpolate(this->position(), cellI);
+    muc_ = td.muInterp().interpolate(this->position(), tetIs);
 
     // Apply dispersion components to carrier phase velocity
     Uc_ = td.cloud().dispersion().update
@@ -159,11 +162,13 @@ const Foam::vector Foam::KinematicParcel<ParcelType>::calcVelocity
     // Momentum transfer coefficient
     const scalar utc = td.cloud().drag().utc(Re, d, mu) + ROOTVSMALL;
 
+    tetIndices tetIs = this->currentTetIndices();
+
     // Momentum source due to particle forces
     const vector FCoupled = mass*td.cloud().forces().calcCoupled
     (
         this->position(),
-        cellI,
+        tetIs,
         dt,
         rhoc_,
         rho,
@@ -175,7 +180,7 @@ const Foam::vector Foam::KinematicParcel<ParcelType>::calcVelocity
     const vector FNonCoupled = mass*td.cloud().forces().calcNonCoupled
     (
         this->position(),
-        cellI,
+        tetIs,
         dt,
         rhoc_,
         rho,
@@ -340,7 +345,9 @@ bool Foam::KinematicParcel<ParcelType>::hitPatch
 (
     const polyPatch& pp,
     TrackData& td,
-    const label patchI
+    const label patchI,
+    const scalar trackFraction,
+    const tetIndices& tetIs
 )
 {
     ParcelType& p = static_cast<ParcelType&>(*this);
@@ -360,15 +367,14 @@ bool Foam::KinematicParcel<ParcelType>::hitPatch
     else
     {
         // Invoke patch interaction model
-        return
-            td.cloud().patchInteraction().correct
-            (
-                pp,
-                this->face(),
-                td.keepParticle,
-                active_,
-                U_
-            );
+        return td.cloud().patchInteraction().correct
+        (
+            static_cast<ParcelType&>(*this),
+            pp,
+            td.keepParticle,
+            trackFraction,
+            tetIs
+        );
     }
 }
 
@@ -378,7 +384,9 @@ bool Foam::KinematicParcel<ParcelType>::hitPatch
 (
     const polyPatch& pp,
     int& td,
-    const label patchI
+    const label patchI,
+    const scalar trackFraction,
+    const tetIndices& tetIs
 )
 {
     return false;
@@ -411,7 +419,8 @@ template<class TrackData>
 void Foam::KinematicParcel<ParcelType>::hitWallPatch
 (
     const wallPolyPatch& wpp,
-    TrackData& td
+    TrackData& td,
+    const tetIndices&
 )
 {
     // Wall interactions handled by generic hitPatch function
@@ -422,7 +431,8 @@ template<class ParcelType>
 void Foam::KinematicParcel<ParcelType>::hitWallPatch
 (
     const wallPolyPatch&,
-    int&
+    int&,
+    const tetIndices&
 )
 {}
 
@@ -440,7 +450,11 @@ void Foam::KinematicParcel<ParcelType>::hitPatch
 
 
 template<class ParcelType>
-void Foam::KinematicParcel<ParcelType>::hitPatch(const polyPatch&, int&)
+void Foam::KinematicParcel<ParcelType>::hitPatch
+(
+    const polyPatch&,
+    int&
+)
 {}
 
 
diff --git a/src/lagrangian/intermediate/parcels/Templates/KinematicParcel/KinematicParcel.H b/src/lagrangian/intermediate/parcels/Templates/KinematicParcel/KinematicParcel.H
index 8b693c62208..b87a1390df4 100644
--- a/src/lagrangian/intermediate/parcels/Templates/KinematicParcel/KinematicParcel.H
+++ b/src/lagrangian/intermediate/parcels/Templates/KinematicParcel/KinematicParcel.H
@@ -331,7 +331,9 @@ public:
         (
             KinematicCloud<ParcelType>& owner,
             const vector& position,
-            const label cellI
+            const label cellI,
+            const label tetFaceI,
+            const label tetPtI
         );
 
         //- Construct from components
@@ -340,6 +342,8 @@ public:
             KinematicCloud<ParcelType>& owner,
             const vector& position,
             const label cellI,
+            const label tetFaceI,
+            const label tetPtI,
             const label typeId,
             const scalar nParticle0,
             const scalar d0,
@@ -544,20 +548,22 @@ public:
             (
                 const polyPatch& p,
                 TrackData& td,
-                const label patchI
+                const label patchI,
+                const scalar trackFraction,
+                const tetIndices& tetIs
             );
 
-
             //- Overridable function to handle the particle hitting a patch
             //  Executed before other patch-hitting functions without trackData
             bool hitPatch
             (
                 const polyPatch& p,
                 int& td,
-                const label patchI
+                const label patchI,
+                const scalar trackFraction,
+                const tetIndices& tetIs
             );
 
-
             //- Overridable function to handle the particle hitting a
             //  processorPatch
             template<class TrackData>
@@ -580,7 +586,8 @@ public:
             void hitWallPatch
             (
                 const wallPolyPatch&,
-                TrackData& td
+                TrackData& td,
+                const tetIndices&
             );
 
             //- Overridable function to handle the particle hitting a wallPatch
@@ -588,7 +595,8 @@ public:
             void hitWallPatch
             (
                 const wallPolyPatch&,
-                int&
+                int&,
+                const tetIndices&
             );
 
             //- Overridable function to handle the particle hitting a polyPatch
diff --git a/src/lagrangian/intermediate/parcels/Templates/KinematicParcel/KinematicParcelI.H b/src/lagrangian/intermediate/parcels/Templates/KinematicParcel/KinematicParcelI.H
index 1a274b258b1..a6f41647c78 100644
--- a/src/lagrangian/intermediate/parcels/Templates/KinematicParcel/KinematicParcelI.H
+++ b/src/lagrangian/intermediate/parcels/Templates/KinematicParcel/KinematicParcelI.H
@@ -81,10 +81,12 @@ inline Foam::KinematicParcel<ParcelType>::KinematicParcel
 (
     KinematicCloud<ParcelType>& owner,
     const vector& position,
-    const label cellI
+    const label cellI,
+    const label tetFaceI,
+    const label tetPtI
 )
 :
-    Particle<ParcelType>(owner, position, cellI),
+    Particle<ParcelType>(owner, position, cellI, tetFaceI, tetPtI),
     active_(true),
     typeId_(owner.parcelTypeId()),
     nParticle_(0),
@@ -109,6 +111,8 @@ inline Foam::KinematicParcel<ParcelType>::KinematicParcel
     KinematicCloud<ParcelType>& owner,
     const vector& position,
     const label cellI,
+    const label tetFaceI,
+    const label tetPtI,
     const label typeId,
     const scalar nParticle0,
     const scalar d0,
@@ -119,7 +123,7 @@ inline Foam::KinematicParcel<ParcelType>::KinematicParcel
     const constantProperties& constProps
 )
 :
-    Particle<ParcelType>(owner, position, cellI),
+    Particle<ParcelType>(owner, position, cellI, tetFaceI, tetPtI),
     active_(true),
     typeId_(typeId),
     nParticle_(nParticle0),
@@ -439,23 +443,7 @@ inline Foam::scalar Foam::KinematicParcel<ParcelType>::wallImpactDistance
     const vector&
 ) const
 {
-    const KinematicCloud<ParcelType>& c =
-        dynamic_cast<const KinematicCloud<ParcelType>&>(this->cloud());
-
-    if (c.collision().controlsWallInteraction())
-    {
-        // Do not use a wall impact distance if the collision model
-        // controls wall interactions to allow proper multiple face
-        // collisions.  In a twisted mesh the particle can be within
-        // range of a wall but not in the cell attached to a wall
-        // face, hence miss the interaction.
-
-        return 0.0;
-    }
-    else
-    {
-        return 0.5*d_;
-    }
+    return 0.5*d_;
 }
 
 
diff --git a/src/lagrangian/intermediate/parcels/Templates/ReactingMultiphaseParcel/ReactingMultiphaseParcel.H b/src/lagrangian/intermediate/parcels/Templates/ReactingMultiphaseParcel/ReactingMultiphaseParcel.H
index ffb7f9b47e1..3fb4ef24218 100644
--- a/src/lagrangian/intermediate/parcels/Templates/ReactingMultiphaseParcel/ReactingMultiphaseParcel.H
+++ b/src/lagrangian/intermediate/parcels/Templates/ReactingMultiphaseParcel/ReactingMultiphaseParcel.H
@@ -289,7 +289,9 @@ public:
         (
             ReactingMultiphaseCloud<ParcelType>& owner,
             const vector& position,
-            const label cellI
+            const label cellI,
+            const label tetFaceI,
+            const label tetPtI
         );
 
 
@@ -299,6 +301,8 @@ public:
             ReactingMultiphaseCloud<ParcelType>& owner,
             const vector& position,
             const label cellI,
+            const label tetFaceI,
+            const label tetPtI,
             const label typeId,
             const scalar nParticle0,
             const scalar d0,
diff --git a/src/lagrangian/intermediate/parcels/Templates/ReactingMultiphaseParcel/ReactingMultiphaseParcelI.H b/src/lagrangian/intermediate/parcels/Templates/ReactingMultiphaseParcel/ReactingMultiphaseParcelI.H
index c7a557fba6f..5899d2a6666 100644
--- a/src/lagrangian/intermediate/parcels/Templates/ReactingMultiphaseParcel/ReactingMultiphaseParcelI.H
+++ b/src/lagrangian/intermediate/parcels/Templates/ReactingMultiphaseParcel/ReactingMultiphaseParcelI.H
@@ -89,10 +89,12 @@ inline Foam::ReactingMultiphaseParcel<ParcelType>::ReactingMultiphaseParcel
 (
     ReactingMultiphaseCloud<ParcelType>& owner,
     const vector& position,
-    const label cellI
+    const label cellI,
+    const label tetFaceI,
+    const label tetPtI
 )
 :
-    ReactingParcel<ParcelType>(owner, position, cellI),
+    ReactingParcel<ParcelType>(owner, position, cellI, tetFaceI, tetPtI),
     YGas_(0),
     YLiquid_(0),
     YSolid_(0),
@@ -106,6 +108,8 @@ inline Foam::ReactingMultiphaseParcel<ParcelType>::ReactingMultiphaseParcel
     ReactingMultiphaseCloud<ParcelType>& owner,
     const vector& position,
     const label cellI,
+    const label tetFaceI,
+    const label tetPtI,
     const label typeId,
     const scalar nParticle0,
     const scalar d0,
@@ -125,6 +129,8 @@ inline Foam::ReactingMultiphaseParcel<ParcelType>::ReactingMultiphaseParcel
         owner,
         position,
         cellI,
+        tetFaceI,
+        tetPtI,
         typeId,
         nParticle0,
         d0,
diff --git a/src/lagrangian/intermediate/parcels/Templates/ReactingParcel/ReactingParcel.C b/src/lagrangian/intermediate/parcels/Templates/ReactingParcel/ReactingParcel.C
index fa5f3f5ede3..f0321988560 100644
--- a/src/lagrangian/intermediate/parcels/Templates/ReactingParcel/ReactingParcel.C
+++ b/src/lagrangian/intermediate/parcels/Templates/ReactingParcel/ReactingParcel.C
@@ -42,7 +42,12 @@ void Foam::ReactingParcel<ParcelType>::setCellValues
 {
     ThermoParcel<ParcelType>::setCellValues(td, dt, cellI);
 
-    pc_ = td.pInterp().interpolate(this->position(), cellI);
+    pc_ = td.pInterp().interpolate
+    (
+        this->position(),
+        this->currentTetIndices()
+    );
+
     if (pc_ < td.constProps().pMin())
     {
         WarningIn
diff --git a/src/lagrangian/intermediate/parcels/Templates/ReactingParcel/ReactingParcel.H b/src/lagrangian/intermediate/parcels/Templates/ReactingParcel/ReactingParcel.H
index 38e607bd39f..415967c7c53 100644
--- a/src/lagrangian/intermediate/parcels/Templates/ReactingParcel/ReactingParcel.H
+++ b/src/lagrangian/intermediate/parcels/Templates/ReactingParcel/ReactingParcel.H
@@ -239,7 +239,9 @@ public:
         (
             ReactingCloud<ParcelType>& owner,
             const vector& position,
-            const label cellI
+            const label cellI,
+            const label tetFaceI,
+            const label tetPtI
         );
 
         //- Construct from components
@@ -248,6 +250,8 @@ public:
             ReactingCloud<ParcelType>& owner,
             const vector& position,
             const label cellI,
+            const label tetFaceI,
+            const label tetPtI,
             const label typeId,
             const scalar nParticle0,
             const scalar d0,
diff --git a/src/lagrangian/intermediate/parcels/Templates/ReactingParcel/ReactingParcelI.H b/src/lagrangian/intermediate/parcels/Templates/ReactingParcel/ReactingParcelI.H
index 736b230f08b..39beae61cf0 100644
--- a/src/lagrangian/intermediate/parcels/Templates/ReactingParcel/ReactingParcelI.H
+++ b/src/lagrangian/intermediate/parcels/Templates/ReactingParcel/ReactingParcelI.H
@@ -75,10 +75,12 @@ inline Foam::ReactingParcel<ParcelType>::ReactingParcel
 (
     ReactingCloud<ParcelType>& owner,
     const vector& position,
-    const label cellI
+    const label cellI,
+    const label tetFaceI,
+    const label tetPtI
 )
 :
-    ThermoParcel<ParcelType>(owner, position, cellI),
+    ThermoParcel<ParcelType>(owner, position, cellI, tetFaceI, tetPtI),
     mass0_(0.0),
     Y_(0),
     pc_(0.0)
@@ -91,6 +93,8 @@ inline Foam::ReactingParcel<ParcelType>::ReactingParcel
     ReactingCloud<ParcelType>& owner,
     const vector& position,
     const label cellI,
+    const label tetFaceI,
+    const label tetPtI,
     const label typeId,
     const scalar nParticle0,
     const scalar d0,
@@ -107,6 +111,8 @@ inline Foam::ReactingParcel<ParcelType>::ReactingParcel
         owner,
         position,
         cellI,
+        tetFaceI,
+        tetPtI,
         typeId,
         nParticle0,
         d0,
diff --git a/src/lagrangian/intermediate/parcels/Templates/ThermoParcel/ThermoParcel.C b/src/lagrangian/intermediate/parcels/Templates/ThermoParcel/ThermoParcel.C
index 6800129a9bd..dbcc01ad2ab 100644
--- a/src/lagrangian/intermediate/parcels/Templates/ThermoParcel/ThermoParcel.C
+++ b/src/lagrangian/intermediate/parcels/Templates/ThermoParcel/ThermoParcel.C
@@ -41,9 +41,11 @@ void Foam::ThermoParcel<ParcelType>::setCellValues
 {
     KinematicParcel<ParcelType>::setCellValues(td, dt, cellI);
 
-    cpc_ = td.cpInterp().interpolate(this->position(), cellI);
+    tetIndices tetIs = this->currentTetIndices();
 
-    Tc_ = td.TInterp().interpolate(this->position(), cellI);
+    cpc_ = td.cpInterp().interpolate(this->position(), tetIs);
+
+    Tc_ = td.TInterp().interpolate(this->position(), tetIs);
 
     if (Tc_ < td.constProps().TMin())
     {
@@ -96,10 +98,14 @@ void Foam::ThermoParcel<ParcelType>::calcSurfaceValues
     // Surface temperature using two thirds rule
     Ts = (2.0*T + Tc_)/3.0;
 
+    tetIndices tetIs = this->currentTetIndices();
+
     // Assuming thermo props vary linearly with T for small dT
-    scalar factor = td.TInterp().interpolate(this->position(), cellI)/Ts;
+    scalar factor = td.TInterp().interpolate (this->position(), tetIs)/Ts;
+
     rhos = this->rhoc_*factor;
-    mus = td.muInterp().interpolate(this->position(), cellI)/factor;
+
+    mus = td.muInterp().interpolate (this->position(), tetIs)/factor;
 
     Pr = td.constProps().Pr();
     kappa = cpc_*mus/Pr;
diff --git a/src/lagrangian/intermediate/parcels/Templates/ThermoParcel/ThermoParcel.H b/src/lagrangian/intermediate/parcels/Templates/ThermoParcel/ThermoParcel.H
index 8715aa570e9..5632d76592b 100644
--- a/src/lagrangian/intermediate/parcels/Templates/ThermoParcel/ThermoParcel.H
+++ b/src/lagrangian/intermediate/parcels/Templates/ThermoParcel/ThermoParcel.H
@@ -252,7 +252,9 @@ public:
         (
             ThermoCloud<ParcelType>& owner,
             const vector& position,
-            const label cellI
+            const label cellI,
+            const label tetFaceI,
+            const label tetPtI
         );
 
         //- Construct from components
@@ -261,6 +263,8 @@ public:
             ThermoCloud<ParcelType>& owner,
             const vector& position,
             const label cellI,
+            const label tetFaceI,
+            const label tetPtI,
             const label typeId,
             const scalar nParticle0,
             const scalar d0,
diff --git a/src/lagrangian/intermediate/parcels/Templates/ThermoParcel/ThermoParcelI.H b/src/lagrangian/intermediate/parcels/Templates/ThermoParcel/ThermoParcelI.H
index 05dce5832d9..b046f142ddd 100644
--- a/src/lagrangian/intermediate/parcels/Templates/ThermoParcel/ThermoParcelI.H
+++ b/src/lagrangian/intermediate/parcels/Templates/ThermoParcel/ThermoParcelI.H
@@ -75,10 +75,12 @@ inline Foam::ThermoParcel<ParcelType>::ThermoParcel
 (
     ThermoCloud<ParcelType>& owner,
     const vector& position,
-    const label cellI
+    const label cellI,
+    const label tetFaceI,
+    const label tetPtI
 )
 :
-    KinematicParcel<ParcelType>(owner, position, cellI),
+    KinematicParcel<ParcelType>(owner, position, cellI, tetFaceI, tetPtI),
     T_(0.0),
     cp_(0.0),
     Tc_(0.0),
@@ -92,6 +94,8 @@ inline Foam::ThermoParcel<ParcelType>::ThermoParcel
     ThermoCloud<ParcelType>& owner,
     const vector& position,
     const label cellI,
+    const label tetFaceI,
+    const label tetPtI,
     const label typeId,
     const scalar nParticle0,
     const scalar d0,
@@ -107,6 +111,8 @@ inline Foam::ThermoParcel<ParcelType>::ThermoParcel
         owner,
         position,
         cellI,
+        tetFaceI,
+        tetPtI,
         typeId,
         nParticle0,
         d0,
diff --git a/src/lagrangian/intermediate/parcels/derived/basicKinematicParcel/basicKinematicParcel.C b/src/lagrangian/intermediate/parcels/derived/basicKinematicParcel/basicKinematicParcel.C
index 61b201f282b..4ec2941bf78 100644
--- a/src/lagrangian/intermediate/parcels/derived/basicKinematicParcel/basicKinematicParcel.C
+++ b/src/lagrangian/intermediate/parcels/derived/basicKinematicParcel/basicKinematicParcel.C
@@ -41,10 +41,19 @@ Foam::basicKinematicParcel::basicKinematicParcel
 (
     KinematicCloud<basicKinematicParcel>& owner,
     const vector& position,
-    const label cellI
+    const label cellI,
+    const label tetFaceI,
+    const label tetPtI
 )
 :
-    KinematicParcel<basicKinematicParcel>(owner, position, cellI)
+    KinematicParcel<basicKinematicParcel>
+    (
+        owner,
+        position,
+        cellI,
+        tetFaceI,
+        tetPtI
+    )
 {}
 
 
@@ -53,6 +62,8 @@ Foam::basicKinematicParcel::basicKinematicParcel
     KinematicCloud<basicKinematicParcel>& owner,
     const vector& position,
     const label cellI,
+    const label tetFaceI,
+    const label tetPtI,
     const label typeId,
     const scalar nParticle0,
     const scalar d0,
@@ -68,6 +79,8 @@ Foam::basicKinematicParcel::basicKinematicParcel
         owner,
         position,
         cellI,
+        tetFaceI,
+        tetPtI,
         typeId,
         nParticle0,
         d0,
diff --git a/src/lagrangian/intermediate/parcels/derived/basicKinematicParcel/basicKinematicParcel.H b/src/lagrangian/intermediate/parcels/derived/basicKinematicParcel/basicKinematicParcel.H
index bd86c395b75..56d9cad296e 100644
--- a/src/lagrangian/intermediate/parcels/derived/basicKinematicParcel/basicKinematicParcel.H
+++ b/src/lagrangian/intermediate/parcels/derived/basicKinematicParcel/basicKinematicParcel.H
@@ -65,7 +65,9 @@ public:
         (
             KinematicCloud<basicKinematicParcel>& owner,
             const vector& position,
-            const label cellI
+            const label cellI,
+            const label tetFaceI,
+            const label tetPtI
         );
 
         //- Construct from components
@@ -74,6 +76,8 @@ public:
             KinematicCloud<basicKinematicParcel>& owner,
             const vector& position,
             const label cellI,
+            const label tetFaceI,
+            const label tetPtI,
             const label typeId,
             const scalar nParticle0,
             const scalar d0,
diff --git a/src/lagrangian/intermediate/parcels/derived/basicReactingMultiphaseParcel/basicReactingMultiphaseParcel.C b/src/lagrangian/intermediate/parcels/derived/basicReactingMultiphaseParcel/basicReactingMultiphaseParcel.C
index 56359e0d961..49e4e06ef4c 100644
--- a/src/lagrangian/intermediate/parcels/derived/basicReactingMultiphaseParcel/basicReactingMultiphaseParcel.C
+++ b/src/lagrangian/intermediate/parcels/derived/basicReactingMultiphaseParcel/basicReactingMultiphaseParcel.C
@@ -31,14 +31,18 @@ Foam::basicReactingMultiphaseParcel::basicReactingMultiphaseParcel
 (
     ReactingMultiphaseCloud<basicReactingMultiphaseParcel>& owner,
     const vector& position,
-    const label cellI
+    const label cellI,
+    const label tetFaceI,
+    const label tetPtI
 )
 :
     ReactingMultiphaseParcel<basicReactingMultiphaseParcel>
     (
         owner,
         position,
-        cellI
+        cellI,
+        tetFaceI,
+        tetPtI
     )
 {}
 
@@ -48,6 +52,8 @@ Foam::basicReactingMultiphaseParcel::basicReactingMultiphaseParcel
     ReactingMultiphaseCloud<basicReactingMultiphaseParcel>& owner,
     const vector& position,
     const label cellI,
+    const label tetFaceI,
+    const label tetPtI,
     const label typeId,
     const scalar nParticle0,
     const scalar d0,
@@ -63,11 +69,13 @@ Foam::basicReactingMultiphaseParcel::basicReactingMultiphaseParcel
         constantProperties& constProps
 )
 :
-    ReactingMultiphaseParcel<basicReactingMultiphaseParcel >
+    ReactingMultiphaseParcel<basicReactingMultiphaseParcel>
     (
         owner,
         position,
         cellI,
+        tetFaceI,
+        tetPtI,
         typeId,
         nParticle0,
         d0,
diff --git a/src/lagrangian/intermediate/parcels/derived/basicReactingMultiphaseParcel/basicReactingMultiphaseParcel.H b/src/lagrangian/intermediate/parcels/derived/basicReactingMultiphaseParcel/basicReactingMultiphaseParcel.H
index 6cc9d2f535a..1c9d2db2d72 100644
--- a/src/lagrangian/intermediate/parcels/derived/basicReactingMultiphaseParcel/basicReactingMultiphaseParcel.H
+++ b/src/lagrangian/intermediate/parcels/derived/basicReactingMultiphaseParcel/basicReactingMultiphaseParcel.H
@@ -66,7 +66,9 @@ public:
         (
              ReactingMultiphaseCloud<basicReactingMultiphaseParcel>& owner,
              const vector& position,
-             const label cellI
+             const label cellI,
+             const label tetFaceI,
+             const label tetPtI
         );
 
         //- Construct from components
@@ -75,6 +77,8 @@ public:
              ReactingMultiphaseCloud<basicReactingMultiphaseParcel>& owner,
              const vector& position,
              const label cellI,
+             const label tetFaceI,
+             const label tetPtI,
              const label typeId,
              const scalar nParticle0,
              const scalar d0,
diff --git a/src/lagrangian/intermediate/parcels/derived/basicReactingParcel/basicReactingParcel.C b/src/lagrangian/intermediate/parcels/derived/basicReactingParcel/basicReactingParcel.C
index 23ef85c02ec..f60f30b552b 100644
--- a/src/lagrangian/intermediate/parcels/derived/basicReactingParcel/basicReactingParcel.C
+++ b/src/lagrangian/intermediate/parcels/derived/basicReactingParcel/basicReactingParcel.C
@@ -29,20 +29,31 @@ License
 
 Foam::basicReactingParcel::basicReactingParcel
 (
-    ReactingCloud<basicReactingParcel >& owner,
+    ReactingCloud<basicReactingParcel>& owner,
     const vector& position,
-    const label cellI
+    const label cellI,
+    const label tetFaceI,
+    const label tetPtI
 )
 :
-    ReactingParcel<basicReactingParcel >(owner, position, cellI)
+    ReactingParcel<basicReactingParcel>
+    (
+        owner,
+        position,
+        cellI,
+        tetFaceI,
+        tetPtI
+    )
 {}
 
 
 Foam::basicReactingParcel::basicReactingParcel
 (
-    ReactingCloud<basicReactingParcel >& owner,
+    ReactingCloud<basicReactingParcel>& owner,
     const vector& position,
     const label cellI,
+    const label tetFaceI,
+    const label tetPtI,
     const label typeId,
     const scalar nParticle0,
     const scalar d0,
@@ -54,11 +65,13 @@ Foam::basicReactingParcel::basicReactingParcel
     const ReactingParcel<basicReactingParcel>::constantProperties& constProps
 )
 :
-    ReactingParcel<basicReactingParcel >
+    ReactingParcel<basicReactingParcel>
     (
         owner,
         position,
         cellI,
+        tetFaceI,
+        tetPtI,
         typeId,
         nParticle0,
         d0,
@@ -74,7 +87,7 @@ Foam::basicReactingParcel::basicReactingParcel
 
 Foam::basicReactingParcel::basicReactingParcel
 (
-    const Cloud<basicReactingParcel >& cloud,
+    const Cloud<basicReactingParcel>& cloud,
     Istream& is,
     bool readFields
 )
diff --git a/src/lagrangian/intermediate/parcels/derived/basicReactingParcel/basicReactingParcel.H b/src/lagrangian/intermediate/parcels/derived/basicReactingParcel/basicReactingParcel.H
index 86e9d726f5a..4329c61d9c3 100644
--- a/src/lagrangian/intermediate/parcels/derived/basicReactingParcel/basicReactingParcel.H
+++ b/src/lagrangian/intermediate/parcels/derived/basicReactingParcel/basicReactingParcel.H
@@ -64,7 +64,9 @@ public:
         (
             ReactingCloud<basicReactingParcel>& owner,
             const vector& position,
-            const label cellI
+            const label cellI,
+            const label tetFaceI,
+            const label tetPtI
         );
 
         //- Construct from components
@@ -73,6 +75,8 @@ public:
             ReactingCloud<basicReactingParcel>& owner,
             const vector& position,
             const label cellI,
+            const label tetFaceI,
+            const label tetPtI,
             const label typeId,
             const scalar nParticle0,
             const scalar d0,
diff --git a/src/lagrangian/intermediate/parcels/derived/basicThermoParcel/basicThermoParcel.C b/src/lagrangian/intermediate/parcels/derived/basicThermoParcel/basicThermoParcel.C
index c233efbd6fe..b77bf79b93c 100644
--- a/src/lagrangian/intermediate/parcels/derived/basicThermoParcel/basicThermoParcel.C
+++ b/src/lagrangian/intermediate/parcels/derived/basicThermoParcel/basicThermoParcel.C
@@ -41,10 +41,12 @@ Foam::basicThermoParcel::basicThermoParcel
 (
     ThermoCloud<basicThermoParcel>& owner,
     const vector position,
-    const label cellI
+    const label cellI,
+    const label tetFaceI,
+    const label tetPtI
 )
 :
-    ThermoParcel<basicThermoParcel>(owner, position, cellI)
+    ThermoParcel<basicThermoParcel>(owner, position, cellI, tetFaceI, tetPtI)
 {}
 
 
@@ -53,6 +55,8 @@ Foam::basicThermoParcel::basicThermoParcel
     ThermoCloud<basicThermoParcel>& owner,
     const vector position,
     const label cellI,
+    const label tetFaceI,
+    const label tetPtI,
     const label typeId,
     const scalar nParticle0,
     const scalar d0,
@@ -68,6 +72,8 @@ Foam::basicThermoParcel::basicThermoParcel
         owner,
         position,
         cellI,
+        tetFaceI,
+        tetPtI,
         typeId,
         nParticle0,
         d0,
diff --git a/src/lagrangian/intermediate/parcels/derived/basicThermoParcel/basicThermoParcel.H b/src/lagrangian/intermediate/parcels/derived/basicThermoParcel/basicThermoParcel.H
index 63c5b505570..71614c7d9d0 100644
--- a/src/lagrangian/intermediate/parcels/derived/basicThermoParcel/basicThermoParcel.H
+++ b/src/lagrangian/intermediate/parcels/derived/basicThermoParcel/basicThermoParcel.H
@@ -64,7 +64,9 @@ public:
        (
             ThermoCloud<basicThermoParcel>& owner,
             const vector position,
-            const label cellI
+            const label cellI,
+            const label tetFaceI,
+            const label tetPtI
        );
 
        //- Construct from components
@@ -73,6 +75,8 @@ public:
             ThermoCloud<basicThermoParcel>& owner,
             const vector position,
             const label cellI,
+            const label tetFaceI,
+            const label tetPtI,
             const label typeId,
             const scalar nParticle0,
             const scalar d0,
diff --git a/src/lagrangian/intermediate/particleForces/particleForces.C b/src/lagrangian/intermediate/particleForces/particleForces.C
index 6591d2b8d44..3a2865d41db 100644
--- a/src/lagrangian/intermediate/particleForces/particleForces.C
+++ b/src/lagrangian/intermediate/particleForces/particleForces.C
@@ -68,8 +68,8 @@ Foam::particleForces::particleForces
     pressureGradient_(dict_.lookup("pressureGradient")),
     paramagnetic_(dict_.lookup("paramagnetic")),
     magneticSusceptibility_(0.0),
-    UName_(dict_.lookupOrDefault<word>("U", "U")),
-    HdotGradHName_(dict_.lookupOrDefault<word>("HdotGradH", "HdotGradH"))
+    UName_(dict_.lookupOrDefault<word>("UName", "U")),
+    HdotGradHName_(dict_.lookupOrDefault<word>("HdotGradHName", "HdotGradH"))
 {
     if (virtualMass_)
     {
@@ -211,7 +211,7 @@ void Foam::particleForces::cacheFields
 Foam::vector Foam::particleForces::calcCoupled
 (
     const vector& position,
-    const label cellI,
+    const tetIndices& tetIs,
     const scalar dt,
     const scalar rhoc,
     const scalar rho,
@@ -236,7 +236,7 @@ Foam::vector Foam::particleForces::calcCoupled
     if (pressureGradient_)
     {
         const volTensorField& gradU = *gradUPtr_;
-        accelTot += rhoc/rho*(U & gradU[cellI]);
+        accelTot += rhoc/rho*(U & gradU[tetIs.cell()]);
     }
 
     return accelTot;
@@ -246,7 +246,7 @@ Foam::vector Foam::particleForces::calcCoupled
 Foam::vector Foam::particleForces::calcNonCoupled
 (
     const vector& position,
-    const label cellI,
+    const tetIndices& tetIs,
     const scalar dt,
     const scalar rhoc,
     const scalar rho,
@@ -272,7 +272,7 @@ Foam::vector Foam::particleForces::calcNonCoupled
         accelTot +=
             3.0*constant::electromagnetic::mu0.value()/rho
            *magneticSusceptibility_/(magneticSusceptibility_ + 3)
-           *HdotGradHInter.interpolate(position, cellI);
+           *HdotGradHInter.interpolate(position, tetIs);
 
         // force is:
 
diff --git a/src/lagrangian/intermediate/particleForces/particleForces.H b/src/lagrangian/intermediate/particleForces/particleForces.H
index 8c76b1f773d..7aacee74b34 100644
--- a/src/lagrangian/intermediate/particleForces/particleForces.H
+++ b/src/lagrangian/intermediate/particleForces/particleForces.H
@@ -41,6 +41,7 @@ SourceFiles
 #include "vector.H"
 #include "volFieldsFwd.H"
 #include "interpolation.H"
+#include "tetIndices.H"
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
@@ -179,7 +180,7 @@ public:
             vector calcCoupled
             (
                 const vector& position,
-                const label cellI,
+                const tetIndices& tetIs,
                 const scalar dt,
                 const scalar rhoc,
                 const scalar rho,
@@ -192,7 +193,7 @@ public:
             vector calcNonCoupled
             (
                 const vector& position,
-                const label cellI,
+                const tetIndices& tetIs,
                 const scalar dt,
                 const scalar rhoc,
                 const scalar rho,
diff --git a/src/lagrangian/intermediate/submodels/Kinematic/DispersionModel/DispersionModel/DispersionModel.H b/src/lagrangian/intermediate/submodels/Kinematic/DispersionModel/DispersionModel/DispersionModel.H
index b1634db21e1..afb063d0f5c 100644
--- a/src/lagrangian/intermediate/submodels/Kinematic/DispersionModel/DispersionModel/DispersionModel.H
+++ b/src/lagrangian/intermediate/submodels/Kinematic/DispersionModel/DispersionModel/DispersionModel.H
@@ -127,7 +127,7 @@ public:
         virtual vector update
         (
             const scalar dt,
-            const label celli,
+            const label cellI,
             const vector& U,
             const vector& Uc,
             vector& UTurb,
diff --git a/src/lagrangian/intermediate/submodels/Kinematic/DispersionModel/GradientDispersionRAS/GradientDispersionRAS.C b/src/lagrangian/intermediate/submodels/Kinematic/DispersionModel/GradientDispersionRAS/GradientDispersionRAS.C
index d214303427e..05617f25dae 100644
--- a/src/lagrangian/intermediate/submodels/Kinematic/DispersionModel/GradientDispersionRAS/GradientDispersionRAS.C
+++ b/src/lagrangian/intermediate/submodels/Kinematic/DispersionModel/GradientDispersionRAS/GradientDispersionRAS.C
@@ -81,7 +81,7 @@ template<class CloudType>
 Foam::vector Foam::GradientDispersionRAS<CloudType>::update
 (
     const scalar dt,
-    const label celli,
+    const label cellI,
     const vector& U,
     const vector& Uc,
     vector& UTurb,
@@ -98,8 +98,8 @@ Foam::vector Foam::GradientDispersionRAS<CloudType>::update
 
     const scalar tTurbLoc = min
     (
-        k[celli]/epsilon[celli],
-        cps*pow(k[celli], 1.5)/epsilon[celli]/(UrelMag + SMALL)
+        k[cellI]/epsilon[cellI],
+        cps*pow(k[cellI], 1.5)/epsilon[cellI]/(UrelMag + SMALL)
     );
 
     // Parcel is perturbed by the turbulence
@@ -111,8 +111,8 @@ Foam::vector Foam::GradientDispersionRAS<CloudType>::update
         {
             tTurb = 0.0;
 
-            scalar sigma = sqrt(2.0*k[celli]/3.0);
-            vector dir = -gradk[celli]/(mag(gradk[celli]) + SMALL);
+            scalar sigma = sqrt(2.0*k[cellI]/3.0);
+            vector dir = -gradk[cellI]/(mag(gradk[cellI]) + SMALL);
 
             // Numerical Recipes... Ch. 7. Random Numbers...
             scalar x1 = 0.0;
diff --git a/src/lagrangian/intermediate/submodels/Kinematic/DispersionModel/GradientDispersionRAS/GradientDispersionRAS.H b/src/lagrangian/intermediate/submodels/Kinematic/DispersionModel/GradientDispersionRAS/GradientDispersionRAS.H
index eb4c97e0614..af74d2578b1 100644
--- a/src/lagrangian/intermediate/submodels/Kinematic/DispersionModel/GradientDispersionRAS/GradientDispersionRAS.H
+++ b/src/lagrangian/intermediate/submodels/Kinematic/DispersionModel/GradientDispersionRAS/GradientDispersionRAS.H
@@ -90,7 +90,7 @@ public:
         virtual vector update
         (
             const scalar dt,
-            const label celli,
+            const label cellI,
             const vector& U,
             const vector& Uc,
             vector& UTurb,
diff --git a/src/lagrangian/intermediate/submodels/Kinematic/DispersionModel/NoDispersion/NoDispersion.H b/src/lagrangian/intermediate/submodels/Kinematic/DispersionModel/NoDispersion/NoDispersion.H
index a8bba39ef46..e63abbda213 100644
--- a/src/lagrangian/intermediate/submodels/Kinematic/DispersionModel/NoDispersion/NoDispersion.H
+++ b/src/lagrangian/intermediate/submodels/Kinematic/DispersionModel/NoDispersion/NoDispersion.H
@@ -80,7 +80,7 @@ public:
         virtual vector update
         (
             const scalar dt,
-            const label celli,
+            const label cellI,
             const vector& U,
             const vector& Uc,
             vector& UTurb,
diff --git a/src/lagrangian/intermediate/submodels/Kinematic/DispersionModel/StochasticDispersionRAS/StochasticDispersionRAS.C b/src/lagrangian/intermediate/submodels/Kinematic/DispersionModel/StochasticDispersionRAS/StochasticDispersionRAS.C
index 20368957d70..a43b00c10f9 100644
--- a/src/lagrangian/intermediate/submodels/Kinematic/DispersionModel/StochasticDispersionRAS/StochasticDispersionRAS.C
+++ b/src/lagrangian/intermediate/submodels/Kinematic/DispersionModel/StochasticDispersionRAS/StochasticDispersionRAS.C
@@ -58,7 +58,7 @@ template<class CloudType>
 Foam::vector Foam::StochasticDispersionRAS<CloudType>::update
 (
     const scalar dt,
-    const label celli,
+    const label cellI,
     const vector& U,
     const vector& Uc,
     vector& UTurb,
@@ -74,8 +74,8 @@ Foam::vector Foam::StochasticDispersionRAS<CloudType>::update
 
     const scalar tTurbLoc = min
     (
-        k[celli]/epsilon[celli],
-        cps*pow(k[celli], 1.5)/epsilon[celli]/(UrelMag + SMALL)
+        k[cellI]/epsilon[cellI],
+        cps*pow(k[cellI], 1.5)/epsilon[cellI]/(UrelMag + SMALL)
     );
 
     // Parcel is perturbed by the turbulence
@@ -87,7 +87,7 @@ Foam::vector Foam::StochasticDispersionRAS<CloudType>::update
         {
             tTurb = 0.0;
 
-            scalar sigma = sqrt(2.0*k[celli]/3.0);
+            scalar sigma = sqrt(2.0*k[cellI]/3.0);
             vector dir = 2.0*this->owner().rndGen().vector01() - vector::one;
             dir /= mag(dir) + SMALL;
 
diff --git a/src/lagrangian/intermediate/submodels/Kinematic/DispersionModel/StochasticDispersionRAS/StochasticDispersionRAS.H b/src/lagrangian/intermediate/submodels/Kinematic/DispersionModel/StochasticDispersionRAS/StochasticDispersionRAS.H
index b9663a90595..dae1658c912 100644
--- a/src/lagrangian/intermediate/submodels/Kinematic/DispersionModel/StochasticDispersionRAS/StochasticDispersionRAS.H
+++ b/src/lagrangian/intermediate/submodels/Kinematic/DispersionModel/StochasticDispersionRAS/StochasticDispersionRAS.H
@@ -79,7 +79,7 @@ public:
         virtual vector update
         (
             const scalar dt,
-            const label celli,
+            const label cellI,
             const vector& U,
             const vector& Uc,
             vector& UTurb,
diff --git a/src/lagrangian/intermediate/submodels/Kinematic/InjectionModel/ConeInjection/ConeInjection.C b/src/lagrangian/intermediate/submodels/Kinematic/InjectionModel/ConeInjection/ConeInjection.C
index cb665641ccb..3047f909aff 100644
--- a/src/lagrangian/intermediate/submodels/Kinematic/InjectionModel/ConeInjection/ConeInjection.C
+++ b/src/lagrangian/intermediate/submodels/Kinematic/InjectionModel/ConeInjection/ConeInjection.C
@@ -80,6 +80,8 @@ Foam::ConeInjection<CloudType>::ConeInjection
     duration_(readScalar(this->coeffDict().lookup("duration"))),
     position_(this->coeffDict().lookup("position")),
     injectorCell_(-1),
+    injectorTetFace_(-1),
+    injectorTetPt_(-1),
     direction_(this->coeffDict().lookup("direction")),
     parcelsPerSecond_
     (
@@ -150,7 +152,13 @@ Foam::ConeInjection<CloudType>::ConeInjection
     this->volumeTotal_ = flowRateProfile_().integrate(0.0, duration_);
 
     // Set/cache the injector cell
-    this->findCellAtPosition(injectorCell_, position_);
+    this->findCellAtPosition
+    (
+        injectorCell_,
+        injectorTetFace_,
+        injectorTetPt_,
+        position_
+    );
 }
 
 
@@ -184,11 +192,15 @@ void Foam::ConeInjection<CloudType>::setPositionAndCell
     const label,
     const scalar,
     vector& position,
-    label& cellOwner
+    label& cellOwner,
+    label& tetFaceI,
+    label& tetPtI
 )
 {
     position = position_;
     cellOwner = injectorCell_;
+    tetFaceI = injectorTetFace_;
+    tetPtI = injectorTetPt_;
 }
 
 
diff --git a/src/lagrangian/intermediate/submodels/Kinematic/InjectionModel/ConeInjection/ConeInjection.H b/src/lagrangian/intermediate/submodels/Kinematic/InjectionModel/ConeInjection/ConeInjection.H
index 6246f4cdc9e..58309fa8654 100644
--- a/src/lagrangian/intermediate/submodels/Kinematic/InjectionModel/ConeInjection/ConeInjection.H
+++ b/src/lagrangian/intermediate/submodels/Kinematic/InjectionModel/ConeInjection/ConeInjection.H
@@ -77,6 +77,12 @@ class ConeInjection
         //- Cell containing injector position []
         label injectorCell_;
 
+        //- tetFace of tet containing injector position []
+        label injectorTetFace_;
+
+        //- tetPt of tet containing injector position []
+        label injectorTetPt_;
+
         //- Injector direction []
         vector direction_;
 
@@ -158,14 +164,16 @@ public:
 
         // Injection geometry
 
-            //- Set the injection position and owner cell
+            //- Set the injection position and owner cell, tetFace and tetPt
             virtual void setPositionAndCell
             (
                 const label parcelI,
                 const label nParcels,
                 const scalar time,
                 vector& position,
-                label& cellOwner
+                label& cellOwner,
+                label& tetFaceI,
+                label& tetPtI
             );
 
             //- Set the parcel properties
diff --git a/src/lagrangian/intermediate/submodels/Kinematic/InjectionModel/ConeInjectionMP/ConeInjectionMP.C b/src/lagrangian/intermediate/submodels/Kinematic/InjectionModel/ConeInjectionMP/ConeInjectionMP.C
index 1d3f49aaa59..05cfb680a4f 100644
--- a/src/lagrangian/intermediate/submodels/Kinematic/InjectionModel/ConeInjectionMP/ConeInjectionMP.C
+++ b/src/lagrangian/intermediate/submodels/Kinematic/InjectionModel/ConeInjectionMP/ConeInjectionMP.C
@@ -99,6 +99,8 @@ Foam::ConeInjectionMP<CloudType>::ConeInjectionMP
         )
     ),
     injectorCells_(positions_.size()),
+    injectorTetFaces_(positions_.size()),
+    injectorTetPts_(positions_.size()),
     axesFile_(this->coeffDict().lookup("axesFile")),
     axes_
     (
@@ -190,6 +192,8 @@ Foam::ConeInjectionMP<CloudType>::ConeInjectionMP
         this->findCellAtPosition
         (
             injectorCells_[i],
+            injectorTetFaces_[i],
+            injectorTetPts_[i],
             positions_[i]
         );
     }
@@ -226,13 +230,17 @@ void Foam::ConeInjectionMP<CloudType>::setPositionAndCell
     const label,
     const scalar,
     vector& position,
-    label& cellOwner
+    label& cellOwner,
+    label& tetFaceI,
+    label& tetPtI
 )
 {
-    const label i = parcelI%positions_.size();
+    const label i = parcelI % positions_.size();
 
     position = positions_[i];
     cellOwner = injectorCells_[i];
+    tetFaceI = injectorTetFaces_[i];
+    tetPtI = injectorTetPts_[i];
 }
 
 
diff --git a/src/lagrangian/intermediate/submodels/Kinematic/InjectionModel/ConeInjectionMP/ConeInjectionMP.H b/src/lagrangian/intermediate/submodels/Kinematic/InjectionModel/ConeInjectionMP/ConeInjectionMP.H
index 5401deebee8..1033defe8b5 100644
--- a/src/lagrangian/intermediate/submodels/Kinematic/InjectionModel/ConeInjectionMP/ConeInjectionMP.H
+++ b/src/lagrangian/intermediate/submodels/Kinematic/InjectionModel/ConeInjectionMP/ConeInjectionMP.H
@@ -75,9 +75,15 @@ class ConeInjectionMP
         //- Field of injector positions
         vectorIOField positions_;
 
-        //- Field of cell labels corresoponding to injector positions
+        //- List of cell labels corresponding to injector positions
         labelList injectorCells_;
 
+        //- List of tetFace labels corresponding to injector positions
+        labelList injectorTetFaces_;
+
+        //- List of tetPt labels corresponding to injector positions
+        labelList injectorTetPts_;
+
         //- Name of file containing axes data
         const word axesFile_;
 
@@ -168,14 +174,16 @@ public:
 
         // Injection geometry
 
-            //- Set the injection position and owner cell
+            //- Set the injection position and owner cell, tetFace and tetPt
             virtual void setPositionAndCell
             (
                 const label parcelI,
                 const label nParcels,
                 const scalar time,
                 vector& position,
-                label& cellOwner
+                label& cellOwner,
+                label& tetFaceI,
+                label& tetPtI
             );
 
             //- Set the parcel properties
diff --git a/src/lagrangian/intermediate/submodels/Kinematic/InjectionModel/FieldActivatedInjection/FieldActivatedInjection.C b/src/lagrangian/intermediate/submodels/Kinematic/InjectionModel/FieldActivatedInjection/FieldActivatedInjection.C
index 533febfadcf..a0dae5cb34f 100644
--- a/src/lagrangian/intermediate/submodels/Kinematic/InjectionModel/FieldActivatedInjection/FieldActivatedInjection.C
+++ b/src/lagrangian/intermediate/submodels/Kinematic/InjectionModel/FieldActivatedInjection/FieldActivatedInjection.C
@@ -105,6 +105,8 @@ Foam::FieldActivatedInjection<CloudType>::FieldActivatedInjection
         )
     ),
     injectorCells_(positions_.size()),
+    injectorTetFaces_(positions_.size()),
+    injectorTetPts_(positions_.size()),
     nParcelsPerInjector_
     (
         readLabel(this->coeffDict().lookup("parcelsPerInjector"))
@@ -137,6 +139,8 @@ Foam::FieldActivatedInjection<CloudType>::FieldActivatedInjection
         this->findCellAtPosition
         (
             injectorCells_[i],
+            injectorTetFaces_[i],
+            injectorTetPts_[i],
             positions_[i]
         );
     }
@@ -173,11 +177,15 @@ void Foam::FieldActivatedInjection<CloudType>::setPositionAndCell
     const label,
     const scalar,
     vector& position,
-    label& cellOwner
+    label& cellOwner,
+    label& tetFaceI,
+    label& tetPtI
 )
 {
     position = positions_[parcelI];
     cellOwner = injectorCells_[parcelI];
+    tetFaceI = injectorTetFaces_[parcelI];
+    tetPtI = injectorTetPts_[parcelI];
 }
 
 
diff --git a/src/lagrangian/intermediate/submodels/Kinematic/InjectionModel/FieldActivatedInjection/FieldActivatedInjection.H b/src/lagrangian/intermediate/submodels/Kinematic/InjectionModel/FieldActivatedInjection/FieldActivatedInjection.H
index f2aebb0fd8c..282e876cc66 100644
--- a/src/lagrangian/intermediate/submodels/Kinematic/InjectionModel/FieldActivatedInjection/FieldActivatedInjection.H
+++ b/src/lagrangian/intermediate/submodels/Kinematic/InjectionModel/FieldActivatedInjection/FieldActivatedInjection.H
@@ -86,13 +86,19 @@ class FieldActivatedInjection
             //- Field of injector (x,y,z) positions
             vectorIOField positions_;
 
-            //- Field of cell labels corresponding to injector positions
+            //- List of cell labels corresponding to injector positions
             labelList injectorCells_;
 
+            //- List of tetFace labels corresponding to injector positions
+            labelList injectorTetFaces_;
+
+            //- List of tetPt labels corresponding to injector positions
+            labelList injectorTetPts_;
+
             //- Number of parcels per injector
             const label nParcelsPerInjector_;
 
-            //- Field of number of parcels injected for each injector
+            //- List of number of parcels injected for each injector
             labelList nParcelsInjected_;
 
 
@@ -101,7 +107,7 @@ class FieldActivatedInjection
             //- Initial parcel velocity
             const vector U0_;
 
-            //- Field of parcel diameters
+            //- List of parcel diameters
             scalarList diameters_;
 
             //- Parcel size PDF model
@@ -158,14 +164,16 @@ public:
 
         // Injection geometry
 
-            //- Set the injection position and owner cell
+            //- Set the injection position and owner cell, tetFace and tetPt
             virtual void setPositionAndCell
             (
                 const label parcelI,
                 const label nParcels,
                 const scalar time,
                 vector& position,
-                label& cellOwner
+                label& cellOwner,
+                label& tetFaceI,
+                label& tetPtI
             );
 
             //- Set the parcel properties
diff --git a/src/lagrangian/intermediate/submodels/Kinematic/InjectionModel/InjectionModel/InjectionModel.C b/src/lagrangian/intermediate/submodels/Kinematic/InjectionModel/InjectionModel/InjectionModel.C
index c56ff296183..9136baa6079 100644
--- a/src/lagrangian/intermediate/submodels/Kinematic/InjectionModel/InjectionModel/InjectionModel.C
+++ b/src/lagrangian/intermediate/submodels/Kinematic/InjectionModel/InjectionModel/InjectionModel.C
@@ -132,6 +132,8 @@ template<class CloudType>
 bool Foam::InjectionModel<CloudType>::findCellAtPosition
 (
     label& cellI,
+    label& tetFaceI,
+    label& tetPtI,
     vector& position,
     bool errorOnNotFound
 )
@@ -140,7 +142,13 @@ bool Foam::InjectionModel<CloudType>::findCellAtPosition
 
     const vector p0 = position;
 
-    cellI = owner_.mesh().findCell(position);
+    owner_.findCellFacePt
+    (
+        position,
+        cellI,
+        tetFaceI,
+        tetPtI
+    );
 
     label procI = -1;
 
@@ -148,10 +156,14 @@ bool Foam::InjectionModel<CloudType>::findCellAtPosition
     {
         procI = Pstream::myProcNo();
     }
+
     reduce(procI, maxOp<label>());
+
     if (procI != Pstream::myProcNo())
     {
         cellI = -1;
+        tetFaceI = -1;
+        tetPtI = -1;
     }
 
     // Last chance - find nearest cell and try that one
@@ -168,10 +180,14 @@ bool Foam::InjectionModel<CloudType>::findCellAtPosition
                 procI = Pstream::myProcNo();
             }
         }
+
         reduce(procI, maxOp<label>());
+
         if (procI != Pstream::myProcNo())
         {
             cellI = -1;
+            tetFaceI = -1;
+            tetPtI = -1;
         }
     }
 
@@ -421,8 +437,21 @@ void Foam::InjectionModel<CloudType>::inject(TrackData& td)
 
             // Determine the injection position and owner cell
             label cellI = -1;
+            label tetFaceI = -1;
+            label tetPtI = -1;
+
             vector pos = vector::zero;
-            setPositionAndCell(parcelI, newParcels, timeInj, pos, cellI);
+
+            setPositionAndCell
+            (
+                parcelI,
+                newParcels,
+                timeInj,
+                pos,
+                cellI,
+                tetFaceI,
+                tetPtI
+            );
 
             if (cellI > -1)
             {
@@ -433,7 +462,14 @@ void Foam::InjectionModel<CloudType>::inject(TrackData& td)
                 meshTools::constrainToMeshCentre(mesh, pos);
 
                 // Create a new parcel
-                parcelType* pPtr = new parcelType(td.cloud(), pos, cellI);
+                parcelType* pPtr = new parcelType
+                (
+                    td.cloud(),
+                    pos,
+                    cellI,
+                    tetFaceI,
+                    tetPtI
+                );
 
                 // Assign new parcel properties in injection model
                 setProperties(parcelI, newParcels, timeInj, *pPtr);
diff --git a/src/lagrangian/intermediate/submodels/Kinematic/InjectionModel/InjectionModel/InjectionModel.H b/src/lagrangian/intermediate/submodels/Kinematic/InjectionModel/InjectionModel/InjectionModel.H
index b40abbde90f..542e038b4ac 100644
--- a/src/lagrangian/intermediate/submodels/Kinematic/InjectionModel/InjectionModel/InjectionModel.H
+++ b/src/lagrangian/intermediate/submodels/Kinematic/InjectionModel/InjectionModel/InjectionModel.H
@@ -183,6 +183,8 @@ protected:
         virtual bool findCellAtPosition
         (
             label& cellI,
+            label& tetFaceI,
+            label& tetPtI,
             vector& position,
             bool errorOnNotFound = true
         );
@@ -305,14 +307,16 @@ public:
 
         // Injection geometry
 
-            //- Set the injection position and owner cell
+            //- Set the injection position and owner cell, tetFace and tetPt
             virtual void setPositionAndCell
             (
                 const label parcelI,
                 const label nParcels,
                 const scalar time,
                 vector& position,
-                label& cellOwner
+                label& cellOwner,
+                label& tetFaceI,
+                label& tetPtI
             ) = 0;
 
             //- Set the parcel properties
diff --git a/src/lagrangian/intermediate/submodels/Kinematic/InjectionModel/KinematicLookupTableInjection/KinematicLookupTableInjection.C b/src/lagrangian/intermediate/submodels/Kinematic/InjectionModel/KinematicLookupTableInjection/KinematicLookupTableInjection.C
index 413af8a0731..45fd5b0df6a 100644
--- a/src/lagrangian/intermediate/submodels/Kinematic/InjectionModel/KinematicLookupTableInjection/KinematicLookupTableInjection.C
+++ b/src/lagrangian/intermediate/submodels/Kinematic/InjectionModel/KinematicLookupTableInjection/KinematicLookupTableInjection.C
@@ -93,13 +93,24 @@ Foam::KinematicLookupTableInjection<CloudType>::KinematicLookupTableInjection
             IOobject::NO_WRITE
         )
     ),
-    injectorCells_(0)
+    injectorCells_(0),
+    injectorTetFaces_(0),
+    injectorTetPts_(0)
 {
     // Set/cache the injector cells
     injectorCells_.setSize(injectors_.size());
+    injectorTetFaces_.setSize(injectors_.size());
+    injectorTetPts_.setSize(injectors_.size());
+
     forAll(injectors_, i)
     {
-        this->findCellAtPosition(injectorCells_[i], injectors_[i].x());
+        this->findCellAtPosition
+        (
+            injectorCells_[i],
+            injectorTetFaces_[i],
+            injectorTetPts_[i],
+            injectors_[i].x()
+        );
     }
 
     // Determine volume of particles to inject
@@ -142,13 +153,17 @@ void Foam::KinematicLookupTableInjection<CloudType>::setPositionAndCell
     const label nParcels,
     const scalar time,
     vector& position,
-    label& cellOwner
+    label& cellOwner,
+    label& tetFaceI,
+    label& tetPtI
 )
 {
     label injectorI = parcelI*injectorCells_.size()/nParcels;
 
     position = injectors_[injectorI].x();
     cellOwner = injectorCells_[injectorI];
+    tetFaceI = injectorTetFaces_[injectorI];
+    tetPtI = injectorTetPts_[injectorI];
 }
 
 
diff --git a/src/lagrangian/intermediate/submodels/Kinematic/InjectionModel/KinematicLookupTableInjection/KinematicLookupTableInjection.H b/src/lagrangian/intermediate/submodels/Kinematic/InjectionModel/KinematicLookupTableInjection/KinematicLookupTableInjection.H
index d888c47582e..1692c92a0f0 100644
--- a/src/lagrangian/intermediate/submodels/Kinematic/InjectionModel/KinematicLookupTableInjection/KinematicLookupTableInjection.H
+++ b/src/lagrangian/intermediate/submodels/Kinematic/InjectionModel/KinematicLookupTableInjection/KinematicLookupTableInjection.H
@@ -81,8 +81,14 @@ class KinematicLookupTableInjection
         //- List of injectors
         kinematicParcelInjectionDataIOList injectors_;
 
-        //- List of injector cells per injector
-        List<label> injectorCells_;
+        //- List of cell labels corresponding to injector positions
+        labelList injectorCells_;
+
+        //- List of tetFace labels corresponding to injector positions
+        labelList injectorTetFaces_;
+
+        //- List of tetPt labels corresponding to injector positions
+        labelList injectorTetPts_;
 
 
 protected:
@@ -135,14 +141,16 @@ public:
 
         // Injection geometry
 
-            //- Set the injection position and owner cell
+            //- Set the injection position and owner cell, tetFace and tetPt
             virtual void setPositionAndCell
             (
                 const label parcelI,
                 const label nParcels,
                 const scalar time,
                 vector& position,
-                label& cellOwner
+                label& cellOwner,
+                label& tetFaceI,
+                label& tetPtI
             );
 
             //- Set the parcel properties
diff --git a/src/lagrangian/intermediate/submodels/Kinematic/InjectionModel/ManualInjection/ManualInjection.C b/src/lagrangian/intermediate/submodels/Kinematic/InjectionModel/ManualInjection/ManualInjection.C
index f64cdc22681..b7e3c702274 100644
--- a/src/lagrangian/intermediate/submodels/Kinematic/InjectionModel/ManualInjection/ManualInjection.C
+++ b/src/lagrangian/intermediate/submodels/Kinematic/InjectionModel/ManualInjection/ManualInjection.C
@@ -92,6 +92,9 @@ Foam::ManualInjection<CloudType>::ManualInjection
         )
     ),
     diameters_(positions_.size()),
+    injectorCells_(positions_.size(), -1),
+    injectorTetFaces_(positions_.size(), -1),
+    injectorTetPts_(positions_.size(), -1),
     U0_(this->coeffDict().lookup("U0")),
     parcelPDF_
     (
@@ -102,38 +105,45 @@ Foam::ManualInjection<CloudType>::ManualInjection
         )
     )
 {
-    Switch checkAndIgnoreOutOfBounds
+    Switch ignoreOutOfBounds
     (
-        this->coeffDict().lookupOrDefault("checkAndIgnoreOutOfBounds", false)
+        this->coeffDict().lookupOrDefault("ignoreOutOfBounds", false)
     );
 
     label nRejected = 0;
 
-    if (checkAndIgnoreOutOfBounds)
-    {
-        // Dummy cell
-        label cellI = -1;
-
-        PackedBoolList keep(positions_.size(), true);
+    PackedBoolList keep(positions_.size(), true);
 
-        forAll(positions_, pI)
+    forAll(positions_, pI)
+    {
+        if
+        (
+            !this->findCellAtPosition
+            (
+                injectorCells_[pI],
+                injectorTetFaces_[pI],
+                injectorTetPts_[pI],
+                positions_[pI],
+                !ignoreOutOfBounds
+            )
+        )
         {
-            if (!this->findCellAtPosition(cellI, positions_[pI], false))
-            {
-                keep[pI] = false;
+            keep[pI] = false;
 
-                nRejected++;
-            }
+            nRejected++;
         }
+    }
 
-        if (nRejected > 0)
-        {
-            inplaceSubset(keep, positions_);
-            inplaceSubset(keep, diameters_);
-
-            Info<< "    " << nRejected
-                << " particles ignored, out of bounds." << endl;
-        }
+    if (nRejected > 0)
+    {
+        inplaceSubset(keep, positions_);
+        inplaceSubset(keep, diameters_);
+        inplaceSubset(keep, injectorCells_);
+        inplaceSubset(keep, injectorTetFaces_);
+        inplaceSubset(keep, injectorTetPts_);
+
+        Info<< "    " << nRejected
+            << " particles ignored, out of bounds." << endl;
     }
 
     // Construct parcel diameters
@@ -178,11 +188,15 @@ void Foam::ManualInjection<CloudType>::setPositionAndCell
     const label,
     const scalar,
     vector& position,
-    label& cellOwner
+    label& cellOwner,
+    label& tetFaceI,
+    label& tetPtI
 )
 {
     position = positions_[parcelI];
-    this->findCellAtPosition(cellOwner, position);
+    cellOwner = injectorCells_[parcelI];
+    tetFaceI = injectorTetFaces_[parcelI];
+    tetPtI = injectorTetPts_[parcelI];
 }
 
 
diff --git a/src/lagrangian/intermediate/submodels/Kinematic/InjectionModel/ManualInjection/ManualInjection.H b/src/lagrangian/intermediate/submodels/Kinematic/InjectionModel/ManualInjection/ManualInjection.H
index 0202c9eb60b..a001f940534 100644
--- a/src/lagrangian/intermediate/submodels/Kinematic/InjectionModel/ManualInjection/ManualInjection.H
+++ b/src/lagrangian/intermediate/submodels/Kinematic/InjectionModel/ManualInjection/ManualInjection.H
@@ -70,7 +70,16 @@ class ManualInjection
         //- Field of parcel diameters
         scalarList diameters_;
 
-        //- Initial parcel velocity
+        //- List of cell labels corresponding to injector positions
+        labelList injectorCells_;
+
+        //- List of tetFace labels corresponding to injector positions
+        labelList injectorTetFaces_;
+
+        //- List of tetPt labels corresponding to injector positions
+        labelList injectorTetPts_;
+
+    //- Initial parcel velocity
         const vector U0_;
 
         //- Parcel size PDF model
@@ -127,14 +136,16 @@ public:
 
         // Injection geometry
 
-            //- Set the injection position and owner cell
+            //- Set the injection position and owner cell, tetFace and tetPt
             virtual void setPositionAndCell
             (
                 const label parcelI,
                 const label nParcels,
                 const scalar time,
                 vector& position,
-                label& cellOwner
+                label& cellOwner,
+                label& tetFaceI,
+                label& tetPtI
             );
 
             //- Set the parcel properties
diff --git a/src/lagrangian/intermediate/submodels/Kinematic/InjectionModel/NoInjection/NoInjection.C b/src/lagrangian/intermediate/submodels/Kinematic/InjectionModel/NoInjection/NoInjection.C
index 15452159300..6303a8f30fa 100644
--- a/src/lagrangian/intermediate/submodels/Kinematic/InjectionModel/NoInjection/NoInjection.C
+++ b/src/lagrangian/intermediate/submodels/Kinematic/InjectionModel/NoInjection/NoInjection.C
@@ -93,6 +93,8 @@ void Foam::NoInjection<CloudType>::setPositionAndCell
     const label,
     const scalar,
     vector&,
+    label&,
+    label&,
     label&
 )
 {}
diff --git a/src/lagrangian/intermediate/submodels/Kinematic/InjectionModel/NoInjection/NoInjection.H b/src/lagrangian/intermediate/submodels/Kinematic/InjectionModel/NoInjection/NoInjection.H
index 6dea7c9dd15..d5026cc75d4 100644
--- a/src/lagrangian/intermediate/submodels/Kinematic/InjectionModel/NoInjection/NoInjection.H
+++ b/src/lagrangian/intermediate/submodels/Kinematic/InjectionModel/NoInjection/NoInjection.H
@@ -101,14 +101,16 @@ public:
 
         // Injection geometry
 
-            //- Set the injection position and owner cell
+            //- Set the injection position and owner cell, tetFace and tetPt
             virtual void setPositionAndCell
             (
                 const label parcelI,
                 const label nParcels,
                 const scalar time,
                 vector& position,
-                label& cellOwner
+                label& cellOwner,
+                label& tetFaceI,
+                label& tetPtI
             );
 
             virtual void setProperties
diff --git a/src/lagrangian/intermediate/submodels/Kinematic/InjectionModel/PatchInjection/PatchInjection.C b/src/lagrangian/intermediate/submodels/Kinematic/InjectionModel/PatchInjection/PatchInjection.C
index b3ed38cf884..8c50cec0e09 100644
--- a/src/lagrangian/intermediate/submodels/Kinematic/InjectionModel/PatchInjection/PatchInjection.C
+++ b/src/lagrangian/intermediate/submodels/Kinematic/InjectionModel/PatchInjection/PatchInjection.C
@@ -98,7 +98,7 @@ Foam::PatchInjection<CloudType>::PatchInjection
             owner.rndGen()
         )
     ),
-    cellOwners_(),
+    injectorCells_(),
     fraction_(1.0)
 {
     const label patchId = owner.mesh().boundaryMesh().findPatchID(patchName_);
@@ -119,9 +119,9 @@ Foam::PatchInjection<CloudType>::PatchInjection
 
     const polyPatch& patch = owner.mesh().boundaryMesh()[patchId];
 
-    cellOwners_ = patch.faceCells();
+    injectorCells_ = patch.faceCells();
 
-    label patchSize = cellOwners_.size();
+    label patchSize = injectorCells_.size();
     label totalPatchSize = patchSize;
     reduce(totalPatchSize, sumOp<label>());
     fraction_ = scalar(patchSize)/totalPatchSize;
@@ -162,19 +162,37 @@ void Foam::PatchInjection<CloudType>::setPositionAndCell
     const label,
     const scalar,
     vector& position,
-    label& cellOwner
+    label& cellOwner,
+    label& tetFaceI,
+    label& tetPtI
 )
 {
-    if (cellOwners_.size() > 0)
+    if (injectorCells_.size() > 0)
     {
-        label cellI = this->owner().rndGen().integer(0, cellOwners_.size() - 1);
+        label cellI = this->owner().rndGen().integer
+        (
+            0,
+            injectorCells_.size() - 1
+        );
+
+        cellOwner = injectorCells_[cellI];
+
+        // The position is at the cell centre, which could be in any
+        // tet of the decomposed cell, so arbitrarily choose the first
+        // face of the cell as the tetFace and the first point after
+        // the base point on the face as the tetPt.  The tracking will
+        // pick the cell consistent with the motion in the first
+        // tracking step.
+        tetFaceI = this->owner().mesh().cells()[cellOwner][0];
+        tetPtI = 1;
 
-        cellOwner = cellOwners_[cellI];
         position = this->owner().mesh().C()[cellOwner];
     }
     else
     {
         cellOwner = -1;
+        tetFaceI = -1;
+        tetPtI = -1;
         // dummy position
         position = pTraits<vector>::max;
     }
diff --git a/src/lagrangian/intermediate/submodels/Kinematic/InjectionModel/PatchInjection/PatchInjection.H b/src/lagrangian/intermediate/submodels/Kinematic/InjectionModel/PatchInjection/PatchInjection.H
index f8d5840ffe0..ee6a5edeb0a 100644
--- a/src/lagrangian/intermediate/submodels/Kinematic/InjectionModel/PatchInjection/PatchInjection.H
+++ b/src/lagrangian/intermediate/submodels/Kinematic/InjectionModel/PatchInjection/PatchInjection.H
@@ -85,8 +85,8 @@ class PatchInjection
         //- Parcel size PDF model
         const autoPtr<pdfs::pdf> parcelPDF_;
 
-        //- Cell owners
-        labelList cellOwners_;
+        //- List of cell labels corresponding to injector positions
+        labelList injectorCells_;
 
         //- Fraction of injection controlled by this processor
         scalar fraction_;
@@ -142,14 +142,16 @@ public:
 
         // Injection geometry
 
-            //- Set the injection position and owner cell
+            //- Set the injection position and owner cell, tetFace and tetPt
             virtual void setPositionAndCell
             (
                 const label parcelI,
                 const label nParcels,
                 const scalar time,
                 vector& position,
-                label& cellOwner
+                label& cellOwner,
+                label& tetFaceI,
+                label& tetPtI
             );
 
             virtual void setProperties
diff --git a/src/lagrangian/intermediate/submodels/Kinematic/PatchInteractionModel/LocalInteraction/LocalInteraction.C b/src/lagrangian/intermediate/submodels/Kinematic/PatchInteractionModel/LocalInteraction/LocalInteraction.C
index b9fd7b819e5..0786df08a75 100644
--- a/src/lagrangian/intermediate/submodels/Kinematic/PatchInteractionModel/LocalInteraction/LocalInteraction.C
+++ b/src/lagrangian/intermediate/submodels/Kinematic/PatchInteractionModel/LocalInteraction/LocalInteraction.C
@@ -55,16 +55,16 @@ Foam::LocalInteraction<CloudType>::LocalInteraction
 )
 :
     PatchInteractionModel<CloudType>(dict, cloud, typeName),
-    patchData_(this->coeffDict().lookup("patches")),
-    patchIds_(patchData_.size())
+    patchProperties_(this->coeffDict().lookup("patches")),
+    patchIds_(patchProperties_.size())
 {
     const polyMesh& mesh = cloud.mesh();
     const polyBoundaryMesh& bMesh = mesh.boundaryMesh();
 
     // check that user patches are valid region patches
-    forAll(patchData_, patchI)
+    forAll(patchProperties_, patchI)
     {
-        const word& patchName = patchData_[patchI].patchName();
+        const word& patchName = patchProperties_[patchI].patchName();
         patchIds_[patchI] = bMesh.findPatchID(patchName);
         if (patchIds_[patchI] < 0)
         {
@@ -97,16 +97,16 @@ Foam::LocalInteraction<CloudType>::LocalInteraction
     }
 
     // check that interactions are valid/specified
-    forAll(patchData_, patchI)
+    forAll(patchProperties_, patchI)
     {
         const word& interactionTypeName =
-            patchData_[patchI].interactionTypeName();
+            patchProperties_[patchI].interactionTypeName();
         const typename PatchInteractionModel<CloudType>::interactionType& it =
             this->wordToInteractionType(interactionTypeName);
 
         if (it == PatchInteractionModel<CloudType>::itOther)
         {
-            const word& patchName = patchData_[patchI].patchName();
+            const word& patchName = patchProperties_[patchI].patchName();
             FatalErrorIn("LocalInteraction(const dictionary&, CloudType&)")
                 << "Unknown patch interaction type "
                 << interactionTypeName << " for patch " << patchName
@@ -137,13 +137,17 @@ bool Foam::LocalInteraction<CloudType>::active() const
 template <class CloudType>
 bool Foam::LocalInteraction<CloudType>::correct
 (
+    typename CloudType::parcelType& p,
     const polyPatch& pp,
-    const label faceId,
     bool& keepParticle,
-    bool& active,
-    vector& U
+    const scalar trackFraction,
+    const tetIndices& tetIs
 ) const
 {
+    vector& U = p.U();
+
+    bool& active = p.active();
+
     label patchI = applyToPatch(pp.index());
 
     if (patchI >= 0)
@@ -151,7 +155,7 @@ bool Foam::LocalInteraction<CloudType>::correct
         typename PatchInteractionModel<CloudType>::interactionType it =
             this->wordToInteractionType
             (
-                patchData_[patchI].interactionTypeName()
+                patchProperties_[patchI].interactionTypeName()
             );
 
         switch (it)
@@ -175,18 +179,26 @@ bool Foam::LocalInteraction<CloudType>::correct
                 keepParticle = true;
                 active = true;
 
-                vector nw = pp.faceAreas()[pp.whichFace(faceId)];
-                nw /= mag(nw);
+                vector nw;
+                vector Up;
+
+                this->patchData(p, pp, trackFraction, tetIs, nw, Up);
+
+                // Calculate motion relative to patch velocity
+                U -= Up;
 
                 scalar Un = U & nw;
                 vector Ut = U - Un*nw;
 
                 if (Un > 0)
                 {
-                    U -= (1.0 + patchData_[patchI].e())*Un*nw;
+                    U -= (1.0 + patchProperties_[patchI].e())*Un*nw;
                 }
 
-                U -= patchData_[patchI].mu()*Ut;
+                U -= patchProperties_[patchI].mu()*Ut;
+
+                // Return velocity to global space
+                U += Up;
 
                 break;
             }
@@ -202,9 +214,9 @@ bool Foam::LocalInteraction<CloudType>::correct
                         "vector&"
                     ") const"
                 )   << "Unknown interaction type "
-                    << patchData_[patchI].interactionTypeName()
+                    << patchProperties_[patchI].interactionTypeName()
                     << "(" << it << ") for patch "
-                    << patchData_[patchI].patchName()
+                    << patchProperties_[patchI].patchName()
                     << ". Valid selections are:" << this->interactionTypeNames_
                     << endl << abort(FatalError);
             }
diff --git a/src/lagrangian/intermediate/submodels/Kinematic/PatchInteractionModel/LocalInteraction/LocalInteraction.H b/src/lagrangian/intermediate/submodels/Kinematic/PatchInteractionModel/LocalInteraction/LocalInteraction.H
index 4fa89004230..7b5446209ac 100644
--- a/src/lagrangian/intermediate/submodels/Kinematic/PatchInteractionModel/LocalInteraction/LocalInteraction.H
+++ b/src/lagrangian/intermediate/submodels/Kinematic/PatchInteractionModel/LocalInteraction/LocalInteraction.H
@@ -51,7 +51,7 @@ class LocalInteraction
     // Private data
 
         //- List of participating patches
-        const List<patchInteractionData> patchData_;
+        const List<patchInteractionData> patchProperties_;
 
         //- List of participating patch ids
         List<label> patchIds_;
@@ -89,11 +89,11 @@ public:
         //  Returns true if particle remains in same cell
         virtual bool correct
         (
+            typename CloudType::parcelType& p,
             const polyPatch& pp,
-            const label faceId,
             bool& keepParticle,
-            bool& active,
-            vector& U
+            const scalar trackFraction,
+            const tetIndices& tetIs
         ) const;
 };
 
diff --git a/src/lagrangian/intermediate/submodels/Kinematic/PatchInteractionModel/PatchInteractionModel/PatchInteractionModel.C b/src/lagrangian/intermediate/submodels/Kinematic/PatchInteractionModel/PatchInteractionModel/PatchInteractionModel.C
index 6947fc93bf6..0eca6dea665 100644
--- a/src/lagrangian/intermediate/submodels/Kinematic/PatchInteractionModel/PatchInteractionModel/PatchInteractionModel.C
+++ b/src/lagrangian/intermediate/submodels/Kinematic/PatchInteractionModel/PatchInteractionModel/PatchInteractionModel.C
@@ -24,6 +24,9 @@ License
 \*---------------------------------------------------------------------------*/
 
 #include "PatchInteractionModel.H"
+#include "fvMesh.H"
+#include "Time.H"
+#include "volFields.H"
 
 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
 
@@ -111,7 +114,8 @@ Foam::PatchInteractionModel<CloudType>::PatchInteractionModel
 :
     dict_(dict),
     owner_(owner),
-    coeffDict_(dict.subDict(type + "Coeffs"))
+    coeffDict_(dict.subDict(type + "Coeffs")),
+    UName_(coeffDict_.lookupOrDefault<word>("UName", "U"))
 {}
 
 
@@ -147,9 +151,166 @@ Foam::PatchInteractionModel<CloudType>::coeffDict() const
 }
 
 
+template<class CloudType>
+const Foam::word& Foam::PatchInteractionModel<CloudType>::UName() const
+{
+    return UName_;
+}
+
+
+template<class CloudType>
+void Foam::PatchInteractionModel<CloudType>::patchData
+(
+    typename CloudType::parcelType& p,
+    const polyPatch& pp,
+    const scalar trackFraction,
+    const tetIndices& tetIs,
+    vector& nw,
+    vector& Up
+) const
+{
+    const fvMesh& mesh = this->owner().mesh();
+
+    const volVectorField& Ufield =
+        mesh.objectRegistry::lookupObject<volVectorField>(UName_);
+
+    label patchI = pp.index();
+    label patchFaceI = pp.whichFace(p.face());
+
+    vector n = tetIs.faceTri(mesh).normal();
+    n /= mag(n);
+
+    vector U = Ufield.boundaryField()[patchI][patchFaceI];
+
+    // Unless the face is rotating, the required normal is n;
+    nw = n;
+
+    if (!mesh.moving())
+    {
+        // Only wall patches may have a non-zero wall velocity from
+        // the velocity field when the mesh is not moving.
+
+        if (isA<wallPolyPatch>(pp))
+        {
+            Up = U;
+        }
+        else
+        {
+            Up = vector::zero;
+        }
+    }
+    else
+    {
+        vector U00 = Ufield.oldTime().boundaryField()[patchI][patchFaceI];
+
+        vector n00 = tetIs.oldFaceTri(mesh).normal();
+
+        // Difference in normal over timestep
+        vector dn = vector::zero;
+
+        if (mag(n00) > SMALL)
+        {
+            // If the old normal is zero (for example in layer
+            // addition) then use the current normal, meaning that the
+            // motion can only be translational, and dn remains zero,
+            // otherwise, calculate dn:
+
+            n00 /= mag(n00);
+
+            dn = n - n00;
+        }
+
+        // Total fraction thought the timestep of the motion,
+        // including stepFraction before the current tracking step
+        // and the current trackFraction
+        // i.e.
+        // let s = stepFraction, t = trackFraction
+        // Motion of x in time:
+        // |-----------------|---------|---------|
+        // x00               x0        xi        x
+        //
+        // where xi is the correct value of x at the required
+        // tracking instant.
+        //
+        // x0 = x00 + s*(x - x00) = s*x + (1 - s)*x00
+        //
+        // i.e. the motion covered by previous tracking portions
+        // within this timestep, and
+        //
+        // xi = x0 + t*(x - x0)
+        //    = t*x + (1 - t)*x0
+        //    = t*x + (1 - t)*(s*x + (1 - s)*x00)
+        //    = (s + t - s*t)*x + (1 - (s + t - s*t))*x00
+        //
+        // let m = (s + t - s*t)
+        //
+        // xi = m*x + (1 - m)*x00 = x00 + m*(x - x00);
+        //
+        // In the same form as before.
+
+        scalar m =
+            p.stepFraction()
+          + trackFraction
+          - (p.stepFraction()*trackFraction);
+
+        // When the mesh is moving, the velocity field on wall patches
+        // will contain the velocity associated with the motion of the
+        // mesh, in which case it is interpolated in time using m.
+        // For other patches the face velocity will need to be
+        // reconstructed from the face centre motion.
+
+        const vector& Cf = mesh.faceCentres()[p.face()];
+
+        vector Cf00 = mesh.faces()[p.face()].centre(mesh.oldPoints());
+
+        if (isA<wallPolyPatch>(pp))
+        {
+            Up = U00 + m*(U - U00);
+        }
+        else
+        {
+            Up = (Cf - Cf00)/mesh.time().deltaTValue();
+        }
+
+        if (mag(dn) > SMALL)
+        {
+            // Rotational motion, nw requires interpolation and a
+            // rotational velocity around face centre correction to Up
+            // is required.
+
+            nw = n00 + m*dn;
+
+            // Cf at tracking instant
+            vector Cfi = Cf00 + m*(Cf - Cf00);
+
+            // Normal vector cross product
+            vector omega = (n00 ^ n);
+
+            scalar magOmega = mag(omega);
+
+            // magOmega = sin(angle between unit normals)
+            // Normalise omega vector by magOmega, then multiply by
+            // angle/dt to give the correct angular velocity vector.
+            omega *= Foam::asin(magOmega)/(magOmega*mesh.time().deltaTValue());
+
+            // Project position onto face and calculate this position
+            // relative to the face centre.
+            vector facePos =
+                p.position()
+              - ((p.position() - Cfi) & nw)*nw
+              - Cfi;
+
+            Up += (omega ^ facePos);
+        }
+
+        // No further action is required if the motion is
+        // translational only, nw and Up have already been set.
+    }
+}
+
+
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
 #include "PatchInteractionModelNew.C"
 
 // ************************************************************************* //
-
diff --git a/src/lagrangian/intermediate/submodels/Kinematic/PatchInteractionModel/PatchInteractionModel/PatchInteractionModel.H b/src/lagrangian/intermediate/submodels/Kinematic/PatchInteractionModel/PatchInteractionModel/PatchInteractionModel.H
index 1bd16847733..c3c883eaeb8 100644
--- a/src/lagrangian/intermediate/submodels/Kinematic/PatchInteractionModel/PatchInteractionModel/PatchInteractionModel.H
+++ b/src/lagrangian/intermediate/submodels/Kinematic/PatchInteractionModel/PatchInteractionModel/PatchInteractionModel.H
@@ -40,6 +40,8 @@ SourceFiles
 #include "autoPtr.H"
 #include "runTimeSelectionTables.H"
 #include "polyPatch.H"
+#include "wallPolyPatch.H"
+#include "tetIndices.H"
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
@@ -82,6 +84,9 @@ private:
         //- The coefficients dictionary
         const dictionary coeffDict_;
 
+        //- Name of velocity field - default = "U"
+        const word UName_;
+
 
 public:
 
@@ -136,6 +141,9 @@ public:
         //- Return the coefficients dictionary
         const dictionary& coeffDict() const;
 
+        //- Return name of velocity field
+        const word& UName() const;
+
 
     // Member Functions
 
@@ -152,12 +160,24 @@ public:
         //  Returns true if particle remains in same cell
         virtual bool correct
         (
+            typename CloudType::parcelType& p,
             const polyPatch& pp,
-            const label faceId,
             bool& keepParticle,
-            bool& active,
-            vector& U
+            const scalar trackFraction,
+            const tetIndices& tetIs
         ) const = 0;
+
+        //- Calculate the patch normal and velocity to interact with,
+        //  accounting for patch motion if required.
+        void patchData
+        (
+            typename CloudType::parcelType& p,
+            const polyPatch& pp,
+            const scalar trackFraction,
+            const tetIndices& tetIs,
+            vector& normal,
+            vector& Up
+        ) const;
 };
 
 
diff --git a/src/lagrangian/intermediate/submodels/Kinematic/PatchInteractionModel/Rebound/Rebound.C b/src/lagrangian/intermediate/submodels/Kinematic/PatchInteractionModel/Rebound/Rebound.C
index 71207833079..69b3749429c 100644
--- a/src/lagrangian/intermediate/submodels/Kinematic/PatchInteractionModel/Rebound/Rebound.C
+++ b/src/lagrangian/intermediate/submodels/Kinematic/PatchInteractionModel/Rebound/Rebound.C
@@ -58,18 +58,25 @@ bool Foam::Rebound<CloudType>::active() const
 template<class CloudType>
 bool Foam::Rebound<CloudType>::correct
 (
+    typename CloudType::parcelType& p,
     const polyPatch& pp,
-    const label faceId,
     bool& keepParticle,
-    bool& active,
-    vector& U
+    const scalar trackFraction,
+    const tetIndices& tetIs
 ) const
 {
+    vector& U = p.U();
+
     keepParticle = true;
-    active = true;
+    p.active() = true;
+
+    vector nw;
+    vector Up;
 
-    vector nw = pp.faceAreas()[pp.whichFace(faceId)];
-    nw /= mag(nw);
+    this->patchData(p, pp, trackFraction, tetIs, nw, Up);
+
+    // Calculate motion relative to patch velocity
+    U -= Up;
 
     scalar Un = U & nw;
 
@@ -78,6 +85,9 @@ bool Foam::Rebound<CloudType>::correct
         U -= UFactor_*2.0*Un*nw;
     }
 
+    // Return velocity to global space
+    U += Up;
+
     return true;
 }
 
diff --git a/src/lagrangian/intermediate/submodels/Kinematic/PatchInteractionModel/Rebound/Rebound.H b/src/lagrangian/intermediate/submodels/Kinematic/PatchInteractionModel/Rebound/Rebound.H
index e9cc326c302..c1ca658470a 100644
--- a/src/lagrangian/intermediate/submodels/Kinematic/PatchInteractionModel/Rebound/Rebound.H
+++ b/src/lagrangian/intermediate/submodels/Kinematic/PatchInteractionModel/Rebound/Rebound.H
@@ -79,11 +79,11 @@ public:
         //  Returns true if particle remains in same cell
         virtual bool correct
         (
+            typename CloudType::parcelType& p,
             const polyPatch& pp,
-            const label faceId,
             bool& keepParticle,
-            bool& active,
-            vector& U
+            const scalar trackFraction,
+            const tetIndices& tetIs
         ) const;
 };
 
diff --git a/src/lagrangian/intermediate/submodels/Kinematic/PatchInteractionModel/StandardWallInteraction/StandardWallInteraction.C b/src/lagrangian/intermediate/submodels/Kinematic/PatchInteractionModel/StandardWallInteraction/StandardWallInteraction.C
index 7d89c40df67..7d2b11cdca4 100644
--- a/src/lagrangian/intermediate/submodels/Kinematic/PatchInteractionModel/StandardWallInteraction/StandardWallInteraction.C
+++ b/src/lagrangian/intermediate/submodels/Kinematic/PatchInteractionModel/StandardWallInteraction/StandardWallInteraction.C
@@ -95,13 +95,17 @@ bool Foam::StandardWallInteraction<CloudType>::active() const
 template <class CloudType>
 bool Foam::StandardWallInteraction<CloudType>::correct
 (
+    typename CloudType::parcelType& p,
     const polyPatch& pp,
-    const label faceId,
     bool& keepParticle,
-    bool& active,
-    vector& U
+    const scalar trackFraction,
+    const tetIndices& tetIs
 ) const
 {
+    vector& U = p.U();
+
+    bool& active = p.active();
+
     if (isA<wallPolyPatch>(pp))
     {
         switch (interactionType_)
@@ -125,8 +129,13 @@ bool Foam::StandardWallInteraction<CloudType>::correct
                 keepParticle = true;
                 active = true;
 
-                vector nw = pp.faceAreas()[pp.whichFace(faceId)];
-                nw /= mag(nw);
+                vector nw;
+                vector Up;
+
+                this->patchData(p, pp, trackFraction, tetIs, nw, Up);
+
+                // Calculate motion relative to patch velocity
+                U -= Up;
 
                 scalar Un = U & nw;
                 vector Ut = U - Un*nw;
@@ -138,6 +147,9 @@ bool Foam::StandardWallInteraction<CloudType>::correct
 
                 U -= mu_*Ut;
 
+                // Return velocity to global space
+                U += Up;
+
                 break;
             }
             default:
diff --git a/src/lagrangian/intermediate/submodels/Kinematic/PatchInteractionModel/StandardWallInteraction/StandardWallInteraction.H b/src/lagrangian/intermediate/submodels/Kinematic/PatchInteractionModel/StandardWallInteraction/StandardWallInteraction.H
index 476bd7d63a2..bae46785ea2 100644
--- a/src/lagrangian/intermediate/submodels/Kinematic/PatchInteractionModel/StandardWallInteraction/StandardWallInteraction.H
+++ b/src/lagrangian/intermediate/submodels/Kinematic/PatchInteractionModel/StandardWallInteraction/StandardWallInteraction.H
@@ -99,11 +99,11 @@ public:
         //  Returns true if particle remains in same cell
         virtual bool correct
         (
+            typename CloudType::parcelType& p,
             const polyPatch& pp,
-            const label faceId,
             bool& keepParticle,
-            bool& active,
-            vector& U
+            const scalar trackFraction,
+            const tetIndices& tetIs
         ) const;
 };
 
diff --git a/src/lagrangian/intermediate/submodels/Kinematic/SurfaceFilmModel/SurfaceFilmModel/SurfaceFilmModel.C b/src/lagrangian/intermediate/submodels/Kinematic/SurfaceFilmModel/SurfaceFilmModel/SurfaceFilmModel.C
index 5ef3c76eb34..41d31901cad 100644
--- a/src/lagrangian/intermediate/submodels/Kinematic/SurfaceFilmModel/SurfaceFilmModel/SurfaceFilmModel.C
+++ b/src/lagrangian/intermediate/submodels/Kinematic/SurfaceFilmModel/SurfaceFilmModel/SurfaceFilmModel.C
@@ -121,11 +121,30 @@ void Foam::SurfaceFilmModel<CloudType>::inject(TrackData& td)
             if (diameterParcelPatch_[j] > 0)
             {
                 const label cellI = injectorCellsPatch[j];
+
+                // The position is at the cell centre, which could be
+                // in any tet of the decomposed cell, so arbitrarily
+                // choose the first face of the cell as the tetFace
+                // and the first point on the face after the base
+                // point as the tetPt.  The tracking will
+                // pick the cell consistent with the motion in the
+                // first tracking step.
+                const label tetFaceI = this->owner().mesh().cells()[cellI][0];
+                const label tetPtI = 1;
+
                 const point& pos = this->owner().mesh().C()[cellI];
 
                 // Create a new parcel
                 typename CloudType::parcelType* pPtr =
-                    new typename CloudType::parcelType(td.cloud(), pos, cellI);
+                    new typename CloudType::parcelType
+                    (
+                        td.cloud(),
+                        pos,
+                        cellI,
+                        tetFaceI,
+                        tetPtI
+                    );
+
                 setParcelProperties(*pPtr, j);
 
                 // Check new parcel properties
diff --git a/src/lagrangian/intermediate/submodels/Reacting/InjectionModel/ReactingLookupTableInjection/ReactingLookupTableInjection.C b/src/lagrangian/intermediate/submodels/Reacting/InjectionModel/ReactingLookupTableInjection/ReactingLookupTableInjection.C
index 00b6667f9d5..8ba48ca48d0 100644
--- a/src/lagrangian/intermediate/submodels/Reacting/InjectionModel/ReactingLookupTableInjection/ReactingLookupTableInjection.C
+++ b/src/lagrangian/intermediate/submodels/Reacting/InjectionModel/ReactingLookupTableInjection/ReactingLookupTableInjection.C
@@ -92,13 +92,24 @@ Foam::ReactingLookupTableInjection<CloudType>::ReactingLookupTableInjection
             IOobject::NO_WRITE
         )
     ),
-    injectorCells_(0)
+    injectorCells_(0),
+    injectorTetFaces_(0),
+    injectorTetPts_(0)
 {
     // Set/cache the injector cells
     injectorCells_.setSize(injectors_.size());
+    injectorTetFaces_.setSize(injectors_.size());
+    injectorTetPts_.setSize(injectors_.size());
+
     forAll(injectors_, i)
     {
-        this->findCellAtPosition(injectorCells_[i], injectors_[i].x());
+        this->findCellAtPosition
+        (
+            injectorCells_[i],
+            injectorTetFaces_[i],
+            injectorTetPts_[i],
+            injectors_[i].x()
+        );
     }
 
     // Determine volume of particles to inject
@@ -141,13 +152,17 @@ void Foam::ReactingLookupTableInjection<CloudType>::setPositionAndCell
     const label nParcels,
     const scalar time,
     vector& position,
-    label& cellOwner
+    label& cellOwner,
+    label& tetFaceI,
+    label& tetPtI
 )
 {
     label injectorI = parcelI*injectorCells_.size()/nParcels;
 
     position = injectors_[injectorI].x();
     cellOwner = injectorCells_[injectorI];
+    tetFaceI = injectorTetFaces_[injectorI];
+    tetPtI = injectorTetPts_[injectorI];
 }
 
 
diff --git a/src/lagrangian/intermediate/submodels/Reacting/InjectionModel/ReactingLookupTableInjection/ReactingLookupTableInjection.H b/src/lagrangian/intermediate/submodels/Reacting/InjectionModel/ReactingLookupTableInjection/ReactingLookupTableInjection.H
index 52cf535665e..88bb33005f2 100644
--- a/src/lagrangian/intermediate/submodels/Reacting/InjectionModel/ReactingLookupTableInjection/ReactingLookupTableInjection.H
+++ b/src/lagrangian/intermediate/submodels/Reacting/InjectionModel/ReactingLookupTableInjection/ReactingLookupTableInjection.H
@@ -84,8 +84,14 @@ class ReactingLookupTableInjection
         //- List of injectors
         reactingParcelInjectionDataIOList injectors_;
 
-        //- List of injector cells per injector
-        List<label> injectorCells_;
+        //- List of cell labels corresoponding to injector positions
+        labelList injectorCells_;
+
+        //- List of tetFace labels corresoponding to injector positions
+        labelList injectorTetFaces_;
+
+        //- List of tetPt labels corresoponding to injector positions
+        labelList injectorTetPts_;
 
 
 protected:
@@ -138,14 +144,16 @@ public:
 
         // Injection geometry
 
-            //- Set the injection position and owner cell
+            //- Set the injection position and owner cell, tetFace and tetPt
             virtual void setPositionAndCell
             (
                 const label parcelI,
                 const label nParcels,
                 const scalar time,
                 vector& position,
-                label& cellOwner
+                label& cellOwner,
+                label& tetFaceI,
+                label& tetPtI
             );
 
             //- Set the parcel properties
diff --git a/src/lagrangian/intermediate/submodels/ReactingMultiphase/InjectionModel/ReactingMultiphaseLookupTableInjection/ReactingMultiphaseLookupTableInjection.C b/src/lagrangian/intermediate/submodels/ReactingMultiphase/InjectionModel/ReactingMultiphaseLookupTableInjection/ReactingMultiphaseLookupTableInjection.C
index 4d68b40b09d..a97ab2d4d89 100644
--- a/src/lagrangian/intermediate/submodels/ReactingMultiphase/InjectionModel/ReactingMultiphaseLookupTableInjection/ReactingMultiphaseLookupTableInjection.C
+++ b/src/lagrangian/intermediate/submodels/ReactingMultiphase/InjectionModel/ReactingMultiphaseLookupTableInjection/ReactingMultiphaseLookupTableInjection.C
@@ -95,13 +95,24 @@ ReactingMultiphaseLookupTableInjection
             IOobject::NO_WRITE
         )
     ),
-    injectorCells_(0)
+    injectorCells_(0),
+    injectorTetFaces_(0),
+    injectorTetPts_(0)
 {
     // Set/cache the injector cells
     injectorCells_.setSize(injectors_.size());
+    injectorTetFaces_.setSize(injectors_.size());
+    injectorTetPts_.setSize(injectors_.size());
+
     forAll(injectors_, i)
     {
-        this->findCellAtPosition(injectorCells_[i], injectors_[i].x());
+        this->findCellAtPosition
+        (
+            injectorCells_[i],
+            injectorTetFaces_[i],
+            injectorTetPts_[i],
+            injectors_[i].x()
+        );
     }
 
     // Determine volume of particles to inject
@@ -146,13 +157,17 @@ void Foam::ReactingMultiphaseLookupTableInjection<CloudType>::setPositionAndCell
     const label nParcels,
     const scalar time,
     vector& position,
-    label& cellOwner
+    label& cellOwner,
+    label& tetFaceI,
+    label& tetPtI
 )
 {
     label injectorI = parcelI*injectorCells_.size()/nParcels;
 
     position = injectors_[injectorI].x();
     cellOwner = injectorCells_[injectorI];
+    tetFaceI = injectorTetFaces_[injectorI];
+    tetPtI = injectorTetPts_[injectorI];
 }
 
 
diff --git a/src/lagrangian/intermediate/submodels/ReactingMultiphase/InjectionModel/ReactingMultiphaseLookupTableInjection/ReactingMultiphaseLookupTableInjection.H b/src/lagrangian/intermediate/submodels/ReactingMultiphase/InjectionModel/ReactingMultiphaseLookupTableInjection/ReactingMultiphaseLookupTableInjection.H
index 2307ca20050..289f664550e 100644
--- a/src/lagrangian/intermediate/submodels/ReactingMultiphase/InjectionModel/ReactingMultiphaseLookupTableInjection/ReactingMultiphaseLookupTableInjection.H
+++ b/src/lagrangian/intermediate/submodels/ReactingMultiphase/InjectionModel/ReactingMultiphaseLookupTableInjection/ReactingMultiphaseLookupTableInjection.H
@@ -87,8 +87,14 @@ class ReactingMultiphaseLookupTableInjection
         //- List of injectors
         reactingMultiphaseParcelInjectionDataIOList injectors_;
 
-        //- List of injector cells per injector
-        List<label> injectorCells_;
+        //- List of cell labels corresoponding to injector positions
+        labelList injectorCells_;
+
+        //- List of tetFace labels corresoponding to injector positions
+        labelList injectorTetFaces_;
+
+        //- List of tetPt labels corresoponding to injector positions
+        labelList injectorTetPts_;
 
 
 protected:
@@ -141,14 +147,16 @@ public:
 
         // Injection geometry
 
-            //- Set the injection position and owner cell
+            //- Set the injection position and owner cell, tetFace and tetPt
             virtual void setPositionAndCell
             (
                 const label parcelI,
                 const label nParcels,
                 const scalar time,
                 vector& position,
-                label& cellOwner
+                label& cellOwner,
+                label& tetFaceI,
+                label& tetPtI
             );
 
             //- Set the parcel properties
diff --git a/src/lagrangian/intermediate/submodels/Thermodynamic/InjectionModel/ThermoLookupTableInjection/ThermoLookupTableInjection.C b/src/lagrangian/intermediate/submodels/Thermodynamic/InjectionModel/ThermoLookupTableInjection/ThermoLookupTableInjection.C
index 88c13c4f8f7..6138a8a5f54 100644
--- a/src/lagrangian/intermediate/submodels/Thermodynamic/InjectionModel/ThermoLookupTableInjection/ThermoLookupTableInjection.C
+++ b/src/lagrangian/intermediate/submodels/Thermodynamic/InjectionModel/ThermoLookupTableInjection/ThermoLookupTableInjection.C
@@ -93,13 +93,24 @@ Foam::ThermoLookupTableInjection<CloudType>::ThermoLookupTableInjection
             IOobject::NO_WRITE
         )
     ),
-    injectorCells_(0)
+    injectorCells_(0),
+    injectorTetFaces_(0),
+    injectorTetPts_(0)
 {
     // Set/cache the injector cells
     injectorCells_.setSize(injectors_.size());
+    injectorTetFaces_.setSize(injectors_.size());
+    injectorTetPts_.setSize(injectors_.size());
+
     forAll(injectors_, i)
     {
-        this->findCellAtPosition(injectorCells_[injectorI], injectors_[i].x());
+        this->findCellAtPosition
+        (
+            injectorCells_[i],
+            injectorTetFaces_[i],
+            injectorTetPts_[i],
+            injectors_[i].x()
+        );
     }
 
     // Determine volume of particles to inject
@@ -142,13 +153,17 @@ void Foam::ThermoLookupTableInjection<CloudType>::setPositionAndCell
     const label nParcels,
     const scalar time,
     vector& position,
-    label& cellOwner
+    label& cellOwner,
+    label& tetFaceI,
+    label& tetPtI
 )
 {
     label injectorI = parcelI*injectorCells_.size()/nParcels;
 
     position = injectors_[injectorI].x();
     cellOwner = injectorCells_[injectorI];
+    tetFaceI = injectorTetFaces_[injectorI];
+    tetPtI = injectorTetPts_[injectorI];
 }
 
 
diff --git a/src/lagrangian/intermediate/submodels/Thermodynamic/InjectionModel/ThermoLookupTableInjection/ThermoLookupTableInjection.H b/src/lagrangian/intermediate/submodels/Thermodynamic/InjectionModel/ThermoLookupTableInjection/ThermoLookupTableInjection.H
index fa009ff00e4..c66f0778f64 100644
--- a/src/lagrangian/intermediate/submodels/Thermodynamic/InjectionModel/ThermoLookupTableInjection/ThermoLookupTableInjection.H
+++ b/src/lagrangian/intermediate/submodels/Thermodynamic/InjectionModel/ThermoLookupTableInjection/ThermoLookupTableInjection.H
@@ -83,8 +83,14 @@ class ThermoLookupTableInjection
         //- List of injectors
         kinematicParcelInjectionDataIOList injectors_;
 
-        //- List of injector cells per injector
-        List<label> injectorCells_;
+        //- List of cell labels corresoponding to injector positions
+        labelList injectorCells_;
+
+        //- List of tetFace labels corresoponding to injector positions
+        labelList injectorTetFaces_;
+
+        //- List of tetPt labels corresoponding to injector positions
+        labelList injectorTetPts_;
 
 
 protected:
@@ -137,14 +143,16 @@ public:
 
         // Injection geometry
 
-            //- Set the injection position and owner cell
+            //- Set the injection position and owner cell, tetFace and tetPt
             virtual void setPositionAndCell
             (
                 const label parcelI,
                 const label nParcels,
                 const scalar time,
                 vector& position,
-                label& cellOwner
+                label& cellOwner,
+                label& tetFaceI,
+                label& tetPtI
             );
 
             //- Set the parcel properties
diff --git a/src/lagrangian/molecularDynamics/molecule/molecule/molecule.C b/src/lagrangian/molecularDynamics/molecule/molecule/molecule.C
index 2af297c6ac4..ef3b1395cc8 100644
--- a/src/lagrangian/molecularDynamics/molecule/molecule/molecule.C
+++ b/src/lagrangian/molecularDynamics/molecule/molecule/molecule.C
@@ -260,7 +260,9 @@ bool Foam::molecule::hitPatch
 (
     const polyPatch&,
     molecule::trackData&,
-    const label
+    const label,
+    const scalar,
+    const tetIndices&
 )
 {
     return false;
@@ -271,7 +273,9 @@ bool Foam::molecule::hitPatch
 (
     const polyPatch&,
     int&,
-    const label
+    const label,
+    const scalar,
+    const tetIndices&
 )
 {
     return false;
@@ -299,10 +303,13 @@ void Foam::molecule::hitProcessorPatch
 void Foam::molecule::hitWallPatch
 (
     const wallPolyPatch& wpp,
-    molecule::trackData& td
+    molecule::trackData& td,
+    const tetIndices& tetIs
 )
 {
-    vector nw = wpp.faceAreas()[wpp.whichFace(face())];
+    // Use of the normal from tetIs is not required as
+    // hasWallImpactDistance for a moleculeCloud is false.
+    vector nw = normal();
     nw /= mag(nw);
 
     scalar vn = v_ & nw;
@@ -318,7 +325,8 @@ void Foam::molecule::hitWallPatch
 void Foam::molecule::hitWallPatch
 (
     const wallPolyPatch&,
-    int&
+    int&,
+    const tetIndices&
 )
 {}
 
diff --git a/src/lagrangian/molecularDynamics/molecule/molecule/molecule.H b/src/lagrangian/molecularDynamics/molecule/molecule/molecule.H
index 448cb0955be..8df55ca8faf 100644
--- a/src/lagrangian/molecularDynamics/molecule/molecule/molecule.H
+++ b/src/lagrangian/molecularDynamics/molecule/molecule/molecule.H
@@ -228,7 +228,9 @@ public:
         (
             const Cloud<molecule>& c,
             const vector& position,
-            const label celli,
+            const label cellI,
+            const label tetFaceI,
+            const label tetPtI,
             const tensor& Q,
             const vector& v,
             const vector& a,
@@ -317,7 +319,9 @@ public:
         (
             const polyPatch&,
             molecule::trackData& td,
-            const label patchI
+            const label patchI,
+            const scalar trackFraction,
+            const tetIndices& tetIs
         );
 
         //- Overridable function to handle the particle hitting a patch
@@ -326,7 +330,9 @@ public:
         (
             const polyPatch& p,
             int& td,
-            const label patchI
+            const label patchI,
+            const scalar trackFraction,
+            const tetIndices& tetIs
         );
 
         //- Overridable function to handle the particle hitting a processorPatch
@@ -348,7 +354,8 @@ public:
         void hitWallPatch
         (
             const wallPolyPatch&,
-            molecule::trackData& td
+            molecule::trackData& td,
+            const tetIndices&
         );
 
         //- Overridable function to handle the particle hitting a wallPatch
@@ -356,7 +363,8 @@ public:
         void hitWallPatch
         (
             const wallPolyPatch&,
-            int&
+            int&,
+            const tetIndices&
         );
 
         //- Overridable function to handle the particle hitting a polyPatch
diff --git a/src/lagrangian/molecularDynamics/molecule/molecule/moleculeI.H b/src/lagrangian/molecularDynamics/molecule/molecule/moleculeI.H
index b759d4731be..be0c19985e3 100644
--- a/src/lagrangian/molecularDynamics/molecule/molecule/moleculeI.H
+++ b/src/lagrangian/molecularDynamics/molecule/molecule/moleculeI.H
@@ -221,7 +221,9 @@ inline Foam::molecule::molecule
 (
     const Cloud<molecule>& c,
     const vector& position,
-    const label celli,
+    const label cellI,
+    const label tetFaceI,
+    const label tetPtI,
     const tensor& Q,
     const vector& v,
     const vector& a,
@@ -234,7 +236,7 @@ inline Foam::molecule::molecule
 
 )
 :
-    Particle<molecule>(c, position, celli),
+    Particle<molecule>(c, position, cellI, tetFaceI, tetPtI),
     Q_(Q),
     v_(v),
     a_(a),
diff --git a/src/lagrangian/molecularDynamics/molecule/moleculeCloud/moleculeCloud.C b/src/lagrangian/molecularDynamics/molecule/moleculeCloud/moleculeCloud.C
index 5fef0b1d0d4..ef50886ebf2 100644
--- a/src/lagrangian/molecularDynamics/molecule/moleculeCloud/moleculeCloud.C
+++ b/src/lagrangian/molecularDynamics/molecule/moleculeCloud/moleculeCloud.C
@@ -742,7 +742,17 @@ void Foam::moleculeCloud::initialiseMolecules
                                 globalPosition
                             );
 
-                            label cell = mesh_.findCell(globalPosition);
+                            label cell = -1;
+                            label tetFace = -1;
+                            label tetPt = -1;
+
+                            findCellFacePt
+                            (
+                                globalPosition,
+                                cell,
+                                tetFace,
+                                tetPt
+                            );
 
                             if (findIndex(zone, cell) != -1)
                             {
@@ -750,6 +760,8 @@ void Foam::moleculeCloud::initialiseMolecules
                                 (
                                     globalPosition,
                                     cell,
+                                    tetFace,
+                                    tetPt,
                                     id,
                                     tethered,
                                     temperature,
@@ -818,9 +830,16 @@ void Foam::moleculeCloud::initialiseMolecules
                                                 globalPosition
                                             );
 
-                                        label cell = mesh_.findCell
+                                        label cell = -1;
+                                        label tetFace = -1;
+                                        label tetPt = -1;
+
+                                        findCellFacePt
                                         (
-                                            globalPosition
+                                            globalPosition,
+                                            cell,
+                                            tetFace,
+                                            tetPt
                                         );
 
                                         if (findIndex(zone, cell) != -1)
@@ -829,6 +848,8 @@ void Foam::moleculeCloud::initialiseMolecules
                                             (
                                                 globalPosition,
                                                 cell,
+                                                tetFace,
+                                                tetPt,
                                                 id,
                                                 tethered,
                                                 temperature,
@@ -888,9 +909,16 @@ void Foam::moleculeCloud::initialiseMolecules
                                                 globalPosition
                                             );
 
-                                        label cell = mesh_.findCell
+                                        label cell = -1;
+                                        label tetFace = -1;
+                                        label tetPt = -1;
+
+                                        findCellFacePt
                                         (
-                                            globalPosition
+                                            globalPosition,
+                                            cell,
+                                            tetFace,
+                                            tetPt
                                         );
 
                                         if (findIndex(zone, cell) != -1)
@@ -899,6 +927,8 @@ void Foam::moleculeCloud::initialiseMolecules
                                             (
                                                 globalPosition,
                                                 cell,
+                                                tetFace,
+                                                tetPt,
                                                 id,
                                                 tethered,
                                                 temperature,
@@ -962,6 +992,8 @@ void Foam::moleculeCloud::createMolecule
 (
     const point& position,
     label cell,
+    label tetFace,
+    label tetPt,
     label id,
     bool tethered,
     scalar temperature,
@@ -972,7 +1004,7 @@ void Foam::moleculeCloud::createMolecule
 
     if (cell == -1)
     {
-        cell = mesh_.findCell(position);
+        findCellFacePt(position, cell, tetFace, tetPt);
     }
 
     if (cell == -1)
@@ -1034,6 +1066,8 @@ void Foam::moleculeCloud::createMolecule
             cloud,
             position,
             cell,
+            tetFace,
+            tetPt,
             Q,
             v,
             vector::zero,
diff --git a/src/lagrangian/molecularDynamics/molecule/moleculeCloud/moleculeCloud.H b/src/lagrangian/molecularDynamics/molecule/moleculeCloud/moleculeCloud.H
index b92f4e5e7b5..7cf3359a351 100644
--- a/src/lagrangian/molecularDynamics/molecule/moleculeCloud/moleculeCloud.H
+++ b/src/lagrangian/molecularDynamics/molecule/moleculeCloud/moleculeCloud.H
@@ -114,6 +114,8 @@ private:
         (
             const point& position,
             label cell,
+            label tetFace,
+            label tetPt,
             label id,
             bool tethered,
             scalar temperature,
diff --git a/src/lagrangian/solidParticle/Make/options b/src/lagrangian/solidParticle/Make/options
index 15874b7b55a..9fe79c9220b 100644
--- a/src/lagrangian/solidParticle/Make/options
+++ b/src/lagrangian/solidParticle/Make/options
@@ -1,7 +1,9 @@
 EXE_INC = \
     -I$(LIB_SRC)/finiteVolume/lnInclude \
+    -I$(LIB_SRC)/meshTools/lnInclude \
     -I$(LIB_SRC)/lagrangian/basic/lnInclude
 
 LIB_LIBS = \
     -llagrangian \
+    -lmeshTools \
     -lfiniteVolume
diff --git a/src/lagrangian/solidParticle/solidParticle.C b/src/lagrangian/solidParticle/solidParticle.C
index 37c8b4478c0..e802e67d06a 100644
--- a/src/lagrangian/solidParticle/solidParticle.C
+++ b/src/lagrangian/solidParticle/solidParticle.C
@@ -54,14 +54,14 @@ bool Foam::solidParticle::move(solidParticle::trackData& td)
 
         // remember which cell the parcel is in
         // since this will change if a face is hit
-        label celli = cell();
+        label cellI = cell();
 
         dt *= trackToFace(position() + dt*U_, td);
 
         tEnd -= dt;
         stepFraction() = 1.0 - tEnd/deltaT;
 
-        cellPointWeight cpw(mesh, position(), celli, face());
+        cellPointWeight cpw(mesh, position(), cellI, face());
         scalar rhoc = td.rhoInterp().interpolate(cpw);
         vector Uc = td.UInterp().interpolate(cpw);
         scalar nuc = td.nuInterp().interpolate(cpw);
@@ -98,7 +98,9 @@ bool Foam::solidParticle::hitPatch
 (
     const polyPatch&,
     solidParticle::trackData&,
-    const label
+    const label,
+    const scalar,
+    const tetIndices&
 )
 {
     return false;
@@ -109,7 +111,9 @@ bool Foam::solidParticle::hitPatch
 (
     const polyPatch&,
     int&,
-    const label
+    const label,
+    const scalar,
+    const tetIndices&
 )
 {
     return false;
@@ -137,10 +141,11 @@ void Foam::solidParticle::hitProcessorPatch
 void Foam::solidParticle::hitWallPatch
 (
     const wallPolyPatch& wpp,
-    solidParticle::trackData& td
+    solidParticle::trackData& td,
+    const tetIndices& tetIs
 )
 {
-    vector nw = wpp.faceAreas()[wpp.whichFace(face())];
+    vector nw = tetIs.faceTri(cloud().pMesh()).normal();
     nw /= mag(nw);
 
     scalar Un = U_ & nw;
@@ -158,7 +163,8 @@ void Foam::solidParticle::hitWallPatch
 void Foam::solidParticle::hitWallPatch
 (
     const wallPolyPatch&,
-    int&
+    int&,
+    const tetIndices&
 )
 {}
 
diff --git a/src/lagrangian/solidParticle/solidParticle.H b/src/lagrangian/solidParticle/solidParticle.H
index 842b0045325..04ff8e9e510 100644
--- a/src/lagrangian/solidParticle/solidParticle.H
+++ b/src/lagrangian/solidParticle/solidParticle.H
@@ -127,8 +127,10 @@ public:
         (
             const Cloud<solidParticle>& c,
             const vector& position,
-            const label celli,
-            const scalar m,
+            const label cellI,
+            const label tetFaceI,
+            const label tetPtI,
+            const scalar d,
             const vector& U
         );
 
@@ -176,7 +178,9 @@ public:
             (
                 const polyPatch&,
                 solidParticle::trackData& td,
-                const label patchI
+                const label patchI,
+                const scalar trackFraction,
+                const tetIndices& tetIs
             );
 
             //- Overridable function to handle the particle hitting a patch
@@ -185,7 +189,9 @@ public:
             (
                 const polyPatch& p,
                 int& td,
-                const label patchI
+                const label patchI,
+                const scalar trackFraction,
+                const tetIndices& tetIs
             );
 
             //- Overridable function to handle the particle hitting a
@@ -208,7 +214,8 @@ public:
             void hitWallPatch
             (
                 const wallPolyPatch&,
-                solidParticle::trackData& td
+                solidParticle::trackData& td,
+                const tetIndices&
             );
 
             //- Overridable function to handle the particle hitting a wallPatch
@@ -216,7 +223,8 @@ public:
             void hitWallPatch
             (
                 const wallPolyPatch&,
-                int&
+                int&,
+                const tetIndices&
             );
 
             //- Overridable function to handle the particle hitting a polyPatch
diff --git a/src/lagrangian/solidParticle/solidParticleCloud.H b/src/lagrangian/solidParticle/solidParticleCloud.H
index b4e87423b33..7a1761593b2 100644
--- a/src/lagrangian/solidParticle/solidParticleCloud.H
+++ b/src/lagrangian/solidParticle/solidParticleCloud.H
@@ -94,6 +94,8 @@ public:
 
         // Access
 
+            inline bool hasWallImpactDistance() const;
+
             inline const fvMesh& mesh() const;
 
             inline scalar rhop() const;
diff --git a/src/lagrangian/solidParticle/solidParticleCloudI.H b/src/lagrangian/solidParticle/solidParticleCloudI.H
index db8b817901a..3b830c0887c 100644
--- a/src/lagrangian/solidParticle/solidParticleCloudI.H
+++ b/src/lagrangian/solidParticle/solidParticleCloudI.H
@@ -25,6 +25,12 @@ License
 
 // * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
 
+inline bool Foam::solidParticleCloud::hasWallImpactDistance() const
+{
+    return true;
+}
+
+
 inline const Foam::fvMesh& Foam::solidParticleCloud::mesh() const
 {
     return mesh_;
diff --git a/src/lagrangian/solidParticle/solidParticleI.H b/src/lagrangian/solidParticle/solidParticleI.H
index a0af6bfad53..5bc146941a1 100644
--- a/src/lagrangian/solidParticle/solidParticleI.H
+++ b/src/lagrangian/solidParticle/solidParticleI.H
@@ -46,12 +46,14 @@ inline Foam::solidParticle::solidParticle
 (
     const Cloud<solidParticle>& c,
     const vector& position,
-    const label celli,
+    const label cellI,
+    const label tetFaceI,
+    const label tetPtI,
     const scalar d,
     const vector& U
 )
 :
-    Particle<solidParticle>(c, position, celli),
+    Particle<solidParticle>(c, position, cellI, tetFaceI, tetPtI),
     d_(d),
     U_(U)
 {}
@@ -64,18 +66,21 @@ inline Foam::solidParticleCloud& Foam::solidParticle::trackData::spc()
     return spc_;
 }
 
+
 inline const Foam::interpolationCellPoint<Foam::scalar>&
 Foam::solidParticle::trackData::rhoInterp() const
 {
     return rhoInterp_;
 }
 
+
 inline const Foam::interpolationCellPoint<Foam::vector>&
 Foam::solidParticle::trackData::UInterp() const
 {
     return UInterp_;
 }
 
+
 inline const Foam::interpolationCellPoint<Foam::scalar>&
 Foam::solidParticle::trackData::nuInterp() const
 {
@@ -93,11 +98,13 @@ inline Foam::scalar Foam::solidParticle::d() const
     return d_;
 }
 
+
 inline Foam::scalar Foam::solidParticle::wallImpactDistance(const vector&) const
 {
     return 0.5*d_;
 }
 
+
 inline const Foam::vector& Foam::solidParticle::U() const
 {
     return U_;
diff --git a/src/mesh/autoMesh/autoHexMesh/meshRefinement/meshRefinementRefine.C b/src/mesh/autoMesh/autoHexMesh/meshRefinement/meshRefinementRefine.C
index 1c867ba760f..bda9fc6e8e9 100644
--- a/src/mesh/autoMesh/autoHexMesh/meshRefinement/meshRefinementRefine.C
+++ b/src/mesh/autoMesh/autoHexMesh/meshRefinement/meshRefinementRefine.C
@@ -279,7 +279,11 @@ Foam::label Foam::meshRefinement::markFeatureRefinement
     Cloud<trackedParticle> cloud(mesh_, IDLList<trackedParticle>());
 
     // Create particles on whichever processor holds the keepPoint.
-    label cellI = mesh_.findCell(keepPoint);
+    label cellI = -1;
+    label tetFaceI = -1;
+    label tetPtI = -1;
+
+    cloud.findCellFacePt(keepPoint, cellI, tetFaceI, tetPtI);
 
     if (cellI != -1)
     {
@@ -308,6 +312,8 @@ Foam::label Foam::meshRefinement::markFeatureRefinement
                             cloud,
                             keepPoint,
                             cellI,
+                            tetFaceI,
+                            tetPtI,
                             featureMesh.points()[pointI],   // endpos
                             featureLevels[featI],           // level
                             featI,                          // featureMesh
diff --git a/src/mesh/autoMesh/autoHexMesh/trackedParticle/ExactParticle.C b/src/mesh/autoMesh/autoHexMesh/trackedParticle/ExactParticle.C
deleted file mode 100644
index 8efba5dc0cc..00000000000
--- a/src/mesh/autoMesh/autoHexMesh/trackedParticle/ExactParticle.C
+++ /dev/null
@@ -1,261 +0,0 @@
-/*---------------------------------------------------------------------------*\
-  =========                 |
-  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
-   \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 1991-2010 OpenCFD Ltd.
-     \\/     M anipulation  |
--------------------------------------------------------------------------------
-License
-    This file is part of OpenFOAM.
-
-    OpenFOAM is free software: you can redistribute it and/or modify it
-    under the terms of the GNU General Public License as published by
-    the Free Software Foundation, either version 3 of the License, or
-    (at your option) any later version.
-
-    OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
-    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
-    for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
-
-\*---------------------------------------------------------------------------*/
-
-#include "ExactParticle.H"
-
-// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
-
-template<class ParticleType>
-template<class TrackingData>
-Foam::label Foam::ExactParticle<ParticleType>::track
-(
-    const vector& endPosition,
-    TrackingData& td
-)
-{
-    this->facei_ = -1;
-
-    // Tracks to endPosition or stop on boundary
-    while (!this->onBoundary() && this->stepFraction_ < 1.0 - SMALL)
-    {
-        this->stepFraction_ +=
-            trackToFace(endPosition, td)
-           *(1.0 - this->stepFraction_);
-    }
-
-    return this->facei_;
-}
-
-
-template<class ParticleType>
-Foam::label Foam::ExactParticle<ParticleType>::track
-(
-    const vector& endPosition
-)
-{
-    int dummyTd;
-    return track(endPosition, dummyTd);
-}
-
-
-template<class ParticleType>
-template<class TrackingData>
-Foam::scalar Foam::ExactParticle<ParticleType>::trackToFace
-(
-    const vector& endPosition,
-    TrackingData& td
-)
-{
-    const polyMesh& mesh = this->cloud().pMesh();
-    const labelList& cFaces = mesh.cells()[this->celli_];
-
-    point intersection(vector::zero);
-    scalar trackFraction = VGREAT;
-    label hitFacei = -1;
-
-    const vector vec = endPosition-this->position_;
-
-    forAll(cFaces, i)
-    {
-        label facei = cFaces[i];
-
-        if (facei != this->face())
-        {
-            pointHit inter = mesh.faces()[facei].intersection
-            (
-                this->position_,
-                vec,
-                mesh.faceCentres()[facei],
-                mesh.points(),
-                intersection::HALF_RAY
-            );
-
-            if (inter.hit() && inter.distance() < trackFraction)
-            {
-                trackFraction = inter.distance();
-                hitFacei = facei;
-                intersection = inter.hitPoint();
-            }
-        }
-    }
-
-    if (hitFacei == -1)
-    {
-        // Did not find any intersection. Fall back to original approximate
-        // algorithm
-        return Particle<ParticleType>::trackToFace
-        (
-            endPosition,
-            td
-        );
-    }
-
-    if (trackFraction >= (1.0-SMALL))
-    {
-        // Nearest intersection beyond endPosition so we hit endPosition.
-        trackFraction = 1.0;
-        this->position_ = endPosition;
-        this->facei_ = -1;
-        return 1.0;
-    }
-    else
-    {
-        this->position_ = intersection;
-        this->facei_ = hitFacei;
-    }
-
-
-    // Normal situation (trackFraction 0..1). Straight copy
-    // of Particle::trackToFace.
-
-    bool internalFace = this->cloud().internalFace(this->facei_);
-
-    // change cell
-    if (internalFace) // Internal face
-    {
-        if (this->celli_ == mesh.faceOwner()[this->facei_])
-        {
-            this->celli_ = mesh.faceNeighbour()[this->facei_];
-        }
-        else if (this->celli_ == mesh.faceNeighbour()[this->facei_])
-        {
-            this->celli_ = mesh.faceOwner()[this->facei_];
-        }
-        else
-        {
-            FatalErrorIn
-            (
-                "ExactParticle::trackToFace"
-                "(const vector&, TrackingData&)"
-            )<< "addressing failure" << nl
-             << abort(FatalError);
-        }
-    }
-    else
-    {
-        ParticleType& p = static_cast<ParticleType&>(*this);
-
-        // Soft-sphere algorithm ignores the boundary
-        if (p.softImpact())
-        {
-            trackFraction = 1.0;
-            this->position_ = endPosition;
-        }
-
-        label patchi = patch(this->facei_);
-        const polyPatch& patch = mesh.boundaryMesh()[patchi];
-
-        if (isA<wedgePolyPatch>(patch))
-        {
-            p.hitWedgePatch
-            (
-                static_cast<const wedgePolyPatch&>(patch), td
-            );
-        }
-        else if (isA<symmetryPolyPatch>(patch))
-        {
-            p.hitSymmetryPatch
-            (
-                static_cast<const symmetryPolyPatch&>(patch), td
-            );
-        }
-        else if (isA<cyclicPolyPatch>(patch))
-        {
-            p.hitCyclicPatch
-            (
-                static_cast<const cyclicPolyPatch&>(patch), td
-            );
-        }
-        else if (isA<processorPolyPatch>(patch))
-        {
-            p.hitProcessorPatch
-            (
-                static_cast<const processorPolyPatch&>(patch), td
-            );
-        }
-        else if (isA<wallPolyPatch>(patch))
-        {
-            p.hitWallPatch
-            (
-                static_cast<const wallPolyPatch&>(patch), td
-            );
-        }
-        else if (isA<polyPatch>(patch))
-        {
-            p.hitPatch
-            (
-                static_cast<const polyPatch&>(patch), td
-            );
-        }
-        else
-        {
-            FatalErrorIn
-            (
-                "ExactParticle::trackToFace"
-                "(const vector& endPosition, scalar& trackFraction)"
-            )<< "patch type " << patch.type() << " not suported" << nl
-             << abort(FatalError);
-        }
-    }
-
-    // If the trackFraction = 0 something went wrong.
-    // Either the particle is flipping back and forth across a face perhaps
-    // due to velocity interpolation errors or it is in a "hole" in the mesh
-    // caused by face warpage.
-    // In both cases resolve the positional ambiguity by moving the particle
-    // slightly towards the cell-centre.
-    if (trackFraction < SMALL)
-    {
-        this->position_ +=
-            1.0e-6*(mesh.cellCentres()[this->celli_] - this->position_);
-    }
-
-    return trackFraction;
-}
-
-
-template<class ParticleType>
-Foam::scalar Foam::ExactParticle<ParticleType>::trackToFace
-(
-    const vector& endPosition
-)
-{
-    int dummyTd;
-    return trackToFace(endPosition, dummyTd);
-}
-
-
-template<class ParticleType>
-Foam::Ostream& Foam::operator<<
-(
-    Ostream& os,
-    const ExactParticle<ParticleType>& p
-)
-{
-    return operator<<(os, static_cast<const Particle<ParticleType>&>(p));
-}
-
-
-// ************************************************************************* //
diff --git a/src/mesh/autoMesh/autoHexMesh/trackedParticle/ExactParticle.H b/src/mesh/autoMesh/autoHexMesh/trackedParticle/ExactParticle.H
deleted file mode 100644
index 5965815d02f..00000000000
--- a/src/mesh/autoMesh/autoHexMesh/trackedParticle/ExactParticle.H
+++ /dev/null
@@ -1,191 +0,0 @@
-/*---------------------------------------------------------------------------*\
-  =========                 |
-  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
-   \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 1991-2010 OpenCFD Ltd.
-     \\/     M anipulation  |
--------------------------------------------------------------------------------
-License
-    This file is part of OpenFOAM.
-
-    OpenFOAM is free software: you can redistribute it and/or modify it
-    under the terms of the GNU General Public License as published by
-    the Free Software Foundation, either version 3 of the License, or
-    (at your option) any later version.
-
-    OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
-    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
-    for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
-
-Class
-    Foam::ExactParticle
-
-Description
-    Special version of Particle to do tracking on non-convex cells.
-
-\*---------------------------------------------------------------------------*/
-
-#ifndef ExactParticle_H
-#define ExactParticle_H
-
-#include "face.H"
-#include "Particle.H"
-
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
-
-namespace Foam
-{
-
-template<class ExactParticle>
-class Cloud;
-
-// Forward declaration of friend functions and operators
-
-template<class ParticleType>
-class ExactParticle;
-
-template<class ParticleType>
-Ostream& operator<<
-(
-    Ostream&,
-    const ExactParticle<ParticleType>&
-);
-
-
-/*---------------------------------------------------------------------------*\
-                           Class ExactParticle Declaration
-\*---------------------------------------------------------------------------*/
-
-template<class ParticleType>
-class ExactParticle
-:
-    public Particle<ParticleType>
-{
-
-public:
-
-    friend class Cloud<ParticleType>;
-
-
-    // Constructors
-
-        //- Construct from components
-        ExactParticle
-        (
-            const Cloud<ParticleType>& cloud,
-            const vector& position,
-            const label celli
-        )
-        :
-            Particle<ParticleType>(cloud, position, celli)
-        {}
-
-
-        //- Construct from Istream
-        ExactParticle
-        (
-            const Cloud<ParticleType>& cloud,
-            Istream& is,
-            bool readFields = true
-        )
-        :
-            Particle<ParticleType>(cloud, is, readFields)
-        {}
-
-
-        //- Factory class to read-construct particles used for parallel transfer
-        class iNew
-        {
-
-            // Private data
-
-            const Cloud<ParticleType>& cloud_;
-
-
-        public:
-
-            iNew(const Cloud<ParticleType>& cloud)
-            :
-                cloud_(cloud)
-            {}
-
-            autoPtr<ParticleType> operator()(Istream& is) const
-            {
-                return autoPtr<ParticleType>
-                (
-                    new ParticleType(cloud_, is)
-                );
-            }
-        };
-
-
-    //- Destructor
-    virtual ~ExactParticle()
-    {}
-
-
-    // Member Functions
-
-            //- Track particle to end of trajectory
-            //  or until it hits the boundary.
-            //  On entry 'stepFraction()' should be set to the fraction of the
-            //  time-step at which the tracking starts and on exit it contains
-            //  the fraction of the time-step completed.
-            //  Returns the boundary face index if the track stops at the
-            //  boundary, -1 otherwise.
-            template<class TrackingData>
-            label track
-            (
-                const vector& endPosition,
-                TrackingData& td
-            );
-
-            //- Calls the templated track with dummy TrackingData
-            label track(const vector& endPosition);
-
-            //- Track particle to a given position and returns 1.0 if the
-            //  trajectory is completed without hitting a face otherwise
-            //  stops at the face and returns the fraction of the trajectory
-            //  completed.
-            //  on entry 'stepFraction()' should be set to the fraction of the
-            //  time-step at which the tracking starts.
-            template<class TrackingData>
-            scalar trackToFace
-            (
-                const vector& endPosition,
-                TrackingData& td
-            );
-
-            //- Calls the templated trackToFace with dummy TrackingData
-            scalar trackToFace(const vector& endPosition);
-
-
-    // Ostream Operator
-
-        friend Ostream& operator<< <ParticleType>
-        (
-            Ostream&,
-            const ExactParticle<ParticleType>&
-        );
-};
-
-
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
-
-} // End namespace Foam
-
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
-
-#ifdef NoRepository
-#   include "ExactParticle.C"
-#endif
-
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
-
-#endif
-
-// ************************************************************************* //
diff --git a/src/mesh/autoMesh/autoHexMesh/trackedParticle/trackedParticle.C b/src/mesh/autoMesh/autoHexMesh/trackedParticle/trackedParticle.C
index ad4a5079d57..5714b3062ee 100644
--- a/src/mesh/autoMesh/autoHexMesh/trackedParticle/trackedParticle.C
+++ b/src/mesh/autoMesh/autoHexMesh/trackedParticle/trackedParticle.C
@@ -32,14 +32,16 @@ Foam::trackedParticle::trackedParticle
 (
     const Cloud<trackedParticle>& c,
     const vector& position,
-    const label celli,
+    const label cellI,
+    const label tetFaceI,
+    const label tetPtI,
     const point& end,
     const label level,
     const label i,
     const label j
 )
 :
-    ExactParticle<trackedParticle>(c, position, celli),
+    Particle<trackedParticle>(c, position, cellI, tetFaceI, tetPtI),
     end_(end),
     level_(level),
     i_(i),
@@ -55,7 +57,7 @@ Foam::trackedParticle::trackedParticle
     bool readFields
 )
 :
-    ExactParticle<trackedParticle>(c, is, readFields)
+    Particle<trackedParticle>(c, is, readFields)
 {
     if (readFields)
     {
@@ -118,7 +120,9 @@ bool Foam::trackedParticle::hitPatch
 (
     const polyPatch&,
     trackedParticle::trackData& td,
-    const label patchI
+    const label patchI,
+    const scalar trackFraction,
+    const tetIndices& tetIs
 )
 {
     return false;
@@ -129,7 +133,9 @@ bool Foam::trackedParticle::hitPatch
 (
     const polyPatch&,
     int&,
-    const label
+    const label,
+    const scalar trackFraction,
+    const tetIndices& tetIs
 )
 {
     return false;
@@ -215,7 +221,8 @@ void Foam::trackedParticle::hitProcessorPatch
 void Foam::trackedParticle::hitWallPatch
 (
     const wallPolyPatch& wpp,
-    trackedParticle::trackData& td
+    trackedParticle::trackData& td,
+    const tetIndices&
 )
 {
     // Remove particle
@@ -226,7 +233,8 @@ void Foam::trackedParticle::hitWallPatch
 void Foam::trackedParticle::hitWallPatch
 (
     const wallPolyPatch& wpp,
-    int&
+    int&,
+    const tetIndices&
 )
 {}
 
@@ -256,7 +264,7 @@ Foam::Ostream& Foam::operator<<(Ostream& os, const trackedParticle& p)
 {
     if (os.format() == IOstream::ASCII)
     {
-        os  << static_cast<const ExactParticle<trackedParticle>&>(p)
+        os  << static_cast<const Particle<trackedParticle>&>(p)
             << token::SPACE << p.end_
             << token::SPACE << p.level_
             << token::SPACE << p.i_
@@ -264,7 +272,7 @@ Foam::Ostream& Foam::operator<<(Ostream& os, const trackedParticle& p)
     }
     else
     {
-        os  << static_cast<const ExactParticle<trackedParticle>&>(p);
+        os  << static_cast<const Particle<trackedParticle>&>(p);
         os.write
         (
             reinterpret_cast<const char*>(&p.end_),
diff --git a/src/mesh/autoMesh/autoHexMesh/trackedParticle/trackedParticle.H b/src/mesh/autoMesh/autoHexMesh/trackedParticle/trackedParticle.H
index ef0c2695e4b..1f62e6f37a5 100644
--- a/src/mesh/autoMesh/autoHexMesh/trackedParticle/trackedParticle.H
+++ b/src/mesh/autoMesh/autoHexMesh/trackedParticle/trackedParticle.H
@@ -26,8 +26,7 @@ Class
 
 Description
     Particle class that marks cells it passes through. Used to mark cells
-    visited by feature edges. Uses ExactParticle tracking class so
-    will work on concave cells.
+    visited by feature edges.
 
 SourceFiles
     trackedParticle.C
@@ -37,7 +36,7 @@ SourceFiles
 #ifndef trackedParticle_H
 #define trackedParticle_H
 
-#include "ExactParticle.H"
+#include "Particle.H"
 #include "autoPtr.H"
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
@@ -53,7 +52,7 @@ class trackedParticleCloud;
 
 class trackedParticle
 :
-    public ExactParticle<trackedParticle>
+    public Particle<trackedParticle>
 {
     // Private data
 
@@ -118,7 +117,9 @@ public:
         (
             const Cloud<trackedParticle>& c,
             const vector& position,
-            const label celli,
+            const label cellI,
+            const label tetFaceI,
+            const label tetPtI,
             const point& end,
             const label level,
             const label i,
@@ -174,13 +175,17 @@ public:
             (
                 const polyPatch&,
                 trackedParticle::trackData& td,
-                const label patchI
+                const label patchI,
+                const scalar trackFraction,
+                const tetIndices& tetIs
             );
             bool hitPatch
             (
                 const polyPatch&,
                 int&,
-                const label patchI
+                const label patchI,
+                const scalar trackFraction,
+                const tetIndices& tetIs
            );
 
             //- Overridable function to handle the particle hitting a wedge
@@ -237,12 +242,14 @@ public:
             void hitWallPatch
             (
                 const wallPolyPatch&,
-                trackedParticle::trackData& td
+                trackedParticle::trackData& td,
+                const tetIndices&
             );
             void hitWallPatch
             (
                 const wallPolyPatch&,
-                int&
+                int&,
+                const tetIndices&
             );
 
             //- Overridable function to handle the particle hitting a polyPatch
diff --git a/src/meshTools/Make/options b/src/meshTools/Make/options
index ef1033b0e8e..45765759c16 100644
--- a/src/meshTools/Make/options
+++ b/src/meshTools/Make/options
@@ -1,7 +1,5 @@
 EXE_INC = \
-    -I$(LIB_SRC)/triSurface/lnInclude \
-    -I$(LIB_SRC)/lagrangian/basic/lnInclude
+    -I$(LIB_SRC)/triSurface/lnInclude
 
 LIB_LIBS = \
-    -ltriSurface \
-    -llagrangian
+    -ltriSurface
diff --git a/src/meshTools/surfaceSets/surfaceSets.H b/src/meshTools/surfaceSets/surfaceSets.H
index 3fd75716fac..faeb38aa4d7 100644
--- a/src/meshTools/surfaceSets/surfaceSets.H
+++ b/src/meshTools/surfaceSets/surfaceSets.H
@@ -102,9 +102,8 @@ class surfaceSets
         //);
 
         ////- Select all points out of pointSet where the distance to
-        ////  the surface is less
-        ////  than a factor times a local length scale (minimum length of
-        ////  connected edges)
+        ////  the surface is less than a factor times a local length
+        ////  scale (minimum length of connected edges)
         //static void getNearPoints
         //(
         //    const primitiveMesh& mesh,
diff --git a/src/meshTools/triSurface/booleanOps/surfaceIntersection/surfaceIntersection.C b/src/meshTools/triSurface/booleanOps/surfaceIntersection/surfaceIntersection.C
index 9e164f73feb..6602fbcf430 100644
--- a/src/meshTools/triSurface/booleanOps/surfaceIntersection/surfaceIntersection.C
+++ b/src/meshTools/triSurface/booleanOps/surfaceIntersection/surfaceIntersection.C
@@ -30,8 +30,6 @@ License
 #include "HashSet.H"
 #include "triSurface.H"
 #include "pointIndexHit.H"
-#include "octreeDataTriSurface.H"
-#include "octree.H"
 #include "mergePoints.H"
 #include "plane.H"
 #include "edgeIntersections.H"
diff --git a/src/meshTools/triSurface/booleanOps/surfaceIntersection/surfaceIntersectionFuncs.C b/src/meshTools/triSurface/booleanOps/surfaceIntersection/surfaceIntersectionFuncs.C
index ec9f3705539..18ebd6988c0 100644
--- a/src/meshTools/triSurface/booleanOps/surfaceIntersection/surfaceIntersectionFuncs.C
+++ b/src/meshTools/triSurface/booleanOps/surfaceIntersection/surfaceIntersectionFuncs.C
@@ -30,8 +30,6 @@ License
 #include "HashSet.H"
 #include "triSurface.H"
 #include "pointIndexHit.H"
-#include "octreeDataTriSurface.H"
-#include "octree.H"
 #include "meshTools.H"
 
 // * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
diff --git a/src/meshTools/triSurface/surfaceFeatures/surfaceFeatures.C b/src/meshTools/triSurface/surfaceFeatures/surfaceFeatures.C
index acfb6749144..3cbe1157a98 100644
--- a/src/meshTools/triSurface/surfaceFeatures/surfaceFeatures.C
+++ b/src/meshTools/triSurface/surfaceFeatures/surfaceFeatures.C
@@ -25,9 +25,9 @@ License
 
 #include "surfaceFeatures.H"
 #include "triSurface.H"
-#include "octree.H"
-#include "octreeDataEdges.H"
-#include "octreeDataPoint.H"
+#include "indexedOctree.H"
+#include "treeDataEdge.H"
+#include "treeDataPoint.H"
 #include "meshTools.H"
 #include "linePointRef.H"
 #include "OFstream.H"
@@ -752,21 +752,21 @@ Foam::Map<Foam::label> Foam::surfaceFeatures::nearestSamples
 (
     const labelList& pointLabels,
     const pointField& samples,
-    const scalarField& maxDist
+    const scalarField& maxDistSqr
 ) const
 {
     // Build tree out of all samples.
 
-    //Note: cannot be done one the fly - gcc4.4 compiler bug.
+    // Note: cannot be done one the fly - gcc4.4 compiler bug.
     treeBoundBox bb(samples);
 
-    octree<octreeDataPoint> ppTree
+    indexedOctree<treeDataPoint> ppTree
     (
-        bb,                         // overall search domain
-        octreeDataPoint(samples),   // all information needed to do checks
-        1,          // min levels
-        20.0,       // maximum ratio of cubes v.s. cells
-        100.0       // max. duplicity; n/a since no bounding boxes.
+        treeDataPoint(samples),   // all information needed to do checks
+        bb,                       // overall search domain
+        8,      // maxLevel
+        10,     // leafsize
+        3.0     // duplicity
     );
 
     // From patch point to surface point
@@ -780,31 +780,23 @@ Foam::Map<Foam::label> Foam::surfaceFeatures::nearestSamples
 
         const point& surfPt = surfPoints[surfPointI];
 
-        point maxDistPt(maxDist[i], maxDist[i], maxDist[i]);
-
-        treeBoundBox tightest(surfPt - maxDistPt, surfPt + maxDistPt);
-        scalar tightestDist = Foam::GREAT;
-
-        label sampleI = ppTree.findNearest
+        pointIndexHit info = ppTree.findNearest
         (
             surfPt,
-            tightest,
-            tightestDist
+            maxDistSqr[i]
         );
 
-        if (sampleI == -1)
+        if (!info.hit())
         {
             FatalErrorIn("surfaceFeatures::nearestSamples")
                 << "Problem for point "
-                << surfPointI << " in tree " << ppTree.octreeBb()
+                << surfPointI << " in tree " << ppTree.bb()
                 << abort(FatalError);
         }
 
-        if
-        (
-            magSqr(samples[sampleI] - surfPt)
-          < Foam::sqr(maxDist[sampleI])
-        )
+        label sampleI = info.index();
+
+        if (magSqr(samples[sampleI] - surfPt) < maxDistSqr[sampleI])
         {
             nearest.insert(sampleI, surfPointI);
         }
@@ -845,26 +837,25 @@ Foam::Map<Foam::label> Foam::surfaceFeatures::nearestSamples
     const labelList& selectedEdges,
     const pointField& samples,
     const scalarField& sampleDist,
-    const scalarField& maxDist,
+    const scalarField& maxDistSqr,
     const scalar minSampleDist
 ) const
 {
     const pointField& surfPoints = surf_.localPoints();
     const edgeList& surfEdges = surf_.edges();
 
-    scalar maxSearch = max(maxDist);
-    vector span(maxSearch, maxSearch, maxSearch);
+    scalar maxSearchSqr = max(maxDistSqr);
 
     //Note: cannot be done one the fly - gcc4.4 compiler bug.
     treeBoundBox bb(samples);
 
-    octree<octreeDataPoint> ppTree
+    indexedOctree<treeDataPoint> ppTree
     (
+        treeDataPoint(samples),   // all information needed to do checks
         bb,                         // overall search domain
-        octreeDataPoint(samples),   // all information needed to do checks
-        1,          // min levels
-        20.0,       // maximum ratio of cubes v.s. cells
-        100.0       // max. duplicity; n/a since no bounding boxes.
+        8,      // maxLevel
+        10,     // leafsize
+        3.0     // duplicity
     );
 
     // From patch point to surface edge.
@@ -902,22 +893,21 @@ Foam::Map<Foam::label> Foam::surfaceFeatures::nearestSamples
         {
             point edgePoint(surfPoints[e.start()] + s*eVec);
 
-            treeBoundBox tightest(edgePoint - span, edgePoint + span);
-            scalar tightestDist = Foam::GREAT;
-
-            label sampleI = ppTree.findNearest
+            pointIndexHit info = ppTree.findNearest
             (
                 edgePoint,
-                tightest,
-                tightestDist
+                maxSearchSqr
             );
 
-            if (sampleI == -1)
+            if (!info.hit())
             {
                 // No point close enough to surface edge.
                 break;
             }
-            if (tightestDist < maxDist[sampleI])
+
+            label sampleI = info.index();
+
+            if (magSqr(info.hitPoint() - edgePoint) < maxDistSqr[sampleI])
             {
                 nearest.insert(sampleI, surfEdgeI);
             }
@@ -985,31 +975,30 @@ Foam::Map<Foam::pointIndexHit> Foam::surfaceFeatures::nearestEdges
     const labelList& selectedSampleEdges,
     const pointField& samplePoints,
     const scalarField& sampleDist,
-    const scalarField& maxDist,
+    const scalarField& maxDistSqr,
     const scalar minSampleDist
 ) const
 {
     // Build tree out of selected sample edges.
-    octree<octreeDataEdges> ppTree
+    indexedOctree<treeDataEdge> ppTree
     (
-        treeBoundBox(samplePoints), // overall search domain
-        octreeDataEdges
+        treeDataEdge
         (
+            false,
             sampleEdges,
             samplePoints,
             selectedSampleEdges
         ),                          // geometric info container for edges
-        1,                          // min levels
-        20.0,                       // maximum ratio of cubes v.s. cells
-        10.0                        // max. duplicity
+        treeBoundBox(samplePoints), // overall search domain
+        8,      // maxLevel
+        10,     // leafsize
+        3.0     // duplicity
     );
 
     const pointField& surfPoints = surf_.localPoints();
     const edgeList& surfEdges = surf_.edges();
 
-    scalar maxSearch = max(maxDist);
-    vector span(maxSearch, maxSearch, maxSearch);
-
+    scalar maxSearchSqr = max(maxDistSqr);
 
     Map<pointIndexHit> nearest(2*sampleEdges.size());
 
@@ -1050,27 +1039,25 @@ Foam::Map<Foam::pointIndexHit> Foam::surfaceFeatures::nearestEdges
         {
             point edgePoint(surfPoints[e.start()] + s*eVec);
 
-            treeBoundBox tightest(edgePoint - span, edgePoint + span);
-            scalar tightestDist = Foam::GREAT;
-
-            label index = ppTree.findNearest
+            pointIndexHit info = ppTree.findNearest
             (
                 edgePoint,
-                tightest,
-                tightestDist
+                maxSearchSqr
             );
 
-            if (index == -1)
+            if (!info.hit())
             {
                 // No edge close enough to surface edge.
                 break;
             }
 
+            label index = info.index();
+
             label sampleEdgeI = ppTree.shapes().edgeLabels()[index];
 
             const edge& e = sampleEdges[sampleEdgeI];
 
-            if (tightestDist < maxDist[e.start()])
+            if (magSqr(info.hitPoint() - edgePoint) < maxDistSqr[e.start()])
             {
                 nearest.insert
                 (
@@ -1136,7 +1123,7 @@ void Foam::surfaceFeatures::nearestSurfEdge
 (
     const labelList& selectedEdges,
     const pointField& samples,
-    const vector& searchSpan,   // Search span
+    scalar searchSpanSqr,   // Search span
     labelList& edgeLabel,
     labelList& edgeEndPoint,
     pointField& edgePoint
@@ -1148,57 +1135,48 @@ void Foam::surfaceFeatures::nearestSurfEdge
 
     const pointField& localPoints = surf_.localPoints();
 
-    octree<octreeDataEdges> ppTree
+    indexedOctree<treeDataEdge> ppTree
     (
-        treeBoundBox(localPoints),  // overall search domain
-        octreeDataEdges
+        treeDataEdge
         (
+            false,
             surf_.edges(),
             localPoints,
             selectedEdges
         ),          // all information needed to do geometric checks
-        1,          // min levels
-        20.0,       // maximum ratio of cubes v.s. cells
-        10.0        // max. duplicity
+        treeBoundBox(localPoints),  // overall search domain
+        8,      // maxLevel
+        10,     // leafsize
+        3.0     // duplicity
     );
 
-
     forAll(samples, i)
     {
         const point& sample = samples[i];
 
-        treeBoundBox tightest(sample - searchSpan, sample + searchSpan);
-
-        scalar tightestDist = magSqr(searchSpan);
-
-        label index =
-            ppTree.findNearest
-            (
-                sample,
-                tightest,
-                tightestDist
-            );
-
+        pointIndexHit info = ppTree.findNearest
+        (
+            sample,
+            searchSpanSqr
+        );
 
-        if (index == -1)
+        if (!info.hit())
         {
             edgeLabel[i] = -1;
         }
         else
         {
-            edgeLabel[i] = selectedEdges[index];
+            edgeLabel[i] = selectedEdges[info.index()];
 
-            // Unfortunately findNearest does not return nearest point so
-            // recalculate
+            // Need to recalculate to classify edgeEndPoint
             const edge& e = surf_.edges()[edgeLabel[i]];
 
-            pointIndexHit pHit =
-                edgeNearest
-                (
-                    localPoints[e.start()],
-                    localPoints[e.end()],
-                    sample
-                );
+            pointIndexHit pHit = edgeNearest
+            (
+                localPoints[e.start()],
+                localPoints[e.end()],
+                sample
+            );
 
             edgePoint[i] = pHit.rawPoint();
             edgeEndPoint[i] = pHit.index();
@@ -1226,22 +1204,21 @@ void Foam::surfaceFeatures::nearestSurfEdge
     pointOnEdge.setSize(selectedSampleEdges.size());
     pointOnFeature.setSize(selectedSampleEdges.size());
 
-
-    octree<octreeDataEdges> ppTree
+    indexedOctree<treeDataEdge> ppTree
     (
-        treeBoundBox(surf_.localPoints()),  // overall search domain
-        octreeDataEdges
+        treeDataEdge
         (
+            false,
             surf_.edges(),
             surf_.localPoints(),
             selectedEdges
         ),          // all information needed to do geometric checks
-        1,          // min levels
-        10.0,       // maximum ratio of cubes v.s. cells
-        10.0        // max. duplicity
+        treeBoundBox(surf_.localPoints()),  // overall search domain
+        8,      // maxLevel
+        10,     // leafsize
+        3.0     // duplicity
     );
 
-
     forAll(selectedSampleEdges, i)
     {
         const edge& e = sampleEdges[selectedSampleEdges[i]];
@@ -1252,23 +1229,22 @@ void Foam::surfaceFeatures::nearestSurfEdge
 
         treeBoundBox tightest(eMid - searchSpan, eMid + searchSpan);
 
-        label index =
-            ppTree.findNearest
-            (
-                edgeLine,
-                tightest,
-                pointOnEdge[i],
-                pointOnFeature[i]
-            );
-
+        pointIndexHit info = ppTree.findNearest
+        (
+            edgeLine,
+            tightest,
+            pointOnEdge[i]
+        );
 
-        if (index == -1)
+        if (!info.hit())
         {
             edgeLabel[i] = -1;
         }
         else
         {
-            edgeLabel[i] = featureEdges_[index];
+            edgeLabel[i] = selectedEdges[info.index()];
+
+            pointOnFeature[i] = info.hitPoint();
         }
     }
 }
diff --git a/src/meshTools/triSurface/surfaceFeatures/surfaceFeatures.H b/src/meshTools/triSurface/surfaceFeatures/surfaceFeatures.H
index 60e9c470859..a12fc0f7615 100644
--- a/src/meshTools/triSurface/surfaceFeatures/surfaceFeatures.H
+++ b/src/meshTools/triSurface/surfaceFeatures/surfaceFeatures.H
@@ -268,28 +268,30 @@ public:
 
         // Find
 
-            //- Find nearest sample for selected surface points (usually the
-            //  set of featurePoints). Return map from index in
-            //  samples to surface point. Do not include points that are further
-            //  than maxDist away (separate maxDist for every sample)
+            //- Find nearest sample for selected surface points
+            //  (usually the set of featurePoints). Return map from
+            //  index in samples to surface point. Do not include
+            //  points that are further than maxDist away (separate
+            //  maxDist for every sample).  Supply maxDistSqr.
             Map<label> nearestSamples
             (
                 const labelList& selectedPoints,
                 const pointField& samples,
-                const scalarField& maxDist
+                const scalarField& maxDistSqr
             ) const;
 
-            //- Find nearest sample for regularly sampled points along the
-            //  selected (surface) edges. Return map from sample to edge.
-            //  maxDist is distance below which gets snapped.
-            //  Edge gets sampled at points sampleDist[sampleI] apart.
-            //  (with a maximum of 10 samples per edge)
+            //- Find nearest sample for regularly sampled points along
+            //  the selected (surface) edges. Return map from sample
+            //  to edge.  maxDistSqr is distance squared below which
+            //  gets snapped.  Edge gets sampled at points
+            //  sampleDist[sampleI] apart.  (with a maximum of 10
+            //  samples per edge)
             Map<label> nearestSamples
             (
                 const labelList& selectedEdges,
                 const pointField& samples,
                 const scalarField& sampleDist,
-                const scalarField& maxDist,
+                const scalarField& maxDistSqr,
                 const scalar minSampleDist = 0.1
             ) const;
 
@@ -303,7 +305,7 @@ public:
                 const labelList& selectedSampleEdges,
                 const pointField& samplePoints,
                 const scalarField& sampleDist,
-                const scalarField& maxDist,
+                const scalarField& maxDistSqr,
                 const scalar minSampleDist = 0.1
             ) const;
 
@@ -319,7 +321,7 @@ public:
             (
                 const labelList& selectedEdges,
                 const pointField& samples,
-                const vector& searchSpan,   // search span
+                scalar searchSpanSqr,   // search span
                 labelList& edgeLabel,
                 labelList& edgeEndPoint,
                 pointField& edgePoint
diff --git a/src/parallel/reconstruct/reconstruct/Make/options b/src/parallel/reconstruct/reconstruct/Make/options
index 71546225cf5..7a728f9dd7c 100644
--- a/src/parallel/reconstruct/reconstruct/Make/options
+++ b/src/parallel/reconstruct/reconstruct/Make/options
@@ -1,7 +1,9 @@
 EXE_INC = \
     -I$(LIB_SRC)/finiteVolume/lnInclude \
+    -I$(LIB_SRC)/meshTools/lnInclude \
     -I$(LIB_SRC)/lagrangian/basic/lnInclude
 
 LIB_LIBS = \
     -lfiniteVolume \
+    -lmeshTools \
     -llagrangian
diff --git a/src/parallel/reconstruct/reconstruct/reconstructLagrangianPositions.C b/src/parallel/reconstruct/reconstruct/reconstructLagrangianPositions.C
index d6e84638b99..bab5aa0df05 100644
--- a/src/parallel/reconstruct/reconstruct/reconstructLagrangianPositions.C
+++ b/src/parallel/reconstruct/reconstruct/reconstructLagrangianPositions.C
@@ -50,12 +50,19 @@ void Foam::reconstructLagrangianPositions
     {
         const labelList& cellMap = cellProcAddressing[i];
 
+        // faceProcAddressing not required currently.
+        // const labelList& faceMap = faceProcAddressing[i];
+
         Cloud<passiveParticle> lpi(meshes[i], cloudName, false);
 
         forAllConstIter(Cloud<passiveParticle>, lpi, iter)
         {
             const passiveParticle& ppi = iter();
 
+            // // Inverting sign if necessary and subtracting 1 from
+            // // faceProcAddressing
+            // label mappedTetFace = mag(faceMap[ppi.tetFace()]) - 1;
+
             lagrangianPositions.append
             (
                 new passiveParticle
diff --git a/src/postProcessing/functionObjects/field/streamLine/streamLine.C b/src/postProcessing/functionObjects/field/streamLine/streamLine.C
index ebe01f1e8b4..8336fd8f9a9 100644
--- a/src/postProcessing/functionObjects/field/streamLine/streamLine.C
+++ b/src/postProcessing/functionObjects/field/streamLine/streamLine.C
@@ -56,6 +56,7 @@ void Foam::streamLine::track()
     //Pout<< "Seeding particles." << endl;
 
     const sampledSet& seedPoints = sampledSetPtr_();
+
     forAll(seedPoints, i)
     {
         //Pout<< "Seeded particle at " << seedPoints[i]
@@ -73,10 +74,10 @@ void Foam::streamLine::track()
             )
         );
     }
+
     label nSeeds = returnReduce(particles.size(), sumOp<label>());
-    Info<< "Seeded " << nSeeds
-        << " particles." << endl;
 
+    Info<< "Seeded " << nSeeds << " particles." << endl;
 
     // Read or lookup fields
     PtrList<volScalarField> vsFlds;
@@ -84,7 +85,6 @@ void Foam::streamLine::track()
     PtrList<volVectorField> vvFlds;
     PtrList<interpolationCellPoint<vector> > vvInterp;
 
-
     label UIndex = -1;
 
     if (loadFromFiles_)
@@ -188,7 +188,6 @@ void Foam::streamLine::track()
         vectorNames_[i] = vvInterp[i].psi().name();
     }
 
-
     // Check that we know the index of U in the interpolators.
 
     if (UIndex == -1)
@@ -202,8 +201,6 @@ void Foam::streamLine::track()
             << exit(FatalError);
     }
 
-
-
     // Sampled data
     // ~~~~~~~~~~~~
 
@@ -490,7 +487,6 @@ void Foam::streamLine::write()
 
             mkDir(vtkPath);
 
-
             // Convert track positions
 
             PtrList<coordSet> tracks(allTracks_.size());
@@ -508,8 +504,6 @@ void Foam::streamLine::write()
                 tracks[trackI].transfer(allTracks_[trackI]);
             }
 
-
-
             // Convert scalar values
 
             if (allScalars_.size() > 0)
diff --git a/src/postProcessing/functionObjects/field/streamLine/streamLineParticle.C b/src/postProcessing/functionObjects/field/streamLine/streamLineParticle.C
index 01d092c5ada..78478222cb5 100644
--- a/src/postProcessing/functionObjects/field/streamLine/streamLineParticle.C
+++ b/src/postProcessing/functionObjects/field/streamLine/streamLineParticle.C
@@ -88,11 +88,11 @@ Foam::streamLineParticle::streamLineParticle
 (
     const Cloud<streamLineParticle>& c,
     const vector& position,
-    const label celli,
+    const label cellI,
     const label lifeTime
 )
 :
-    Particle<streamLineParticle>(c, position, celli),
+    Particle<streamLineParticle>(c, position, cellI),
     lifeTime_(lifeTime)
 {}
 
@@ -246,7 +246,9 @@ bool Foam::streamLineParticle::hitPatch
 (
     const polyPatch&,
     streamLineParticle::trackData& td,
-    const label patchI
+    const label patchI,
+    const scalar trackFraction,
+    const tetIndices& tetIs
 )
 {
     // Disable generic patch interaction
@@ -258,7 +260,9 @@ bool Foam::streamLineParticle::hitPatch
 (
     const polyPatch&,
     int&,
-    const label
+    const label,
+    const scalar,
+    const tetIndices&
 )
 {
     // Disable generic patch interaction
@@ -345,7 +349,8 @@ void Foam::streamLineParticle::hitProcessorPatch
 void Foam::streamLineParticle::hitWallPatch
 (
     const wallPolyPatch& wpp,
-    streamLineParticle::trackData& td
+    streamLineParticle::trackData& td,
+    const tetIndices&
 )
 {
     // Remove particle
@@ -356,7 +361,8 @@ void Foam::streamLineParticle::hitWallPatch
 void Foam::streamLineParticle::hitWallPatch
 (
     const wallPolyPatch& wpp,
-    int&
+    int&,
+    const tetIndices&
 )
 {}
 
diff --git a/src/postProcessing/functionObjects/field/streamLine/streamLineParticle.H b/src/postProcessing/functionObjects/field/streamLine/streamLineParticle.H
index eceb8a3f88e..d556e4791de 100644
--- a/src/postProcessing/functionObjects/field/streamLine/streamLineParticle.H
+++ b/src/postProcessing/functionObjects/field/streamLine/streamLineParticle.H
@@ -141,7 +141,7 @@ public:
         (
             const Cloud<streamLineParticle>& c,
             const vector& position,
-            const label celli,
+            const label cellI,
             const label lifeTime
         );
 
@@ -182,13 +182,18 @@ public:
             (
                 const polyPatch&,
                 streamLineParticle::trackData& td,
-                const label patchI
+                const label patchI,
+                const scalar trackFraction,
+                const tetIndices& tetIs
             );
+
             bool hitPatch
             (
                 const polyPatch&,
                 int&,
-                const label patchI
+                const label,
+                const scalar,
+                const tetIndices&
            );
 
             //- Overridable function to handle the particle hitting a wedge
@@ -245,12 +250,14 @@ public:
             void hitWallPatch
             (
                 const wallPolyPatch&,
-                streamLineParticle::trackData& td
+                streamLineParticle::trackData& td,
+                const tetIndices&
             );
             void hitWallPatch
             (
                 const wallPolyPatch&,
-                int&
+                int&,
+                const tetIndices&
             );
 
             //- Overridable function to handle the particle hitting a polyPatch
diff --git a/src/sampling/Make/options b/src/sampling/Make/options
index 6629b3652c0..0e2c6780d2b 100644
--- a/src/sampling/Make/options
+++ b/src/sampling/Make/options
@@ -9,4 +9,5 @@ LIB_LIBS = \
     -lfiniteVolume \
     -lmeshTools \
     -lsurfMesh \
-    -ltriSurface
+    -ltriSurface \
+    -llagrangian
diff --git a/src/sampling/meshToMeshInterpolation/meshToMesh/calculateMeshToMeshAddressing.C b/src/sampling/meshToMeshInterpolation/meshToMesh/calculateMeshToMeshAddressing.C
index 372ba4a1c63..0248c41d30e 100644
--- a/src/sampling/meshToMeshInterpolation/meshToMesh/calculateMeshToMeshAddressing.C
+++ b/src/sampling/meshToMeshInterpolation/meshToMesh/calculateMeshToMeshAddressing.C
@@ -31,9 +31,9 @@ Description
 #include "meshToMesh.H"
 #include "SubField.H"
 
-#include "octree.H"
-#include "octreeDataCell.H"
-#include "octreeDataFace.H"
+#include "indexedOctree.H"
+#include "treeDataCell.H"
+#include "treeDataFace.H"
 
 // * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
 
@@ -100,21 +100,18 @@ void Foam::meshToMesh::calcAddressing()
         Info<< "   typical dimension      :" << shiftedBb.typDim() << endl;
     }
 
-    // Wrap indices and mesh information into helper object
-    octreeDataCell shapes(fromMesh_);
-
-    octree<octreeDataCell> oc
+    indexedOctree<treeDataCell> oc
     (
-        shiftedBb,  // overall bounding box
-        shapes,     // all information needed to do checks on cells
-        1,          // min. levels
-        10.0,       // max. size of leaves
-        10.0        // maximum ratio of cubes v.s. cells
+        treeDataCell(false, fromMesh_),
+        shiftedBb,      // overall bounding box
+        8,              // maxLevel
+        10,             // leafsize
+        3.0             // duplicity
     );
 
     if (debug)
     {
-        oc.printStats(Info);
+        oc.print(Pout, false, 0);
     }
 
     cellAddresses
@@ -174,38 +171,29 @@ void Foam::meshToMesh::calcAddressing()
                     wallBb.max() + vector(typDim, typDim, typDim)
                 );
 
-                // Wrap data for octree into container
-                octreeDataFace shapes(fromPatch);
-
-                octree<octreeDataFace> oc
+                indexedOctree<treeDataFace> oc
                 (
+                    treeDataFace(false, fromPatch),
                     shiftedBb,  // overall search domain
-                    shapes,     // all information needed to do checks on cells
-                    1,          // min levels
-                    20.0,       // maximum ratio of cubes v.s. cells
-                    2.0
+                    8,          // maxLevel
+                    10,         // leafsize
+                    3.0         // duplicity
                 );
 
-
                 const vectorField::subField centresToBoundary =
                     toPatch.faceCentres();
 
                 boundaryAddressing_[patchi].setSize(toPatch.size());
 
-                treeBoundBox tightest;
-                scalar tightestDist;
+                scalar distSqr = sqr(GREAT);
 
                 forAll(toPatch, toi)
                 {
-                    tightest = wallBb;                  // starting search bb
-                    tightestDist = Foam::GREAT;        // starting max distance
-
                     boundaryAddressing_[patchi][toi] = oc.findNearest
                     (
                         centresToBoundary[toi],
-                        tightest,
-                        tightestDist
-                    );
+                        distSqr
+                    ).index();
                 }
             }
         }
@@ -225,7 +213,7 @@ void Foam::meshToMesh::cellAddresses
     const pointField& points,
     const fvMesh& fromMesh,
     const List<bool>& boundaryCell,
-    const octree<octreeDataCell>& oc
+    const indexedOctree<treeDataCell>& oc
 ) const
 {
     // the implemented search method is a simple neighbour array search.
@@ -290,7 +278,7 @@ void Foam::meshToMesh::cellAddresses
             // the octree search to find it.
             if (boundaryCell[curCell])
             {
-                cellAddressing_[toI] = oc.find(p);
+                cellAddressing_[toI] = oc.findInside(p);
             }
             else
             {
@@ -342,7 +330,7 @@ void Foam::meshToMesh::cellAddresses
                 if (!found)
                 {
                     // Still not found so us the octree
-                    cellAddressing_[toI] = oc.find(p);
+                    cellAddressing_[toI] = oc.findInside(p);
                 }
             }
         }
diff --git a/src/sampling/meshToMeshInterpolation/meshToMesh/meshToMesh.H b/src/sampling/meshToMeshInterpolation/meshToMesh/meshToMesh.H
index 1edd13866ce..a3e83a73e9b 100644
--- a/src/sampling/meshToMeshInterpolation/meshToMesh/meshToMesh.H
+++ b/src/sampling/meshToMeshInterpolation/meshToMesh/meshToMesh.H
@@ -50,9 +50,9 @@ namespace Foam
 {
 
 template<class Type>
-class octree;
+class indexedOctree;
 
-class octreeDataCell;
+class treeDataCell;
 
 /*---------------------------------------------------------------------------*\
                          Class meshToMesh Declaration
@@ -99,7 +99,7 @@ class meshToMesh
             const pointField& points,
             const fvMesh& fromMesh,
             const List<bool>& boundaryCell,
-            const octree<octreeDataCell>& oc
+            const indexedOctree<treeDataCell>& oc
         ) const;
 
         void calculateInverseDistanceWeights() const;
diff --git a/src/sampling/sampledSurface/sampledTriSurfaceMesh/sampledTriSurfaceMesh.C b/src/sampling/sampledSurface/sampledTriSurfaceMesh/sampledTriSurfaceMesh.C
index fc5c31911e8..b117a4b2916 100644
--- a/src/sampling/sampledSurface/sampledTriSurfaceMesh/sampledTriSurfaceMesh.C
+++ b/src/sampling/sampledSurface/sampledTriSurfaceMesh/sampledTriSurfaceMesh.C
@@ -24,7 +24,6 @@ License
 \*---------------------------------------------------------------------------*/
 
 #include "sampledTriSurfaceMesh.H"
-#include "treeDataPoint.H"
 #include "meshSearch.H"
 #include "Tuple2.H"
 #include "globalIndex.H"
diff --git a/tutorials/combustion/dieselFoam/aachenBomb/constant/sprayProperties b/tutorials/combustion/dieselFoam/aachenBomb/constant/sprayProperties
index de0933465ba..878439295f1 100644
--- a/tutorials/combustion/dieselFoam/aachenBomb/constant/sprayProperties
+++ b/tutorials/combustion/dieselFoam/aachenBomb/constant/sprayProperties
@@ -17,7 +17,7 @@ FoamFile
 
 interpolationSchemes
 {
-    U               cellPointFace;
+    U               cellPoint;
     rho             cell;
     p               cell;
     T               cell;
diff --git a/tutorials/heatTransfer/buoyantBoussinesqSimpleFoam/iglooWithFridges/system/snappyHexMeshDict b/tutorials/heatTransfer/buoyantBoussinesqSimpleFoam/iglooWithFridges/system/snappyHexMeshDict
index 75432f06b99..6f099592107 100644
--- a/tutorials/heatTransfer/buoyantBoussinesqSimpleFoam/iglooWithFridges/system/snappyHexMeshDict
+++ b/tutorials/heatTransfer/buoyantBoussinesqSimpleFoam/iglooWithFridges/system/snappyHexMeshDict
@@ -358,11 +358,14 @@ meshQualityControls
     //  Set to very negative number (e.g. -1E30) to disable.
     minVol 1e-13;
 
-    //- Minimum tet volume. Is absolute volume of the tet formed by the
-    //  face-centre decomposition triangle and the cell centre.
-    //  Set to a sensible fraction of the smallest cell volume expected.
-    //  Set to very negative number (e.g. -1E30) to disable.
-    minTetVol 1e-20;
+    //- Minimum quality of the tet formed by the face-centre
+    //  and variable base point minimum decomposition triangles and
+    //  the cell centre.  Set to very negative number (e.g. -1E30) to
+    //  disable.
+    //     <0 = inside out tet,
+    //      0 = flat tet
+    //      1 = regular tet
+    minTetQuality 1e-9;
 
     //- Minimum face area. Set to <0 to disable.
     minArea -1;
diff --git a/tutorials/heatTransfer/chtMultiRegionFoam/snappyMultiRegionHeater/system/snappyHexMeshDict b/tutorials/heatTransfer/chtMultiRegionFoam/snappyMultiRegionHeater/system/snappyHexMeshDict
index 1cc6ba8dbae..f86acc42264 100644
--- a/tutorials/heatTransfer/chtMultiRegionFoam/snappyMultiRegionHeater/system/snappyHexMeshDict
+++ b/tutorials/heatTransfer/chtMultiRegionFoam/snappyMultiRegionHeater/system/snappyHexMeshDict
@@ -340,11 +340,14 @@ meshQualityControls
     //  Set to very negative number (e.g. -1E30) to disable.
     minVol 0;
 
-    //- Minimum tet volume. Is absolute volume of the tet formed by the
-    //  face-centre decomposition triangle and the cell centre.
-    //  Set to a sensible fraction of the smallest cell volume expected.
-    //  Set to very negative number (e.g. -1E30) to disable.
-    minTetVol 1e-20;
+    //- Minimum quality of the tet formed by the face-centre
+    //  and variable base point minimum decomposition triangles and
+    //  the cell centre.  Set to very negative number (e.g. -1E30) to
+    //  disable.
+    //     <0 = inside out tet,
+    //      0 = flat tet
+    //      1 = regular tet
+    minTetQuality 1e-9;
 
     //- Minimum face area. Set to <0 to disable.
     minArea -1;
diff --git a/tutorials/incompressible/pimpleDyMFoam/wingMotion/wingMotion_snappyHexMesh/system/snappyHexMeshDict b/tutorials/incompressible/pimpleDyMFoam/wingMotion/wingMotion_snappyHexMesh/system/snappyHexMeshDict
index 37b684ec9ab..78068461c3e 100644
--- a/tutorials/incompressible/pimpleDyMFoam/wingMotion/wingMotion_snappyHexMesh/system/snappyHexMeshDict
+++ b/tutorials/incompressible/pimpleDyMFoam/wingMotion/wingMotion_snappyHexMesh/system/snappyHexMeshDict
@@ -272,11 +272,14 @@ meshQualityControls
     //  Set to very negative number (e.g. -1E30) to disable.
     minVol 1e-13;
 
-    //- Minimum tet volume. Is absolute volume of the tet formed by the
-    //  face-centre decomposition triangle and the cell centre.
-    //  Set to a sensible fraction of the smallest cell volume expected.
-    //  Set to very negative number (e.g. -1E30) to disable.
-    minTetVol 1e-20;
+    //- Minimum quality of the tet formed by the face-centre
+    //  and variable base point minimum decomposition triangles and
+    //  the cell centre.  Set to very negative number (e.g. -1E30) to
+    //  disable.
+    //     <0 = inside out tet,
+    //      0 = flat tet
+    //      1 = regular tet
+    minTetQuality 1e-9;
 
     //- Minimum face area. Set to <0 to disable.
     minArea -1;
diff --git a/tutorials/incompressible/simpleFoam/motorBike/system/snappyHexMeshDict b/tutorials/incompressible/simpleFoam/motorBike/system/snappyHexMeshDict
index 2cf5bad3999..de5c2a8aa77 100644
--- a/tutorials/incompressible/simpleFoam/motorBike/system/snappyHexMeshDict
+++ b/tutorials/incompressible/simpleFoam/motorBike/system/snappyHexMeshDict
@@ -551,11 +551,14 @@ meshQualityControls
     //  Set to very negative number (e.g. -1E30) to disable.
     minVol 1e-13;
 
-    //- Minimum tet volume. Is absolute volume of the tet formed by the
-    //  face-centre decomposition triangle and the cell centre.
-    //  Set to a sensible fraction of the smallest cell volume expected.
-    //  Set to very negative number (e.g. -1E30) to disable.
-    minTetVol 1e-20;
+    //- Minimum quality of the tet formed by the face-centre
+    //  and variable base point minimum decomposition triangles and
+    //  the cell centre.  Set to very negative number (e.g. -1E30) to
+    //  disable.
+    //     <0 = inside out tet,
+    //      0 = flat tet
+    //      1 = regular tet
+    minTetQuality 1e-9;
 
     //- Minimum face area. Set to <0 to disable.
     minArea -1;
diff --git a/tutorials/incompressible/simpleFoam/windTurbineTerrain/system/snappyHexMeshDict b/tutorials/incompressible/simpleFoam/windTurbineTerrain/system/snappyHexMeshDict
index d32abbb74bc..7b7a4e57142 100644
--- a/tutorials/incompressible/simpleFoam/windTurbineTerrain/system/snappyHexMeshDict
+++ b/tutorials/incompressible/simpleFoam/windTurbineTerrain/system/snappyHexMeshDict
@@ -362,11 +362,14 @@ meshQualityControls
     //  Set to very negative number (e.g. -1E30) to disable.
     minVol 1e-13;
 
-    //- Minimum tet volume. Is absolute volume of the tet formed by the
-    //  face-centre decomposition triangle and the cell centre.
-    //  Set to a sensible fraction of the smallest cell volume expected.
-    //  Set to very negative number (e.g. -1E30) to disable.
-    minTetVol 1e-20;
+    //- Minimum quality of the tet formed by the face-centre
+    //  and variable base point minimum decomposition triangles and
+    //  the cell centre.  Set to very negative number (e.g. -1E30) to
+    //  disable.
+    //     <0 = inside out tet,
+    //      0 = flat tet
+    //      1 = regular tet
+    minTetQuality 1e-9;
 
     //- Minimum face area. Set to <0 to disable.
     minArea -1;
diff --git a/tutorials/lagrangian/coalChemistryFoam/simplifiedSiwek/constant/coalCloud1Properties b/tutorials/lagrangian/coalChemistryFoam/simplifiedSiwek/constant/coalCloud1Properties
index b7eb5e23a77..5624bb507b4 100644
--- a/tutorials/lagrangian/coalChemistryFoam/simplifiedSiwek/constant/coalCloud1Properties
+++ b/tutorials/lagrangian/coalChemistryFoam/simplifiedSiwek/constant/coalCloud1Properties
@@ -73,7 +73,7 @@ constantProperties
 interpolationSchemes
 {
     rho             cell;
-    U               cellPointFace;
+    U               cellPoint;
     mu              cell;
     T               cell;
     Cp              cell;
diff --git a/tutorials/lagrangian/porousExplicitSourceReactingParcelFoam/filter/constant/reactingCloud1Properties b/tutorials/lagrangian/porousExplicitSourceReactingParcelFoam/filter/constant/reactingCloud1Properties
index 1e7431118ee..4534388250f 100644
--- a/tutorials/lagrangian/porousExplicitSourceReactingParcelFoam/filter/constant/reactingCloud1Properties
+++ b/tutorials/lagrangian/porousExplicitSourceReactingParcelFoam/filter/constant/reactingCloud1Properties
@@ -74,7 +74,7 @@ constantProperties
 interpolationSchemes
 {
     rho             cell;
-    U               cellPointFace;
+    U               cellPoint;
     mu              cell;
     T               cell;
     Cp              cell;
diff --git a/tutorials/lagrangian/porousExplicitSourceReactingParcelFoam/parcelInBox/constant/reactingCloud1Properties b/tutorials/lagrangian/porousExplicitSourceReactingParcelFoam/parcelInBox/constant/reactingCloud1Properties
index b9e6be98a56..dc6f9aba8bd 100644
--- a/tutorials/lagrangian/porousExplicitSourceReactingParcelFoam/parcelInBox/constant/reactingCloud1Properties
+++ b/tutorials/lagrangian/porousExplicitSourceReactingParcelFoam/parcelInBox/constant/reactingCloud1Properties
@@ -73,7 +73,7 @@ constantProperties
 interpolationSchemes
 {
     rho             cell;
-    U               cellPointFace;
+    U               cellPoint;
     mu              cell;
     T               cell;
     Cp              cell;
diff --git a/tutorials/lagrangian/porousExplicitSourceReactingParcelFoam/verticalChannel/constant/reactingCloud1Properties b/tutorials/lagrangian/porousExplicitSourceReactingParcelFoam/verticalChannel/constant/reactingCloud1Properties
index f04e8560be7..44a046965f7 100644
--- a/tutorials/lagrangian/porousExplicitSourceReactingParcelFoam/verticalChannel/constant/reactingCloud1Properties
+++ b/tutorials/lagrangian/porousExplicitSourceReactingParcelFoam/verticalChannel/constant/reactingCloud1Properties
@@ -73,7 +73,7 @@ constantProperties
 interpolationSchemes
 {
     rho             cell;
-    U               cellPointFace;
+    U               cellPoint;
     mu              cell;
     T               cell;
     Cp              cell;
diff --git a/tutorials/lagrangian/reactingParcelFilmFoam/evaporationTest/constant/reactingCloud1Properties b/tutorials/lagrangian/reactingParcelFilmFoam/evaporationTest/constant/reactingCloud1Properties
index fc14437180b..3f3b7a8aac9 100644
--- a/tutorials/lagrangian/reactingParcelFilmFoam/evaporationTest/constant/reactingCloud1Properties
+++ b/tutorials/lagrangian/reactingParcelFilmFoam/evaporationTest/constant/reactingCloud1Properties
@@ -67,7 +67,7 @@ constantProperties
 interpolationSchemes
 {
     rho             cell;
-    U               cell; // cellPointFace;
+    U               cell; // cellPoint;
     mu              cell;
     T               cell;
     Cp              cell;
diff --git a/tutorials/lagrangian/reactingParcelFilmFoam/hotBoxes/constant/reactingCloud1Properties b/tutorials/lagrangian/reactingParcelFilmFoam/hotBoxes/constant/reactingCloud1Properties
index ce8c2019aac..072f100d499 100644
--- a/tutorials/lagrangian/reactingParcelFilmFoam/hotBoxes/constant/reactingCloud1Properties
+++ b/tutorials/lagrangian/reactingParcelFilmFoam/hotBoxes/constant/reactingCloud1Properties
@@ -67,7 +67,7 @@ constantProperties
 interpolationSchemes
 {
     rho             cell;
-    U               cell; // cellPointFace;
+    U               cell; // cellPoint;
     mu              cell;
     T               cell;
     Cp              cell;
diff --git a/tutorials/lagrangian/reactingParcelFilmFoam/multipleBoxes/constant/reactingCloud1Properties b/tutorials/lagrangian/reactingParcelFilmFoam/multipleBoxes/constant/reactingCloud1Properties
index a148c74674b..51f281bdad5 100644
--- a/tutorials/lagrangian/reactingParcelFilmFoam/multipleBoxes/constant/reactingCloud1Properties
+++ b/tutorials/lagrangian/reactingParcelFilmFoam/multipleBoxes/constant/reactingCloud1Properties
@@ -67,7 +67,7 @@ constantProperties
 interpolationSchemes
 {
     rho             cell;
-    U               cell; // cellPointFace;
+    U               cell; // cellPoint;
     mu              cell;
     T               cell;
     Cp              cell;
diff --git a/tutorials/lagrangian/reactingParcelFilmFoam/panel/constant/reactingCloud1Properties b/tutorials/lagrangian/reactingParcelFilmFoam/panel/constant/reactingCloud1Properties
index 27a63e876a1..73494b022da 100644
--- a/tutorials/lagrangian/reactingParcelFilmFoam/panel/constant/reactingCloud1Properties
+++ b/tutorials/lagrangian/reactingParcelFilmFoam/panel/constant/reactingCloud1Properties
@@ -67,7 +67,7 @@ constantProperties
 interpolationSchemes
 {
     rho             cell;
-    U               cell; // cellPointFace;
+    U               cell; // cellPoint;
     mu              cell;
     T               cell;
     Cp              cell;
diff --git a/tutorials/lagrangian/reactingParcelFoam/evaporationTest/constant/reactingCloud1Properties b/tutorials/lagrangian/reactingParcelFoam/evaporationTest/constant/reactingCloud1Properties
index 49e1a85b97c..ca70790376d 100644
--- a/tutorials/lagrangian/reactingParcelFoam/evaporationTest/constant/reactingCloud1Properties
+++ b/tutorials/lagrangian/reactingParcelFoam/evaporationTest/constant/reactingCloud1Properties
@@ -67,7 +67,7 @@ constantProperties
 interpolationSchemes
 {
     rho             cell;
-    U               cellPointFace;
+    U               cellPoint;
     mu              cell;
     T               cell;
     Cp              cell;
diff --git a/tutorials/mesh/moveDynamicMesh/simpleHarmonicMotion/0.org/pointDisplacement b/tutorials/mesh/moveDynamicMesh/simpleHarmonicMotion/0.org/pointDisplacement
new file mode 100644
index 00000000000..d2b845620a5
--- /dev/null
+++ b/tutorials/mesh/moveDynamicMesh/simpleHarmonicMotion/0.org/pointDisplacement
@@ -0,0 +1,88 @@
+/*--------------------------------*- C++ -*----------------------------------*\
+| =========                 |                                                 |
+| \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox           |
+|  \\    /   O peration     | Version:  dev                                   |
+|   \\  /    A nd           | Web:      www.OpenFOAM.org                      |
+|    \\/     M anipulation  |                                                 |
+\*---------------------------------------------------------------------------*/
+FoamFile
+{
+    version     2.0;
+    format      ascii;
+    class       pointVectorField;
+    location    "0.01";
+    object      pointDisplacement;
+}
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+dimensions      [0 1 0 0 0 0 0];
+
+internalField   uniform (0 0 0);
+
+boundaryField
+{
+    stationaryWalls
+    {
+        type            fixedValue;
+        value           uniform (0 0 0);
+    }
+    movingBlock
+    {
+        type            uncoupledSixDoFRigidBodyDisplacement;
+        centreOfMass    (0.5 0.5 0.5);
+        momentOfInertia (0.1052 0.1052 0.1778);
+        mass            9.6;
+        velocity        (0 0 0);
+        orientation     (1 0 0 0 1 0 0 0 1);
+        acceleration    (0 0 0);
+        angularMomentum (0 0 0);
+        torque          (0 0 0);
+        gravity         (0 0 0);
+        rhoInf          1;
+        report          on;
+        restraints
+        {
+            topSpring
+            {
+                sixDoFRigidBodyMotionRestraint linearSpring;
+
+                linearSpringCoeffs
+                {
+                    anchor          (0.5 0.5 1);
+                    refAttachmentPt $centreOfMass;
+                    stiffness       5000;
+                    damping         50;
+                    restLength      0.4;
+                }
+            }
+        }
+        constraints
+        {
+            maxIterations       500;
+
+            fixedAxes1
+            {
+                sixDoFRigidBodyMotionConstraint fixedOrientation;
+                tolerance           1e-6;
+                relaxationFactor    1.0;
+                fixedOrientationCoeffs {}
+            }
+
+            fixedLine1
+            {
+                sixDoFRigidBodyMotionConstraint fixedLine;
+                tolerance           1e-6;
+                relaxationFactor    1.0;
+                fixedLineCoeffs
+                {
+                    refPoint      $centreOfMass;
+                    direction     (0 0 1);
+                }
+            }
+        }
+        value           uniform (0 0 0);
+    }
+}
+
+
+// ************************************************************************* //
diff --git a/tutorials/mesh/moveDynamicMesh/simpleHarmonicMotion/Allclean b/tutorials/mesh/moveDynamicMesh/simpleHarmonicMotion/Allclean
new file mode 100755
index 00000000000..b134761b231
--- /dev/null
+++ b/tutorials/mesh/moveDynamicMesh/simpleHarmonicMotion/Allclean
@@ -0,0 +1,11 @@
+#!/bin/sh
+cd ${0%/*} || exit 1    # run from this directory
+
+# Source tutorial clean functions
+. $WM_PROJECT_DIR/bin/tools/CleanFunctions
+
+rm -rf 0 t_vs_cm  t_vs_lv shm.eps > /dev/null 2>&1
+
+cleanCase
+
+# ----------------------------------------------------------------- end-of-file
diff --git a/tutorials/mesh/moveDynamicMesh/simpleHarmonicMotion/Allrun b/tutorials/mesh/moveDynamicMesh/simpleHarmonicMotion/Allrun
new file mode 100755
index 00000000000..f7afb77a5ba
--- /dev/null
+++ b/tutorials/mesh/moveDynamicMesh/simpleHarmonicMotion/Allrun
@@ -0,0 +1,19 @@
+#!/bin/sh
+
+cd ${0%/*} || exit 1    # run from this directory
+
+# Source tutorial run functions
+. $WM_PROJECT_DIR/bin/tools/RunFunctions
+
+# Set application name
+application="moveDynamicMesh"
+
+runApplication blockMesh
+runApplication topoSet
+runApplication subsetMesh -overwrite c0 -patch movingBlock
+cp -r 0.org 0 > /dev/null 2>&1
+runApplication $application
+./extractData log.$application
+gnuplot shm.gnuplot
+
+# ----------------------------------------------------------------- end-of-file
diff --git a/tutorials/mesh/moveDynamicMesh/simpleHarmonicMotion/constant/dynamicMeshDict b/tutorials/mesh/moveDynamicMesh/simpleHarmonicMotion/constant/dynamicMeshDict
new file mode 100644
index 00000000000..329480c68d6
--- /dev/null
+++ b/tutorials/mesh/moveDynamicMesh/simpleHarmonicMotion/constant/dynamicMeshDict
@@ -0,0 +1,26 @@
+/*--------------------------------*- C++ -*----------------------------------*\
+| =========                 |                                                 |
+| \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox           |
+|  \\    /   O peration     | Version:  dev                                   |
+|   \\  /    A nd           | Web:      http://www.OpenFOAM.org               |
+|    \\/     M anipulation  |                                                 |
+\*---------------------------------------------------------------------------*/
+FoamFile
+{
+    version     2.0;
+    format      ascii;
+    class       dictionary;
+    object      motionProperties;
+}
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+dynamicFvMesh      dynamicMotionSolverFvMesh;
+
+motionSolverLibs ("libfvMotionSolvers.so");
+
+solver            displacementLaplacian;
+
+diffusivity       inverseDistance (movingBlock);
+
+
+// ************************************************************************* //
diff --git a/tutorials/mesh/moveDynamicMesh/simpleHarmonicMotion/constant/polyMesh/blockMeshDict b/tutorials/mesh/moveDynamicMesh/simpleHarmonicMotion/constant/polyMesh/blockMeshDict
new file mode 100644
index 00000000000..c2ae0e3bfe8
--- /dev/null
+++ b/tutorials/mesh/moveDynamicMesh/simpleHarmonicMotion/constant/polyMesh/blockMeshDict
@@ -0,0 +1,59 @@
+/*--------------------------------*- C++ -*----------------------------------*\
+| =========                 |                                                 |
+| \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox           |
+|  \\    /   O peration     | Version:  dev                                   |
+|   \\  /    A nd           | Web:      http://www.OpenFOAM.org               |
+|    \\/     M anipulation  |                                                 |
+\*---------------------------------------------------------------------------*/
+FoamFile
+{
+    version     2.0;
+    format      ascii;
+    class       dictionary;
+    object      blockMeshDict;
+}
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+convertToMeters 1;
+
+vertices
+(
+    (0 0 0)
+    (1 0 0)
+    (1 1 0)
+    (0 1 0)
+    (0 0 1)
+    (1 0 1)
+    (1 1 1)
+    (0 1 1)
+);
+
+blocks
+(
+    hex (0 1 2 3 4 5 6 7) (9 9 14) simpleGrading (1 1 1)
+);
+
+edges
+(
+);
+
+patches
+(
+    patch stationaryWalls
+    (
+        (0 3 2 1)
+        (2 6 5 1)
+        (1 5 4 0)
+        (3 7 6 2)
+        (0 4 7 3)
+        (4 5 6 7)
+    )
+    patch movingBlock
+    ()
+);
+
+mergePatchPairs
+(
+);
+
+// ************************************************************************* //
diff --git a/tutorials/discreteMethods/molecularDynamics/mdEquilibrationFoam/periodicCubeWater/0/U b/tutorials/mesh/moveDynamicMesh/simpleHarmonicMotion/constant/polyMesh/boundary
similarity index 71%
rename from tutorials/discreteMethods/molecularDynamics/mdEquilibrationFoam/periodicCubeWater/0/U
rename to tutorials/mesh/moveDynamicMesh/simpleHarmonicMotion/constant/polyMesh/boundary
index c7bc8234a29..eef245f9f4c 100644
--- a/tutorials/discreteMethods/molecularDynamics/mdEquilibrationFoam/periodicCubeWater/0/U
+++ b/tutorials/mesh/moveDynamicMesh/simpleHarmonicMotion/constant/polyMesh/boundary
@@ -9,29 +9,26 @@ FoamFile
 {
     version     2.0;
     format      ascii;
-    class       volVectorField;
-    object      U;
+    class       polyBoundaryMesh;
+    location    "constant/polyMesh";
+    object      boundary;
 }
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
-dimensions      [0 1 -1 0 0 0 0];
-
-internalField   uniform (0 0 0);
-
-boundaryField
-{
-    periodicX
+2
+(
+    stationaryWalls
     {
-        type            cyclic;
+        type            wall;
+        nFaces          666;
+        startFace       2994;
     }
-    periodicY
+    movingBlock
     {
-        type            cyclic;
+        type            patch;
+        nFaces          42;
+        startFace       3660;
     }
-    periodicZ
-    {
-        type            cyclic;
-    }
-}
+)
 
 // ************************************************************************* //
diff --git a/tutorials/mesh/moveDynamicMesh/simpleHarmonicMotion/extractData b/tutorials/mesh/moveDynamicMesh/simpleHarmonicMotion/extractData
new file mode 100755
index 00000000000..784f45c5fc5
--- /dev/null
+++ b/tutorials/mesh/moveDynamicMesh/simpleHarmonicMotion/extractData
@@ -0,0 +1,42 @@
+#!/bin/sh
+#------------------------------------------------------------------------------
+# =========                 |
+# \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+#  \\    /   O peration     |
+#   \\  /    A nd           | Copyright (C) 2010-2010 OpenCFD Ltd.
+#    \\/     M anipulation  |
+#-------------------------------------------------------------------------------
+# License
+#     This file is part of OpenFOAM.
+#
+#     OpenFOAM is free software: you can redistribute it and/or modify it
+#     under the terms of the GNU General Public License as published by
+#     the Free Software Foundation, either version 3 of the License, or
+#     (at your option) any later version.
+#
+#     OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
+#     ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+#     FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+#     for more details.
+#
+#     You should have received a copy of the GNU General Public License
+#     along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
+#
+# Script
+#     extractData
+#
+# Description
+#     Extracts motion data from a simple harmonic motion dynamicMesh case
+#
+#------------------------------------------------------------------------------
+
+grep "Centre of mass" $1 | cut -d ":" -f 2 | cut -d " " -f 4 | tr -d ")" > cM
+grep "Linear velocity" $1 | cut -d ":" -f 2 | cut -d " " -f 4 | tr -d ")" > lV
+grep -e "^Time = " $1 | cut -d " " -f 3 > times
+
+paste times cM > t_vs_cm
+paste times lV > t_vs_lv
+
+rm cM lV times
+
+#------------------------------------------------------------------------------
\ No newline at end of file
diff --git a/tutorials/mesh/moveDynamicMesh/simpleHarmonicMotion/shm.gnuplot b/tutorials/mesh/moveDynamicMesh/simpleHarmonicMotion/shm.gnuplot
new file mode 100644
index 00000000000..8027230526e
--- /dev/null
+++ b/tutorials/mesh/moveDynamicMesh/simpleHarmonicMotion/shm.gnuplot
@@ -0,0 +1,76 @@
+#------------------------------------------------------------------------------
+# =========                 |
+# \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+#  \\    /   O peration     |
+#   \\  /    A nd           | Copyright (C) 2010-2010 OpenCFD Ltd.
+#    \\/     M anipulation  |
+#-------------------------------------------------------------------------------
+# License
+#     This file is part of OpenFOAM.
+#
+#     OpenFOAM is free software: you can redistribute it and/or modify it
+#     under the terms of the GNU General Public License as published by
+#     the Free Software Foundation, either version 3 of the License, or
+#     (at your option) any later version.
+#
+#     OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
+#     ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+#     FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+#     for more details.
+#
+#     You should have received a copy of the GNU General Public License
+#     along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
+#
+# Script
+#     shm.gnuplot
+#
+# Description
+#     Creates an .eps graph of OpenFOAM results vs analytical solution
+#     for a simple harmonic motion dynamicMesh case
+#
+#------------------------------------------------------------------------------
+
+reset
+
+set samples 2000
+
+k = 5000.0
+m = 9.6
+c = 50.0
+a = -0.1
+
+omega = sqrt(k/m)
+zeta = c/(2.0*m*omega)
+
+phi = atan((sqrt(1.0 - zeta**2))/zeta)
+A = a/sin(phi)
+
+pos(A, t, omega, phi, zeta) = A*exp(-zeta*omega*t)*sin(sqrt(1-zeta**2)*omega*t + phi)
+vel(A, t, omega, phi, zeta) = \
+A*exp(-zeta*omega*t)*\
+( \
+  sqrt(1-zeta**2)*omega*cos(sqrt(1-zeta**2)*omega*t + phi) \
+- zeta*omega*sin(sqrt(1-zeta**2)*omega*t + phi) \
+)
+
+set xlabel "Time/[s]"
+set ylabel "Position"
+
+set ytics nomirror
+set y2tics
+
+set yrange [-0.1:0.1]
+set y2range [-2:2]
+
+set xzeroaxis
+
+set terminal postscript eps color enhanced solid
+set output "shm.eps"
+
+plot \
+    "t_vs_cm" u 1:($2 - 0.6) w l t "Simulation, centre of mass relative to start", \
+    pos(A, x, omega, phi, zeta) w l t "Analytical solution, centre of mass", \
+    "t_vs_lv" u 1:2 w l axes x1y2 t "Simulation, vertical velocity", \
+    vel(A, x, omega, phi, zeta) w l axes x1y2 t "Analytical solution, vertical velocity"
+
+#------------------------------------------------------------------------------
diff --git a/tutorials/mesh/moveDynamicMesh/simpleHarmonicMotion/system/controlDict b/tutorials/mesh/moveDynamicMesh/simpleHarmonicMotion/system/controlDict
new file mode 100644
index 00000000000..2d71827bb43
--- /dev/null
+++ b/tutorials/mesh/moveDynamicMesh/simpleHarmonicMotion/system/controlDict
@@ -0,0 +1,68 @@
+/*--------------------------------*- C++ -*----------------------------------*\
+| =========                 |                                                 |
+| \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox           |
+|  \\    /   O peration     | Version:  dev                                   |
+|   \\  /    A nd           | Web:      www.OpenFOAM.org                      |
+|    \\/     M anipulation  |                                                 |
+\*---------------------------------------------------------------------------*/
+FoamFile
+{
+    version     2.0;
+    format      ascii;
+    class       dictionary;
+    location    "system";
+    object      controlDict;
+}
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+application     interDyMFoam;
+
+startFrom       startTime;
+
+startTime       0;
+
+stopAt          endTime;
+
+endTime         4;
+
+deltaT          0.002;
+
+writeControl    adjustableRunTime;
+
+writeInterval   0.25;
+
+purgeWrite      0;
+
+writeFormat     ascii;
+
+writePrecision  12;
+
+writeCompression uncompressed;
+
+timeFormat      general;
+
+timePrecision   6;
+
+runTimeModifiable yes;
+
+adjustTimeStep  yes;
+
+maxCo           0.2;
+
+maxDeltaT       0.025;
+
+libs
+(
+    "libincompressibleRASModels.so"
+    "libforces.so"
+);
+
+
+// libs
+// (
+//     "libgenericPatchFields.so"
+//     "libincompressibleRASModels.so"
+//     "libforces.so"
+// );
+
+// ************************************************************************* //
diff --git a/tutorials/mesh/moveDynamicMesh/simpleHarmonicMotion/system/fvSchemes b/tutorials/mesh/moveDynamicMesh/simpleHarmonicMotion/system/fvSchemes
new file mode 100644
index 00000000000..9b11d59f3d3
--- /dev/null
+++ b/tutorials/mesh/moveDynamicMesh/simpleHarmonicMotion/system/fvSchemes
@@ -0,0 +1,50 @@
+/*--------------------------------*- C++ -*----------------------------------*\
+| =========                 |                                                 |
+| \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox           |
+|  \\    /   O peration     | Version:  dev                                   |
+|   \\  /    A nd           | Web:      www.OpenFOAM.org                      |
+|    \\/     M anipulation  |                                                 |
+\*---------------------------------------------------------------------------*/
+FoamFile
+{
+    version     2.0;
+    format      ascii;
+    class       dictionary;
+    location    "system";
+    object      fvSchemes;
+}
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+ddtSchemes
+{
+}
+
+gradSchemes
+{
+    default         Gauss linear;
+}
+
+divSchemes
+{
+}
+
+laplacianSchemes
+{
+    default         Gauss linear corrected;
+}
+
+interpolationSchemes
+{
+    default         linear;
+}
+
+snGradSchemes
+{
+}
+
+fluxRequired
+{
+}
+
+
+// ************************************************************************* //
diff --git a/tutorials/mesh/moveDynamicMesh/simpleHarmonicMotion/system/fvSolution b/tutorials/mesh/moveDynamicMesh/simpleHarmonicMotion/system/fvSolution
new file mode 100644
index 00000000000..209b3b035f8
--- /dev/null
+++ b/tutorials/mesh/moveDynamicMesh/simpleHarmonicMotion/system/fvSolution
@@ -0,0 +1,34 @@
+/*--------------------------------*- C++ -*----------------------------------*\
+| =========                 |                                                 |
+| \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox           |
+|  \\    /   O peration     | Version:  dev                                   |
+|   \\  /    A nd           | Web:      www.OpenFOAM.org                      |
+|    \\/     M anipulation  |                                                 |
+\*---------------------------------------------------------------------------*/
+FoamFile
+{
+    version     2.0;
+    format      ascii;
+    class       dictionary;
+    location    "system";
+    object      fvSolution;
+}
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+solvers
+{
+    cellDisplacement
+    {
+        solver          GAMG;
+        tolerance       1e-06;
+        relTol          0;
+        smoother        GaussSeidel;
+        cacheAgglomeration true;
+        nCellsInCoarsestLevel 10;
+        agglomerator    faceAreaPair;
+        mergeLevels     1;
+    }
+}
+
+
+// ************************************************************************* //
diff --git a/tutorials/discreteMethods/molecularDynamics/mdEquilibrationFoam/periodicCubeArgon/0/U b/tutorials/mesh/moveDynamicMesh/simpleHarmonicMotion/system/topoSetDict
similarity index 72%
rename from tutorials/discreteMethods/molecularDynamics/mdEquilibrationFoam/periodicCubeArgon/0/U
rename to tutorials/mesh/moveDynamicMesh/simpleHarmonicMotion/system/topoSetDict
index c7bc8234a29..91b2397d23b 100644
--- a/tutorials/discreteMethods/molecularDynamics/mdEquilibrationFoam/periodicCubeArgon/0/U
+++ b/tutorials/mesh/moveDynamicMesh/simpleHarmonicMotion/system/topoSetDict
@@ -9,29 +9,30 @@ FoamFile
 {
     version     2.0;
     format      ascii;
-    class       volVectorField;
-    object      U;
+    class       dictionary;
+    object      topoSetDict;
 }
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
-
-dimensions      [0 1 -1 0 0 0 0];
 
-internalField   uniform (0 0 0);
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
-boundaryField
-{
-    periodicX
+actions
+(
     {
-        type            cyclic;
+        name c0;
+        type cellSet;
+        action new;
+        source boxToCell;
+        sourceInfo
+        {
+           box   (0.35 0.35 0.44) (0.65 0.65 0.56);
+        }
     }
-    periodicY
-    {
-        type            cyclic;
-    }
-    periodicZ
+
     {
-        type            cyclic;
+        name c0;
+        type cellSet;
+        action invert;
     }
-}
+);
 
 // ************************************************************************* //
diff --git a/tutorials/multiphase/interPhaseChangeFoam/cavitatingBullet/system/snappyHexMeshDict b/tutorials/multiphase/interPhaseChangeFoam/cavitatingBullet/system/snappyHexMeshDict
index d2c7fee5e42..456bd93ea0d 100644
--- a/tutorials/multiphase/interPhaseChangeFoam/cavitatingBullet/system/snappyHexMeshDict
+++ b/tutorials/multiphase/interPhaseChangeFoam/cavitatingBullet/system/snappyHexMeshDict
@@ -292,11 +292,14 @@ meshQualityControls
     //  Set to very negative number (e.g. -1E30) to disable.
     minVol 1e-20;
 
-    //- Minimum tet volume. Is absolute volume of the tet formed by the
-    //  face-centre decomposition triangle and the cell centre.
-    //  Set to a sensible fraction of the smallest cell volume expected.
-    //  Set to very negative number (e.g. -1E30) to disable.
-    minTetVol 1e-20;
+    //- Minimum quality of the tet formed by the face-centre
+    //  and variable base point minimum decomposition triangles and
+    //  the cell centre.  Set to very negative number (e.g. -1E30) to
+    //  disable.
+    //     <0 = inside out tet,
+    //      0 = flat tet
+    //      1 = regular tet
+    minTetQuality 1e-9;
 
     //- Minimum face area. Set to <0 to disable.
     minArea -1;
-- 
GitLab