From 775d0b277ba52f3b18de57cb29e4b0eac7d2e2f7 Mon Sep 17 00:00:00 2001 From: Mark Olesen Date: Wed, 27 Sep 2023 19:35:55 +0200 Subject: [PATCH] ENH: improve OpenFOAM I/O for std::vector container - input: added support (similar to DynamicList) - output: redirect through UList output, which enables binary etc - these changes enable support for broadcast and other parallel IO for std::vector ENH: support SubList of std::vector (entire length) - allows a 'glue' layer for re-casting std::vector to UList etc --- .../Test-parallel-broadcast.C | 26 ++- src/OpenFOAM/containers/Lists/List/SubList.H | 3 + src/OpenFOAM/containers/Lists/List/SubListI.H | 10 + src/OpenFOAM/containers/Lists/List/UList.H | 6 +- .../containers/Lists/List/stdVectorIO.C | 171 ++++++++++++++++-- 5 files changed, 202 insertions(+), 14 deletions(-) diff --git a/applications/test/parallel-broadcast/Test-parallel-broadcast.C b/applications/test/parallel-broadcast/Test-parallel-broadcast.C index 38a68382e3..02b68242d9 100644 --- a/applications/test/parallel-broadcast/Test-parallel-broadcast.C +++ b/applications/test/parallel-broadcast/Test-parallel-broadcast.C @@ -5,7 +5,7 @@ \\ / A nd | www.openfoam.com \\/ M anipulation | ------------------------------------------------------------------------------- - Copyright (C) 2022 OpenCFD Ltd. + Copyright (C) 2022-2023 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -75,6 +75,16 @@ void testBroadcast(List& values) } +template +void testBroadcast(std::vector& values) +{ + Info<< nl << "is_contiguous:" << is_contiguous::value << endl; + Pout<< "pre-broadcast: " << flatOutput(values) << endl; + Pstream::broadcast(values); + Pout<< "post-broadcast: " << flatOutput(values) << endl; +} + + void testBroadcast(bitSet& values) { Pout<< "pre-broadcast: " @@ -135,6 +145,20 @@ int main(int argc, char *argv[]) testBroadcast(values); } + { + std::vector values; + if (Pstream::master()) + { + values.resize(UPstream::nProcs()); + + for (decltype(values.size()) i=0; i < values.size(); ++i) + { + values[i] = "vector_" + Foam::name(i); + } + } + testBroadcast(values); + } + { vector values(vector::uniform(-1)); if (Pstream::master()) diff --git a/src/OpenFOAM/containers/Lists/List/SubList.H b/src/OpenFOAM/containers/Lists/List/SubList.H index f1c6aa2b38..886068bf07 100644 --- a/src/OpenFOAM/containers/Lists/List/SubList.H +++ b/src/OpenFOAM/containers/Lists/List/SubList.H @@ -91,6 +91,9 @@ public: //- Construct from UList, the entire size inline explicit SubList(const UList& list) noexcept; + //- Construct from std::vector, the entire size + inline explicit SubList(const std::vector& list) noexcept; + //- Construct from FixedList, the entire size template inline explicit SubList(const FixedList& list); diff --git a/src/OpenFOAM/containers/Lists/List/SubListI.H b/src/OpenFOAM/containers/Lists/List/SubListI.H index 389e9e85f8..f9057480d2 100644 --- a/src/OpenFOAM/containers/Lists/List/SubListI.H +++ b/src/OpenFOAM/containers/Lists/List/SubListI.H @@ -49,6 +49,16 @@ inline Foam::SubList::SubList {} +template +inline Foam::SubList::SubList +( + const std::vector& list +) noexcept +: + UList(const_cast(list.data()), label(list.size())) +{} + + template template inline Foam::SubList::SubList diff --git a/src/OpenFOAM/containers/Lists/List/UList.H b/src/OpenFOAM/containers/Lists/List/UList.H index 1d70c02703..169e50f35b 100644 --- a/src/OpenFOAM/containers/Lists/List/UList.H +++ b/src/OpenFOAM/containers/Lists/List/UList.H @@ -676,7 +676,11 @@ Ostream& operator<<(Ostream& os, const UList& list) return list.writeList(os, Detail::ListPolicy::short_length::value); } -//- Write std::vector to Ostream. ASCII only, no line-breaks +//- Read std::vector contents from Istream +template +Istream& operator>>(Istream& is, std::vector& list); + +//- Write std::vector to Ostream (via UList) template Ostream& operator<<(Ostream& os, const std::vector& list); diff --git a/src/OpenFOAM/containers/Lists/List/stdVectorIO.C b/src/OpenFOAM/containers/Lists/List/stdVectorIO.C index d3359cd864..e286a14edf 100644 --- a/src/OpenFOAM/containers/Lists/List/stdVectorIO.C +++ b/src/OpenFOAM/containers/Lists/List/stdVectorIO.C @@ -26,36 +26,183 @@ License \*---------------------------------------------------------------------------*/ #include "UList.H" +#include "Istream.H" #include "Ostream.H" +#include "contiguous.H" #include "token.H" #include // * * * * * * * * * * * * * * * IOstream Operators * * * * * * * * * * * * // template -Foam::Ostream& Foam::operator<<(Ostream& os, const std::vector& list) +Foam::Istream& Foam::operator>>(Istream& is, std::vector& list) { - auto iter = list.cbegin(); - const auto last = list.cend(); + is.fatalCheck(FUNCTION_NAME); - // Write ascii list contents, no line breaks + token tok(is); - os << label(list.size()) << token::BEGIN_LIST; + is.fatalCheck("Istream >> std::vector : reading first token"); - // Contents - if (iter != last) + if (tok.isCompound()) { - os << *iter; + // No compound handling ... - for (++iter; (iter != last); (void)++iter) + list.clear(); // Clear old contents + FatalIOErrorInFunction(is) + << "Support for compoundToken - not implemented" << nl + << exit(FatalIOError); + } + else if (tok.isLabel()) + { + // Label: could be int(..), int{...} or just a plain '0' + + const label len = tok.labelToken(); + + // Resize to length required + list.resize(len); + + if (is.format() == IOstreamOption::BINARY && is_contiguous::value) { - os << token::SPACE << *iter; + // Binary and contiguous + + if (len) + { + Detail::readContiguous + ( + is, + reinterpret_cast(list.data()), // data_bytes() + std::streamsize(list.size())*sizeof(T) // size_bytes() + ); + + is.fatalCheck + ( + "Istream >> std::vector : " + "reading binary block" + ); + } + } + else if (std::is_same::value) + { + // Special treatment for char data (binary I/O only) + const auto oldFmt = is.format(IOstreamOption::BINARY); + + if (len) + { + // read(...) includes surrounding start/end delimiters + is.read + ( + reinterpret_cast(list.data()), // data_bytes() + std::streamsize(list.size())*sizeof(T) // size_bytes() + ); + + is.fatalCheck + ( + "Istream >> std::vector : " + "reading binary block" + ); + } + + is.format(oldFmt); + } + else + { + // Begin of contents marker + const char delimiter = is.readBeginList("List"); + + if (len) + { + if (delimiter == token::BEGIN_LIST) + { + auto iter = list.begin(); + const auto last = list.end(); + + // Contents + for (/*nil*/; (iter != last); (void)++iter) + { + is >> *iter; + + is.fatalCheck + ( + "Istream >> std::vector : " + "reading entry" + ); + } + } + else + { + // Uniform content (delimiter == token::BEGIN_BLOCK) + + T elem; + is >> elem; + + is.fatalCheck + ( + "Istream >> std::vector : " + "reading the single entry" + ); + + // Fill with the value + list.assign(list.size(), elem); + } + } + + // End of contents marker + is.readEndList("List"); } } + else if (tok.isPunctuation(token::BEGIN_LIST)) + { + // "(...)" : read as bracketed list - os << token::END_LIST; + // Slightly sub-optimal since it has intermediate resizing, + // however don't expect this as input very often. - os.check(FUNCTION_NAME); + list.clear(); // Clear addressing, leave storage intact (probably) + + is >> tok; + is.fatalCheck(FUNCTION_NAME); + + while (!tok.isPunctuation(token::END_LIST)) + { + is.putBack(tok); + + // C++17 + // is >> list.emplace_back(); + + // C++11 + list.emplace_back(); + is >> list.back(); + + is.fatalCheck + ( + "Istream >> std::vector : " + "reading entry" + ); + + is >> tok; + is.fatalCheck(FUNCTION_NAME); + } + } + else + { + list.clear(); // Clear old contents + + FatalIOErrorInFunction(is) + << "incorrect first token, expected or '(', found " + << tok.info() << nl + << exit(FatalIOError); + } + + return is; +} + + +template +Foam::Ostream& Foam::operator<<(Ostream& os, const std::vector& list) +{ + // Use UList for output + UList proxy(const_cast(list.data()), label(list.size())); + os << proxy; return os; }