diff --git a/applications/test/00-machine-sizes/Test-machine-sizes.cpp b/applications/test/00-machine-sizes/Test-machine-sizes.cpp
index 0e88c4c943ae5d02a22f0445c9264739c6155af0..917782dfde1f3f0228e9ebff3acf2ae632abea05 100644
--- a/applications/test/00-machine-sizes/Test-machine-sizes.cpp
+++ b/applications/test/00-machine-sizes/Test-machine-sizes.cpp
@@ -5,7 +5,7 @@
     \\  /    A nd           | www.openfoam.com
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
-    Copyright (C) 2018-2022 OpenCFD Ltd.
+    Copyright (C) 2018-2025 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -25,9 +25,12 @@ License
 
 Description
     Test the sizeof for basic types.
+    Also tests how the data mapping of OpenFOAM types to UPstream (MPI)
+    type ids are handled.
+
     Can be compiled and run without any OpenFOAM libraries.
 
-        g++ -std=c++11 -oTest-machine-sizes Test-machine-sizes.cpp
+        g++ -std=c++17 -oTest-machine-sizes Test-machine-sizes.cpp
 
 \*---------------------------------------------------------------------------*/
 
@@ -37,6 +40,114 @@ Description
 #include <iostream>
 #include <limits>
 #include <typeinfo>
+#include <type_traits>
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+// Partial copy from UPstream.H
+
+//- Some MPI data types
+//
+//- Mapping of some fundamental and aggregate types to MPI data types
+enum class dataTypes : int
+{
+    // Builtin Types [8]:
+    DataTypes_begin,    //!< Begin builtin types (internal use)
+    type_byte = DataTypes_begin,  // also for char, unsigned char
+    type_int32,
+    type_int64,
+    type_uint32,
+    type_uint64,
+    type_float,
+    type_double,
+    type_long_double,
+    invalid
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+// Partial copy from UPstreamTraits.H
+
+//- A supported UPstream data type (intrinsic or user-defined)
+template<class T>
+struct UPstream_base_dataType : std::false_type
+{
+    static constexpr auto datatype_id = dataTypes::invalid;
+};
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+// Specializations of the above,
+// each to match the elements of UPstream::dataTypes
+
+#undef  defineUPstreamDataTraits
+#define defineUPstreamDataTraits(TypeId, Type)                                \
+    template<> struct UPstream_base_dataType<Type> : std::true_type           \
+    {                                                                         \
+        static constexpr auto datatype_id = dataTypes::TypeId;                \
+    };
+
+
+defineUPstreamDataTraits(type_byte,   char);
+defineUPstreamDataTraits(type_byte,   unsigned char);
+defineUPstreamDataTraits(type_int32,  int32_t);
+defineUPstreamDataTraits(type_int64,  int64_t);
+defineUPstreamDataTraits(type_uint32, uint32_t);
+defineUPstreamDataTraits(type_uint64, uint64_t);
+defineUPstreamDataTraits(type_float,  float);
+defineUPstreamDataTraits(type_double, double);
+defineUPstreamDataTraits(type_long_double, long double);
+
+#undef defineUPstreamDataTraits
+
+
+//- Explicit handling of data type aliases. This is necessary since
+//- different systems map things like 'unsigned long' differently but we
+//- restrict ourselves to int32/int64 types
+template<class T>
+struct UPstream_alias_dataType
+:
+    std::bool_constant
+    <
+        // Base type (no alias needed)
+        UPstream_base_dataType<std::remove_cv_t<T>>::value ||
+        (
+            // Or some int 32/64 type to re-map
+            std::is_integral_v<T>
+         && (sizeof(T) == sizeof(int32_t) || sizeof(T) == sizeof(int64_t))
+        )
+    >
+{
+    // Is it using the base type? (no alias needed)
+    static constexpr bool is_base =
+        UPstream_base_dataType<std::remove_cv_t<T>>::value;
+
+    using base = std::conditional_t
+    <
+        UPstream_base_dataType<std::remove_cv_t<T>>::value,  // is_base
+        std::remove_cv_t<T>,
+        std::conditional_t
+        <
+            (
+                std::is_integral_v<T>
+             && (sizeof(T) == sizeof(int32_t) || sizeof(T) == sizeof(int64_t))
+            ),
+            std::conditional_t
+            <
+                (sizeof(T) == sizeof(int32_t)),
+                std::conditional_t<std::is_signed_v<T>, int32_t, uint32_t>,
+                std::conditional_t<std::is_signed_v<T>, int64_t, uint64_t>
+            >,
+            char  // Fallback value (assuming it is contiguous)
+        >
+    >;
+
+    static constexpr auto datatype_id =
+        UPstream_base_dataType<base>::datatype_id;
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
 template<class T>
 void print(const char* name, bool showLimits = true)
@@ -47,28 +158,78 @@ void print(const char* name, bool showLimits = true)
     if (showLimits)
     {
         std::cout
-            << " \"max\"=" << std::numeric_limits<T>::max();
+            << " max=<";
+
+        if constexpr (sizeof(T) == 1)
+        {
+            std::cout << int(std::numeric_limits<T>::max());
+        }
+        else
+        {
+            std::cout << std::numeric_limits<T>::max();
+        }
+        std::cout << '>';
+    }
+
+    // A declared or deduced MPI type, or aliased
+    std::cout
+        << " is_mpi=" << UPstream_base_dataType<T>::value
+        << " (" << int(UPstream_base_dataType<T>::datatype_id) << ")";
+
+    if (UPstream_alias_dataType<T>::value)
+    {
+        if (UPstream_alias_dataType<T>::is_base)
+        {
+            std::cout<< " is_base";
+        }
+        else
+        {
+            std::cout<< " is_alias ("
+                << int(UPstream_alias_dataType<T>::datatype_id) << ")";
+        }
+    }
+    else
+    {
+        std::cout<< " no_alias";
     }
 
     std::cout<< '\n';
 }
 
 
+
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 // Main program:
 
 int main(int argc, char *argv[])
 {
     std::cout<< "c++ = " << __cplusplus << '\n';
-    std::cout<< "machine sizes\n---\n\n";
-
+    std::cout<< "machine sizes (and some MPI traits)\n---\n\n";
+
+    print<int8_t>("int8_t");
+    print<uint8_t>("uint8_t");
+    print<int16_t>("int16_t");
+    print<uint16_t>("uint16_t");
+    print<int32_t>("int32_t");
+    print<uint32_t>("uint32_t");
+    print<int64_t>("int64_t");
+    print<uint64_t>("uint64_t");
+
+    std::cout << '\n';
+    print<char>("char");
+    print<unsigned char>("unsigned char");
     print<short>("short");
     print<int>("int");
+    print<unsigned>("unsigned");
     print<long>("long");
     print<unsigned long>("unsigned long");
-    print<std::size_t>("std::size_t");
     print<long long>("long long");
 
+    std::cout << '\n';
+    print<std::size_t>("std::size_t");
+    print<std::streamsize>("std::streamsize");
+
+    std::cout << '\n';
     print<float>("float");
     print<double>("double");
     print<long double>("long double");
diff --git a/applications/test/UPstreamTraits/Make/files b/applications/test/UPstreamTraits/Make/files
new file mode 100644
index 0000000000000000000000000000000000000000..5e1dc7bafdba3010d60bbbdc568983fbc54663e3
--- /dev/null
+++ b/applications/test/UPstreamTraits/Make/files
@@ -0,0 +1,3 @@
+Test-UPstreamTraits.cxx
+
+EXE = $(FOAM_USER_APPBIN)/Test-UPstreamTraits
diff --git a/applications/test/UPstreamTraits/Make/options b/applications/test/UPstreamTraits/Make/options
new file mode 100644
index 0000000000000000000000000000000000000000..18e6fe47afacb902cddccf82632772447704fd88
--- /dev/null
+++ b/applications/test/UPstreamTraits/Make/options
@@ -0,0 +1,2 @@
+/* EXE_INC = */
+/* EXE_LIBS = */
diff --git a/applications/test/UPstreamTraits/Test-UPstreamTraits.cxx b/applications/test/UPstreamTraits/Test-UPstreamTraits.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..9c747c61fa7fd2821a039ac161cda0875daaf75c
--- /dev/null
+++ b/applications/test/UPstreamTraits/Test-UPstreamTraits.cxx
@@ -0,0 +1,267 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | www.openfoam.com
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+    Copyright (C) 2025 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/>.
+
+Description
+    Simple compilation tests and access for UPstream types
+
+\*---------------------------------------------------------------------------*/
+
+#include "pTraits.H"
+#include "contiguous.H"
+#include "FixedList.H"
+#include "boolVector.H"  // A FixedList pretending to be a vector
+#include "barycentric.H"
+#include "complex.H"
+#include "vector.H"
+#include "tensor.H"
+#include "uLabel.H"
+#include "Switch.H"
+#include "IOstreams.H"
+#include "UPstream.H"
+
+#include <type_traits>
+
+using namespace Foam;
+
+// Just for debugging
+const List<std::string> dataType_names
+({
+    "byte",
+    "int32",
+    "int64",
+    "uint32",
+    "uint64",
+    "float",
+    "double",
+    "long_double",
+
+    "float(2)",
+    "double(2)",
+    "float(3)",
+    "double(3)",
+    "float(6)",
+    "double(6)",
+    "float(9)",
+    "double(9)"
+});
+
+//- Test for pTraits typeName member : default is false
+template<class T, class = void>
+struct check_has_typeName : std::false_type {};
+
+//- Test for pTraits zero
+template<class T>
+struct check_has_typeName
+<
+    T,
+    std::void_t<decltype(pTraits<std::remove_cv_t<T>>::typeName)>
+>
+:
+    std::true_type
+{};
+
+
+// Possible future change...
+// //- A supported UPstream data type (intrinsic or user-defined)
+// template<>
+// struct UPstream_base_dataType<complex> : std::true_type
+// {
+//     static constexpr auto datatype_id = []()
+//     {
+//         if constexpr (sizeof(complex) == 2*sizeof(float))
+//             return UPstream::dataTypes::type_2float;
+//         else
+//             return UPstream::dataTypes::type_2double;
+//     }();
+// };
+
+
+template<class T>
+void printTypeName(const bool showSize = false)
+{
+    // Both float and double have pTraits typeName = "scalar"!
+    if constexpr (std::is_same_v<float, std::remove_cv_t<T>>)
+    {
+        Info<< "<float>";
+    }
+    else if constexpr (std::is_same_v<double, std::remove_cv_t<T>>)
+    {
+        Info<< "<double>";
+    }
+    else if constexpr (check_has_typeName<T>::value)
+    {
+        Info<< pTraits<std::remove_cv_t<T>>::typeName;
+    }
+    else
+    {
+        Info<< typeid(T).name();
+    }
+    if (showSize)
+    {
+        Info<< " (" << sizeof(T) << " bytes)";
+    }
+}
+
+template<class Type, bool UseTypeName = true>
+void printPstreamTraits(const std::string_view name = std::string_view())
+{
+    Info<< "========" << nl;
+    Info<< "type: ";
+    if (!name.empty())
+    {
+        Info<< name << ' ';
+    }
+    if constexpr (UseTypeName)
+    {
+        printTypeName<Type>(true);
+    }
+    else
+    {
+        Info<< typeid(Type).name();
+        Info<< " (" << sizeof(Type) << " bytes)";
+    }
+
+    Info<< ", cmpt:";
+    printTypeName<typename Foam::pTraits_cmptType<Type>::type>(true);
+
+    Info<< nl
+        << "  is_contiguous:"
+        << is_contiguous<Type>::value
+        << ", is base:"
+        << UPstream_base_dataType<Type>::value
+        << ", is cmpt:"
+        << UPstream_dataType<Type>::value << nl;
+
+    Info<< "is base:"
+        << UPstream_base_dataType<Type>::value
+        << " (type:" << int(UPstream_base_dataType<Type>::datatype_id)
+        << ")  is alias:" << UPstream_alias_dataType<Type>::value
+        << " (type:" << int(UPstream_alias_dataType<Type>::datatype_id)
+        << ")" << nl;
+
+
+    {
+        int index = int(UPstream_base_dataType<Type>::datatype_id);
+        Info<< "datatype: " << index;
+
+        if (index < dataType_names.size())
+        {
+            Info<< ' ' << dataType_names[index];
+        }
+        Info<< nl;
+    }
+
+    {
+        // Use element or component type (or byte-wise) for data type
+        using base = typename UPstream_dataType<Type>::base;
+        constexpr auto datatype = UPstream_dataType<Type>::datatype_id;
+
+        Info<< "datatype => ";
+        printTypeName<base>();
+        Info<< " (" << sizeof(Type)/sizeof(base) << " elems)" << nl
+            << "datatype: " << static_cast<int>(datatype) << nl;
+    }
+}
+
+
+template<class BinaryOp>
+void printOpCodeTraits(BinaryOp bop, std::string_view name)
+{
+    Info<< "op: " << name << ' ';
+    if constexpr (UPstream_opType<BinaryOp>::value)
+    {
+        Info<< "supported";
+    }
+    else
+    {
+        Info<< "unknown";
+    }
+    Info<< ": " << int(UPstream_opType<BinaryOp>::opcode_id) << nl;
+}
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+// Main program:
+
+int main()
+{
+    printPstreamTraits<bool>();
+    printPstreamTraits<label>();
+
+    printPstreamTraits<int>("<int>");
+    printPstreamTraits<long>("<long>");
+    printPstreamTraits<unsigned>("<unsigned>");
+    printPstreamTraits<unsigned int>("<unsigned int>");
+    printPstreamTraits<unsigned long>("<long long>");
+
+    printPstreamTraits<const float>();
+    printPstreamTraits<floatVector>();
+
+    printPstreamTraits<scalar>();
+    printPstreamTraits<double>();
+    printPstreamTraits<doubleVector>();
+
+    // Avoid typeName for barycentric. It is declared, but not defined
+    printPstreamTraits<barycentric, false>("barycentric");
+
+    printPstreamTraits<complex>();     // Uses specialized pTraits_...
+
+    printPstreamTraits<boolVector>();  // Uses specialized pTraits_...
+    printPstreamTraits<floatVector>();
+    printPstreamTraits<doubleVector>();
+    printPstreamTraits<tensor>();
+    printPstreamTraits<word>();
+    printPstreamTraits<std::string>();
+
+    // This will not identify properly at the moment...
+    printPstreamTraits<FixedList<doubleVector, 4>>();
+    printPstreamTraits<labelPair>();
+
+    Info<< nl
+        << "========" << nl
+        << "Mapping of binary reduction ops" << nl;
+
+    printOpCodeTraits(minOp<scalar>{}, "min");
+    printOpCodeTraits(maxOp<vector>{}, "max");
+    printOpCodeTraits(sumOp<vector>{}, "sum");
+    printOpCodeTraits(plusOp<vector>{}, "plus");
+    printOpCodeTraits(multiplyOp<scalar>{}, "multiply");
+    printOpCodeTraits(divideOp<vector>{}, "divide");
+    printOpCodeTraits(minMagSqrOp<vector>{}, "minMagSqr");
+
+    printOpCodeTraits(bitAndOp<vector>{}, "bitAnd<vector>");
+    printOpCodeTraits(bitOrOp<vector>{}, "bitOr<vector>");
+
+    printOpCodeTraits(bitOrOp<float>{}, "bitOr<float>");
+    printOpCodeTraits(bitAndOp<unsigned>{}, "bitAnd<unsigned>");
+    printOpCodeTraits(bitOrOp<unsigned>{}, "bitOr<unsigned>");
+
+    Info<< nl << "End\n" << endl;
+
+    return 0;
+}
+
+
+// ************************************************************************* //
diff --git a/applications/test/parallel-barrier1/Make/files b/applications/test/parallel-barrier1/Make/files
new file mode 100644
index 0000000000000000000000000000000000000000..9256068d0b27adc0c9041e0174c053b42de2fdf9
--- /dev/null
+++ b/applications/test/parallel-barrier1/Make/files
@@ -0,0 +1,3 @@
+Test-parallel-barrier1.cxx
+
+EXE = $(FOAM_USER_APPBIN)/Test-parallel-barrier1
diff --git a/applications/test/parallel-barrier1/Make/options b/applications/test/parallel-barrier1/Make/options
new file mode 100644
index 0000000000000000000000000000000000000000..18e6fe47afacb902cddccf82632772447704fd88
--- /dev/null
+++ b/applications/test/parallel-barrier1/Make/options
@@ -0,0 +1,2 @@
+/* EXE_INC = */
+/* EXE_LIBS = */
diff --git a/applications/test/parallel-barrier1/Test-parallel-barrier1.cxx b/applications/test/parallel-barrier1/Test-parallel-barrier1.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..586539752d4be48a5f69d17fa7ac6b7db00fa372
--- /dev/null
+++ b/applications/test/parallel-barrier1/Test-parallel-barrier1.cxx
@@ -0,0 +1,125 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | www.openfoam.com
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+    Copyright (C) 2025 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/>.
+
+Application
+    Test-parallel-barrier1
+
+Description
+    Simple test of local barriers communication
+
+\*---------------------------------------------------------------------------*/
+
+#include "argList.H"
+#include "clockTime.H"
+#include "IPstream.H"
+#include "OPstream.H"
+
+using namespace Foam;
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+int main(int argc, char *argv[])
+{
+    argList::noCheckProcessorDirectories();
+    argList::addVerboseOption();
+    argList::addOption("delay", "sec", "Seconds to sleep (default 2)");
+
+    #include "setRootCase.H"
+
+    if (!UPstream::parRun())
+    {
+        Info<< "###############" << nl
+            << "Not running in parallel. Stopping now" << nl
+            << "###############" << endl;
+        return 1;
+    }
+
+    const auto delay = args.getOrDefault<label>("delay", 2);
+
+    Info<< nl
+        << "Testing local barrier, sleep=" << delay << endl;
+
+
+    const auto myProci = UPstream::myProcNo(UPstream::worldComm);
+    const auto numProc = UPstream::nProcs(UPstream::worldComm);
+
+    constexpr int uniqTag = 1516;
+
+    clockTime timing;
+
+    if (UPstream::master(UPstream::worldComm))
+    {
+        // Wait for the last rank
+        UPstream::wait_done(numProc-1, UPstream::worldComm);
+
+        // Wait for any other rank
+        if (numProc > 2)
+        {
+            int from = UPstream::wait_done(-1, UPstream::worldComm, uniqTag);
+            Pout<< "done signal from: " << from << endl;
+        }
+    }
+    else if (myProci == numProc-1)
+    {
+        Foam::sleep(delay);
+        UPstream::send_done(UPstream::masterNo(), UPstream::worldComm);
+    }
+
+    // Cascade sequencing (and delays)
+    if (numProc > 7)
+    {
+        if (myProci == 2)
+        {
+            Foam::sleep(2*delay);
+            UPstream::send_done(4, UPstream::worldComm);
+        }
+        else if (myProci == 4)
+        {
+            UPstream::wait_done(2, UPstream::worldComm);
+            Foam::sleep(2*delay);
+            UPstream::send_done(5, UPstream::worldComm);
+        }
+        else if (myProci == 5)
+        {
+            UPstream::wait_done(4, UPstream::worldComm);
+        }
+    }
+
+    // Some arbitrary signaling rank
+    if ((numProc > 2) && (myProci == numProc/2))
+    {
+        Pout<< "send done signal " << myProci << " -> 0" << endl;
+        UPstream::send_done(UPstream::masterNo(), UPstream::worldComm, uniqTag);
+    }
+
+    Pout<< "done: " << timing.elapsedTime() << " s" << endl;
+
+    Info<< "\nEnd\n" << endl;
+
+    return 0;
+}
+
+
+// ************************************************************************* //
diff --git a/src/OpenFOAM/db/IOstreams/Pstreams/UPstream.H b/src/OpenFOAM/db/IOstreams/Pstreams/UPstream.H
index e9ff4fc3710f635eac09cbde093e011ed05bda0f..d0e23c298cc3969f8412033640e77c51e0190747 100644
--- a/src/OpenFOAM/db/IOstreams/Pstreams/UPstream.H
+++ b/src/OpenFOAM/db/IOstreams/Pstreams/UPstream.H
@@ -90,6 +90,78 @@ public:
         sync            //!< (MPI_Ssend, MPI_Issend)
     };
 
+    //- Mapping of some fundamental and aggregate types to MPI data types
+    enum class dataTypes : int
+    {
+        // NOTE: changes here require adjustment in
+        // PstreamGlobals, UPstreamTraits
+
+        // Builtin Types [8]:
+        DataTypes_begin,    //!< (internal use) begin all data types
+        type_byte = DataTypes_begin,  //!< byte, char, unsigned char, ...
+        type_int32,
+        type_int64,
+        type_uint32,
+        type_uint64,
+        type_float,
+        type_double,
+        type_long_double,
+
+        //! (internal use) end of builtin data types marker
+        BuiltinTypes_end, UserTypes_begin = BuiltinTypes_end,
+        //!< (internal use) begin of user data types marker
+
+        // User Types [6]:
+        type_3float = UserTypes_begin, //!< 3*float (eg, floatVector)
+        type_3double,   //!< 3*double (eg, doubleVector)
+        type_6float,    //!< 6*float (eg, floatSymmTensor, complex vector)
+        type_6double,   //!< 6*double (eg, doubleSymmTensor, complex vector)
+        type_9float,    //!< 9*float (eg, floatTensor)
+        type_9double,   //!< 9*double (eg, doubleTensor)
+
+        // Internal markers
+        invalid,        //!< invalid type (NULL)
+        //! (internal use) end of user data types marker
+        UserTypes_end = invalid, DataTypes_end = invalid
+        //!< (internal use) end of all data types marker
+    };
+
+    //- Mapping of some MPI op codes.
+    //  Currently excluding min/max location until they are needed
+    enum class opCodes : int
+    {
+        // NOTE: changes here require adjustment in
+        // PstreamGlobals, UPstreamTraits
+
+        ReduceOps_begin,  //!< (internal use) begin reduce/window
+
+        // Reduce or window operations [10]
+        op_min = ReduceOps_begin, //!< min(x,y)
+        op_max,         //!< max(x,y)
+        op_sum,         //!< (x + y)
+        op_prod,        //!< (x * y)
+        op_bool_and,    //!< Logical \c and
+        op_bool_or,     //!< Logical \c or
+        op_bool_xor,    //!< Logical \c xor
+        op_bit_and,     //!< Bit-wise \c and for (unsigned) integral types
+        op_bit_or,      //!< Bit-wise \c or for (unsigned) integral types
+        op_bit_xor,     //!< Bit-wise \c xor for (unsigned) integral types
+
+        //! (internal use) end of reduce-ops marker
+        ReduceOps_end, WindowOps_begin = ReduceOps_end,
+        //!< (internal use) begin end of window-ops marker
+
+        // Window-only operations [2]
+        op_replace = WindowOps_begin,  //!< Replace (window only)
+        op_no_op,       //!< No-op (window only)
+
+        // Internal markers
+        invalid,        //!< invalid op (NULL)
+        //! (internal use) end of window-ops marker
+        WindowOps_end = invalid, OpCodes_end = invalid
+        //!< (internal use) end of all ops marker
+    };
+
 
     // Public Classes
 
@@ -777,10 +849,38 @@ public:
         //- Impose a synchronisation barrier (optionally non-blocking)
         static void barrier
         (
-            const label communicator,
+            const int communicator,
             UPstream::Request* req = nullptr
         );
 
+        //- Impose a point-to-point synchronisation barrier
+        //- by sending a zero-byte \em "done" message to given rank.
+        //  A no-op for non-parallel
+        static void send_done
+        (
+            //! The destination rank
+            const int toProcNo,
+            //! The communicator index (eg, UPstream::worldComm)
+            const int communicator,
+            //! Message tag (must match on receiving side)
+            const int tag = UPstream::msgType()+1970
+        );
+
+        //- Impose a point-to-point synchronisation barrier
+        //- by receiving a zero-byte \em "done" message from given rank
+        //  A no-op for non-parallel
+        //  \returns the source rank (useful for ANY_SOURCE messages)
+        //  or -1 for non-parallel
+        static int wait_done
+        (
+            //! The source rank (negative == ANY_SOURCE)
+            const int fromProcNo,
+            //! The communicator index (eg, UPstream::worldComm)
+            const int communicator,
+            //! Message tag (must match on sending side)
+            const int tag = UPstream::msgType()+1970
+        );
+
         //- Probe for an incoming message.
         //
         //  \param commsType    Non-blocking or not
@@ -795,7 +895,7 @@ public:
             const UPstream::commsTypes commsType,
             const int fromProcNo,
             const int tag = UPstream::msgType(),
-            const label communicator = worldComm
+            const int communicator = worldComm
         );
 
 
@@ -824,7 +924,7 @@ public:
 
         //- Transfer the (wrapped) MPI request to the internal global list
         //- and invalidate the parameter (ignores null requests)
-        //  A no-op for non-parallel,
+        //  A no-op for non-parallel
         static void addRequest(UPstream::Request& req);
 
         //- Non-blocking comms: cancel and free outstanding request.
diff --git a/src/OpenFOAM/db/IOstreams/Pstreams/UPstreamTraits.H b/src/OpenFOAM/db/IOstreams/Pstreams/UPstreamTraits.H
index 188d3db64342c791a08d10e62fecf868a6dea289..224300002d4750fd6cee9714d7d63c4e28815165 100644
--- a/src/OpenFOAM/db/IOstreams/Pstreams/UPstreamTraits.H
+++ b/src/OpenFOAM/db/IOstreams/Pstreams/UPstreamTraits.H
@@ -26,7 +26,28 @@ License
 Description
     A set of traits associated with UPstream communication
 
-SourceFiles
+    - UPstream_dataType trait:
+      This wrapper is unwinds the type to check against base/alias, but also
+      checks if it is a component aggregate of a supported UPstream data type.
+      This will be that main entry point for usage.
+
+    - UPstream_opType trait:
+      Provides a mapping of OpenFOAM ops to their MPI equivalent.
+      The \c opcode_id is the corresponding internal representation.
+
+Note
+    Additional helper traits:
+
+    - UPstream_base_dataType trait:
+      Tests true/false if the specified data type has an internal
+      MPI equivalent.  The \c datatype_id is the corresponding
+      internal enumeration. Even if this tests as false, it will
+      always return \c type_byte as the fallback for general contiguous data
+
+    - UPstream_alias_dataType trait:
+      Provides mapping for <int/long/long long,...> to the fundamental
+      32/64 bit integrals, since <int/long/long long,...> may not otherwise
+      directly map on all systems.
 
 \*---------------------------------------------------------------------------*/
 
@@ -36,9 +57,314 @@ SourceFiles
 #include "UPstream.H"
 #include <cstdint>
 #include <ios>  // For streamsize
+#include <type_traits>
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+// Forward Declarations
+
+// Some vector-space types
+// -----------------------
+//! \cond
+template<class T> class Vector;
+template<class T> class SymmTensor;
+template<class T> class Tensor;
+//! \endcond
+
+// -------------------------
+// Some binary operators (as per ops.H), but since ListOps.H is included
+// by UPstream.H, don't need to forward declare
+// -------------------------
+// template<class T> struct minOp;
+// template<class T> struct maxOp;
+// template<class T> struct plusOp;
+// template<class T> struct sumOp;
+// template<class T> struct multiplyOp;
+// template<class T> struct bitAndOp;
+// template<class T> struct bitOrOp;
+// template<class T> struct bitXorOp;
+
+//! \cond
+template<class T> struct UPstream_dataType;
+template<class T> struct UPstream_opType;
+//! \endcond
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+// Base traits
+
+//- A supported UPstream (MPI) reduce/window operation type
+template<class T>
+struct UPstream_opType : std::false_type
+{
+    static constexpr auto opcode_id = UPstream::opCodes::invalid;
+};
+
+
+//- A supported UPstream data type (intrinsic or user-defined)
+template<class T>
+struct UPstream_base_dataType : std::false_type
+{
+    static constexpr auto datatype_id = UPstream::dataTypes::invalid;
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+// Trait specializations (op-codes)
+
+//- Map minOp\<T\> to \c UPstream::opCodes::op_min
+template<class T>
+struct UPstream_opType<Foam::minOp<T>> : std::true_type
+{
+    static constexpr auto opcode_id = UPstream::opCodes::op_min;
+};
+
+//- Map maxOp\<T\> to \c UPstream::opCodes::op_max
+template<class T>
+struct UPstream_opType<Foam::maxOp<T>> : std::true_type
+{
+    static constexpr auto opcode_id = UPstream::opCodes::op_max;
+};
+
+//- Map sumOp\<T\> to \c UPstream::opCodes::op_sum
+template<class T>
+struct UPstream_opType<Foam::sumOp<T>> : std::true_type
+{
+    static constexpr auto opcode_id = UPstream::opCodes::op_sum;
+};
+
+//- Map plusOp\<T\> to \c UPstream::opCodes::op_sum
+//- as a recognized alternative to sumOp\<T\>
+template<class T>
+struct UPstream_opType<Foam::plusOp<T>> : std::true_type
+{
+    static constexpr auto opcode_id = UPstream::opCodes::op_sum;
+};
+
+//- Map multiplyOp\<T\> to \c UPstream::opCodes::op_prod
+template<class T>
+struct UPstream_opType<Foam::multiplyOp<T>> : std::true_type
+{
+    static constexpr auto opcode_id = UPstream::opCodes::op_prod;
+};
+
+// NOTE (2025-02):
+// currently no mappings provided for
+// (op_bool_and, op_bool_or, op_bool_xor) until the calling semantics
+// have been properly defined
+
+
+// These are only viable for unsigned integral types,
+// probably not for signed integral types.
+// Be extra restrictive for now
+
+//- Map bitAndOp\<T\> to \c UPstream::opCodes::op_bit_and
+//- (for unsigned integrals)
+template<class T>
+struct UPstream_opType<Foam::bitAndOp<T>>
+:
+    // ie, std::unsigned_integral<T> concept
+    std::bool_constant<std::is_integral_v<T> && !std::is_signed_v<T>>
+{
+    static constexpr auto opcode_id = []() constexpr noexcept
+    {
+        if constexpr (std::is_integral_v<T> && !std::is_signed_v<T>)
+            return UPstream::opCodes::op_bit_and;
+        else
+            return UPstream::opCodes::invalid;
+    }();
+};
+
+//- Map bitOrOp\<T\> to \c UPstream::opCodes::op_bit_or
+//- (for unsigned integrals)
+template<class T>
+struct UPstream_opType<Foam::bitOrOp<T>>
+:
+    // ie, std::unsigned_integral<T> concept
+    std::bool_constant<std::is_integral_v<T> && !std::is_signed_v<T>>
+{
+    static constexpr auto opcode_id = []() constexpr noexcept
+    {
+        if constexpr (std::is_integral_v<T> && !std::is_signed_v<T>)
+            return UPstream::opCodes::op_bit_or;
+        else
+            return UPstream::opCodes::invalid;
+    }();
+};
+
+//- Map bitXorOp\<T\> to \c UPstream::opCodes::op_bit_xor
+//- (for unsigned integrals)
+template<class T>
+struct UPstream_opType<Foam::bitXorOp<T>>
+:
+    // ie, std::unsigned_integral<T> concept
+    std::bool_constant<std::is_integral_v<T> && !std::is_signed_v<T>>
+{
+    static constexpr auto opcode_id = []() constexpr noexcept
+    {
+        if constexpr (std::is_integral_v<T> && !std::is_signed_v<T>)
+            return UPstream::opCodes::op_bit_xor;
+        else
+            return UPstream::opCodes::invalid;
+    }();
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+// Trait specializations (data types)
+
+// Specializations to match elements of UPstream::dataTypes
+#undef  defineUPstreamDataTraits
+#define defineUPstreamDataTraits(TypeId, Type)                                \
+                                                                              \
+    /*! \brief Map \c Type to UPstream::dataTypes::TypeId */                  \
+    template<> struct UPstream_base_dataType<Type> : std::true_type           \
+    {                                                                         \
+        static constexpr auto datatype_id = UPstream::dataTypes::TypeId;      \
+    };                                                                        \
+    /*! \brief Map \c const \c Type to \c UPstream::dataTypes::TypeId */      \
+    template<> struct UPstream_base_dataType<const Type> : std::true_type     \
+    {                                                                         \
+        static constexpr auto datatype_id = UPstream::dataTypes::TypeId;      \
+    };
+
+
+// Intrinsic Types [8]:
+// Note: uses 'int32_t,int64_t,...' instead of 'int,long,...' to minimize
+// the possibility of duplicates types.
+// OpenFOAM defines Foam::label as either int32_t,int64_t (not int,long) too.
+defineUPstreamDataTraits(type_byte,   char);
+defineUPstreamDataTraits(type_byte,   unsigned char);
+defineUPstreamDataTraits(type_int32,  int32_t);
+defineUPstreamDataTraits(type_int64,  int64_t);
+defineUPstreamDataTraits(type_uint32, uint32_t);
+defineUPstreamDataTraits(type_uint64, uint64_t);
+defineUPstreamDataTraits(type_float,  float);
+defineUPstreamDataTraits(type_double, double);
+defineUPstreamDataTraits(type_long_double, long double);
+
+// User Types [6]:
+defineUPstreamDataTraits(type_3float,  Vector<float>);
+defineUPstreamDataTraits(type_3double, Vector<double>);
+defineUPstreamDataTraits(type_6float,  SymmTensor<float>);
+defineUPstreamDataTraits(type_6double, SymmTensor<double>);
+defineUPstreamDataTraits(type_9float,  Tensor<float>);
+defineUPstreamDataTraits(type_9double, Tensor<double>);
+
+#undef defineUPstreamDataTraits
+
+
+// ------------------------------------------------------------------------- //
+
+//- Explicit handling of data type aliases. This is necessary since
+//- different systems map things like 'unsigned long' differently but we
+//- restrict ourselves to int32/int64 types
+template<class T>
+struct UPstream_alias_dataType
+:
+    std::bool_constant
+    <
+        // Base type (no alias needed)
+        UPstream_base_dataType<std::remove_cv_t<T>>::value ||
+        (
+            // Or some int 32/64 type to re-map
+            std::is_integral_v<T>
+         && (sizeof(T) == sizeof(int32_t) || sizeof(T) == sizeof(int64_t))
+        )
+    >
+{
+    // Is it using the base type? (no alias needed)
+    static constexpr bool is_base =
+        UPstream_base_dataType<std::remove_cv_t<T>>::value;
+
+    using base = std::conditional_t
+    <
+        UPstream_base_dataType<std::remove_cv_t<T>>::value,
+        std::remove_cv_t<T>,  // <- using base
+        std::conditional_t    // <- using alias
+        <
+            (
+                std::is_integral_v<T>
+             && (sizeof(T) == sizeof(int32_t) || sizeof(T) == sizeof(int64_t))
+            ),
+            std::conditional_t
+            <
+                (sizeof(T) == sizeof(int32_t)),
+                std::conditional_t<std::is_signed_v<T>, int32_t, uint32_t>,
+                std::conditional_t<std::is_signed_v<T>, int64_t, uint64_t>
+            >,
+            char  // Fallback is a byte (eg, arbitrary contiguous data)
+        >
+    >;
+
+    static constexpr auto datatype_id =
+        UPstream_base_dataType<base>::datatype_id;
+};
+
+
+// ------------------------------------------------------------------------- //
+
+//- A supported UPstream data type (fundamental or user-defined)
+//- or a component aggregate of a supported UPstream data type.
+//
+// Is true for the following conditions:
+// - The \c Type is directly supported
+// - The \c cmptType (eg, from VectorSpace) exists and is directly supported
+// - Fallback to byte-wise representation (ie, for contiguous)
+// .
+template<class T>
+struct UPstream_dataType
+:
+    std::bool_constant
+    <
+        UPstream_alias_dataType<T>::value
+     || UPstream_alias_dataType<typename pTraits_cmptType<T>::type>::value
+    >
+{
+    // Is it using the base type? (ie, not using components)
+    static constexpr bool is_base = UPstream_alias_dataType<T>::value;
+
+    //- The underlying data type (if supported) or byte
+    using base = std::conditional_t
+    <
+        UPstream_alias_dataType<T>::value,
+        typename UPstream_alias_dataType<T>::base,  // <- using base
+        typename UPstream_alias_dataType
+        <typename pTraits_cmptType<T>::type>::base  // <- using components
+    >;
+
+    //- The corresponding UPstream::dataTypes enumeration
+    static constexpr auto datatype_id =
+        UPstream_base_dataType<base>::datatype_id;
+
+    //- The size in terms of the number of underlying data elements
+    static std::streamsize size(std::streamsize count) noexcept
+    {
+        if constexpr (UPstream_alias_dataType<T>::value)
+        {
+            // using base: no multiplier
+            return count;
+        }
+        else
+        {
+            // using components: with multiplier
+            return count*(sizeof(T)/sizeof(base));
+        }
+    }
+};
+
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
+} // End namespace Foam
+
+
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
 #endif
diff --git a/src/Pstream/dummy/UPstream.C b/src/Pstream/dummy/UPstream.C
index 50cf97f6071b64310a52ffa9d3096d6dc810a1f4..4f70336800be6bb6fb4291aaf9792ee1cea6ec2d 100644
--- a/src/Pstream/dummy/UPstream.C
+++ b/src/Pstream/dummy/UPstream.C
@@ -109,20 +109,40 @@ void Foam::UPstream::freeCommunicatorComponents(const label index)
 {}
 
 
-void Foam::UPstream::barrier(const label communicator, UPstream::Request* req)
+void Foam::UPstream::barrier(const int communicator, UPstream::Request* req)
 {}
 
 
+void Foam::UPstream::send_done
+(
+    const int toProc,
+    const int communicator,
+    const int tag
+)
+{}
+
+
+int Foam::UPstream::wait_done
+(
+    const int fromProc,
+    const int communicator,
+    const int tag
+)
+{
+    return -1;
+}
+
+
 std::pair<int,int64_t>
 Foam::UPstream::probeMessage
 (
     const UPstream::commsTypes commsType,
     const int fromProcNo,
     const int tag,
-    const label communicator
+    const int communicator
 )
 {
-    return std::pair<int,int64_t>(-1, 0);
+    return {-1, 0};
 }
 
 
diff --git a/src/Pstream/mpi/PstreamGlobals.C b/src/Pstream/mpi/PstreamGlobals.C
index e5383b722e0831fbb0c247de998666ef6e1d6ce0..b7aa206aea7f0afa884b9bfcb3db4446e4b6e12a 100644
--- a/src/Pstream/mpi/PstreamGlobals.C
+++ b/src/Pstream/mpi/PstreamGlobals.C
@@ -34,6 +34,12 @@ Foam::DynamicList<bool> Foam::PstreamGlobals::pendingMPIFree_;
 Foam::DynamicList<MPI_Comm> Foam::PstreamGlobals::MPICommunicators_;
 Foam::DynamicList<MPI_Request> Foam::PstreamGlobals::outstandingRequests_;
 
+Foam::PstreamGlobals::DataTypeLookupTable
+Foam::PstreamGlobals::MPIdataTypes_(MPI_DATATYPE_NULL);
+
+Foam::PstreamGlobals::OpCodesLookupTable
+Foam::PstreamGlobals::MPIopCodes_(MPI_OP_NULL);
+
 
 // * * * * * * * * * * * * * * * Communicators * * * * * * * * * * * * * * * //
 
@@ -60,4 +66,247 @@ void Foam::PstreamGlobals::initCommunicator(const label index)
 }
 
 
+// * * * * * * * * * * * * * * * * Data Types  * * * * * * * * * * * * * * * //
+
+void Foam::PstreamGlobals::initDataTypes()
+{
+    static_assert
+    (
+        PstreamGlobals::DataTypeLookupTable::max_size()
+     == (int(UPstream::dataTypes::DataTypes_end)+1),
+        "Lookup table size != number of dataTypes enumerations"
+    );
+
+    // From enumeration to MPI datatype
+    #undef  defineType
+    #define defineType(Idx, BaseType) \
+    MPIdataTypes_[int(UPstream::dataTypes::Idx)] = BaseType;
+
+    // Intrinsic Types [8]:
+    defineType(type_byte,   MPI_BYTE);
+    defineType(type_int32,  MPI_INT32_T);
+    defineType(type_int64,  MPI_INT64_T);
+    defineType(type_uint32, MPI_UINT32_T);
+    defineType(type_uint64, MPI_UINT64_T);
+    defineType(type_float,  MPI_FLOAT);
+    defineType(type_double, MPI_DOUBLE);
+    defineType(type_long_double, MPI_LONG_DOUBLE);
+
+    #undef defineType
+
+    // User-define types
+    #undef  defineUserType
+    #define defineUserType(Idx, Count, BaseType, Name)                        \
+    {                                                                         \
+        auto& dt = MPIdataTypes_[int(UPstream::dataTypes::Idx)];              \
+        MPI_Type_contiguous(Count, BaseType, &dt);                            \
+        MPI_Type_set_name(dt, Name);                                          \
+        MPI_Type_commit(&dt);                                                 \
+    }
+
+    // User Types [6]:
+    defineUserType(type_3float,  3, MPI_FLOAT,  "float[3]");
+    defineUserType(type_3double, 3, MPI_DOUBLE, "double[3]");
+    defineUserType(type_6float,  6, MPI_FLOAT,  "float[6]");
+    defineUserType(type_6double, 6, MPI_DOUBLE, "double[6]");
+    defineUserType(type_9float,  9, MPI_FLOAT,  "float[9]");
+    defineUserType(type_9double, 9, MPI_DOUBLE, "double[9]");
+
+    #undef defineUserType
+}
+
+
+void Foam::PstreamGlobals::deinitDataTypes()
+{
+    // User types only
+    auto first =
+    (
+        MPIdataTypes_.begin() + int(UPstream::dataTypes::UserTypes_begin)
+    );
+    const auto last =
+    (
+        MPIdataTypes_.begin() + int(UPstream::dataTypes::UserTypes_end)
+    );
+
+    for (; first != last; ++first)
+    {
+        if (MPI_DATATYPE_NULL != *first)
+        {
+            MPI_Type_free(&(*first));
+        }
+    }
+}
+
+
+// Debugging
+bool Foam::PstreamGlobals::checkDataTypes()
+{
+    // Check all types, not just user types
+    auto first =
+    (
+        MPIdataTypes_.begin()
+    );
+    const auto last =
+    (
+        MPIdataTypes_.begin() + int(UPstream::dataTypes::DataTypes_end)
+    );
+
+    for (; (first != last); ++first)
+    {
+        if (MPI_DATATYPE_NULL == *first)
+        {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+
+// Debugging
+void Foam::PstreamGlobals::printDataTypes(bool all)
+{
+    int rank = -1;
+    if
+    (
+        (MPI_SUCCESS != MPI_Comm_rank(MPI_COMM_WORLD, &rank))
+     || (rank != 0)
+    )
+    {
+        return;
+    }
+
+    const auto print = [&](auto firstIndex, auto lastIndex)
+    {
+        auto first =
+        (
+            PstreamGlobals::MPIdataTypes_.begin() + int(firstIndex)
+        );
+        const auto last =
+        (
+            PstreamGlobals::MPIdataTypes_.begin() + int(lastIndex)
+        );
+
+        for (; (first != last); ++first)
+        {
+            std::cerr
+                << "  name = "
+                << PstreamGlobals::dataType_name(*first) << '\n';
+        }
+    };
+
+    if (all)
+    {
+        std::cerr << "enumerated data types:\n";
+        print
+        (
+            UPstream::dataTypes::DataTypes_begin,
+            UPstream::dataTypes::DataTypes_end
+        );
+    }
+    else
+    {
+        // User types only.
+        std::cerr << "enumerated user-defined data types:\n";
+        print
+        (
+            UPstream::dataTypes::UserTypes_begin,
+            UPstream::dataTypes::UserTypes_end
+        );
+    }
+}
+
+
+std::string Foam::PstreamGlobals::dataType_name(MPI_Datatype datatype)
+{
+    if (MPI_DATATYPE_NULL == datatype)
+    {
+        return std::string("(null)");
+    }
+
+    char buf[MPI_MAX_OBJECT_NAME];
+    int len;
+
+    if (MPI_SUCCESS == MPI_Type_get_name(datatype, buf, &len))
+    {
+        if (len > 0)
+        {
+            return std::string(buf, len);
+        }
+        else
+        {
+            return std::string("(anon)");
+        }
+    }
+
+    return std::string("???");
+}
+
+
+// * * * * * * * * * * * * * * * * Op Codes  * * * * * * * * * * * * * * * * //
+
+void Foam::PstreamGlobals::initOpCodes()
+{
+    static_assert
+    (
+        PstreamGlobals::OpCodesLookupTable::max_size()
+     == (int(UPstream::opCodes::OpCodes_end)+1),
+        "Lookup table size != number of opCodes enumerations"
+    );
+
+    // From enumeration to MPI datatype
+    #undef  defineCode
+    #define defineCode(Idx, CodeType) \
+    MPIopCodes_[int(UPstream::opCodes::Idx)] = CodeType;
+
+    defineCode(op_min,  MPI_MIN);
+    defineCode(op_max,  MPI_MAX);
+    defineCode(op_sum,  MPI_SUM);
+    defineCode(op_prod, MPI_PROD);
+
+    // TBD: still need to sort out if they are MPI_C_BOOL or MPI_CXX_BOOL
+    // ...
+    defineCode(op_bool_and, MPI_LAND);
+    defineCode(op_bool_or,  MPI_LOR);
+    defineCode(op_bool_xor, MPI_LXOR);
+
+    defineCode(op_bit_and,  MPI_BAND);
+    defineCode(op_bit_or,   MPI_BOR);
+    defineCode(op_bit_xor,  MPI_BXOR);
+
+    // Do not include MPI_MINLOC, MPI_MAXLOC since they are tied to
+    // float_int, double_int and larger or other types
+
+    // window-only
+    defineCode(op_replace, MPI_REPLACE);
+    defineCode(op_no_op, MPI_NO_OP);
+
+    #undef defineCode
+}
+
+
+void Foam::PstreamGlobals::deinitOpCodes()
+{}
+
+
+bool Foam::PstreamGlobals::checkOpCodes()
+{
+    auto first = MPIopCodes_.begin();
+    const auto last =
+    (
+        MPIopCodes_.begin() + int(UPstream::opCodes::OpCodes_end)
+    );
+
+    for (; (first != last); ++first)
+    {
+        if (MPI_OP_NULL == *first)
+        {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+
 // ************************************************************************* //
diff --git a/src/Pstream/mpi/PstreamGlobals.H b/src/Pstream/mpi/PstreamGlobals.H
index 2ccb245e3388439885e5250181b91a0ef028a17f..bf46434ad788e1b16eb3cb1f1d4658906491988b 100644
--- a/src/Pstream/mpi/PstreamGlobals.H
+++ b/src/Pstream/mpi/PstreamGlobals.H
@@ -40,6 +40,7 @@ SourceFiles
 #define Foam_PstreamGlobals_H
 
 #include "DynamicList.H"
+#include "FixedList.H"
 #include "UPstream.H"  // For UPstream::Request
 #include "openfoam_mpi.H"
 
@@ -62,6 +63,17 @@ extern DynamicList<MPI_Comm> MPICommunicators_;
 //- Outstanding non-blocking operations.
 extern DynamicList<MPI_Request> outstandingRequests_;
 
+typedef Foam::FixedList<MPI_Datatype, 15> DataTypeLookupTable;
+
+//- MPI data types corresponding to some fundamental and OpenFOAM types.
+//- Indexed by UPstream::dataTypes enum
+extern DataTypeLookupTable MPIdataTypes_;
+
+typedef Foam::FixedList<MPI_Op, 13> OpCodesLookupTable;
+
+//- MPI operation types, indexed by UPstream::opCodes enum
+extern OpCodesLookupTable MPIopCodes_;
+
 
 // * * * * * * * * * * * * * * * Communicators * * * * * * * * * * * * * * * //
 
@@ -89,6 +101,76 @@ inline bool warnCommunicator(int comm) noexcept
 }
 
 
+// * * * * * * * * * * * * * * * * Data Types  * * * * * * * * * * * * * * * //
+
+//- Create mapping into MPIdataTypes_ and define user data types
+void initDataTypes();
+
+//- Free any user data types
+void deinitDataTypes();
+
+//- Debugging only: check if data type mappings are non-null
+bool checkDataTypes();
+
+//- Debugging only: print data type names (all or just user-defined)
+void printDataTypes(bool all = false);
+
+//- Lookup of dataTypes enumeration as an MPI_Datatype
+inline MPI_Datatype getDataType(UPstream::dataTypes id)
+{
+    return MPIdataTypes_[static_cast<int>(id)];
+}
+
+//- Fatal if data type is not valid
+inline void checkDataType(UPstream::dataTypes id)
+{
+    if (id == UPstream::dataTypes::invalid)
+    {
+        FatalErrorInFunction
+            << "Invalid data type"
+            << Foam::abort(FatalError);
+    }
+}
+
+//- Return MPI internal name for specified MPI_Datatype
+std::string dataType_name(MPI_Datatype datatype);
+
+//- Return MPI internal name for dataTypes enumeration
+inline std::string dataType_name(UPstream::dataTypes id)
+{
+    return dataType_name(MPIdataTypes_[static_cast<int>(id)]);
+}
+
+
+// * * * * * * * * * * * * * * * * Op Codes  * * * * * * * * * * * * * * * * //
+
+//- Create mapping into MPIopCodes_
+void initOpCodes();
+
+//- Free any user-defined op codes
+void deinitOpCodes();
+
+//- Debugging only: check if op code mappings are non-null
+bool checkOpCodes();
+
+//- Lookup of opCodes enumeration as an MPI_Op
+inline MPI_Op getOpCode(UPstream::opCodes id)
+{
+    return MPIopCodes_[static_cast<int>(id)];
+}
+
+//- Fatal if opcode is not valid
+inline void checkOpCode(UPstream::opCodes id)
+{
+    if (id == UPstream::opCodes::invalid)
+    {
+        FatalErrorInFunction
+            << "Invalid operation code"
+            << Foam::abort(FatalError);
+    }
+}
+
+
 // * * * * * * * * * * * * * * * * Requests  * * * * * * * * * * * * * * * * //
 
 //- Reset UPstream::Request to MPI_REQUEST_NULL
@@ -113,7 +195,7 @@ inline void push_request
         // Transcribe as UPstream::Request
         *req = UPstream::Request(request);
     }
-    else
+    else if (MPI_REQUEST_NULL != request)
     {
         // Push onto list of requests
         PstreamGlobals::outstandingRequests_.push_back(request);
diff --git a/src/Pstream/mpi/UPstream.C b/src/Pstream/mpi/UPstream.C
index 82659a6b070d29f8a5acf4c22303eea4f3febaa3..a3bfe3ab4296730979bc254c236f2dd175fa948c 100644
--- a/src/Pstream/mpi/UPstream.C
+++ b/src/Pstream/mpi/UPstream.C
@@ -251,6 +251,17 @@ bool Foam::UPstream::init(int& argc, char**& argv, const bool needsThread)
         ourMpi = true;
     }
 
+    // Define data type mappings and user data types. Defined now so that
+    // any OpenFOAM Pstream operations may make immediate use of them.
+    PstreamGlobals::initDataTypes();
+    PstreamGlobals::initOpCodes();
+
+    if (UPstream::debug)
+    {
+        PstreamGlobals::printDataTypes();
+    }
+
+
     // Check argument list for local world
     label worldIndex = -1;
     for (int argi = 1; argi < argc; ++argi)
@@ -591,6 +602,9 @@ void Foam::UPstream::shutdown(int errNo)
         }
     }
 
+    // Free any user data types
+    PstreamGlobals::deinitDataTypes();
+    PstreamGlobals::deinitOpCodes();
 
     MPI_Finalize();
 }
@@ -1050,10 +1064,10 @@ bool Foam::UPstream::setSharedMemoryCommunicators()
 }
 
 
-void Foam::UPstream::barrier(const label communicator, UPstream::Request* req)
+void Foam::UPstream::barrier(const int communicator, UPstream::Request* req)
 {
     // No-op for non-parallel or not on communicator
-    if (!UPstream::parRun() || !UPstream::is_rank(communicator))
+    if (!UPstream::is_parallel(communicator))
     {
         PstreamGlobals::reset_request(req);
         return;
@@ -1099,19 +1113,78 @@ void Foam::UPstream::barrier(const label communicator, UPstream::Request* req)
 }
 
 
+void Foam::UPstream::send_done
+(
+    const int toProcNo,
+    const int communicator,
+    const int tag  // Message tag (must match on receiving side)
+)
+{
+    if (!UPstream::is_parallel(communicator))
+    {
+        // Nothing to do
+        return;
+    }
+
+    {
+        MPI_Send
+        (
+            nullptr, 0, MPI_BYTE, toProcNo, tag,
+            PstreamGlobals::MPICommunicators_[communicator]
+        );
+    }
+}
+
+
+int Foam::UPstream::wait_done
+(
+    const int fromProcNo,
+    const int communicator,
+    const int tag  // Message tag (must match on sending side)
+)
+{
+    if (!UPstream::is_parallel(communicator))
+    {
+        // Nothing to do
+        return -1;
+    }
+    else if (fromProcNo < 0)
+    {
+        MPI_Status status;
+        MPI_Recv
+        (
+            nullptr, 0, MPI_BYTE, MPI_ANY_SOURCE, tag,
+            PstreamGlobals::MPICommunicators_[communicator],
+           &status
+        );
+        return status.MPI_SOURCE;
+    }
+    else
+    {
+        MPI_Recv
+        (
+            nullptr, 0, MPI_BYTE, fromProcNo, tag,
+            PstreamGlobals::MPICommunicators_[communicator],
+            MPI_STATUS_IGNORE
+        );
+        return fromProcNo;
+    }
+}
+
+
 std::pair<int,int64_t>
 Foam::UPstream::probeMessage
 (
     const UPstream::commsTypes commsType,
     const int fromProcNo,
     const int tag,
-    const label communicator
+    const int communicator
 )
 {
     std::pair<int,int64_t> result(-1, 0);
 
     // No-op for non-parallel or not on communicator
-    if (!UPstream::parRun() || !UPstream::is_rank(communicator))
+    if (!UPstream::is_parallel(communicator))
     {
         return result;
     }