From 0e087e5517311e8c09f287ae2f508807a544b31c Mon Sep 17 00:00:00 2001 From: Elwardi Date: Thu, 27 Nov 2025 22:10:11 +0100 Subject: [PATCH 01/30] feat: cloudSupport library --- src/lagrangian/Make/files | 3 + src/lagrangian/Make/options | 27 + src/lagrangian/cloudSupport/cloudSupport.C | 599 +++++++++++++++++++++ src/lagrangian/cloudSupport/cloudSupport.H | 136 +++++ 4 files changed, 765 insertions(+) create mode 100644 src/lagrangian/Make/files create mode 100644 src/lagrangian/Make/options create mode 100644 src/lagrangian/cloudSupport/cloudSupport.C create mode 100644 src/lagrangian/cloudSupport/cloudSupport.H diff --git a/src/lagrangian/Make/files b/src/lagrangian/Make/files new file mode 100644 index 0000000..7ec9854 --- /dev/null +++ b/src/lagrangian/Make/files @@ -0,0 +1,3 @@ +cloudSupport/cloudSupport.C + +LIB = $(FOAM_USER_LIBBIN)/libamrLagrangian diff --git a/src/lagrangian/Make/options b/src/lagrangian/Make/options new file mode 100644 index 0000000..b90127d --- /dev/null +++ b/src/lagrangian/Make/options @@ -0,0 +1,27 @@ +EXE_INC = $(DEBUG_FLAGS) \ + -I$(LIB_SRC)/finiteVolume/lnInclude \ + -I$(LIB_SRC)/meshTools/lnInclude \ + -I$(LIB_SRC)/lagrangian/basic/lnInclude \ + -I$(LIB_SRC)/lagrangian/intermediate/lnInclude \ + -I$(LIB_SRC)/lagrangian/distributionModels/lnInclude \ + -I$(LIB_SRC)/thermophysicalModels/specie/lnInclude \ + -I$(LIB_SRC)/thermophysicalModels/basic/lnInclude \ + -I$(LIB_SRC)/thermophysicalModels/thermophysicalProperties/lnInclude \ + -I$(LIB_SRC)/thermophysicalModels/SLGThermo/lnInclude \ + -I$(LIB_SRC)/thermophysicalModels/radiation/lnInclude \ + -I$(LIB_SRC)/thermophysicalModels/reactionThermo/lnInclude \ + -I$(LIB_SRC)/regionModels/regionModel/lnInclude \ + -I$(LIB_SRC)/regionModels/surfaceFilmModels/lnInclude \ + -I$(LIB_SRC)/regionFaModels/lnInclude \ + -I$(LIB_SRC)/finiteArea/lnInclude \ + -I$(LIB_SRC)/faOptions/lnInclude \ + -I$(LIB_SRC)/transportModels/compressible/lnInclude \ + -I$(LIB_SRC)/TurbulenceModels/turbulenceModels/lnInclude \ + -I$(LIB_SRC)/TurbulenceModels/compressible/lnInclude \ + -I$(LIB_SRC)/sampling/lnInclude + +LIB_LIBS = \ + -lfiniteVolume \ + -lmeshTools \ + -llagrangian \ + -llagrangianIntermediate diff --git a/src/lagrangian/cloudSupport/cloudSupport.C b/src/lagrangian/cloudSupport/cloudSupport.C new file mode 100644 index 0000000..5e0c61e --- /dev/null +++ b/src/lagrangian/cloudSupport/cloudSupport.C @@ -0,0 +1,599 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | Copyright (C) 2025 + \\/ M anipulation | +------------------------------------------------------------------------------- +License + This file is a derivative work 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 "cloudSupport.H" +#include "polyMesh.H" +#include "PstreamBuffers.H" +#include "mapDistribute.H" +#include "globalIndex.H" +#include "DynamicList.H" +#include "HashTable.H" +#include "IDLList.H" + +// Intermediate cloud includes for dynamic_cast support +#include "basicKinematicCloud.H" +#include "basicKinematicCollidingCloud.H" +#include "basicKinematicMPPICCloud.H" +#include "basicThermoCloud.H" +#include "basicReactingCloud.H" +#include "basicReactingMultiphaseCloud.H" + +// Parcel type includes for streaming/creating parcels +#include "basicKinematicParcel.H" +#include "basicKinematicCollidingParcel.H" +#include "basicKinematicMPPICParcel.H" +#include "basicThermoParcel.H" +#include "basicReactingParcel.H" +#include "basicReactingMultiphaseParcel.H" + +// Debug switch for detailed particle tracking output +int cloudSupportDebug = Foam::debug::debugSwitch("cloudSupport", 0); + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +namespace Foam +{ +namespace cloudSupport +{ + // Static storage for pending positions during distribution (legacy) + static HashTable> pendingPositions_; + + // Static storage for pending parcel data during distribution + // Key: cloudName, Value: raw binary data containing serialized parcels + static HashTable> pendingParcelData_; + + // Helper macro to try dynamic_cast and call storeGlobalPositions + #define TRY_STORE_POSITIONS(CloudType) \ + if (auto* ptr = dynamic_cast(&c)) \ + { \ + ptr->storeGlobalPositions(); \ + Info<< " Cloud '" << c.name() << "' (" << #CloudType \ + << "): " << ptr->nParcels() << " particles" << endl; \ + handled = true; \ + } + + // Helper function to store positions for a single cloud + bool storePositionsForCloud(cloud& c) + { + bool handled = false; + + // Try each known cloud type in order of specificity + // (most derived first to get best match) + + // Reacting multiphase clouds (most derived) + TRY_STORE_POSITIONS(basicReactingMultiphaseCloud) + else TRY_STORE_POSITIONS(basicReactingCloud) + else TRY_STORE_POSITIONS(basicThermoCloud) + else TRY_STORE_POSITIONS(basicKinematicCollidingCloud) + else TRY_STORE_POSITIONS(basicKinematicMPPICCloud) + else TRY_STORE_POSITIONS(basicKinematicCloud) + // passiveParticle clouds (base case) + else if (auto* ptr = dynamic_cast*>(&c)) + { + ptr->storeGlobalPositions(); + Info<< " Cloud '" << c.name() << "' (passiveParticleCloud): " + << ptr->size() << " particles" << endl; + handled = true; + } + + return handled; + } + + #undef TRY_STORE_POSITIONS +} +} + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +void Foam::cloudSupport::storeGlobalPositions(const fvMesh& mesh) +{ + // Get ALL clouds registered with the mesh using the base cloud class + UPtrList allClouds = mesh.csorted(); + + if (allClouds.empty()) + { + return; + } + + Info<< "cloudSupport: Storing global positions for " + << allClouds.size() << " cloud(s)" << endl; + + for (const cloud& constCloud : allClouds) + { + cloud& c = const_cast(constCloud); + + if (!storePositionsForCloud(c)) + { + // Unknown cloud type - warn but don't fail + WarningInFunction + << "Unknown cloud type for '" << c.name() + << "' - positions not stored. " + << "Load balancing may fail for this cloud." << endl; + } + } +} + + +void Foam::cloudSupport::autoMapClouds +( + const fvMesh& mesh, + const mapPolyMesh& map +) +{ + // Note: autoMap is virtual in the base cloud class, so we can call it + // polymorphically. However, it requires storeGlobalPositions() to have + // been called first + + UPtrList allClouds = mesh.csorted(); + + if (allClouds.empty()) + { + return; + } + + Info<< "cloudSupport: Remapping " << allClouds.size() + << " cloud(s) after topology change" << endl; + + for (const cloud& constCloud : allClouds) + { + cloud& c = const_cast(constCloud); + const label oldSize = c.nParcels(); + + // autoMap is virtual in base class - call polymorphically + c.autoMap(map); + + Info<< " Cloud '" << c.name() + << "': " << oldSize << " -> " << c.nParcels() + << " particles" << endl; + } +} + + +// Helper macro to extract and transfer parcel positions + data between procs +// This preserves all parcel properties (position, velocity, diameter, etc.) +// Positions are stored separately to allow locating cells on the new mesh. +#define DISTRIBUTE_PARCELS(CloudType, ParcelType) \ + if (auto* ptr = dynamic_cast(&c)) \ + { \ + /* Lists of positions to be transferred to each processor */ \ + List> posTransferLists(UPstream::nProcs()); \ + \ + /* Extract positions and sort by destination processor */ \ + for (const auto& p : *ptr) \ + { \ + const label celli = p.cell(); \ + if (celli >= 0 && celli < distribution.size()) \ + { \ + posTransferLists[distribution[celli]].append(p.position()); \ + } \ + } \ + \ + /* Clear the cloud */ \ + ptr->clear(); \ + \ + if (cloudSupportDebug) \ + { \ + forAll(posTransferLists, procI) \ + { \ + Pout<< " -> proc " << procI << ": " \ + << posTransferLists[procI].size() << " parcels" << nl; \ + } \ + } \ + \ + /* Stream positions into send buffers */ \ + forAll(posTransferLists, procI) \ + { \ + if (posTransferLists[procI].size()) \ + { \ + UOPstream os(procI, pBufs); \ + os << posTransferLists[procI]; \ + } \ + } \ + \ + pBufs.finishedSends(); \ + \ + /* Receive positions */ \ + DynamicList receivedPositions; \ + for (const int proci : pBufs.allProcs()) \ + { \ + if (pBufs.recvDataCount(proci)) \ + { \ + UIPstream is(proci, pBufs); \ + List positions(is); \ + receivedPositions.append(positions); \ + \ + if (cloudSupportDebug) \ + { \ + Pout<< " <- proc " << proci << ": " \ + << positions.size() << " parcels" << nl; \ + } \ + } \ + } \ + \ + /* Store positions for later relocation */ \ + pendingPositions_.set(cloudName, receivedPositions); \ + globalNewSize = returnReduce(receivedPositions.size(), sumOp