diff --git a/etc/bashrc b/etc/bashrc
index 8a74703c1e..548b3dea0f 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 e603ec6f86..f178d7f3ab 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 a197dbb0a1..8147ec9990 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 7c0823f3fc..50c0c9bf65 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 0000000000..8dca735a50
--- /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 .
+
+\*---------------------------------------------------------------------------*/
+
+#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 7bc223e001..c02a48f9cf 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 29c8588445..ed5d69dc73 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 3f86d412a6..2e18d513bd 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 0000000000..da65f06a7f
--- /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 .
+
+\*---------------------------------------------------------------------------*/
+
+#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
+#include
+#include
+
+#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 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
+ (
+ "aligned_allocator",
+ rm.getAllocator("HOST"),
+
+ // alignment
+ 256
+ );
+
+ pooled_allocator =
+ rm.makeAllocator
+ (
+ "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
+ (
+ "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
+ (
+ "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 /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 b7289be8fe..be824287de 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
#include "contiguous.H" // Also includes
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
@@ -103,6 +104,18 @@ template<> struct no_linebreak : 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
inline T* allocate(IntType n)
{
- // Plain new
- return new T[n];
+ if constexpr (ListPolicy::is_aligned_type())
+ {
+ // 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
inline void deallocate(T* ptr)
{
- // Plain new
- delete[] ptr;
+ if constexpr (ListPolicy::is_aligned_type())
+ {
+ 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
inline void deallocate(T* ptr, [[maybe_unused]] IntType n)
{
- // Plain new
- delete[] ptr;
+ if constexpr (ListPolicy::is_aligned_type())
+ {
+ // 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 d60038c71f..4ba7cc4ca6 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"
@@ -2180,6 +2181,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 0000000000..56879c3d42
--- /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 .
+
+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 // 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
+
+// ************************************************************************* //