From d68902f4a735417f9ee996835880aa00aafb93ad Mon Sep 17 00:00:00 2001
From: Mark Olesen <Mark.Olesen@esi-group.com>
Date: Mon, 9 May 2022 11:48:23 +0200
Subject: [PATCH] ENH: relocate Foam::sort from PtrListOps to UPtrList.H

- can sort directly without ListOps or other intermediates
  (eg labelList order).

- PtrListOps::less/greater wrappers -> UPtrList::less/greater
---
 applications/test/PtrList/Test-PtrList.C      | 163 ++++++++++--------
 .../PtrLists/PtrListDetail/PtrListDetail.C    |   3 +
 .../PtrLists/PtrListDetail/PtrListDetail.H    |   9 +-
 .../PtrLists/PtrListDetail/PtrListDetailI.H   |  14 ++
 .../PtrLists/PtrListOps/PtrListOps.H          |  66 +------
 .../PtrLists/PtrListOps/PtrListOpsTemplates.C |  48 ++----
 .../containers/PtrLists/UPtrList/UPtrList.C   |  84 ++++-----
 .../containers/PtrLists/UPtrList/UPtrList.H   | 113 ++++++++++--
 .../containers/PtrLists/UPtrList/UPtrListI.H  |   7 +
 9 files changed, 294 insertions(+), 213 deletions(-)

diff --git a/applications/test/PtrList/Test-PtrList.C b/applications/test/PtrList/Test-PtrList.C
index 488618e0b37..a8cf8686afb 100644
--- a/applications/test/PtrList/Test-PtrList.C
+++ b/applications/test/PtrList/Test-PtrList.C
@@ -59,7 +59,7 @@ public:
 
     ~Scalar()
     {
-        Info<<"delete Scalar: " << data_ << endl;
+        Info<< "delete Scalar: " << data_ << endl;
     }
 
     const scalar& value() const
@@ -324,7 +324,7 @@ int main(int argc, char *argv[])
         list1.set(i, new Scalar(1.3*i));
     }
 
-    Info<<"Emplace set " << list2.size() << " values" << nl;
+    Info<< "Emplace set " << list2.size() << " values" << nl;
     forAll(list2, i)
     {
         list2.emplace(i, (10 + 1.3*i));
@@ -338,9 +338,9 @@ int main(int argc, char *argv[])
 
 
     Info<< nl
-        <<"list1: " << list1 << nl
-        <<"list2: " << list2 << nl
-        <<"list-appended: " << listApp << endl;
+        << "list1: " << list1 << nl
+        << "list2: " << list2 << nl
+        << "list-appended: " << listApp << endl;
 
 
     // Release values
@@ -357,27 +357,27 @@ int main(int argc, char *argv[])
             }
         }
 
-        Info<<"Released pointers from";
+        Info<< "Released pointers from";
         print(Info, listApp) << nl;
 
-        Info<<"Into plain list of pointers";
+        Info<< "Into plain list of pointers";
         print(Info, ptrs) << nl;
 
         PtrDynList<Scalar> newlist1(ptrs);
 
-        Info<<"Constructed from plain list of pointers";
+        Info<< "Constructed from plain list of pointers";
         print(Info, ptrs) << nl;
         print(Info, newlist1) << nl;
     }
 
 
-    Info<<"indirectly delete some items via set(.., nullptr) :" << endl;
+    Info<< "indirectly delete some items via set(.., nullptr) :" << endl;
     for (label i = 2; i < 5; i++)
     {
         list1.set(i, nullptr);
     }
 
-    Info<<"release some items:" << endl;
+    Info<< "release some items:" << endl;
 
     for (label i = -2; i < 5; i++)
     {
@@ -389,31 +389,31 @@ int main(int argc, char *argv[])
         }
     }
 
-    Info<<"list1: ";
+    Info<< "list1: ";
     print(Info, list1) << nl;
 
     list1.resize(list1.squeezeNull());
-    Info<<"squeezed null: ";
+    Info<< "squeezed null: ";
     print(Info, list1) << nl;
 
-    Info<<"transfer list2 -> list1:" << endl;
+    Info<< "transfer list2 -> list1:" << endl;
     list1.transfer(list2);
 
-    Info<<"list1: " << list1 << nl
-        <<"list2: " << list2 << endl;
+    Info<< "list1: " << list1 << nl
+        << "list2: " << list2 << endl;
 
-    Info<<"indirectly delete some items via setSize :" << endl;
+    Info<< "indirectly delete some items via setSize :" << endl;
     list1.setSize(4);
 
-    Info<<"list1: " << list1 << endl;
+    Info<< "list1: " << list1 << endl;
 
     {
         PtrList<Scalar> list1a(list1, false);
 
-        Info<<"Clone constructed" << endl;
-        Info<<"in:  " << list1 << nl
-            <<"out: " << list1a << nl
-            <<"addresses:" << nl;
+        Info<< "Clone constructed" << endl;
+        Info<< "in:  " << list1 << nl
+            << "out: " << list1a << nl
+            << "addresses:" << nl;
         printAddr(Info, list1);
         printAddr(Info, list1a);
         Info<<"values:" << nl;
@@ -423,112 +423,112 @@ int main(int argc, char *argv[])
         {
             auto* ptr = &(list1a.first());
             list1a.set(0, ptr);
-            Info<<"values:" << nl;
+            Info<< "values:" << nl;
             print(Info, list1a);
         }
 
 
         PtrList<Scalar> list1b(list1a, true);
 
-        Info<<"Reuse constructed" << endl;
-        Info<<"in:  " << list1a << nl
-            <<"out: " << list1b << nl
-            <<"addresses:" << nl;
+        Info<< "Reuse constructed" << endl;
+        Info<< "in:  " << list1a << nl
+            << "out: " << list1b << nl
+            << "addresses:" << nl;
         printAddr(Info, list1a);
         printAddr(Info, list1b);
 
 
         PtrList<Scalar> list1c(list1b.clone());
 
-        Info<<"Explicit clone()" << endl;
-        Info<<"in:  " << list1b << nl
-            <<"out: " << list1c << nl
-            <<"addresses:" << nl;
+        Info<< "Explicit clone()" << endl;
+        Info<< "in:  " << list1b << nl
+            << "out: " << list1c << nl
+            << "addresses:" << nl;
         printAddr(Info, list1b);
         printAddr(Info, list1c);
     }
 
     PtrList<Scalar> list3(std::move(list1));
-    Info<<"Move constructed" << endl;
+    Info<< "Move constructed" << endl;
 
-    Info<<"list1: " << list1 << nl
-        <<"list2: " << list2 << nl
-        <<"list3: " << list3 << endl;
+    Info<< "list1: " << list1 << nl
+        << "list2: " << list2 << nl
+        << "list3: " << list3 << endl;
 
 
-    Info<<"Move construct:" << endl;
+    Info<< "Move construct:" << endl;
 
     PtrList<Scalar> list4(std::move(list3));
 
-    Info<<"list3: " << list3 << nl
-        <<"list4: " << list4 << endl;
+    Info<< "list3: " << list3 << nl
+        << "list4: " << list4 << endl;
 
-    Info<<"Move assign:" << endl;
+    Info<< "Move assign:" << endl;
     list3 = std::move(list4);
 
-    Info<<"list3: " << list3 << nl
-        <<"list4: " << list4 << endl;
+    Info<< "list3: " << list3 << nl
+        << "list4: " << list4 << endl;
 
 
-    Info<<"UPtrList from PtrList" << nl;
+    Info<< "UPtrList from PtrList" << nl;
 
     UPtrList<Scalar> ulist1(list3);
 
-    Info<<"ulist1: " << ulist1 << nl;
-    Info<<"PtrList addresses:";
+    Info<< "ulist1: " << ulist1 << nl;
+    Info<< "PtrList addresses:";
     printAddr(Info, list3);
-    Info<<"UPtrList addresses:";
+    Info<< "UPtrList addresses:";
     printAddr(Info, ulist1);
     Info<< nl;
 
     {
-        Info<<"UPtrList(const UPtrList&)" << nl;
+        Info<< "UPtrList(const UPtrList&)" << nl;
 
         const UPtrList<Scalar>& cref = ulist1;
 
         UPtrList<Scalar> ulist1cp(cref);
 
-        Info<<"src addresses:";
+        Info<< "src addresses:";
         printAddr(Info, cref);
-        Info<<"dst addresses:";
+        Info<< "dst addresses:";
         printAddr(Info, ulist1cp);
         Info<< nl;
     }
 
 
-    Info<<"Move construct:" << endl;
+    Info<< "Move construct:" << endl;
 
     UPtrList<Scalar> ulist2(std::move(ulist1));
 
-    Info<<"ulist1: " << ulist1 << nl
-        <<"ulist2: " << ulist2 << nl;
+    Info<< "ulist1: " << ulist1 << nl
+        << "ulist2: " << ulist2 << nl;
 
-    Info<<"Copy assign:" << endl;
+    Info<< "Copy assign:" << endl;
     ulist1 = ulist2;
 
-    Info<<"ulist1: " << ulist1 << nl
-        <<"ulist2: " << ulist2 << nl;
+    Info<< "ulist1: " << ulist1 << nl
+        << "ulist2: " << ulist2 << nl;
 
-    Info<<"Move assign:" << endl;
+    Info<< "Move assign:" << endl;
     ulist1 = std::move(ulist2);
 
-    Info<<"ulist1: " << ulist1 << nl
-        <<"ulist2: " << ulist2 << nl;
+    Info<< "ulist1: " << ulist1 << nl
+        << "ulist2: " << ulist2 << nl;
 
     // Test iterator random access
     {
         auto iter1 = ulist1.begin();
         auto iter2 = iter1 + 3;
 
-        Info<<"begin:" << *iter1 << " (+3):" << *iter2 << nl;
+        Info<< "begin:" << *iter1 << " (+3):" << *iter2 << nl;
         Info<< "diff= " << (iter1 - iter2) << nl;
         Info<< "iter[2]=" << iter1[2] << nl;
         Info<< "iter1 < iter2 : " << (iter1 < iter2) << nl;
         Info<< "iter1 >= iter2 : " << (iter1 >= iter2) << nl;
 
-        Info<<"->" << iter1->value() << nl;
-        Info<<"*"  << (*iter1).value() << nl;
-        Info<<"()" << iter1().value() << nl;
+        Info<< "->" << iter1->value() << nl;
+        Info<< "*"  << (*iter1).value() << nl;
+        Info<< "()" << iter1().value() << nl;
     }
 
     PtrList<plane> planes;
@@ -541,7 +541,7 @@ int main(int argc, char *argv[])
         Info<< "    plane " << p << endl;
     }
 
-    Info<<"Testing PtrDynList" << nl;
+    Info<< "Testing PtrDynList" << nl;
 
     PtrDynList<plane> dynPlanes;
 
@@ -566,10 +566,10 @@ int main(int argc, char *argv[])
     report(Info, dynPlanes, true);
 
     dynPlanes.clear();
-    Info<<"clear()" << nl;
+    Info<< "clear()" << nl;
     report(Info, dynPlanes);
 
-    Info<<"now append again" << endl;
+    Info<< "now append again" << endl;
     {
         dynPlanes.append(new plane(vector::one, vector::one));
         dynPlanes.append(new plane(vector(1,2,3), vector::one));
@@ -597,13 +597,13 @@ int main(int argc, char *argv[])
 
         stdPlanes.resize(stdPlanes.squeezeNull());
 
-        Info<<"After pruning nullptr entries" << endl;
+        Info<< "After pruning nullptr entries" << endl;
         printAddr(Info, stdPlanes);
     }
 
     dynPlanes.resize(dynPlanes.squeezeNull());
 
-    Info<<"After pruning nullptr entries" << endl;
+    Info<< "After pruning nullptr entries" << endl;
     report(Info, dynPlanes, true);
 
 
@@ -621,25 +621,48 @@ int main(int argc, char *argv[])
         sortedOrder(dynPlanes2, order);
         Info<< "sorted order: " << flatOutput(order) << nl;
 
-        sortedOrder(dynPlanes2, order, PtrListOps::greater<plane>(dynPlanes2));
+        sortedOrder(dynPlanes2, order, UPtrList<plane>::greater(dynPlanes2));
         Info<< "sorted order: " << flatOutput(order) << nl;
 
+        // Shuffle
+        shuffle(dynPlanes2);
+        Info<< "Shuffled" << endl;
+        report(Info, dynPlanes2, false);
+
+        // Reverse sort
+        sort
+        (
+            dynPlanes2,
+            [](const plane& a, const plane& b) { return (b < a); }
+        );
+        Info<< "Reverse sorted" << endl;
+        report(Info, dynPlanes2, false);
+
+        // Forward sort
         sort(dynPlanes2);
+        Info<< "Sorted" << endl;
+        report(Info, dynPlanes2, false);
+
+        // Reverse pointer list - not yet available or needed!
+        /// reverse(dynPlanes2);
+        /// Info<< "Reversed" << endl;
+        /// report(Info, dynPlanes2, false);
+
         // dynPlanes2.squeezeNull();
 
-        Info<<"Append" << endl;
+        Info<< "Append" << endl;
         report(Info, dynPlanes2, false);
 
         dynPlanes.append(std::move(dynPlanes2));
 
-        Info<<"Result" << endl;
+        Info<< "Result" << endl;
         report(Info, dynPlanes, false);
 
-        Info<<"From" << endl;
+        Info<< "From" << endl;
         report(Info, dynPlanes2, false);
     }
 
-    Info<<"free()" << endl;
+    Info<< "free()" << endl;
 
     dynPlanes.free();
     report(Info, dynPlanes, true);
diff --git a/src/OpenFOAM/containers/PtrLists/PtrListDetail/PtrListDetail.C b/src/OpenFOAM/containers/PtrLists/PtrListDetail/PtrListDetail.C
index a72ec9461a6..a71e0f4a198 100644
--- a/src/OpenFOAM/containers/PtrLists/PtrListDetail/PtrListDetail.C
+++ b/src/OpenFOAM/containers/PtrLists/PtrListDetail/PtrListDetail.C
@@ -50,6 +50,9 @@ Foam::label Foam::Detail::PtrListDetail<T>::count() const
 template<class T>
 Foam::label Foam::Detail::PtrListDetail<T>::findNull() const
 {
+    // Same as  List<T*>::find(nullptr);
+    // except perhaps without pointer ambiguities...
+
     label idx = 0;
 
     for (const T* ptr : *this)
diff --git a/src/OpenFOAM/containers/PtrLists/PtrListDetail/PtrListDetail.H b/src/OpenFOAM/containers/PtrLists/PtrListDetail/PtrListDetail.H
index 8e7ccf43722..70b4637c3b1 100644
--- a/src/OpenFOAM/containers/PtrLists/PtrListDetail/PtrListDetail.H
+++ b/src/OpenFOAM/containers/PtrLists/PtrListDetail/PtrListDetail.H
@@ -42,8 +42,8 @@ SourceFiles
 
 \*---------------------------------------------------------------------------*/
 
-#ifndef PtrListDetail_H
-#define PtrListDetail_H
+#ifndef Foam_PtrListDetail_H
+#define Foam_PtrListDetail_H
 
 #include "List.H"
 
@@ -92,9 +92,12 @@ public:
         //- Return the count of non-nullptr entries
         label count() const;
 
-        //- Locate the first null entry, -1 if there are not any
+        //- Locate the first null entry, -1 if there are none (or empty list)
         label findNull() const;
 
+        //- FatalError if any null exists in the list
+        inline void checkNonNull() const;
+
         //- Assign all pointers to nullptr, without deleting.
         void setNull();
 
diff --git a/src/OpenFOAM/containers/PtrLists/PtrListDetail/PtrListDetailI.H b/src/OpenFOAM/containers/PtrLists/PtrListDetail/PtrListDetailI.H
index a8777f39ad9..7b5425c1f48 100644
--- a/src/OpenFOAM/containers/PtrLists/PtrListDetail/PtrListDetailI.H
+++ b/src/OpenFOAM/containers/PtrLists/PtrListDetail/PtrListDetailI.H
@@ -109,6 +109,20 @@ inline void Foam::Detail::PtrListDetail<T>::resize(const label newLen)
 }
 
 
+template<class T>
+inline void Foam::Detail::PtrListDetail<T>::checkNonNull() const
+{
+    const label idx = this->findNull();
+
+    if (idx >= 0)
+    {
+        FatalErrorInFunction
+            << "Element " << idx << " is null" << nl
+            << abort(FatalError);
+    }
+}
+
+
 // * * * * * * * * * * * * * * * Member Operators  * * * * * * * * * * * * * //
 
 template<class T>
diff --git a/src/OpenFOAM/containers/PtrLists/PtrListOps/PtrListOps.H b/src/OpenFOAM/containers/PtrLists/PtrListOps/PtrListOps.H
index 8bd574602d3..bdc9df5f717 100644
--- a/src/OpenFOAM/containers/PtrLists/PtrListOps/PtrListOps.H
+++ b/src/OpenFOAM/containers/PtrLists/PtrListOps/PtrListOps.H
@@ -5,7 +5,7 @@
     \\  /    A nd           | www.openfoam.com
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
-    Copyright (C) 2019-2021 OpenCFD Ltd.
+    Copyright (C) 2019-2022 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -40,8 +40,8 @@ SourceFiles
 
 \*---------------------------------------------------------------------------*/
 
-#ifndef PtrListOps_H
-#define PtrListOps_H
+#ifndef Foam_PtrListOps_H
+#define Foam_PtrListOps_H
 
 #include "predicates.H"
 #include "PtrList.H"
@@ -54,31 +54,23 @@ namespace Foam
 
 //- Return (stable) sort order for the list
 template<class T>
-labelList sortedOrder(const UPtrList<T>& input);
+labelList sortedOrder(const UPtrList<T>& list);
 
 //- Generate (stable) sort order for the list
 template<class T>
-void sortedOrder(const UPtrList<T>& input, labelList& order);
+void sortedOrder(const UPtrList<T>& list, labelList& order);
 
 //- Generate (stable) sort order for the list,
 //- using the specified list compare predicate
 template<class T, class ListComparePredicate>
 void sortedOrder
 (
-    const UPtrList<T>& input,
+    const UPtrList<T>& list,
     labelList& order,
     const ListComparePredicate& comp
 );
 
 
-//- Inplace (stable) sorting of pointer list.
-template<class T>
-void sort(UPtrList<T>& list);
-
-//- Inplace (stable) sorting of pointer list.
-template<class T, class Compare>
-void sort(UPtrList<T>& list, const Compare& comp);
-
 //- Inplace shuffle of pointer list.
 template<class T>
 void shuffle(UPtrList<T>& list);
@@ -91,52 +83,6 @@ void shuffle(UPtrList<T>& list);
 namespace PtrListOps
 {
 
-// Public Classes
-
-//- A UPtrList compare binary predicate for normal sort.
-//  Null entries sort to the end
-template<class T>
-struct less
-{
-    const UPtrList<T>& values;
-
-    less(const UPtrList<T>& list)
-    :
-        values(list)
-    {}
-
-    bool operator()(const label a, const label b) const
-    {
-        const T* const ptr1 = values(a);
-        const T* const ptr2 = values(b);
-
-        return (ptr1 && ptr2) ? (*ptr1 < *ptr2) : !ptr2;
-    }
-};
-
-
-//- A UPtrList compare binary predicate for reverse sort.
-//  Null entries sort to the end
-template<class T>
-struct greater
-{
-    const UPtrList<T>& values;
-
-    greater(const UPtrList<T>& list)
-    :
-        values(list)
-    {}
-
-    bool operator()(const label a, const label b) const
-    {
-        const T* const ptr1 = values(a);
-        const T* const ptr2 = values(b);
-
-        return (ptr1 && ptr2) ? (*ptr2 < *ptr1) : !ptr2;
-    }
-};
-
-
 //- List of values generated by applying the access operation
 //- to each list item.
 //
diff --git a/src/OpenFOAM/containers/PtrLists/PtrListOps/PtrListOpsTemplates.C b/src/OpenFOAM/containers/PtrLists/PtrListOps/PtrListOpsTemplates.C
index aec159d25f7..85d6fedf70f 100644
--- a/src/OpenFOAM/containers/PtrLists/PtrListOps/PtrListOpsTemplates.C
+++ b/src/OpenFOAM/containers/PtrLists/PtrListOps/PtrListOpsTemplates.C
@@ -5,7 +5,7 @@
     \\  /    A nd           | www.openfoam.com
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
-    Copyright (C) 2019-2021 OpenCFD Ltd.
+    Copyright (C) 2019-2022 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -32,11 +32,11 @@ License
 template<class T>
 Foam::labelList Foam::sortedOrder
 (
-    const UPtrList<T>& input
+    const UPtrList<T>& list
 )
 {
     labelList order;
-    sortedOrder(input, order, typename PtrListOps::less<T>(input));
+    Foam::sortedOrder(list, order, typename UPtrList<T>::less(list));
     return order;
 }
 
@@ -44,59 +44,49 @@ Foam::labelList Foam::sortedOrder
 template<class T>
 void Foam::sortedOrder
 (
-    const UPtrList<T>& input,
+    const UPtrList<T>& list,
     labelList& order
 )
 {
-    sortedOrder(input, order, typename PtrListOps::less<T>(input));
+    Foam::sortedOrder(list, order, typename UPtrList<T>::less(list));
 }
 
 
 template<class T, class ListComparePredicate>
 void Foam::sortedOrder
 (
-    const UPtrList<T>& input,
+    const UPtrList<T>& list,
     labelList& order,
     const ListComparePredicate& comp
 )
 {
     // List lengths must be identical. Old content is overwritten
-    order.resize_nocopy(input.size());
-
-    ListOps::identity(order);
-
-    Foam::stableSort(order, comp);
-}
-
-
-template<class T>
-void Foam::sort(UPtrList<T>& list)
-{
-    labelList order;
-    sortedOrder(list, order);
-    list.sortOrder(order, false);  // false = allow nullptr
-}
+    order.resize_nocopy(list.size());
 
+    // Same as std::iota and ListOps::identity
+    label value = 0;
+    for (label& item : order)
+    {
+        item = value;
+        ++value;
+    }
 
-template<class T, class Compare>
-void Foam::sort(UPtrList<T>& list, const Compare& comp)
-{
-    labelList order;
-    sortedOrder(list, order, comp);
-    list.sortOrder(order, false);  // false = allow nullptr
+    std::stable_sort(order.begin(), order.end(), comp);
 }
 
 
 template<class T>
 void Foam::shuffle(UPtrList<T>& list)
 {
+    // Cannot use std::shuffle directly since that would dereference
+    // the list entries, which may contain null pointers.
+    // The alternative would be to expose the pointer details (a bit ugly).
     labelList order(identity(list.size()));
     Foam::shuffle(order);
-    list.sortOrder(order, false);  // false = allow nullptr
+    list.sortOrder(order, false);  // false = no nullptr check
 }
 
 
-
 // Templated implementation for types(), names(), etc - file-scope
 template<class ReturnType, class T, class AccessOp>
 Foam::List<ReturnType> Foam::PtrListOps::get
diff --git a/src/OpenFOAM/containers/PtrLists/UPtrList/UPtrList.C b/src/OpenFOAM/containers/PtrLists/UPtrList/UPtrList.C
index 476054ef504..9ae4fcdec70 100644
--- a/src/OpenFOAM/containers/PtrLists/UPtrList/UPtrList.C
+++ b/src/OpenFOAM/containers/PtrLists/UPtrList/UPtrList.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2015-2019 OpenCFD Ltd.
+    Copyright (C) 2015-2022 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -66,11 +66,7 @@ Foam::label Foam::UPtrList<T>::squeezeNull()
 
 
 template<class T>
-void Foam::UPtrList<T>::reorder
-(
-    const labelUList& oldToNew,
-    const bool testNull
-)
+void Foam::UPtrList<T>::reorder(const labelUList& oldToNew, const bool check)
 {
     const label len = this->size();
 
@@ -87,37 +83,31 @@ void Foam::UPtrList<T>::reorder
 
     for (label i=0; i<len; ++i)
     {
-        const label idx = oldToNew[i];
+        const label newIdx = oldToNew[i];
 
-        if (idx < 0 || idx >= len)
+        if (newIdx < 0 || newIdx >= len)
         {
             FatalErrorInFunction
-                << "Illegal index " << idx << nl
+                << "Illegal index " << newIdx << nl
                 << "Valid indices are [0," << len << ") for type "
                 << typeid(T).name() << nl
                 << abort(FatalError);
         }
 
-        if (newList[idx])
+        if (newList[newIdx])
         {
             FatalErrorInFunction
-                << "reorder map is not unique; element " << idx
+                << "reorder map is not unique; element " << newIdx
                 << " already used for type " << typeid(T).name()
                 << abort(FatalError);
         }
-        newList[idx] = ptrs_[i];
+        newList[newIdx] = ptrs_[i];
     }
 
-    // Verify that all pointers were indeed set
-    if (testNull)
+    // Verify all pointers were indeed set
+    if (check)
     {
-        const label idx = newList.findNull();
-        if (idx >= 0)
-        {
-            FatalErrorInFunction
-                << "Element " << idx << " not set after reordering." << nl
-                << abort(FatalError);
-        }
+        newList.checkNonNull();
     }
 
     ptrs_.transfer(newList);
@@ -125,11 +115,7 @@ void Foam::UPtrList<T>::reorder
 
 
 template<class T>
-void Foam::UPtrList<T>::sortOrder
-(
-    const labelUList& order,
-    const bool testNull
-)
+void Foam::UPtrList<T>::sortOrder(const labelUList& order, const bool check)
 {
     const label len = this->size();
 
@@ -147,39 +133,33 @@ void Foam::UPtrList<T>::sortOrder
 
     for (label i=0; i<len; ++i)
     {
-        const label idx = order[i];
+        const label oldIdx = order[i];
 
-        if (idx < 0 || idx >= len)
+        if (oldIdx < 0 || oldIdx >= len)
         {
             FatalErrorInFunction
-                << "Illegal index " << idx << nl
+                << "Illegal index " << oldIdx << nl
                 << "Valid indices are [0," << len << ") for type "
                 << typeid(T).name() << nl
                 << abort(FatalError);
         }
 
-        if (guard[idx])
+        if (guard[oldIdx])
         {
             FatalErrorInFunction
-                << "order map is not unique; element " << idx
+                << "order map is not unique; element " << oldIdx
                 << " already used for type " << typeid(T).name()
                 << abort(FatalError);
         }
 
-        guard[idx] = ptrs_[idx];
-        newList[i] = ptrs_[idx];
+        guard[oldIdx] = ptrs_[oldIdx];
+        newList[i] = ptrs_[oldIdx];
     }
 
     // Verify that all pointers were indeed set
-    if (testNull)
+    if (check)
     {
-        const label idx = newList.findNull();
-        if (idx >= 0)
-        {
-            FatalErrorInFunction
-                << "Element " << idx << " not set after reordering." << nl
-                << abort(FatalError);
-        }
+        newList.checkNonNull();
     }
 
     ptrs_.transfer(newList);
@@ -195,4 +175,26 @@ Foam::Ostream& Foam::operator<<(Ostream& os, const UPtrList<T>& list)
 }
 
 
+// * * * * * * * * * * * * * * * Global Functions  * * * * * * * * * * * * * //
+
+template<class T, class Compare>
+void Foam::sort(UPtrList<T>& list, const Compare& comp)
+{
+    std::stable_sort
+    (
+        list.begin_ptr(),
+        list.end_ptr(),
+        typename UPtrList<T>::template value_compare<Compare>(comp)
+    );
+}
+
+
+template<class T>
+void Foam::sort(UPtrList<T>& list)
+{
+    // ie, lessOp<T>() or std::less<T>()
+    Foam::sort(list, [](const T& a, const T& b) { return (a < b); });
+}
+
+
 // ************************************************************************* //
diff --git a/src/OpenFOAM/containers/PtrLists/UPtrList/UPtrList.H b/src/OpenFOAM/containers/PtrLists/UPtrList/UPtrList.H
index 0cf86426268..4f88822e985 100644
--- a/src/OpenFOAM/containers/PtrLists/UPtrList/UPtrList.H
+++ b/src/OpenFOAM/containers/PtrLists/UPtrList/UPtrList.H
@@ -110,6 +110,72 @@ public:
         class const_iterator;
 
 
+    // Public Classes
+
+        //- A wrapper for a binary comparison of values that interjects
+        //- pointer dereferencing with null pointer guards.
+        //  It will also sort any null pointers to the end
+        //  (eg, rubbish that can be truncated)
+        template<class Compare>
+        struct value_compare
+        {
+            const Compare& comp;
+
+            value_compare(const Compare& cmp)
+            :
+                comp(cmp)
+            {}
+
+            //- Compare dereferenced pointers
+            bool operator()(const T* const a, const T* const b) const
+            {
+                return (a && b) ? comp(*a, *b) : bool(a);
+            }
+        };
+
+        //- A UPtrList compare binary predicate for normal sort order.
+        //- Null entries (if any) sort to the end.
+        struct less
+        {
+            const UPtrList<T>& values;
+
+            less(const UPtrList<T>& list)
+            :
+                values(list)
+            {}
+
+            //- Compare dereferenced pointer locations for normal sort.
+            bool operator()(const label ai, const label bi) const
+            {
+                const T* const a = values.get(ai);
+                const T* const b = values.get(bi);
+
+                return (a && b) ? (*a < *b) : bool(a);
+            }
+        };
+
+        //- A UPtrList compare binary predicate for reverse sort order.
+        //  Null entries (if any) sort to the end.
+        struct greater
+        {
+            const UPtrList<T>& values;
+
+            greater(const UPtrList<T>& list)
+            :
+                values(list)
+            {}
+
+            //- Compare dereferenced pointer locations for reverse sort
+            bool operator()(const label ai, const label bi) const
+            {
+                const T* const a = values.get(ai);
+                const T* const b = values.get(bi);
+
+                return (a && b) ? (*b < *a) : bool(b);
+            }
+        };
+
+
     // Constructors
 
         //- Default construct
@@ -215,13 +281,19 @@ public:
 
         //- Reorder elements.
         //- Reordering must be unique (ie, shuffle).
-        //  Optionally verify that all pointers have been set.
-        void reorder(const labelUList& oldToNew, const bool testNull = true);
+        //  Optionally check that all pointers have been set.
+        void reorder(const labelUList& oldToNew, const bool check = false);
 
         //- Reorder elements according to new order mapping (newToOld).
         //- Reordering must be unique (ie, shuffle).
-        //  Optionally verify that all pointers have been set.
-        void sortOrder(const labelUList& order, const bool testNull = true);
+        //  Optionally check that all pointers have been set.
+        void sortOrder(const labelUList& order, const bool check = false);
+
+
+    // Checks
+
+        //- Check and raise FatalError if any nullptr exists in the list
+        inline void checkNonNull() const;
 
 
     // Member Operators
@@ -362,22 +434,29 @@ public:
         };
 
 
-        //- Return an iterator to begin traversing the UPtrList
+        //- Iterator to begin of raw pointers traversal (use with caution)
+        T** begin_ptr() noexcept { return ptrs_.begin(); }
+
+        //- Iterator beyond end of raw pointers traversal (use with caution)
+        T** end_ptr() noexcept { return ptrs_.end(); }
+
+
+        //- Return an iterator to begin of UPtrList traversal
         inline iterator begin() noexcept;
 
-        //- Return an iterator to end traversing the UPtrList
+        //- Return iterator beyond end of UPtrList traversal
         inline iterator end() noexcept;
 
-        //- Return an const_iterator to begin traversing the UPtrList
+        //- Return const_iterator to begin of UPtrList traversal
         inline const_iterator cbegin() const noexcept;
 
-        //- Return an const_iterator to end traversing the UPtrList
+        //- Return const_iterator beyond end of UPtrList traversal
         inline const_iterator cend() const noexcept;
 
-        //- Return an const_iterator to begin traversing the UPtrList
+        //- Return const_iterator to begin of UPtrList traversal
         inline const_iterator begin() const noexcept;
 
-        //- Return an const_iterator to end traversing the UPtrList
+        //- Return const_iterator beyond end of UPtrList traversal
         inline const_iterator end() const noexcept;
 
 
@@ -392,6 +471,20 @@ public:
 };
 
 
+// * * * * * * * * * * * * * * Global Functions  * * * * * * * * * * * * * * //
+
+//- Inplace (stable) sorting of pointer list.
+template<class T>
+void sort(UPtrList<T>& list);
+
+//- Inplace (stable) sorting of pointer list using given comparator,
+//- which compares objects, not pointers.
+//  This sort function includes null pointer guards and will also sort
+//  any null pointers to the end (eg, rubbish that can be truncated)
+template<class T, class Compare>
+void sort(UPtrList<T>& list, const Compare& comp);
+
+
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
 } // End namespace Foam
diff --git a/src/OpenFOAM/containers/PtrLists/UPtrList/UPtrListI.H b/src/OpenFOAM/containers/PtrLists/UPtrList/UPtrListI.H
index 0bc1262089c..1716ddf253d 100644
--- a/src/OpenFOAM/containers/PtrLists/UPtrList/UPtrListI.H
+++ b/src/OpenFOAM/containers/PtrLists/UPtrList/UPtrListI.H
@@ -215,6 +215,13 @@ inline T* Foam::UPtrList<T>::set(const label i, T* ptr)
 }
 
 
+template<class T>
+inline void Foam::UPtrList<T>::checkNonNull() const
+{
+    ptrs_.checkNonNull();
+}
+
+
 // * * * * * * * * * * * * * * * Member Operators  * * * * * * * * * * * * * //
 
 template<class T>
-- 
GitLab