ENH: extend bitSet functionality

- num_blocks(), test_set() as per boost
- broadcast(), reduceAnd(), reduceOr() to simplify parallel operations
- matrix-like output for PackedList::writeList()

BUG: Pstream::broadcastList() missing resize on sub-ranks

- latent bug since it was unused in any OpenFOAM code
This commit is contained in:
Mark Olesen
2025-08-28 14:33:18 +02:00
parent 19caabbd56
commit bd57627955
17 changed files with 433 additions and 117 deletions

View File

@ -1,3 +1,3 @@
Test-PackedList.C Test-PackedList.cxx
EXE = $(FOAM_USER_APPBIN)/Test-PackedList EXE = $(FOAM_USER_APPBIN)/Test-PackedList

View File

@ -142,6 +142,9 @@ int main(int argc, char *argv[])
Info<< "got: " << bset1 << nl Info<< "got: " << bset1 << nl
<< "and: " << bset2 << nl << "and: " << bset2 << nl
<< "and: " << bset3 << nl; << "and: " << bset3 << nl;
Info<< "==";
bset3.writeList(Info, 10) << nl; // matrix-like output
} }
} }

View File

@ -137,11 +137,11 @@ inline bool compare
const std::string& expected const std::string& expected
) )
{ {
const List<unsigned int>& store = bitset.storage(); const auto& store = bitset.storage();
std::string has; std::string has;
for (label blocki=0; blocki < bitset.nBlocks(); ++blocki) for (label blocki=0; blocki < bitset.num_blocks(); ++blocki)
{ {
has += toString(store[blocki]); has += toString(store[blocki]);
} }

View File

@ -185,9 +185,9 @@ int main(int argc, char *argv[])
} }
broadcast_chunks<labelList, label>(input1); broadcast_chunks<labelList, label>(input1);
Pstream::maxCommsSize = 33; UPstream::maxCommsSize = 33;
args.readIfPresent("comms-size", Pstream::maxCommsSize); args.readIfPresent("comms-size", UPstream::maxCommsSize);
broadcast_chunks<labelList, label>(input1); broadcast_chunks<labelList, label>(input1);
@ -197,11 +197,11 @@ int main(int argc, char *argv[])
PstreamBuffers pBufs; PstreamBuffers pBufs;
labelList sendData; labelList sendData;
if (Pstream::master()) if (UPstream::master())
{ {
sendData = identity(500); sendData = identity(500);
for (const int proci : Pstream::subProcs()) for (const int proci : UPstream::subProcs())
{ {
UOPstream os(proci, pBufs); UOPstream os(proci, pBufs);
os << sendData; os << sendData;
@ -211,7 +211,7 @@ int main(int argc, char *argv[])
Info<< "call finishedSends()" << endl; Info<< "call finishedSends()" << endl;
pBufs.finishedScatters(); pBufs.finishedScatters();
if (!Pstream::master()) if (UPstream::is_subrank())
{ {
UIPstream is(UPstream::masterNo(), pBufs); UIPstream is(UPstream::masterNo(), pBufs);
is >> sendData; is >> sendData;
@ -225,11 +225,11 @@ int main(int argc, char *argv[])
labelListList recvBufs(UPstream::nProcs()); labelListList recvBufs(UPstream::nProcs());
labelList recvSizes; labelList recvSizes;
if (Pstream::master()) if (UPstream::master())
{ {
for (const int proci : Pstream::allProcs()) for (const int proci : UPstream::allProcs())
{ {
if (proci != Pstream::myProcNo()) if (proci != UPstream::myProcNo())
{ {
sendBufs[proci] = identity(500); sendBufs[proci] = identity(500);
} }
@ -253,11 +253,11 @@ int main(int argc, char *argv[])
Map<labelList> recvBufs; Map<labelList> recvBufs;
Map<label> recvSizes; Map<label> recvSizes;
if (Pstream::master()) if (UPstream::master())
{ {
for (const int proci : Pstream::allProcs()) for (const int proci : UPstream::allProcs())
{ {
if (proci != Pstream::myProcNo()) if (proci != UPstream::myProcNo())
{ {
sendBufs(proci) = identity(500); sendBufs(proci) = identity(500);
} }

View File

@ -110,21 +110,25 @@ int main(int argc, char *argv[])
<< " (self) reduced " << selfVal << nl; << " (self) reduced " << selfVal << nl;
// Identical size on all procs // Identical size on all procs
bitSet procUsed(nProcs);
if ((myRank % 4) == 0)
{ {
procUsed.set(myRank); bitSet localUsed(nProcs);
localUsed.set(myRank, ((myRank % 4) == 0));
Pout<< "local procUsed " << localUsed << nl;
localUsed.reduceOr(UPstream::worldComm, false);
Pout<< "reduce procUsed " << localUsed << nl;
}
// With allGather
{
bitSet procUsed
(
bitSet::allGather((myRank % 4) == 0)
);
Pout<< "allGather: " << procUsed << nl;
} }
Pout<< "local procUsed " << procUsed << nl;
reduce
(
procUsed.data(),
procUsed.size_data(),
bitOrOp<unsigned int>()
);
Pout<< "reduce procUsed " << procUsed << nl;
// Identical size on all procs // Identical size on all procs
// encode as 0:empty, 1:uniform, 2:nonuniform, 3:mixed // encode as 0:empty, 1:uniform, 2:nonuniform, 3:mixed
@ -147,12 +151,26 @@ int main(int argc, char *argv[])
} }
Pout<< "local uniform " << uniformity << nl; Pout<< "local uniform " << uniformity << nl;
reduce // reduce with op<..>()
#if 1
Foam::reduce
( (
uniformity.data(), uniformity.data(),
uniformity.size_data(), uniformity.num_blocks(),
bitOrOp<unsigned int>() bitOrOp<unsigned int>(),
UPstream::msgType(), // ignored
UPstream::worldComm
); );
#else
// Direct call to MPI_Allreduce
UPstream::mpiAllReduce
(
uniformity.data(),
uniformity.num_blocks(),
UPstream::opCodes::op_bit_or,
UPstream::worldComm
);
#endif
Pout<< "reduce uniform " << uniformity << nl; Pout<< "reduce uniform " << uniformity << nl;
} }
@ -160,8 +178,8 @@ int main(int argc, char *argv[])
{ {
Pair<label> val Pair<label> val
( (
Pstream::myProcNo(UPstream::commWorld()), UPstream::myProcNo(UPstream::commWorld()),
Pstream::myProcNo(UPstream::commWorld()) UPstream::myProcNo(UPstream::commWorld())
); );
Pair<label> worldVal = val; Pair<label> worldVal = val;

View File

@ -79,7 +79,7 @@ int main(int argc, char *argv[])
#include "setRootCase.H" #include "setRootCase.H"
if (!Pstream::parRun()) if (!UPstream::parRun())
{ {
Info<< "\nWarning: not parallel - skipping further tests\n" << endl; Info<< "\nWarning: not parallel - skipping further tests\n" << endl;
return 0; return 0;
@ -97,7 +97,7 @@ int main(int argc, char *argv[])
DynamicList<MPI_Request> recvRequests(10); DynamicList<MPI_Request> recvRequests(10);
if (!Pstream::master()) if (UPstream::is_subrank())
{ {
// Send some random length to master // Send some random length to master

View File

@ -76,7 +76,7 @@ int main(int argc, char *argv[])
#include "setRootCase.H" #include "setRootCase.H"
if (!Pstream::parRun()) if (!UPstream::parRun())
{ {
Info<< "\nWarning: not parallel - skipping further tests\n" << endl; Info<< "\nWarning: not parallel - skipping further tests\n" << endl;
return 0; return 0;
@ -96,7 +96,7 @@ int main(int argc, char *argv[])
// Map request indices to procs // Map request indices to procs
Map<label> recvFromProc(20); Map<label> recvFromProc(20);
if (!Pstream::master()) if (UPstream::is_subrank())
{ {
// Send some random length to master // Send some random length to master

View File

@ -52,7 +52,7 @@ int main(int argc, char *argv[])
const bool optNonBlocking = args.found("non-blocking"); const bool optNonBlocking = args.found("non-blocking");
if (!Pstream::parRun()) if (!UPstream::parRun())
{ {
Info<< "\nWarning: not parallel - skipping further tests\n" << endl; Info<< "\nWarning: not parallel - skipping further tests\n" << endl;
return 0; return 0;
@ -73,7 +73,7 @@ int main(int argc, char *argv[])
DynamicList<UPstream::Request> sendRequests(10); DynamicList<UPstream::Request> sendRequests(10);
DynamicList<UPstream::Request> recvRequests(10); DynamicList<UPstream::Request> recvRequests(10);
if (!Pstream::master()) if (UPstream::is_subrank())
{ {
// Send some random length to master // Send some random length to master

View File

@ -6,7 +6,7 @@
\\/ M anipulation | \\/ M anipulation |
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Copyright (C) 2011-2016 OpenFOAM Foundation Copyright (C) 2011-2016 OpenFOAM Foundation
Copyright (C) 2017-2023 OpenCFD Ltd. Copyright (C) 2017-2025 OpenCFD Ltd.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
License License
This file is part of OpenFOAM. This file is part of OpenFOAM.
@ -422,20 +422,20 @@ public:
// Low-level access // Low-level access
//- The number of internal storage blocks //- The number of internal storage blocks
inline label nBlocks() const; inline label num_blocks() const noexcept;
//- Return the underlying storage blocks //- Return the underlying storage blocks
inline const List<unsigned int>& storage() const; const List<block_type>& storage() const noexcept { return blocks_; }
//- Return the underlying storage blocks //- Return the underlying storage blocks
// Manipulate with utmost caution // Manipulate with utmost caution
inline List<unsigned int>& storage(); List<block_type>& storage() noexcept { return blocks_; }
//- A const pointer to the raw storage //- A const pointer to the raw storage
inline const unsigned int* cdata() const noexcept; const block_type* cdata() const noexcept { return blocks_.cdata(); }
//- A pointer to the raw storage //- A pointer to the raw storage
inline unsigned int* data() noexcept; block_type* data() noexcept { return blocks_.data(); }
//- A const pointer to the raw storage, reinterpreted as byte data //- A const pointer to the raw storage, reinterpreted as byte data
inline const char* cdata_bytes() const noexcept; inline const char* cdata_bytes() const noexcept;
@ -443,15 +443,15 @@ public:
//- A pointer to the raw storage, reinterpreted as byte data //- A pointer to the raw storage, reinterpreted as byte data
inline char* data_bytes() noexcept; inline char* data_bytes() noexcept;
//- The number of integer blocks addressed in the raw storage //- The number of integer blocks addressed in the raw storage.
//- Same as num_blocks().
inline std::streamsize size_data() const noexcept; inline std::streamsize size_data() const noexcept;
//- The number of bytes used in the raw storage //- The number of bytes addressed in the raw storage
//- including any unused padding. //- including any padding.
inline std::streamsize size_bytes() const noexcept; inline std::streamsize size_bytes() const noexcept;
//- The number of bytes used in the raw storage //- Same as size_bytes()
//- including any unused padding.
inline std::streamsize byteSize() const noexcept; inline std::streamsize byteSize() const noexcept;
@ -465,7 +465,7 @@ public:
//- Write List, with line-breaks in ASCII when length exceeds shortLen. //- Write List, with line-breaks in ASCII when length exceeds shortLen.
// Using '0' suppresses line-breaks entirely. // Using '0' suppresses line-breaks entirely.
Ostream& writeList(Ostream& os, const label shortLen=0) const; Ostream& writeList(Ostream& os, label shortLen=0) const;
//- Write as a dictionary entry with keyword //- Write as a dictionary entry with keyword
void writeEntry(const word& keyword, Ostream& os) const; void writeEntry(const word& keyword, Ostream& os) const;
@ -529,10 +529,10 @@ public:
public: public:
//- Copy construct //- Copy construct
reference(const reference&) = default; reference(const reference&) noexcept = default;
//- Move construct //- Move construct
reference(reference&&) = default; reference(reference&&) noexcept = default;
//- Value assignment //- Value assignment
inline void operator=(const reference& other); inline void operator=(const reference& other);

View File

@ -609,40 +609,12 @@ inline void Foam::PackedList<Width>::shrink_to_fit()
template<unsigned Width> template<unsigned Width>
inline Foam::List<unsigned int>& Foam::PackedList<Width>::storage() inline Foam::label Foam::PackedList<Width>::num_blocks() const noexcept
{
return blocks_;
}
template<unsigned Width>
inline const Foam::List<unsigned int>& Foam::PackedList<Width>::storage() const
{
return blocks_;
}
template<unsigned Width>
inline Foam::label Foam::PackedList<Width>::nBlocks() const
{ {
return num_blocks(size()); return num_blocks(size());
} }
template<unsigned Width>
inline const unsigned int* Foam::PackedList<Width>::cdata() const noexcept
{
return blocks_.cdata();
}
template<unsigned Width>
inline unsigned int* Foam::PackedList<Width>::data() noexcept
{
return blocks_.data();
}
template<unsigned Width> template<unsigned Width>
inline const char* Foam::PackedList<Width>::cdata_bytes() const noexcept inline const char* Foam::PackedList<Width>::cdata_bytes() const noexcept
{ {
@ -667,7 +639,7 @@ inline std::streamsize Foam::PackedList<Width>::size_data() const noexcept
template<unsigned Width> template<unsigned Width>
inline std::streamsize Foam::PackedList<Width>::size_bytes() const noexcept inline std::streamsize Foam::PackedList<Width>::size_bytes() const noexcept
{ {
return size_data() * sizeof(block_type); return num_blocks(size()) * sizeof(block_type);
} }

View File

@ -5,7 +5,7 @@
\\ / A nd | www.openfoam.com \\ / A nd | www.openfoam.com
\\/ M anipulation | \\/ M anipulation |
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Copyright (C) 2018-2022 OpenCFD Ltd. Copyright (C) 2018-2025 OpenCFD Ltd.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
License License
This file is part of OpenFOAM. This file is part of OpenFOAM.
@ -178,12 +178,14 @@ template<unsigned Width>
Foam::Ostream& Foam::PackedList<Width>::writeList Foam::Ostream& Foam::PackedList<Width>::writeList
( (
Ostream& os, Ostream& os,
const label shortLen label shortLen
) const ) const
{ {
const PackedList<Width>& list = *this; const PackedList<Width>& list = *this;
const label len = list.size(); const label len = list.size();
if (shortLen < 0) shortLen = 1; // <- sanity
if (os.format() == IOstreamOption::BINARY) if (os.format() == IOstreamOption::BINARY)
{ {
// Binary (always contiguous) // Binary (always contiguous)
@ -209,7 +211,7 @@ Foam::Ostream& Foam::PackedList<Width>::writeList
os << len << token::BEGIN_LIST; os << len << token::BEGIN_LIST;
// Contents // Contents
for (label i=0; i < len; ++i) for (label i = 0; i < len; ++i)
{ {
if (i) os << token::SPACE; if (i) os << token::SPACE;
os << label(list.get(i)); os << label(list.get(i));
@ -226,9 +228,33 @@ Foam::Ostream& Foam::PackedList<Width>::writeList
os << nl << len << nl << token::BEGIN_LIST << nl; os << nl << len << nl << token::BEGIN_LIST << nl;
// Contents // Contents
for (label i=0; i < len; ++i) if (shortLen <= 1)
{ {
os << label(list.get(i)) << nl; // simple multi-line
for (label i = 0; i < len; ++i)
{
os << label(list.get(i)) << nl;
}
}
else
{
// 'matrix' of values
label line = 0;
for (label i = 0; i < len; ++i, ++line)
{
if (line == shortLen)
{
os << nl;
line = 0;
}
else if (line)
{
os << token::SPACE;
}
os << label(list.get(i));
}
if (line) os << nl;
} }
// End delimiter // End delimiter

View File

@ -5,7 +5,7 @@
\\ / A nd | www.openfoam.com \\ / A nd | www.openfoam.com
\\/ M anipulation | \\/ M anipulation |
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Copyright (C) 2018-2022 OpenCFD Ltd. Copyright (C) 2018-2025 OpenCFD Ltd.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
License License
This file is part of OpenFOAM. This file is part of OpenFOAM.
@ -28,14 +28,19 @@ License
#include "bitSet.H" #include "bitSet.H"
#include "labelRange.H" #include "labelRange.H"
#include "IOstreams.H" #include "IOstreams.H"
#include "UPstream.H"
#include "addToRunTimeSelectionTable.H"
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * // // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
namespace Foam namespace Foam
{ {
defineTypeNameAndDebug(bitSet, 0); defineTypeNameAndDebug(bitSet, 0);
}
// TBD: add IO support of compound type?
// defineNamedCompoundTypeName(bitSet, List<1>);
// addNamedCompoundToRunTimeSelectionTable(bitSet, bitSet, List<1>);
}
// * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * // // * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
@ -76,7 +81,7 @@ Foam::bitSet& Foam::bitSet::minusEq(const bitSet& other)
Foam::bitSet& Foam::bitSet::andEq(const bitSet& other) Foam::bitSet& Foam::bitSet::andEq(const bitSet& other)
{ {
if (&other == this) if (FOAM_UNLIKELY(&other == this))
{ {
// Self '&=' : no-op // Self '&=' : no-op
@ -273,19 +278,24 @@ Foam::bitSet::bitSet(const labelRange& range)
void Foam::bitSet::assign(const UList<bool>& bools) void Foam::bitSet::assign(const UList<bool>& bools)
{ {
const label len = bools.size(); fill(false);
resize(bools.size());
clear(); unsigned bitIdx = 0u;
resize(len); auto* packed = blocks_.data();
// Could also handle block-wise (in the future?) // Set according to indices that are true
for (const auto b : bools)
// Set according to indices that are true.
for (label i = 0; i < len; ++i)
{ {
if (bools[i]) if (b)
{ {
set(i); *packed |= (1u << bitIdx);
}
if (++bitIdx >= PackedList<1>::elem_per_block)
{
bitIdx = 0u;
++packed;
} }
} }
} }
@ -540,4 +550,209 @@ Foam::List<bool> Foam::bitSet::values() const
} }
// * * * * * * * * * * * * * * Parallel Functions * * * * * * * * * * * * * //
void Foam::bitSet::broadcast(int communicator, bool syncSizes)
{
if (communicator < 0)
{
communicator = UPstream::worldComm;
}
if (!UPstream::is_parallel(communicator))
{
return;
}
int64_t len(size());
if (syncSizes)
{
UPstream::broadcast(&len, 1, communicator);
if (UPstream::is_subrank(communicator))
{
fill(false);
resize(len);
}
}
if (len)
{
// Only broadcast non-empty
UPstream::broadcast(this->data(), this->num_blocks(), communicator);
}
}
void Foam::bitSet::reduceAnd(int communicator, bool syncSizes)
{
if (communicator < 0)
{
communicator = UPstream::worldComm;
}
if (!UPstream::is_parallel(communicator))
{
return;
}
const label origSize(size());
if (syncSizes)
{
// Operation is an intersection
// - common size may be smaller than the original size
int64_t commonSize(size());
UPstream::mpiAllReduce
(
&commonSize,
1,
UPstream::opCodes::op_min,
communicator
);
resize(commonSize);
}
if (!empty())
{
UPstream::mpiAllReduce
(
this->data(),
this->num_blocks(),
UPstream::opCodes::op_bit_and,
communicator
);
clear_trailing_bits(); // safety
}
// Undo side effects from the reduction
if (syncSizes)
{
resize(origSize);
}
}
void Foam::bitSet::reduceOr(int communicator, bool syncSizes)
{
if (communicator < 0)
{
communicator = UPstream::worldComm;
}
if (!UPstream::is_parallel(communicator))
{
return;
}
// const label origSize(size());
if (syncSizes)
{
// Operation can increase the addressed size
// Extend size based on the addressed length.
// This is greedy, but produces consistent sizing
int64_t commonSize(size());
// Alternative: Extend size based on the bits used.
// - tighter, but inconsistent sizes result
// // label commonSize(find_last()+1);
UPstream::mpiAllReduce
(
&commonSize,
1,
UPstream::opCodes::op_max,
communicator
);
extend(commonSize);
}
if (!empty())
{
UPstream::mpiAllReduce
(
this->data(),
this->num_blocks(),
UPstream::opCodes::op_bit_or,
communicator
);
clear_trailing_bits(); // safety
}
}
Foam::bitSet Foam::bitSet::gatherValues(bool localValue, int communicator)
{
if (communicator < 0)
{
communicator = UPstream::worldComm;
}
bitSet allValues;
if (!UPstream::is_parallel(communicator))
{
// non-parallel: return own value
// TBD: only when UPstream::is_rank(communicator) as well?
allValues.resize(1);
allValues.set(0, localValue);
}
else
{
List<bool> bools;
if (UPstream::master(communicator))
{
bools.resize(UPstream::nProcs(communicator), false);
}
UPstream::mpiGather
(
&localValue, // Send
bools.data(), // Recv
1, // Num send/recv data per rank
communicator
);
// Transcribe to bitSet (on master)
allValues.assign(bools);
}
return allValues;
}
// Note that for allGather()
// - MPI_Gather of individual bool values and broadcast the packed result
// - this avoids bit_or on 32bit values everywhere, since we know a priori
// that each rank only contributes 1bit of info
Foam::bitSet Foam::bitSet::allGather(bool localValue, int communicator)
{
if (communicator < 0)
{
communicator = UPstream::worldComm;
}
bitSet allValues(bitSet::gatherValues(localValue, communicator));
if (UPstream::is_parallel(communicator))
{
// Identical size on all ranks
allValues.resize(UPstream::nProcs(communicator));
// Sizes are consistent - broadcast without resizing
allValues.broadcast(communicator, false);
}
return allValues;
}
// ************************************************************************* // // ************************************************************************* //

View File

@ -5,7 +5,7 @@
\\ / A nd | www.openfoam.com \\ / A nd | www.openfoam.com
\\/ M anipulation | \\/ M anipulation |
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Copyright (C) 2018-2023 OpenCFD Ltd. Copyright (C) 2018-2025 OpenCFD Ltd.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
License License
This file is part of OpenFOAM. This file is part of OpenFOAM.
@ -46,7 +46,6 @@ See also
#ifndef Foam_bitSet_H #ifndef Foam_bitSet_H
#define Foam_bitSet_H #define Foam_bitSet_H
#include "className.H"
#include "PackedList.H" #include "PackedList.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
@ -111,7 +110,11 @@ protected:
public: public:
// Forward declaration of access classes //- A bitSet acts like a packed boolList
typedef bool value_type;
// Forward Declarations
class reference; class reference;
class const_iterator; class const_iterator;
@ -331,6 +334,13 @@ public:
// set(pos) on individual bits. // set(pos) on individual bits.
void set(const labelRange& range); void set(const labelRange& range);
//- Test for \em True value at specified position
//- and change the value at that position.
// Does auto-vivify for non-existent, non-zero entries.
//
// \note Method name compatibility with std::bitset
bool test_set(const label i, const bool val = true);
// Unsetting single or multiple values // Unsetting single or multiple values
@ -475,10 +485,10 @@ public:
public: public:
//- Copy construct //- Copy construct
reference(const reference&) = default; reference(const reference&) noexcept = default;
//- Move construct //- Move construct
reference(reference&&) = default; reference(reference&&) noexcept = default;
//- Flip the bit at the position, no range-checking //- Flip the bit at the position, no range-checking
inline void flip(); inline void flip();
@ -605,6 +615,61 @@ public:
} }
// Parallel Operations
//- Broadcast the contents
void broadcast
(
//! The UPstream communicator (default is worldComm)
int communicator = -1,
//! False: sizes already consistent. True: adjust sizes.
bool syncSizes = true
);
//- Inplace \c bit_and parallel reduction
void reduceAnd
(
//! The UPstream communicator (default is worldComm)
int communicator = -1,
//! False: sizes already consistent. True: adjust (shrink) sizes.
bool syncSizes = true
);
//- Inplace \c bit_or parallel reduction
void reduceOr
(
//! The UPstream communicator (default is worldComm)
int communicator = -1,
//! False: sizes already consistent. True: adjust (extend) sizes.
bool syncSizes = true
);
//- Gather individual values into bitSet locations.
// On master, the resulting bitSet has size == nProcs,
// otherwise zero length.
// \n
// For \b non-parallel :
// the length of the returned set is 1 with localValue.
static bitSet gatherValues
(
//! The processor-local value
bool localValue,
//! The UPstream communicator (default is worldComm)
int communicator = -1
);
//- Allgather individual values into bitSet locations.
// The resulting bitSet has size nProcs, identical on all ranks.
// Behaves like Pstream::allGatherValues() but returning a bitSet.
static bitSet allGather
(
//! The processor-local value
bool localValue,
//! The UPstream communicator (default is worldComm)
int communicator = -1
);
// Housekeeping // Housekeeping
//- Same as contains() //- Same as contains()

View File

@ -5,7 +5,7 @@
\\ / A nd | www.openfoam.com \\ / A nd | www.openfoam.com
\\/ M anipulation | \\/ M anipulation |
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Copyright (C) 2018-2022 OpenCFD Ltd. Copyright (C) 2018-2025 OpenCFD Ltd.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
License License
This file is part of OpenFOAM. This file is part of OpenFOAM.
@ -482,23 +482,27 @@ inline void Foam::bitSet::fill(const bool val)
{ {
if (empty()) if (empty())
{ {
return; // Trivial case return;
} }
else if (val)
const label nblocks = num_blocks(size());
// Fill value for complete blocks
const unsigned int blockval = (val ? ~0u : 0u);
for (label blocki=0; blocki < nblocks; ++blocki)
{
blocks_[blocki] = blockval;
}
if (val)
{ {
std::fill_n(blocks_.data(), num_blocks(), ~0u);
clear_trailing_bits(); clear_trailing_bits();
} }
else
{
// or: PackedList<1>::reset();
// or: blocks_ = 0u;
std::fill_n(blocks_.data(), num_blocks(), 0u);
}
}
inline bool Foam::bitSet::test_set(const label i, const bool val)
{
bool old = test(i);
PackedList<1>::set(i, val);
return old;
} }

View File

@ -51,6 +51,9 @@ SourceFiles
namespace Foam namespace Foam
{ {
// Forward Declarations
class bitSet;
/*---------------------------------------------------------------------------*\ /*---------------------------------------------------------------------------*\
Class Pstream Declaration Class Pstream Declaration
\*---------------------------------------------------------------------------*/ \*---------------------------------------------------------------------------*/

View File

@ -149,6 +149,11 @@ void Foam::Pstream::broadcastList
{ {
return; return;
} }
else if constexpr (std::is_same_v<bitSet, ListType>)
{
// Specialized handling implemented within bitSet itself
list.broadcast(communicator);
}
else if constexpr (is_contiguous_v<typename ListType::value_type>) else if constexpr (is_contiguous_v<typename ListType::value_type>)
{ {
// List data are contiguous // List data are contiguous
@ -166,6 +171,11 @@ void Foam::Pstream::broadcastList
communicator communicator
); );
if (UPstream::is_subrank(communicator))
{
list.resize_nocopy(len);
}
if (len) if (len)
{ {
// Only broadcast non-empty content // Only broadcast non-empty content