/*---------------------------------------------------------------------------*\ ========= | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | \\ / A nd | www.openfoam.com \\/ M anipulation | ------------------------------------------------------------------------------- Copyright (C) 2023-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 "distributedDILUPreconditioner.H" #include "processorLduInterface.H" #include "cyclicLduInterface.H" #include "cyclicAMILduInterface.H" #include "processorColour.H" // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * // namespace Foam { defineTypeNameAndDebug(distributedDILUPreconditioner, 0); lduMatrix::preconditioner:: addasymMatrixConstructorToTable adddistributedDILUPreconditionerAsymMatrixConstructorToTable_; const lduMesh* distributedDILUPreconditioner::meshPtr_ = nullptr; autoPtr distributedDILUPreconditioner::procColoursPtr_(nullptr); } // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // void Foam::distributedDILUPreconditioner::updateMatrixInterfaces ( const bool add, const FieldField& coupleCoeffs, const labelList& selectedInterfaces, const solveScalarField& psiif, solveScalarField& result, const direction cmpt ) const { const auto& matrix = solver_.matrix(); const auto& lduAddr = matrix.lduAddr(); const auto& interfaces = solver_.interfaces(); const label startOfRequests = Pstream::nRequests(); // From lduMatrix::initMatrixInterfaces // Avoid any conflicts with inter-processor comms const int oldTag = UPstream::incrMsgType(321); for (const label interfacei : selectedInterfaces) { interfaces[interfacei].initInterfaceMatrixUpdate ( result, add, lduAddr, interfacei, psiif, coupleCoeffs[interfacei], cmpt, UPstream::commsTypes::nonBlocking ); } UPstream::waitRequests(startOfRequests); // Consume for (const label interfacei : selectedInterfaces) { interfaces[interfacei].updateInterfaceMatrix ( result, add, lduAddr, interfacei, psiif, coupleCoeffs[interfacei], cmpt, UPstream::commsTypes::nonBlocking ); } UPstream::msgType(oldTag); // Restore tag } void Foam::distributedDILUPreconditioner::sendGlobal ( const labelList& selectedInterfaces, solveScalarField& psi, const label colouri ) const { const auto& interfaces = solver_.interfaces(); if (selectedInterfaces.size()) { // Save old data FieldField one(interfaces.size()); FieldField old(interfaces.size()); for (const label inti : selectedInterfaces) { const auto& intf = interfaces[inti].interface(); const auto& fc = intf.faceCells(); one.set(inti, new scalarField(fc.size(), 1.0)); old.set(inti, new solveScalarField(psi, intf.faceCells())); } updateMatrixInterfaces ( false, // add to psi one, selectedInterfaces, psi, // send data psi, // result 0 // cmpt ); if (!colourBufs_.set(colouri)) { colourBufs_.set ( colouri, new FieldField ( interfaces.size() ) ); } auto& colourBuf = colourBufs_[colouri]; colourBuf.setSize(interfaces.size()); for (const label inti : selectedInterfaces) { const auto& intf = interfaces[inti].interface(); const auto& fc = intf.faceCells(); if (!colourBuf.set(inti)) { colourBuf.set(inti, new Field(fc.size())); } auto& cb = colourBuf[inti]; //cb.resize_nocopy(fc.size()); auto& oldValues = old[inti]; forAll(cb, face) { const label cell = fc[face]; // Store change in boundary value cb[face] = psi[cell]-oldValues[face]; // Restore old value std::swap(psi[cell], oldValues[face]); } } } } void Foam::distributedDILUPreconditioner::receive ( const labelList& selectedInterfaces, DynamicList& requests ) const { const auto& interfaces = solver_.interfaces(); const auto& interfaceBouCoeffs = solver_.interfaceBouCoeffs(); // Start reads for (const label inti : selectedInterfaces) { const auto& intf = interfaces[inti].interface(); const auto* ppp = isA(intf); auto& recvBuf = recvBufs_[inti]; recvBuf.resize_nocopy(interfaceBouCoeffs[inti].size()); UIPstream::read ( requests.emplace_back(), ppp->neighbProcNo(), recvBuf, ppp->tag()+70, // random offset ppp->comm() ); } } void Foam::distributedDILUPreconditioner::send ( const labelList& selectedInterfaces, const solveScalarField& psiInternal, DynamicList& requests ) const { const auto& interfaces = solver_.interfaces(); // Start writes for (const label inti : selectedInterfaces) { const auto& intf = interfaces[inti].interface(); const auto* ppp = isA(intf); const auto& faceCells = intf.faceCells(); auto& sendBuf = sendBufs_[inti]; sendBuf.resize_nocopy(faceCells.size()); forAll(faceCells, face) { sendBuf[face] = psiInternal[faceCells[face]]; } UOPstream::write ( requests.emplace_back(), ppp->neighbProcNo(), sendBuf, ppp->tag()+70, // random offset ppp->comm() ); } } void Foam::distributedDILUPreconditioner::wait ( DynamicList& requests, const bool cancel ) const { if (cancel) { UPstream::cancelRequests(requests); } else { UPstream::waitRequests(requests); } requests.clear(); } // Diagonal calculation // ~~~~~~~~~~~~~~~~~~~~ void Foam::distributedDILUPreconditioner::addInterfaceDiag ( solveScalarField& rD, const label inti, const Field& recvBuf ) const { const auto& interfaces = solver_.interfaces(); const auto& interfaceBouCoeffs = solver_.interfaceBouCoeffs(); const auto& interfaceIntCoeffs = solver_.interfaceIntCoeffs(); const auto& intf = interfaces[inti].interface(); // TBD: do not use patch faceCells but passed-in addressing? const auto& faceCells = intf.faceCells(); const auto& bc = interfaceBouCoeffs[inti]; const auto& ic = interfaceIntCoeffs[inti]; forAll(recvBuf, face) { // Note:interfaceBouCoeffs, interfaceIntCoeffs on the receiving // side are negated rD[faceCells[face]] -= bc[face]*ic[face]/recvBuf[face]; } } void Foam::distributedDILUPreconditioner::forwardInternalDiag ( solveScalarField& rD, const label colouri ) const { // Add forward constributions to diagonal const auto& matrix = solver_.matrix(); const auto& lduAddr = matrix.lduAddr(); const label* const __restrict__ uPtr = lduAddr.upperAddr().begin(); const label* const __restrict__ lPtr = lduAddr.lowerAddr().begin(); const scalar* const __restrict__ upperPtr = matrix.upper().begin(); const scalar* const __restrict__ lowerPtr = matrix.lower().begin(); const label nFaces = matrix.upper().size(); if (cellColourPtr_) { const auto& cellColour = *cellColourPtr_; for (label face=0; face& recvBuf ) const { const auto& interfaces = solver_.interfaces(); const auto& interfaceBouCoeffs = solver_.interfaceBouCoeffs(); const auto& intf = interfaces[inti].interface(); const auto& faceCells = intf.faceCells(); const auto& bc = interfaceBouCoeffs[inti]; const solveScalar* __restrict__ rDPtr = rD_.begin(); solveScalar* __restrict__ wAPtr = wA.begin(); forAll(recvBuf, face) { // Note: interfaceBouCoeffs is -upperPtr const label cell = faceCells[face]; wAPtr[cell] += rDPtr[cell]*bc[face]*recvBuf[face]; } } void Foam::distributedDILUPreconditioner::forwardInternal ( solveScalarField& wA, const label colouri ) const { const auto& matrix = solver_.matrix(); const auto& lduAddr = matrix.lduAddr(); solveScalar* __restrict__ wAPtr = wA.begin(); const solveScalar* __restrict__ rDPtr = rD_.begin(); const label* const __restrict__ uPtr = lduAddr.upperAddr().begin(); const label* const __restrict__ lPtr = lduAddr.lowerAddr().begin(); const label* const __restrict__ losortPtr = lduAddr.losortAddr().begin(); const scalar* const __restrict__ lowerPtr = matrix.lower().begin(); const label nFaces = matrix.upper().size(); if (cellColourPtr_) { const auto& cellColour = *cellColourPtr_; for (label face=0; face=0; face--) { const label cell = uPtr[face]; if (cellColour[cell] == colouri) { // Note: lower cell guaranteed in same colour wAPtr[lPtr[face]] -= rDPtr[lPtr[face]]*upperPtr[face]*wAPtr[cell]; } } } else { for (label face=nFacesM1; face>=0; face--) { wAPtr[lPtr[face]] -= rDPtr[lPtr[face]]*upperPtr[face]*wAPtr[uPtr[face]]; } } } void Foam::distributedDILUPreconditioner::calcReciprocalD ( solveScalarField& rD ) const { const auto& matrix = solver_.matrix(); // Make sure no outstanding receives wait(lowerRecvRequests_); // Start reads (into recvBufs_) receive(lowerNbrs_, lowerRecvRequests_); // Start with diagonal const scalarField& diag = matrix.diag(); rD.resize_nocopy(diag.size()); std::copy(diag.begin(), diag.end(), rD.begin()); // Subtract coupled contributions { // Wait for finish. Received result in recvBufs wait(lowerRecvRequests_); for (const label inti : lowerNbrs_) { addInterfaceDiag(rD, inti, recvBufs_[inti]); } } // - divide the internal mesh into domains/colour, similar to domain // decomposition // - we could use subsetted bits of the mesh or just loop over only // the cells of the current domain // - a domain only uses boundary values of lower numbered domains // - this also limits the interfaces that need to be evaluated since // we assume an interface only changes local faceCells and these // are all of the same colour for (label colouri = 0; colouri < nColours_; colouri++) { if (cellColourPtr_) // && colourBufs_.set(colouri)) { for (const label inti : lowerGlobalRecv_[colouri]) { addInterfaceDiag(rD, inti, colourBufs_[colouri][inti]); } } forwardInternalDiag(rD, colouri); // Store effect of exchanging rD to higher interfaces in colourBufs_ if (cellColourPtr_) { sendGlobal(higherGlobalSend_[colouri], rD, higherColour_[colouri]); } } // Make sure no outstanding sends wait(higherSendRequests_); // Start writes of rD (using sendBufs) send(higherNbrs_, rD, higherSendRequests_); // Calculate the reciprocal of the preconditioned diagonal const label nCells = rD.size(); for (label cell=0; cell("coupled", true, keyType::LITERAL)), rD_(sol.matrix().diag().size()) { const lduMesh& mesh = sol.matrix().mesh(); const auto& interfaces = sol.interfaces(); const auto& interfaceBouCoeffs = sol.interfaceBouCoeffs(); // Allocate buffers // ~~~~~~~~~~~~~~~~ sendBufs_.setSize(interfaces.size()); recvBufs_.setSize(interfaces.size()); forAll(interfaceBouCoeffs, inti) { if (interfaces.set(inti)) { const auto& bc = interfaceBouCoeffs[inti]; sendBufs_.set(inti, new solveScalarField(bc.size(), Zero)); recvBufs_.set(inti, new solveScalarField(bc.size(), Zero)); } } // Determine processor colouring // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //const processorColour& colours = processorColour::New(mesh); //const label myColour = colours[Pstream::myProcNo()]; if (&mesh != meshPtr_) { procColoursPtr_.reset(new labelList(Pstream::nProcs(mesh.comm()))); const label nProcColours = processorColour::colour(mesh, procColoursPtr_()); if (debug) { Info<< typeName << " : calculated processor colours:" << nProcColours << endl; } meshPtr_ = &mesh; } const labelList& procColours = procColoursPtr_(); const label myColour = procColours[Pstream::myProcNo(mesh.comm())]; bool haveGlobalCoupled = false; bool haveDistributedAMI = false; forAll(interfaces, inti) { if (interfaces.set(inti)) { const auto& intf = interfaces[inti].interface(); const auto* ppp = isA(intf); if (ppp) { const label nbrColour = procColours[ppp->neighbProcNo()]; //const label nbrColour = ppp->neighbProcNo(); if (nbrColour < myColour) { lowerNbrs_.append(inti); } else if (nbrColour > myColour) { higherNbrs_.append(inti); } else { WarningInFunction << "weird processorLduInterface" << " from " << ppp->myProcNo() << " to " << ppp->neighbProcNo() << endl; } } else //if (isA(intf)) { haveGlobalCoupled = true; const auto* AMIpp = isA(intf); if (AMIpp) { //const auto& AMI = //( // AMIpp->owner() // ? AMIpp->AMI() // : AMIpp->neighbPatch().AMI() //); //haveDistributedAMI = AMI.distributed(); if (debug) { Info<< typeName << " : interface " << inti << " of type " << intf.type() << " is distributed:" << haveDistributedAMI << endl; } } } } } if (debug) { Info<< typeName << " : lower proc interfaces:" << flatOutput(lowerNbrs_) << " higher proc interfaces:" << flatOutput(higherNbrs_) << endl; } // Determine local colouring/zoneing // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Start off with all cells colour 0 nColours_ = 1; cellColourPtr_.reset(); if (coupled_ && haveGlobalCoupled) { labelList patchToColour; cellColourPtr_.reset(new labelList(mesh.lduAddr().size())); //if (haveDistributedAMI) //{ // nColours_ = 1; // *cellColourPtr_ = 0; // patchToColour = labelList(interfaces.size(), 0); //} //else { nColours_ = processorColour::cellColour ( mesh, cellColourPtr_(), patchToColour ); } lowerGlobalRecv_.resize_nocopy(nColours_); lowerGlobalSend_.resize_nocopy(nColours_); lowerColour_.setSize(nColours_, -1); higherGlobalRecv_.resize_nocopy(nColours_); higherGlobalSend_.resize_nocopy(nColours_); higherColour_.setSize(nColours_, -1); colourBufs_.setSize(nColours_); forAll(interfaces, inti) { if (interfaces.set(inti)) { const auto& intf = interfaces[inti].interface(); label nbrInti = -1; const auto* AMIpp = isA(intf); if (AMIpp) { nbrInti = AMIpp->neighbPatchID(); } const auto* cycpp = isA(intf); if (cycpp) { nbrInti = cycpp->neighbPatchID(); } if (nbrInti != -1) { const label colouri = patchToColour[inti]; const label nbrColouri = patchToColour[nbrInti]; if ( (haveDistributedAMI && (inti < nbrInti)) || (!haveDistributedAMI && (colouri < nbrColouri)) ) { // Send to higher higherGlobalSend_[colouri].append(nbrInti); higherColour_[colouri] = nbrColouri; // Receive from higher higherGlobalRecv_[colouri].append(inti); } else { // Send to lower lowerGlobalSend_[colouri].append(nbrInti); lowerColour_[colouri] = nbrColouri; // Receive from lower lowerGlobalRecv_[colouri].append(inti); } } } } } if (debug) { Info<< typeName << " : local colours:" << nColours_ << endl; Info<< incrIndent; forAll(lowerGlobalRecv_, colouri) { const auto& lowerRecv = lowerGlobalRecv_[colouri]; if (lowerRecv.size()) { const auto& lowerSend = lowerGlobalSend_[colouri]; const auto& lowerCol = lowerColour_[colouri]; Info<< indent << "Lower global interfaces for colour:" << colouri << " receiving from:" << flatOutput(lowerRecv) << " sending to:" << flatOutput(lowerSend) << " with colour:" << lowerCol << endl; } } forAll(higherGlobalRecv_, colouri) { const auto& higherRecv = higherGlobalRecv_[colouri]; if (higherRecv.size()) { const auto& higherSend = higherGlobalSend_[colouri]; const auto& higherCol = higherColour_[colouri]; Info<< indent << "Higher global interfaces for colour:" << colouri << " receiving from:" << flatOutput(higherRecv) << " sending to:" << flatOutput(higherSend) << " with colour:" << higherCol << endl; } } } calcReciprocalD(rD_); } // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * // Foam::distributedDILUPreconditioner::~distributedDILUPreconditioner() { DebugPout<< "~distributedDILUPreconditioner()" << endl; // Wait on all requests before storage is being taken down wait(lowerSendRequests_); wait(lowerRecvRequests_); wait(higherSendRequests_); wait(higherRecvRequests_); // TBD: cancel/ignore outstanding messages //wait(lowerSendRequests_, true); //wait(lowerRecvRequests_, true); //wait(higherSendRequests_, true); //wait(higherRecvRequests_, true); } // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // void Foam::distributedDILUPreconditioner::precondition ( solveScalarField& wA, const solveScalarField& rA, const direction ) const { solveScalar* __restrict__ wAPtr = wA.begin(); const solveScalar* __restrict__ rAPtr = rA.begin(); const solveScalar* __restrict__ rDPtr = rD_.begin(); const label nCells = wA.size(); // Forward sweep // ~~~~~~~~~~~~~ // Make sure no receives are still in flight (should not happen) wait(lowerRecvRequests_); // Start reads (into recvBufs) receive(lowerNbrs_, lowerRecvRequests_); // Initialise 'internal' cells for (label cell=0; cell= 0; colouri--) { // Do non-processor boundaries for this colour if (cellColourPtr_) // && colourBufs_.set(colouri)) { for (const label inti : higherGlobalRecv_[colouri]) { addInterface(wA, inti, colourBufs_[colouri][inti]); } } backwardInternal(wA, colouri); // Store effect of exchanging rD to higher interfaces // in colourBufs_ if (cellColourPtr_) { sendGlobal ( lowerGlobalSend_[colouri], wA, lowerColour_[colouri] ); } } } // Make sure no outstanding sends wait(lowerSendRequests_); // Start writes of wA (using sendBufs) send(lowerNbrs_, wA, lowerSendRequests_); } void Foam::distributedDILUPreconditioner::setFinished ( const solverPerformance& s ) const { DebugPout<< "setFinished fieldName:" << s.fieldName() << endl; // Wait on all requests before storage is being taken down // (could rely on construction order?) wait(lowerSendRequests_); wait(lowerRecvRequests_); wait(higherSendRequests_); wait(higherRecvRequests_); // TBD: cancel/ignore outstanding messages //wait(lowerSendRequests_, true); //wait(lowerRecvRequests_, true); //wait(higherSendRequests_, true); //wait(higherRecvRequests_, true); } // ************************************************************************* //