From 7b7229fc13f45bd31d6f632f73b6b9639e8e3c25 Mon Sep 17 00:00:00 2001
From: Mark Olesen <Mark.Olesen@esi-group.com>
Date: Mon, 12 May 2025 11:24:45 +0200
Subject: [PATCH] ENH: integrate memory pool support for List allocations

- provides an optional memory management using a memory pool.
  Currently can support Umpire (https://github.com/LLNL/Umpire)

  When available, its use can be controlled by the FOAM_MEMORY_POOL
  environment variable, or the memory_pool Optimisation switch
  (etc/controlDict).

Notes:

  Use of the memory-pool is controlled by the 'is_aligned_type()' test
  and the minimum field size, controlled by the 'use_memory_pool()' test.

  If the memory-pool is not enabled or not required according to the two
  above tests, the allocation falls back to either an aligned or unaligned
  allocation (depending on the field size).

  The thresholds for aligned, unaligned, memory-pool allocation
  are still a compile-time option. Made by direct edit of the
  corrsponding functions.
---
 etc/bashrc                                    |   5 +
 etc/controlDict                               |   3 +
 etc/cshrc                                     |   5 +
 src/OSspecific/MSwindows/Make/files           |   2 +
 .../MSwindows/memory/MemoryPool.cxx           |  83 +++
 src/OSspecific/POSIX/Allwmake                 |  42 ++
 src/OSspecific/POSIX/Make/files               |   2 +
 src/OSspecific/POSIX/Make/options             |   5 +-
 src/OSspecific/POSIX/memory/MemoryPool.cxx    | 510 ++++++++++++++++++
 .../containers/Lists/policy/ListPolicy.H      | 102 +++-
 src/OpenFOAM/global/argList/argList.C         |   4 +
 src/OpenFOAM/memory/pool/MemoryPool.H         | 116 ++++
 12 files changed, 872 insertions(+), 7 deletions(-)
 create mode 100644 src/OSspecific/MSwindows/memory/MemoryPool.cxx
 create mode 100644 src/OSspecific/POSIX/memory/MemoryPool.cxx
 create mode 100644 src/OpenFOAM/memory/pool/MemoryPool.H

diff --git a/etc/bashrc b/etc/bashrc
index 60030f8ce9c..9fc45639441 100644
--- a/etc/bashrc
+++ b/etc/bashrc
@@ -132,6 +132,11 @@ projectDir="$HOME/OpenFOAM/OpenFOAM-$WM_PROJECT_VERSION"
 # projectDir="@PROJECT_DIR@"
 : # Safety statement (if the user removed all fallback values)
 
+# [FOAM_MEMORY_POOL] - Optional memory management
+#                    - overrides the 'memory_pool' etc/controlDict entry
+# = "true | false | host [size=nn] [incr=nn]"
+#export FOAM_MEMORY_POOL="host"
+
 # [FOAM_SIGFPE] - Trap floating-point exceptions.
 #               - overrides the 'trapFpe' controlDict entry
 # = true | false
diff --git a/etc/controlDict b/etc/controlDict
index c3e7985415f..2bece2b4606 100644
--- a/etc/controlDict
+++ b/etc/controlDict
@@ -221,6 +221,9 @@ OptimisationSwitches
     // Other
     // =====
 
+    // Optional memory management (sizing in MB)
+    // memory_pool     "host; size=1024; incr=5"
+
     // Trap floating point exception.
     // Can override with FOAM_SIGFPE env variable (true|false)
     trapFpe         1;
diff --git a/etc/cshrc b/etc/cshrc
index b36054eabd4..5e19865b00b 100644
--- a/etc/cshrc
+++ b/etc/cshrc
@@ -134,6 +134,11 @@ set projectDir=`lsof +p $$ |& sed -ne 's#^[^/]*##;\@/'"$projectName"'[^/]*/etc/c
 # Or optionally hard-coded (eg, with autoconfig)
 # set projectDir="@PROJECT_DIR@"
 
+# [FOAM_MEMORY_POOL] - Optional memory management
+#                    - overrides the 'memory_pool' etc/controlDict entry
+# = "true | false | host [size=nn] [incr=nn]"
+#setenv FOAM_MEMORY_POOL "host"
+
 # [FOAM_SIGFPE] - Trap floating-point exceptions.
 #               - overrides the 'trapFpe' controlDict entry
 # = true | false
diff --git a/src/OSspecific/MSwindows/Make/files b/src/OSspecific/MSwindows/Make/files
index 7c0823f3fce..50c0c9bf652 100644
--- a/src/OSspecific/MSwindows/Make/files
+++ b/src/OSspecific/MSwindows/Make/files
@@ -3,6 +3,8 @@ MSwindows.C
 cpuInfo/cpuInfo.C
 memInfo/memInfo.C
 
+memory/MemoryPool.cxx
+
 signals/sigFpe.cxx
 signals/sigInt.cxx
 signals/sigQuit.cxx
diff --git a/src/OSspecific/MSwindows/memory/MemoryPool.cxx b/src/OSspecific/MSwindows/memory/MemoryPool.cxx
new file mode 100644
index 00000000000..8dca735a50d
--- /dev/null
+++ b/src/OSspecific/MSwindows/memory/MemoryPool.cxx
@@ -0,0 +1,83 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  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/>.
+
+\*---------------------------------------------------------------------------*/
+
+#include "MemoryPool.H"
+
+// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
+
+// bool Foam::MemoryPool::create(const std::string& ctrl, bool verbose)
+// {
+//     return false;
+// }
+
+
+bool Foam::MemoryPool::create(bool verbose)
+{
+    // No banner information since it is currently never an option
+    return false;
+}
+
+
+void Foam::MemoryPool::destroy(bool verbose)
+{}
+
+
+bool Foam::MemoryPool::active() noexcept
+{
+    return false;
+}
+
+
+bool Foam::MemoryPool::suspend() noexcept
+{
+    return false;
+}
+
+
+void Foam::MemoryPool::resume() noexcept
+{}
+
+
+bool Foam::MemoryPool::is_pool(void* ptr)
+{
+    return false;
+}
+
+
+void* Foam::MemoryPool::try_allocate(std::size_t nbytes)
+{
+    return nullptr;
+}
+
+
+bool Foam::MemoryPool::try_deallocate(void* ptr)
+{
+    return (!ptr);
+}
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
diff --git a/src/OSspecific/POSIX/Allwmake b/src/OSspecific/POSIX/Allwmake
index 7bc223e0015..c02a48f9cfc 100755
--- a/src/OSspecific/POSIX/Allwmake
+++ b/src/OSspecific/POSIX/Allwmake
@@ -2,6 +2,7 @@
 cd "${0%/*}" || exit                                # Run from this directory
 targetType=libo                                     # Preferred library type
 . ${WM_PROJECT_DIR:?}/wmake/scripts/AllwmakeParseArguments $*
+. ${WM_PROJECT_DIR:?}/wmake/scripts/have_umpire
 
 #------------------------------------------------------------------------------
 # Hack for MacOS (with Gcc).
@@ -59,6 +60,47 @@ then
     export COMP_FLAGS="-DFOAM_USE_INOTIFY"
 fi
 
+#------------------------------------------------------------------------------
+
+# Have -lumpire, but also -lcamp etc.
+# Also need to follow the link order
+get_umpire_libs()
+{
+    if [ -d "${UMPIRE_LIB_DIR}" ]
+    then
+        set -- $(
+            # Expected link order
+            for name in umpire fmt camp
+            do
+                [ -f "$UMPIRE_LIB_DIR/lib${name}.a" ] && echo "-l$name"
+            done
+        )
+        echo "$@"
+    else
+        echo
+    fi
+}
+
+
+if have_umpire
+then
+    libNames="$(get_umpire_libs)"
+
+    if [ -n "$libNames" ]
+    then
+        echo "    found umpire -- enabling memory pool interface" 1>&2
+        echo "    umpire libs: $libNames" 1>&2
+
+        COMP_FLAGS="$COMP_FLAGS -DFOAM_USE_UMPIRE -I${UMPIRE_INC_DIR}"
+        LINK_FLAGS="$LINK_FLAGS -L${UMPIRE_LIB_DIR} $libNames"
+        export COMP_FLAGS LINK_FLAGS
+    else
+        echo "    expecting umpire, but did not resolve the libraries" 1>&2
+    fi
+fi
+
+#------------------------------------------------------------------------------
+
 # Make object (non-shared by default)
 # Never want/need openmp, especially for static objects
 wmake -no-openmp $targetType
diff --git a/src/OSspecific/POSIX/Make/files b/src/OSspecific/POSIX/Make/files
index 29c85884451..ed5d69dc73b 100644
--- a/src/OSspecific/POSIX/Make/files
+++ b/src/OSspecific/POSIX/Make/files
@@ -4,6 +4,8 @@ cpuInfo/cpuInfo.C
 cpuTime/cpuTimePosix.C
 memInfo/memInfo.C
 
+memory/MemoryPool.cxx
+
 signals/sigFpe.cxx
 signals/sigSegv.cxx
 signals/sigInt.cxx
diff --git a/src/OSspecific/POSIX/Make/options b/src/OSspecific/POSIX/Make/options
index 3f86d412a62..2e18d513bdb 100644
--- a/src/OSspecific/POSIX/Make/options
+++ b/src/OSspecific/POSIX/Make/options
@@ -1 +1,4 @@
-EXE_INC = $(COMP_FLAGS)
+/* umpire uses old-style cast etc */
+EXE_INC = $(COMP_FLAGS) $(c++LESSWARN)
+
+LIBO_LIBS = $(LINK_FLAGS)
diff --git a/src/OSspecific/POSIX/memory/MemoryPool.cxx b/src/OSspecific/POSIX/memory/MemoryPool.cxx
new file mode 100644
index 00000000000..da65f06a7fc
--- /dev/null
+++ b/src/OSspecific/POSIX/memory/MemoryPool.cxx
@@ -0,0 +1,510 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  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/>.
+
+\*---------------------------------------------------------------------------*/
+
+#include "MemoryPool.H"
+#include "debug.H"
+#include "dictionary.H"
+#include "sigFpe.H"
+#include "OSspecific.H"  // For getEnv
+
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
+
+#ifdef FOAM_USE_UMPIRE
+
+// #include <cerrno>
+#include <cinttypes>
+#include <tuple>
+
+#include "umpire/Allocator.hpp"
+#include "umpire/ResourceManager.hpp"
+#include "umpire/strategy/AlignedAllocator.hpp"
+#include "umpire/strategy/DynamicPoolList.hpp"
+
+static bool disabled_(false);
+static umpire::Allocator aligned_allocator;
+static umpire::Allocator pooled_allocator;
+static umpire::ResourceManager* manager_(nullptr);
+static umpire::ResourceManager* suspended_(nullptr);
+
+#endif
+
+// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
+
+#ifdef FOAM_USE_UMPIRE
+
+namespace
+{
+
+// Different supported allocation types
+enum class Types { undefined, none, host, device, managed };
+
+typedef std::tuple<Types, std::size_t, std::size_t> ctrlTuple;
+
+// Extract key=INT, the key includes the '='
+int getIntParameter(const std::string& key, const std::string& ctrl)
+{
+    int val(0);
+
+    const auto pos = ctrl.find(key);
+
+    if (pos == std::string::npos)
+    {
+        return val;
+    }
+
+    const char* buf = (ctrl.data() + pos + key.size());
+
+    char *endptr = nullptr;
+    errno = 0;
+    auto parsed = std::strtoimax(buf, &endptr, 10);
+
+    if (errno || endptr == buf)
+    {
+        // Some type of error OR no conversion
+    }
+    else
+    {
+        val = int(parsed);
+    }
+
+    return val;
+}
+
+
+ctrlTuple getControlValues(const std::string& ctrl)
+{
+    ctrlTuple result(Types::undefined, 0, 0);
+
+    bool checkParam = false;
+
+    // Also find things that look like Switch constants.
+    // Unfortunately need to do this manually since Switch::find()
+    // itself would not manage to parse something like "true; size=10"
+
+    if (ctrl.empty())
+    {
+        // Nothing => undefined
+    }
+    else if
+    (
+        std::string::npos != ctrl.find("false")     // ctrl.contains("false")
+     || std::string::npos != ctrl.find("off")       // ctrl.contains("off")
+     || std::string::npos != ctrl.find("no")        // ctrl.contains("no")
+     || std::string::npos != ctrl.find("none")      // ctrl.contains("none")
+    )
+    {
+        std::get<0>(result) = Types::none;
+    }
+    else if
+    (
+        std::string::npos != ctrl.find("true")      // ctrl.contains("true")
+     || std::string::npos != ctrl.find("on")        // ctrl.contains("on")
+     || std::string::npos != ctrl.find("yes")       // ctrl.contains("yes")
+
+     || std::string::npos != ctrl.find("host")      // ctrl.contains("host")
+     || std::string::npos != ctrl.find("system")    // ctrl.contains("system")
+    )
+    {
+        std::get<0>(result) = Types::host;
+        checkParam = true;
+    }
+
+    // These need more testing
+    else if
+    (
+        std::string::npos != ctrl.find("device")    // ctrl.contains("device")
+    )
+    {
+        std::get<0>(result) = Types::device;
+        checkParam = true;
+    }
+    else if
+    (
+        std::string::npos != ctrl.find("managed")   // ctrl.contains("managed")
+    )
+    {
+        std::get<0>(result) = Types::managed;
+        checkParam = true;
+    }
+
+    if (checkParam)
+    {
+        std::get<1>(result) = getIntParameter("size=", ctrl);
+        std::get<2>(result) = getIntParameter("incr=", ctrl);
+    }
+
+    return result;
+}
+
+
+bool create_from(const ctrlTuple& controls, bool verbose)
+{
+    using namespace Foam;
+
+    if (manager_ || suspended_)
+    {
+        // Already created
+        return true;
+    }
+
+    // Type, initial size, increment
+    auto [which, size, incr] = controls;
+
+    // std::cerr
+    //     << "which=" << int(which)
+    //     << ", size=" << int(size)
+    //     << ", incr=" << int(incr) << '\n';
+
+
+    constexpr size_t MegaByte(1024*1024);
+
+    switch (which)
+    {
+        case Types::undefined :
+        {
+            if (verbose)
+            {
+                Info<< "memory pool : unused" << nl;
+            }
+            break;
+        }
+
+        case Types::none :
+        {
+            if (verbose)
+            {
+                Info<< "memory pool : disabled" << nl;
+            }
+            break;
+        }
+
+        case Types::host :
+        {
+            // Default sizing parameters
+            if (!size) size = 1024;
+            if (!incr) incr = 5;
+
+            auto& rm = umpire::ResourceManager::getInstance();
+            manager_ = &rm;
+
+            aligned_allocator =
+                rm.makeAllocator<umpire::strategy::AlignedAllocator>
+                (
+                    "aligned_allocator",
+                    rm.getAllocator("HOST"),
+
+                    // alignment
+                    256
+                );
+
+            pooled_allocator =
+                rm.makeAllocator<umpire::strategy::DynamicPoolList>
+                (
+                    "openfoam_HOST_pool",
+                    aligned_allocator,
+
+                    // initial block allocation size
+                    (size*MegaByte),
+
+                    // incremental block allocation size
+                    (incr*MegaByte)
+                );
+
+            if (verbose)
+            {
+                Info<< "memory pool : host (size="
+                    << int(size) << "MB, incr="
+                    << int(incr) << "MB)\n";
+            }
+            break;
+        }
+
+        case Types::device :
+        {
+            auto& rm = umpire::ResourceManager::getInstance();
+            manager_ = &rm;
+
+            aligned_allocator = rm.getAllocator("DEVICE");
+
+            pooled_allocator =
+                rm.makeAllocator<umpire::strategy::DynamicPoolList>
+                (
+                    "openfoam_DEVICE_pool",
+                    aligned_allocator
+                );
+
+            if (verbose)
+            {
+                Info<< "memory pool : device" << nl;
+            }
+            break;
+        }
+
+        case Types::managed :
+        {
+            // Default sizing parameters
+            if (!size) size = 10*1024;
+            if (!incr) incr = 10;
+
+            auto& rm = umpire::ResourceManager::getInstance();
+            manager_ = &rm;
+
+            aligned_allocator = rm.getAllocator("UM");
+
+            pooled_allocator =
+                rm.makeAllocator<umpire::strategy::DynamicPoolList>
+                (
+                    "openfoam_UM_pool",
+                    aligned_allocator,
+
+                    // initial block allocation size
+                    (size*MegaByte),
+
+                    // incremental block allocation size
+                    (incr*MegaByte)
+                );
+
+            if (verbose)
+            {
+                Info<< "memory pool : managed (size="
+                    << int(size) << "MB, incr="
+                    << int(incr) << "MB)\n";
+            }
+            break;
+        }
+    }
+
+    return (which != Types::undefined && which != Types::none);
+}
+
+
+} // End anonymous namespace
+
+#endif  // FOAM_USE_UMPIRE
+
+
+// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
+
+// bool Foam::MemoryPool::create(const std::string& ctrl, bool verbose)
+// {
+//     #ifdef FOAM_USE_UMPIRE
+//     if (manager_ || suspended_)
+//     {
+//         // Already created
+//         return true;
+//     }
+//
+//     auto controls = getControlValues(ctrl);
+//
+//     return create_from(controls, verbose);
+//     #else
+//     return false;
+//     #endif
+// }
+
+
+bool Foam::MemoryPool::create(bool verbose)
+{
+    #ifdef FOAM_USE_UMPIRE
+    if (disabled_)
+    {
+        // Disallowed
+        return false;
+    }
+    else if (manager_ || suspended_)
+    {
+        // Already created
+        return true;
+    }
+
+    // First check environment
+    auto controls = getControlValues(Foam::getEnv("FOAM_MEMORY_POOL"));
+
+    if (std::get<0>(controls) == Types::none)
+    {
+        // Disabled from environment - has highest priority
+        disabled_ = true;
+    }
+
+    // Currently no easy way to handle <system>/controlDict...
+
+    // Fallback from etc/controlDict
+    if (std::get<0>(controls) == Types::undefined)
+    {
+        // From central etc/controlDict
+        const auto& dict = Foam::debug::optimisationSwitches();
+
+        if (auto* eptr = dict.findStream("memory_pool", keyType::LITERAL))
+        {
+            const token& firstToken = eptr->front();
+
+            if (firstToken.isStringType())
+            {
+                controls = getControlValues(firstToken.stringToken());
+            }
+        }
+    }
+
+    return create_from(controls, verbose);
+    #else
+    if (verbose)
+    {
+        Info<< "memory pool : not available" << nl;
+    }
+    return false;
+    #endif
+}
+
+
+void Foam::MemoryPool::destroy(bool verbose)
+{
+    // Nothing currently needed but could add in something like this:
+
+    // if (manager_ || suspended_)
+    // {
+    //     pooled_allocator.release();
+    // }
+
+    // However, need to find the proper sequence within
+    // Foam::exit() or UPstream::exit() ...
+}
+
+
+bool Foam::MemoryPool::active() noexcept
+{
+    #ifdef FOAM_USE_UMPIRE
+    return bool(manager_);
+    #else
+    return false;
+    #endif
+}
+
+
+bool Foam::MemoryPool::suspend() noexcept
+{
+    #ifdef FOAM_USE_UMPIRE
+    bool status(suspended_);
+    if (manager_)  // <- and (!suspended_)
+    {
+        std::swap(manager_, suspended_);
+    }
+    return status;
+    #else
+    return false;
+    #endif
+}
+
+
+void Foam::MemoryPool::resume() noexcept
+{
+    #ifdef FOAM_USE_UMPIRE
+    if (suspended_)  // <- and (!manager_)
+    {
+        std::swap(manager_, suspended_);
+    }
+    #endif
+}
+
+
+bool Foam::MemoryPool::is_pool(void* ptr)
+{
+    #ifdef FOAM_USE_UMPIRE
+    if (ptr)
+    {
+        if (manager_)
+        {
+            return manager_->hasAllocator(ptr);
+        }
+        else if (suspended_)
+        {
+            return suspended_->hasAllocator(ptr);
+        }
+    }
+    #endif
+
+    return false;
+}
+
+
+void* Foam::MemoryPool::try_allocate(std::size_t nbytes)
+{
+    void* ptr = nullptr;
+
+    #ifdef FOAM_USE_UMPIRE
+    if (manager_)
+    {
+        ptr = pooled_allocator.allocate(nbytes);
+
+        // std::cerr<< "allocate(" << int(nbytes) << ")\n";
+
+        // Optionally fill with NaN (depends on current flags)
+        Foam::sigFpe::fillNan_if(ptr, nbytes);
+
+        if (!ptr)
+        {
+            // Pout<< "umpire failed to allocate memory\n";
+        }
+    }
+    #endif
+
+    return ptr;
+}
+
+
+bool Foam::MemoryPool::try_deallocate(void* ptr)
+{
+    #ifdef FOAM_USE_UMPIRE
+    if (ptr)
+    {
+        if (manager_)
+        {
+            if (manager_->hasAllocator(ptr))  // <- ie, is_pool()
+            {
+                // std::cerr<< "deallocate()\n";
+                manager_->deallocate(ptr);
+                return true;
+            }
+        }
+        else if (suspended_)
+        {
+            // Deallocate even if nominally suspended
+
+            if (suspended_->hasAllocator(ptr))  // <- ie, is_pool()
+            {
+                // std::cerr<< "deallocate()\n";
+                suspended_->deallocate(ptr);
+                return true;
+            }
+        }
+    }
+    #endif
+
+    return (!ptr);
+}
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
diff --git a/src/OpenFOAM/containers/Lists/policy/ListPolicy.H b/src/OpenFOAM/containers/Lists/policy/ListPolicy.H
index b7289be8fe0..be824287de9 100644
--- a/src/OpenFOAM/containers/Lists/policy/ListPolicy.H
+++ b/src/OpenFOAM/containers/Lists/policy/ListPolicy.H
@@ -34,6 +34,7 @@ Description
 #ifndef Foam_ListPolicy_H
 #define Foam_ListPolicy_H
 
+#include "MemoryPool.H"  // Also includes <cstdint>
 #include "contiguous.H"  // Also includes <type_traits>
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
@@ -103,6 +104,18 @@ template<> struct no_linebreak<wordRe> : std::true_type {};
 // - use_offload(n) :
 //   Lower threshold for switching to device offloading
 //
+//
+// Use of the memory-pool is controlled by the 'is_aligned_type()' test
+// and the minimum field size, controlled by the 'use_memory_pool()' test.
+//
+// If the memory-pool is not enabled or not required according to the two
+// above tests, the allocation falls back to either an aligned or unaligned
+// allocation.
+//
+// The decision about when to choose aligned vs unaligned allocation
+// is still a compile-time option. Made by direct edit of the
+// appropriate functions.
+//
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
 //- Consider aligned allocation for the given type?
@@ -146,27 +159,104 @@ inline constexpr bool use_offload(IntType n) noexcept
 }
 
 
+//- Default alignment for larger fields
+inline constexpr std::align_val_t default_alignment() noexcept
+{
+    return std::align_val_t(256);
+}
+
+
+//- Allocate from memory pool (if active), or aligned, or normal
 template<class T, class IntType>
 inline T* allocate(IntType n)
 {
-    // Plain new
-    return new T[n];
+    if constexpr (ListPolicy::is_aligned_type<T>())
+    {
+        // Note: threshold for use_memory_pool() >= use_alignment()
+
+        if (ListPolicy::use_alignment(n))
+        {
+            if
+            (
+                void *pool_ptr
+                (
+                    // Consider memory pool for large amounts of data
+                    ListPolicy::use_memory_pool(n)
+                  ? Foam::MemoryPool::try_allocate(sizeof(T)*n)
+                  : nullptr
+                );
+                pool_ptr
+            )
+            {
+                // Placement new
+                return new (pool_ptr) T[n];
+            }
+            else
+            {
+                return new (ListPolicy::default_alignment()) T[n];
+            }
+        }
+        else
+        {
+            // Plain new
+            return new T[n];
+        }
+    }
+    else
+    {
+        // Plain new
+        return new T[n];
+    }
 }
 
 
+//- Deallocate from memory pool, or normal
 template<class T, class IntType>
 inline void deallocate(T* ptr)
 {
-    // Plain new
-    delete[] ptr;
+    if constexpr (ListPolicy::is_aligned_type<T>())
+    {
+        if (ptr && !Foam::MemoryPool::try_deallocate(ptr))
+        {
+            // Plain new
+            delete[] ptr;
+        }
+    }
+    else
+    {
+        // Plain new
+        delete[] ptr;
+    }
 }
 
 
+//- Deallocate from memory pool, or aligned, or normal
 template<class T, class IntType>
 inline void deallocate(T* ptr, [[maybe_unused]] IntType n)
 {
-    // Plain new
-    delete[] ptr;
+    if constexpr (ListPolicy::is_aligned_type<T>())
+    {
+        // Note: threshold for use_memory_pool() >= use_alignment()
+
+        if (ListPolicy::use_alignment(n))
+        {
+            if (ptr && !Foam::MemoryPool::try_deallocate(ptr))
+            {
+                // Alignment depends on the number of elements
+                ::operator delete[](ptr, ListPolicy::default_alignment());
+            }
+        }
+        else
+        {
+            // Plain new
+            delete[] ptr;
+        }
+    }
+    else
+    {
+        // Plain new
+        delete[] ptr;
+    }
 }
 
 
diff --git a/src/OpenFOAM/global/argList/argList.C b/src/OpenFOAM/global/argList/argList.C
index 0040be93d62..3806e974caa 100644
--- a/src/OpenFOAM/global/argList/argList.C
+++ b/src/OpenFOAM/global/argList/argList.C
@@ -37,6 +37,7 @@ License
 #include "IOobject.H"
 #include "dynamicCode.H"
 #include "simpleObjectRegistry.H"
+#include "MemoryPool.H"
 #include "sigFpe.H"
 #include "sigInt.H"
 #include "sigQuit.H"
@@ -2182,6 +2183,9 @@ void Foam::argList::parse
         sigQuit::set(bannerEnabled());
         sigSegv::set(bannerEnabled());
 
+        // Create memory pool (if any) after MPI has been setup
+        MemoryPool::create(bannerEnabled());
+
         if (UPstream::master() && bannerEnabled())
         {
             Info<< "fileModificationChecking : "
diff --git a/src/OpenFOAM/memory/pool/MemoryPool.H b/src/OpenFOAM/memory/pool/MemoryPool.H
new file mode 100644
index 00000000000..56879c3d42f
--- /dev/null
+++ b/src/OpenFOAM/memory/pool/MemoryPool.H
@@ -0,0 +1,116 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  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/>.
+
+Class
+    Foam::MemoryPool
+
+Description
+    Optional memory management using a memory pool such as Umpire
+    (https://github.com/LLNL/Umpire).
+
+    When compiled with Umpire, its use can be controlled by the
+    \c FOAM_MEMORY_POOL environment variable, or the
+    \c memory_pool Optimisation switch (etc/controlDict).
+
+    It currently looks for any of the following entries, in this order:
+    - true - same as \em "host"
+    - false/none - disabled.
+    - \em "host" - uses host memory pool
+    - \em "system" - same as \em "host"
+    - \em "device" - uses device memory pool
+    - \em "managed" - uses managed host/device memory pool
+    .
+
+    The parameters "size=nn" and "incr=nn" (in MegaBytes) can be used
+    to specify alternatives to the default sizing.
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef Foam_MemoryPool_H
+#define Foam_MemoryPool_H
+
+#include <cstdint>  // For size_t
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+/*---------------------------------------------------------------------------*\
+                         Class MemoryPool Declaration
+\*---------------------------------------------------------------------------*/
+
+class MemoryPool
+{
+public:
+
+    // Constructors
+
+        //- Create a memory pool instance (if not already active).
+        //  Uses environment or etc/controlDict entry
+        static bool create(bool verbose = false);
+
+
+    // Destructor
+
+        //- Remove the memory pool instance (currently does nothing)
+        static void destroy(bool verbose = false);
+
+
+    // Member Functions
+
+        //- True if pool is active (ie, created and not suspended)
+        static bool active() noexcept;
+
+        //- Suspend use of memory pool (for allocation).
+        //  \return previous suspend status
+        static bool suspend() noexcept;
+
+        //- Resume use of memory pool (if previously active)
+        static void resume() noexcept;
+
+        //- Test if given pointer belongs to the pool
+        static bool is_pool(void *ptr);
+
+        //- Allocate from pool (if active).
+        //  \returns nullptr if the pool is not active
+        static void* try_allocate(std::size_t nbytes);
+
+        //- Deallocate a pointer managed by the pool
+        //  \returns True if a nullptr (no-op) or when the pointer was
+        //      managed by the pool.
+        static bool try_deallocate(void *ptr);
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
-- 
GitLab