diff --git a/applications/test/vector/Test-vector.C b/applications/test/vector/Test-vector.C
index 2cfebcd81dd207629eea47fbec161698ab385b89..417d25aad989914919a4a8f81bff95f1bf3097ea 100644
--- a/applications/test/vector/Test-vector.C
+++ b/applications/test/vector/Test-vector.C
@@ -5,7 +5,7 @@
     \\  /    A nd           | www.openfoam.com
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
-    Copyright (C) 2018-2021 OpenCFD Ltd.
+    Copyright (C) 2018-2022 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -31,7 +31,7 @@ Description
 
 \*---------------------------------------------------------------------------*/
 
-#include "vector.H"
+#include "vectorField.H"
 #include "IOstreams.H"
 #include <algorithm>
 #include <random>
@@ -110,6 +110,16 @@ void testData(const VecSpace& vs)
 }
 
 
+template<class Type>
+void testNormalise(Field<Type>& fld)
+{
+    Info<< nl << pTraits<Type>::typeName << " Field" << nl
+        << "  orig: " << fld << nl;
+    fld.normalise();
+    Info<< "  norm: " << fld << nl;
+}
+
+
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 //  Main program:
 
@@ -138,6 +148,38 @@ int main(int argc, char *argv[])
         Info<< "shuffled: " << vec2 << nl;
     }
 
+    // Basic tests for fields
+    {
+        scalarField sfld1
+        ({
+            0.0,
+            1.0,
+            2.0
+        });
+
+        testNormalise(sfld1);
+
+        Field<floatVector> vfld1
+        ({
+            floatVector(0.0, 1.0, 2.0),
+            floatVector(0, 0, floatScalarSMALL),
+            floatVector(0, 0, floatScalarVSMALL),
+            floatVector(0, 2, 1),
+        });
+
+        testNormalise(vfld1);
+
+        Field<doubleVector> vfld2
+        ({
+            doubleVector(0.0, 1.0, 2.0),
+            doubleVector(0, 0, doubleScalarSMALL),
+            doubleVector(0, 0, doubleScalarVSMALL),
+            doubleVector(0, 2, 1),
+        });
+
+        testNormalise(vfld2);
+    }
+
     Info<< "\nEnd\n" << nl;
 
     return 0;
diff --git a/src/OpenFOAM/Make/files b/src/OpenFOAM/Make/files
index d08c39dadf7b0d73f5c12b04780f9a494d96b867..d58a27f7ae37a7b52333f9edd121a26b55890c80 100644
--- a/src/OpenFOAM/Make/files
+++ b/src/OpenFOAM/Make/files
@@ -726,6 +726,7 @@ $(Fields)/labelField/labelFieldIOField.C
 $(Fields)/scalarField/scalarField.C
 $(Fields)/scalarField/scalarIOField.C
 $(Fields)/scalarField/scalarFieldIOField.C
+$(Fields)/vectorField/vectorField.C
 $(Fields)/vectorField/vectorIOField.C
 $(Fields)/vectorField/vectorFieldIOField.C
 $(Fields)/vector2DField/vector2DIOField.C
diff --git a/src/OpenFOAM/fields/FieldFields/FieldField/FieldField.C b/src/OpenFOAM/fields/FieldFields/FieldField/FieldField.C
index 29f4fe8c2156abefe4917e72a9c0ae708266ac04..4905f9c03a8f621055f292702130f2b83d7b8f29 100644
--- a/src/OpenFOAM/fields/FieldFields/FieldField/FieldField.C
+++ b/src/OpenFOAM/fields/FieldFields/FieldField/FieldField.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2019 OpenCFD Ltd.
+    Copyright (C) 2019-2022 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -225,6 +225,16 @@ void FieldField<Field, Type>::negate()
 }
 
 
+template<template<class> class Field, class Type>
+void FieldField<Field, Type>::normalise()
+{
+    forAll(*this, i)
+    {
+        this->operator[](i).normalise();
+    }
+}
+
+
 template<template<class> class Field, class Type>
 tmp<FieldField<Field, typename FieldField<Field, Type>::cmptType>>
 FieldField<Field, Type>::component
diff --git a/src/OpenFOAM/fields/FieldFields/FieldField/FieldField.H b/src/OpenFOAM/fields/FieldFields/FieldField/FieldField.H
index d7d591de6a2f0043f9e2db58482b041c0117dc79..7b408ceca52a5a05237b267d7a4754f02c7e3d60 100644
--- a/src/OpenFOAM/fields/FieldFields/FieldField/FieldField.H
+++ b/src/OpenFOAM/fields/FieldFields/FieldField/FieldField.H
@@ -6,6 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
+    Copyright (C) 2022 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -34,8 +35,8 @@ SourceFiles
 
 \*---------------------------------------------------------------------------*/
 
-#ifndef FieldField_H
-#define FieldField_H
+#ifndef Foam_FieldField_H
+#define Foam_FieldField_H
 
 #include "tmp.H"
 #include "PtrList.H"
@@ -48,7 +49,7 @@ SourceFiles
 namespace Foam
 {
 
-// Forward declarations
+// Forward Declarations
 
 template<template<class> class Field, class Type>
 class FieldField;
@@ -131,11 +132,14 @@ public:
         );
 
 
-    // Member functions
+    // Member Functions
 
-        //- Negate this field
+        //- Negate this field. See notes in Field
         void negate();
 
+        //- Normalise this field. See notes in Field
+        void normalise();
+
         //- Return a component field of the field
         tmp<FieldField<Field, cmptType>> component(const direction) const;
 
diff --git a/src/OpenFOAM/fields/Fields/Field/Field.C b/src/OpenFOAM/fields/Fields/Field/Field.C
index f7fd4066ada18d3f05b3d0525ca0ffdb11eb124e..31ad1b228649f30010fb98c7eb1935be192167d2 100644
--- a/src/OpenFOAM/fields/Fields/Field/Field.C
+++ b/src/OpenFOAM/fields/Fields/Field/Field.C
@@ -533,6 +533,12 @@ void Foam::Field<Type>::negate()
 }
 
 
+// A no-op except for vector specialization
+template<class Type>
+void Foam::Field<Type>::normalise()
+{}
+
+
 template<class Type>
 Foam::tmp<Foam::Field<typename Foam::Field<Type>::cmptType>>
 Foam::Field<Type>::component
diff --git a/src/OpenFOAM/fields/Fields/Field/Field.H b/src/OpenFOAM/fields/Fields/Field/Field.H
index 55f11617b87d9a9b83a911f03bf9c172874c91cf..8ea04193fb195c6554da31f913af2449f5a12a31 100644
--- a/src/OpenFOAM/fields/Fields/Field/Field.H
+++ b/src/OpenFOAM/fields/Fields/Field/Field.H
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2015-2021 OpenCFD Ltd.
+    Copyright (C) 2015-2022 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -42,8 +42,8 @@ SourceFiles
 
 \*---------------------------------------------------------------------------*/
 
-#ifndef Field_H
-#define Field_H
+#ifndef Foam_Field_H
+#define Foam_Field_H
 
 #include "tmp.H"
 #include "direction.H"
@@ -340,10 +340,16 @@ public:
             const UList<scalar>& weights
         );
 
-        //- Negate this field (negative).
+        //- Inplace negate this field (negative).
         //  Inverts the state for a bool field.
         void negate();
 
+        //- Inplace normalise this field.
+        //- Generally a no-op except for vector fields.
+        //  Vector fields are normalised by their magnitude.
+        //  Small vectors (mag less than ROOTVSMALL) are set to zero.
+        void normalise();
+
         //- Return a component field of the field
         tmp<Field<cmptType>> component(const direction) const;
 
diff --git a/src/OpenFOAM/fields/Fields/vectorField/vectorField.C b/src/OpenFOAM/fields/Fields/vectorField/vectorField.C
new file mode 100644
index 0000000000000000000000000000000000000000..bc759e49a67f76747ab209e762591b3b108c093d
--- /dev/null
+++ b/src/OpenFOAM/fields/Fields/vectorField/vectorField.C
@@ -0,0 +1,112 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | www.openfoam.com
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+    Copyright (C) 2022 OpenCFD Ltd.
+-------------------------------------------------------------------------------
+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 "vectorField.H"
+
+// * * * * * * * * * * * * * * * Specializations * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+// Note: the mag of vector and division is written out to avoid any casting
+// between float and double.
+//
+// This enables specialization for floatVector and doubleVector independent
+// of the definition of 'scalar' or 'vector' - useful for mixed-precision
+// operation.
+
+template<>
+void Field<Vector<float>>::normalise()
+{
+    typedef float cmptType;
+
+    constexpr float tol = floatScalarROOTVSMALL;
+
+    for (Vector<cmptType>& v : *this)
+    {
+        // Foam::mag but using cmptType instead of scalar
+        cmptType s
+        (
+            ::sqrt(magSqr(v.x()) + magSqr(v.y()) + magSqr(v.z()))
+        );
+
+        if (s < tol)
+        {
+            v.x() = 0;
+            v.y() = 0;
+            v.z() = 0;
+        }
+        else
+        {
+            v.x() /= s;
+            v.y() /= s;
+            v.z() /= s;
+        }
+    }
+}
+
+
+template<>
+void Field<Vector<double>>::normalise()
+{
+    typedef double cmptType;
+
+    constexpr double tol = doubleScalarROOTVSMALL;
+
+    for (Vector<cmptType>& v : *this)
+    {
+        // Foam::mag but using cmptType instead of scalar
+        cmptType s
+        (
+            ::sqrt(magSqr(v.x()) + magSqr(v.y()) + magSqr(v.z()))
+        );
+
+        if (s < tol)
+        {
+            v.x() = 0;
+            v.y() = 0;
+            v.z() = 0;
+        }
+        else
+        {
+            v.x() /= s;
+            v.y() /= s;
+            v.z() /= s;
+        }
+    }
+}
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+// ************************************************************************* //
diff --git a/src/OpenFOAM/fields/Fields/vectorField/vectorField.H b/src/OpenFOAM/fields/Fields/vectorField/vectorField.H
index 48c2144929faacc43ae2bb97d3f9b5593f87624f..a0fbed9c1cd5278d089847652ea9e1eb6f1fdd13 100644
--- a/src/OpenFOAM/fields/Fields/vectorField/vectorField.H
+++ b/src/OpenFOAM/fields/Fields/vectorField/vectorField.H
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2017 OpenFOAM Foundation
-    Copyright (C) 2019 OpenCFD Ltd.
+    Copyright (C) 2019-2022 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -35,8 +35,8 @@ SourceFiles
 
 \*---------------------------------------------------------------------------*/
 
-#ifndef vectorField_H
-#define vectorField_H
+#ifndef Foam_vectorField_H
+#define Foam_vectorField_H
 
 #include "scalarField.H"
 #include "vector.H"
@@ -52,6 +52,13 @@ typedef Field<vector> vectorField;
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
+//- Inplace normalise (float) vector field
+template<> void Field<Vector<float>>::normalise();
+
+//- Inplace normalise (double) vector field
+template<> void Field<Vector<double>>::normalise();
+
+
 //- Zip together vector field from components
 template<class Cmpt>
 void zip
diff --git a/src/OpenFOAM/fields/GeometricFields/GeometricField/GeometricField.C b/src/OpenFOAM/fields/GeometricFields/GeometricField/GeometricField.C
index 1532c6fffdb2b5f946f3851ece3eb84348cdbef4..b93df0811cc40887bec1c23d3602b75f3e374c99 100644
--- a/src/OpenFOAM/fields/GeometricFields/GeometricField/GeometricField.C
+++ b/src/OpenFOAM/fields/GeometricFields/GeometricField/GeometricField.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2017 OpenFOAM Foundation
-    Copyright (C) 2015-2021 OpenCFD Ltd.
+    Copyright (C) 2015-2022 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -1193,6 +1193,14 @@ void Foam::GeometricField<Type, PatchField, GeoMesh>::negate()
 }
 
 
+template<class Type, template<class> class PatchField, class GeoMesh>
+void Foam::GeometricField<Type, PatchField, GeoMesh>::normalise()
+{
+    primitiveFieldRef().normalise();
+    boundaryFieldRef().normalise();
+}
+
+
 // * * * * * * * * * * * * * * * Member Operators  * * * * * * * * * * * * * //
 
 template<class Type, template<class> class PatchField, class GeoMesh>
diff --git a/src/OpenFOAM/fields/GeometricFields/GeometricField/GeometricField.H b/src/OpenFOAM/fields/GeometricFields/GeometricField/GeometricField.H
index 24401b338d221ff51773f3f0ca82fda91bf501d2..db89a7527712324422dd18fcbcf14046a2fd289a 100644
--- a/src/OpenFOAM/fields/GeometricFields/GeometricField/GeometricField.H
+++ b/src/OpenFOAM/fields/GeometricFields/GeometricField/GeometricField.H
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2017 OpenFOAM Foundation
-    Copyright (C) 2015-2020 OpenCFD Ltd.
+    Copyright (C) 2015-2022 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -527,9 +527,12 @@ public:
 
     // Member Function *this Operators
 
-        //- Negate the field inplace
+        //- Negate the field inplace. See notes in Field
         void negate();
 
+        //- Normalise the field inplace. See notes in Field
+        void normalise();
+
         //- Replace specified field component with content from another field
         void replace
         (
diff --git a/src/OpenFOAM/fields/pointPatchFields/pointPatchField/pointPatchField.H b/src/OpenFOAM/fields/pointPatchFields/pointPatchField/pointPatchField.H
index 15e2af8241b305ddf6bea8818cad2c70246840d4..92aee089a6663076d6d90504cc3cd8bac5ca861f 100644
--- a/src/OpenFOAM/fields/pointPatchFields/pointPatchField/pointPatchField.H
+++ b/src/OpenFOAM/fields/pointPatchFields/pointPatchField/pointPatchField.H
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2017 OpenFOAM Foundation
-    Copyright (C) 2019 OpenCFD Ltd.
+    Copyright (C) 2019-2022 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -42,8 +42,8 @@ SourceFiles
 
 \*---------------------------------------------------------------------------*/
 
-#ifndef pointPatchField_H
-#define pointPatchField_H
+#ifndef Foam_pointPatchField_H
+#define Foam_pointPatchField_H
 
 #include "pointPatch.H"
 #include "DimensionedField.H"
@@ -54,7 +54,7 @@ SourceFiles
 namespace Foam
 {
 
-// Forward declarations
+// Forward Declarations
 
 class objectRegistry;
 class dictionary;
@@ -75,7 +75,7 @@ Ostream& operator<<(Ostream&, const pointPatchField<Type>&);
 template<class Type>
 class pointPatchField
 {
-    // Private data
+    // Private Data
 
         //- Reference to patch
         const pointPatch& patch_;
@@ -471,14 +471,13 @@ public:
         );
 
 
-    // Housekeeping
+    // Other Methods
 
-    // Includes some dummy methods to ensure that various
-    // FieldFieldFunctions will be definable.
-
-        //- Negate the field inplace
-        //  [dummy placeholder for FieldField] (2019-11)
+        //- Negate the field inplace. Dummy placeholder for FieldField
         void negate() {}
+
+        //- Normalise the field inplace. Dummy placeholder for FieldField
+        void normalise() {}
 };
 
 
diff --git a/src/OpenFOAM/primitives/Vector/Vector.H b/src/OpenFOAM/primitives/Vector/Vector.H
index b222616520932508f4368887ea0ef3b6b0a7193e..05ae0934c10e4d5f896ba1fe97e54e3f29e6023a 100644
--- a/src/OpenFOAM/primitives/Vector/Vector.H
+++ b/src/OpenFOAM/primitives/Vector/Vector.H
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2018-2019 OpenCFD Ltd.
+    Copyright (C) 2018-2022 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -41,8 +41,8 @@ SourceFiles
 
 \*---------------------------------------------------------------------------*/
 
-#ifndef Vector_H
-#define Vector_H
+#ifndef Foam_Vector_H
+#define Foam_Vector_H
 
 #include "contiguous.H"
 #include "VectorSpace.H"
@@ -130,15 +130,20 @@ public:
         //- Access to the vector z component
         inline Cmpt& z();
 
+        //- Inplace normalise the vector by its magnitude
+        //  For small magnitudes (less than ROOTVSMALL) set to zero.
+        //  Will not be particularly useful for a vector of labels
+        inline Vector<Cmpt>& normalise(const scalar tol = ROOTVSMALL);
 
-        //- Normalise the vector by its magnitude
-        inline Vector<Cmpt>& normalise();
+        //- Inplace removal of components that are collinear to the given
+        //- unit vector.
+        inline Vector<Cmpt>& removeCollinear(const Vector<Cmpt>& unitVec);
 
 
         //- Return *this (used for point which is a typedef to Vector<scalar>.
         inline const Vector<Cmpt>& centre
         (
-            const Foam::List<Vector<Cmpt>>&
+            const Foam::UList<Vector<Cmpt>>&  /* (unused) */
         ) const;
 };
 
diff --git a/src/OpenFOAM/primitives/Vector/VectorI.H b/src/OpenFOAM/primitives/Vector/VectorI.H
index ad2f5e3cd968f890103eb9cd1699e67e6f9f831a..3f697e184456ffe3308b33fc1fe7d59814f88f59 100644
--- a/src/OpenFOAM/primitives/Vector/VectorI.H
+++ b/src/OpenFOAM/primitives/Vector/VectorI.H
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2018-2020 OpenCFD Ltd.
+    Copyright (C) 2018-2022 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -112,7 +112,7 @@ inline Cmpt& Foam::Vector<Cmpt>::z()
 template<class Cmpt>
 inline const Foam::Vector<Cmpt>& Foam::Vector<Cmpt>::centre
 (
-    const Foam::List<Vector<Cmpt>>&
+    const Foam::UList<Vector<Cmpt>>&
 ) const
 {
     return *this;
@@ -120,11 +120,11 @@ inline const Foam::Vector<Cmpt>& Foam::Vector<Cmpt>::centre
 
 
 template<class Cmpt>
-inline Foam::Vector<Cmpt>& Foam::Vector<Cmpt>::normalise()
+inline Foam::Vector<Cmpt>& Foam::Vector<Cmpt>::normalise(const scalar tol)
 {
     const scalar s(Foam::mag(*this));
 
-    if (s < ROOTVSMALL)
+    if (s < tol)
     {
         *this = Zero;
     }
@@ -137,6 +137,15 @@ inline Foam::Vector<Cmpt>& Foam::Vector<Cmpt>::normalise()
 }
 
 
+template<class Cmpt>
+inline Foam::Vector<Cmpt>&
+Foam::Vector<Cmpt>::removeCollinear(const Vector<Cmpt>& unitVec)
+{
+    *this -= (*this & unitVec) * unitVec;
+    return *this;
+}
+
+
 // * * * * * * * * * * * * * * * Global Operators  * * * * * * * * * * * * * //
 
 namespace Foam
diff --git a/src/OpenFOAM/primitives/Vector2D/Vector2D.H b/src/OpenFOAM/primitives/Vector2D/Vector2D.H
index 23664464d3ace407d868ea7fb81d90ce7b5d620e..f8126bbf49b74758271b404c899e07124cbc66b7 100644
--- a/src/OpenFOAM/primitives/Vector2D/Vector2D.H
+++ b/src/OpenFOAM/primitives/Vector2D/Vector2D.H
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2018-2021 OpenCFD Ltd.
+    Copyright (C) 2018-2022 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -37,8 +37,8 @@ SourceFiles
 
 \*---------------------------------------------------------------------------*/
 
-#ifndef Vector2D_H
-#define Vector2D_H
+#ifndef Foam_Vector2D_H
+#define Foam_Vector2D_H
 
 #include "contiguous.H"
 #include "VectorSpace.H"
@@ -116,8 +116,16 @@ public:
         //- Access to the vector y component
         inline Cmpt& y();
 
+
         //- Normalise the vector by its magnitude
-        inline Vector2D<Cmpt>& normalise();
+        //  For small magnitudes (less than ROOTVSMALL) set to zero.
+        //  Will not be particularly useful for a vector of labels
+        inline Vector2D<Cmpt>& normalise(const scalar tol = ROOTVSMALL);
+
+        //- Inplace removal of components that are collinear to the given
+        //- unit vector.
+        inline Vector2D<Cmpt>& removeCollinear(const Vector2D<Cmpt>& unitVec);
+
 
         //- Perp dot product (dot product with perpendicular vector)
         inline scalar perp(const Vector2D<Cmpt>& b) const;
diff --git a/src/OpenFOAM/primitives/Vector2D/Vector2DI.H b/src/OpenFOAM/primitives/Vector2D/Vector2DI.H
index 46599dfc4875678a651476c550bc796fb48e20a4..a5c63b818dabff0b95975f320cb35e95b91e0ebf 100644
--- a/src/OpenFOAM/primitives/Vector2D/Vector2DI.H
+++ b/src/OpenFOAM/primitives/Vector2D/Vector2DI.H
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2018-2021 OpenCFD Ltd.
+    Copyright (C) 2018-2022 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -91,11 +91,11 @@ inline Cmpt& Foam::Vector2D<Cmpt>::y()
 
 
 template<class Cmpt>
-inline Foam::Vector2D<Cmpt>& Foam::Vector2D<Cmpt>::normalise()
+inline Foam::Vector2D<Cmpt>& Foam::Vector2D<Cmpt>::normalise(const scalar tol)
 {
     const scalar s(Foam::mag(*this));
 
-    if (s < ROOTVSMALL)
+    if (s < tol)
     {
         *this = Zero;
     }
@@ -108,6 +108,15 @@ inline Foam::Vector2D<Cmpt>& Foam::Vector2D<Cmpt>::normalise()
 }
 
 
+template<class Cmpt>
+inline Foam::Vector2D<Cmpt>&
+Foam::Vector2D<Cmpt>::removeCollinear(const Vector2D<Cmpt>& unitVec)
+{
+    *this -= (*this & unitVec) * unitVec;
+    return *this;
+}
+
+
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
 namespace Foam
diff --git a/src/OpenFOAM/primitives/VectorSpace/VectorSpaceI.H b/src/OpenFOAM/primitives/VectorSpace/VectorSpaceI.H
index 5bde1e96e8bda48bbecf73c4e8dac72107b6f2a2..68a75cf0ff10d06d188c61dbc9cdd51d3d9849d2 100644
--- a/src/OpenFOAM/primitives/VectorSpace/VectorSpaceI.H
+++ b/src/OpenFOAM/primitives/VectorSpace/VectorSpaceI.H
@@ -482,6 +482,8 @@ inline scalar mag
 }
 
 
+//- Return the vector type normalised by its magnitude
+//  For small magnitudes (less than ROOTVSMALL) return zero.
 template<class Form, class Cmpt, direction Ncmpts>
 inline VectorSpace<Form, Cmpt, Ncmpts> normalised
 (
diff --git a/src/OpenFOAM/primitives/quaternion/quaternion.H b/src/OpenFOAM/primitives/quaternion/quaternion.H
index 9935d4c8cbe708fe529fc2b48bf03467c39858a4..4f0ab6b903e28adefbbd33e68944871daf133e63 100644
--- a/src/OpenFOAM/primitives/quaternion/quaternion.H
+++ b/src/OpenFOAM/primitives/quaternion/quaternion.H
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2019-2020 OpenCFD Ltd.
+    Copyright (C) 2019-2022 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -36,14 +36,15 @@ SourceFiles
 
 \*---------------------------------------------------------------------------*/
 
-#ifndef quaternion_H
-#define quaternion_H
+#ifndef Foam_quaternion_H
+#define Foam_quaternion_H
 
 #include "scalar.H"
 #include "vector.H"
 #include "tensor.H"
 #include "word.H"
 #include "Enum.H"
+#include "stdFoam.H"  // For deprecation macros
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
@@ -141,7 +142,7 @@ public:
 
     // Constructors
 
-        //- Construct zero initialized
+        //- Construct initialized to zero
         inline quaternion(const Foam::zero);
 
         //- Construct given scalar and vector parts
@@ -151,12 +152,12 @@ public:
         inline quaternion(const vector& d, const scalar theta);
 
         //- Construct a rotation quaternion given direction d
-        //- and cosine angle cosTheta and flag if d is normalized
+        //- and cosine angle cosTheta and flag if d is normalised
         inline quaternion
         (
             const vector& d,
             const scalar cosTheta,
-            const bool normalized
+            const bool isNormalised
         );
 
         //- Construct a real quaternion from the given scalar part,
@@ -185,33 +186,31 @@ public:
 
     // Access
 
-       //- Scalar part of the quaternion ( = cos(theta/2) for rotation)
-       inline scalar w() const;
-
-       //- Vector part of the quaternion ( = axis of rotation)
-       inline const vector& v() const;
+        //- Scalar part of the quaternion ( = cos(theta/2) for rotation)
+        inline scalar w() const noexcept;
 
-       //- The rotation tensor corresponding to the quaternion
-       inline tensor R() const;
+        //- Vector part of the quaternion ( = axis of rotation)
+        inline const vector& v() const noexcept;
 
-       //- Return the Euler rotation angles corresponding to the
-       //- specified rotation order
-       inline vector eulerAngles(const eulerOrder order) const;
+        //- The rotation tensor corresponding to the quaternion
+        inline tensor R() const;
 
-       //- Return the quaternion normalized by its magnitude
-       inline quaternion normalized() const;
+        //- Return the Euler rotation angles corresponding to the
+        //- specified rotation order
+        inline vector eulerAngles(const eulerOrder order) const;
 
 
     // Edit
 
-       //- Scalar part of the quaternion ( = cos(theta/2) for rotation)
-       inline scalar& w();
+        //- Scalar part of the quaternion ( = cos(theta/2) for rotation)
+        inline scalar& w() noexcept;
 
-       //- Vector part of the quaternion ( = axis of rotation)
-       inline vector& v();
+        //- Vector part of the quaternion ( = axis of rotation)
+        inline vector& v() noexcept;
 
-       //- Normalize the quaternion by its magnitude
-       inline void normalize();
+        //- Inplace normalise the quaternion by its magnitude
+        //  For small magnitudes (less than ROOTVSMALL) set to zero.
+        inline quaternion& normalise();
 
 
     // Transform
@@ -222,10 +221,10 @@ public:
        //- Rotate the given vector anti-clockwise
        inline vector invTransform(const vector& v) const;
 
-       //- Rotate the given quaternion (and normalize)
+       //- Rotate the given quaternion (and normalise)
        inline quaternion transform(const quaternion& q) const;
 
-       //- Rotate the given quaternion anti-clockwise (and normalize)
+       //- Rotate the given quaternion anti-clockwise (and normalise)
        inline quaternion invTransform(const quaternion& q) const;
 
 
@@ -242,8 +241,27 @@ public:
         //- Change vector portion only
         inline void operator=(const vector& v);
 
+        //- Assign scalar and vector to zero
+        inline void operator=(const Foam::zero);
+
         inline void operator*=(const scalar s);
         inline void operator/=(const scalar s);
+
+
+    // Housekeeping
+
+        //- Inplace normalise the quaternion by its magnitude
+        FOAM_DEPRECATED_FOR(2022-04, "normalise() method")
+        void normalize() { this->normalise(); }
+
+        //- Return the quaternion normalised by its magnitude
+        FOAM_DEPRECATED_FOR(2022-04, "Use Foam::normalised() function")
+        quaternion normalized() const
+        {
+            quaternion q(*this);
+            q.normalise();
+            return q;
+        }
 };
 
 
@@ -264,8 +282,12 @@ inline scalar mag(const quaternion& q);
 //- Return the conjugate of the given quaternion
 inline quaternion conjugate(const quaternion& q);
 
-//- Return the normalized (unit) quaternion of the given quaternion
-inline quaternion normalize(const quaternion& q);
+//- Return the normalised (unit) quaternion of the given quaternion
+inline quaternion normalised(const quaternion& q);
+
+//- Return the normalised (unit) quaternion of the given quaternion
+FOAM_DEPRECATED_FOR(2022-04, "Use Foam::normalised() function")
+inline quaternion normalize(const quaternion& q) { return Foam::normalised(q); }
 
 //- Return the inverse of the given quaternion
 inline quaternion inv(const quaternion& q);
diff --git a/src/OpenFOAM/primitives/quaternion/quaternionI.H b/src/OpenFOAM/primitives/quaternion/quaternionI.H
index 51efba620e9b5e71318137f5a740db09f7f8ff26..84592253e5c45a5277936c7098f72b145de96f27 100644
--- a/src/OpenFOAM/primitives/quaternion/quaternionI.H
+++ b/src/OpenFOAM/primitives/quaternion/quaternionI.H
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2019-2020 OpenCFD Ltd.
+    Copyright (C) 2019-2022 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -45,7 +45,7 @@ inline Foam::quaternion::quaternion(const scalar w, const vector& v)
 inline Foam::quaternion::quaternion(const vector& d, const scalar theta)
 :
     w_(cos(0.5*theta)),
-    v_((sin(0.5*theta)/mag(d))*d)
+    v_(sin(0.5*theta)*Foam::normalised(d))
 {}
 
 
@@ -53,19 +53,19 @@ inline Foam::quaternion::quaternion
 (
     const vector& d,
     const scalar cosTheta,
-    const bool normalized
+    const bool isNormalised
 )
 {
-    scalar cosHalfTheta2 = 0.5*(cosTheta + 1);
+    const scalar cosHalfTheta2 = 0.5*(cosTheta + 1);
     w_ = sqrt(cosHalfTheta2);
 
-    if (normalized)
+    if (isNormalised)
     {
         v_ = sqrt(1 - cosHalfTheta2)*d;
     }
     else
     {
-        v_ = (sqrt(1 - cosHalfTheta2)/mag(d))*d;
+        v_ = sqrt(1 - cosHalfTheta2)*Foam::normalised(d);
     }
 }
 
@@ -282,39 +282,43 @@ inline Foam::quaternion::quaternion
 
 // * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
 
-inline Foam::scalar Foam::quaternion::w() const
+inline Foam::scalar Foam::quaternion::w() const noexcept
 {
     return w_;
 }
 
 
-inline const Foam::vector& Foam::quaternion::v() const
+inline const Foam::vector& Foam::quaternion::v() const noexcept
 {
     return v_;
 }
 
 
-inline Foam::scalar& Foam::quaternion::w()
+inline Foam::scalar& Foam::quaternion::w() noexcept
 {
     return w_;
 }
 
 
-inline Foam::vector& Foam::quaternion::v()
+inline Foam::vector& Foam::quaternion::v() noexcept
 {
     return v_;
 }
 
 
-inline Foam::quaternion Foam::quaternion::normalized() const
+inline Foam::quaternion& Foam::quaternion::normalise()
 {
-    return *this/mag(*this);
-}
-
+    const scalar s(Foam::mag(*this));
 
-inline void Foam::quaternion::normalize()
-{
-    operator/=(mag(*this));
+    if (s < ROOTVSMALL)
+    {
+        *this = Zero;
+    }
+    else
+    {
+        *this /= s;
+    }
+    return *this;
 }
 
 
@@ -338,7 +342,7 @@ inline Foam::vector Foam::quaternion::invTransform(const vector& u) const
 
 inline Foam::quaternion Foam::quaternion::transform(const quaternion& q) const
 {
-    return Foam::normalize((*this)*q);
+    return Foam::normalised((*this)*q);
 }
 
 
@@ -347,7 +351,7 @@ inline Foam::quaternion Foam::quaternion::invTransform
     const quaternion& q
 ) const
 {
-    return Foam::normalize(conjugate(*this)*q);
+    return Foam::normalised(conjugate(*this)*q);
 }
 
 
@@ -618,6 +622,13 @@ inline void Foam::quaternion::operator=(const vector& v)
 }
 
 
+inline void Foam::quaternion::operator=(const Foam::zero)
+{
+    w_ = 0;
+    v_ = Zero;
+}
+
+
 inline void Foam::quaternion::operator*=(const scalar s)
 {
     w_ *= s;
@@ -653,14 +664,31 @@ inline Foam::quaternion Foam::conjugate(const quaternion& q)
 
 inline Foam::quaternion Foam::inv(const quaternion& q)
 {
-    scalar magSqrq = magSqr(q);
-    return quaternion(q.w()/magSqrq, -q.v()/magSqrq);
+    const scalar s(Foam::magSqr(q));
+
+    if (s < VSMALL)
+    {
+        return Zero;
+    }
+    else
+    {
+        return quaternion(q.w()/s, -q.v()/s);
+    }
 }
 
 
-inline Foam::quaternion Foam::normalize(const quaternion& q)
+inline Foam::quaternion Foam::normalised(const quaternion& q)
 {
-    return q/mag(q);
+    const scalar s(Foam::mag(q));
+
+    if (s < ROOTVSMALL)
+    {
+        return Zero;
+    }
+    else
+    {
+        return q/s;
+    }
 }
 
 
diff --git a/src/OpenFOAM/primitives/septernion/septernion.C b/src/OpenFOAM/primitives/septernion/septernion.C
index a291622c2577224ef190619882b744f40f3f17ff..10bad879548b490f291191cdc88241d7dbef098c 100644
--- a/src/OpenFOAM/primitives/septernion/septernion.C
+++ b/src/OpenFOAM/primitives/septernion/septernion.C
@@ -94,7 +94,7 @@ Foam::septernion Foam::average
         }
     }
 
-    sa.r().normalize();
+    sa.r().normalise();
 
     return sa;
 }
diff --git a/src/OpenFOAM/primitives/triad/triad.C b/src/OpenFOAM/primitives/triad/triad.C
index 018e721c0fafca986b6c545ccc029e1b21771606..f8036b7b502e952af5c846893ca5882c4e9af6cb 100644
--- a/src/OpenFOAM/primitives/triad/triad.C
+++ b/src/OpenFOAM/primitives/triad/triad.C
@@ -99,7 +99,7 @@ Foam::triad::triad(const quaternion& q)
 
 // * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
 
-void Foam::triad::orthogonalize()
+void Foam::triad::orthogonalise()
 {
     // Hack for 2D z-slab cases
     // if (!set(2))
diff --git a/src/OpenFOAM/primitives/triad/triad.H b/src/OpenFOAM/primitives/triad/triad.H
index b158b42531418883a09e65b29bbf030be202ae7f..8ed84b6bce295aaa0cfd94e8e8a9488b419d2615 100644
--- a/src/OpenFOAM/primitives/triad/triad.H
+++ b/src/OpenFOAM/primitives/triad/triad.H
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2012-2016 OpenFOAM Foundation
-    Copyright (C) 2018-2019 OpenCFD Ltd.
+    Copyright (C) 2018-2022 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -40,8 +40,8 @@ SourceFiles
 
 \*---------------------------------------------------------------------------*/
 
-#ifndef triad_H
-#define triad_H
+#ifndef Foam_triad_H
+#define Foam_triad_H
 
 #include "vector.H"
 #include "tensor.H"
@@ -70,7 +70,7 @@ public:
 
     // Constructors
 
-        //- Construct null
+        //- Default construct
         inline triad();
 
         //- Construct from components
@@ -112,11 +112,12 @@ public:
         //- Return the vector orthogonal to the two provided
         static inline vector orthogonal(const vector& v1, const vector& v2);
 
-        //- Orthogonalize this triad so that it is ortho-normal
-        void orthogonalize();
+        //- Inplace orthogonalise so that it is ortho-normal
+        void orthogonalise();
 
-        //- Normalize each set axis vector to have a unit magnitude
-        void normalize();
+        //- Normalise each set axis vector to have a unit magnitude
+        //  Uses vector::normalise with handling of small magnitudes.
+        void normalise();
 
         //- Align this triad with the given vector v
         //  by rotating the most aligned axis to be coincident with v
@@ -151,7 +152,7 @@ public:
         inline void operator=(const tensor& t);
 
         //- Add the triad t2 to this triad
-        //  without normalizing or orthogonalizing
+        //  without normalising or orthogonalising
         void operator+=(const triad& t2);
 
 
@@ -159,6 +160,15 @@ public:
 
         friend Istream& operator>>(Istream&, triad&);
         friend Ostream& operator<<(Ostream&, const triad&);
+
+
+    // Housekeeping
+
+        //- Same as orthogonalise
+        void orthogonalize() { this->orthogonalise(); }
+
+        //- Same as normalise
+        void normalize() { this->normalise(); }
 };
 
 
diff --git a/src/OpenFOAM/primitives/triad/triadI.H b/src/OpenFOAM/primitives/triad/triadI.H
index 3cde6246b1d31e8680089444af970ec357a67ab8..8debae6ce1b04db6f2b8626ca88e78e8fe1ecf4f 100644
--- a/src/OpenFOAM/primitives/triad/triadI.H
+++ b/src/OpenFOAM/primitives/triad/triadI.H
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2012-2016 OpenFOAM Foundation
-    Copyright (C) 2018 OpenCFD Ltd.
+    Copyright (C) 2018-2022 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -117,11 +117,11 @@ inline Foam::vector Foam::triad::orthogonal
 }
 
 
-inline void Foam::triad::normalize()
+inline void Foam::triad::normalise()
 {
-    if (set(0)) operator[](0) /= mag(operator[](0));
-    if (set(1)) operator[](1) /= mag(operator[](1));
-    if (set(2)) operator[](2) /= mag(operator[](2));
+    if (set(0)) (*this)[0].normalise();
+    if (set(1)) (*this)[1].normalise();
+    if (set(2)) (*this)[2].normalise();
 }
 
 
diff --git a/src/rigidBodyDynamics/rigidBodySolvers/rigidBodySolver/rigidBodySolver.C b/src/rigidBodyDynamics/rigidBodySolvers/rigidBodySolver/rigidBodySolver.C
index a4f9579133cc07a761620f2bfb1982ad9d04ebb5..1a08097d7b2634009382a69d7eb6580f60dbed6e 100644
--- a/src/rigidBodyDynamics/rigidBodySolvers/rigidBodySolver/rigidBodySolver.C
+++ b/src/rigidBodyDynamics/rigidBodySolvers/rigidBodySolver/rigidBodySolver.C
@@ -78,8 +78,9 @@ void Foam::RBD::rigidBodySolver::correctQuaternionJoints()
                     // Transform the previous time unit quaternion
                     quaternion quat
                     (
-                        normalize(model_.joints()[i].unitQuaternion(q0())*dQuat)
+                        model_.joints()[i].unitQuaternion(q0())*dQuat
                     );
+                    quat.normalise();
 
                     // Update the joint unit quaternion
                     model_.joints()[i].unitQuaternion(quat, q());