From 5e4d678c980284b46ec208e842c0e8672974998e Mon Sep 17 00:00:00 2001 From: Mark Olesen Date: Thu, 29 Jul 2021 13:28:41 +0200 Subject: [PATCH] ENH: make precision adaptors modifiable (#2173) - allows reuse similar to refPtr for wrapping different content. - additional control for when contents are copied back, instead of waiting for the adaptor to go out of scope. Eg, if (adaptor.active()) { adaptor.commit(); adaptor.clear(); } - static ConstPrecisionAdaptor::get method renamed to 'select' as a better description of its purpose and avoid confusion with non-static 'get' method. Was previously only used within GAMGPreconditioner, but even there it is better just to use the ConstPrecisionAdaptor directly. --- applications/test/PrecisionAdaptor/Make/files | 3 + .../test/PrecisionAdaptor/Make/options | 2 + .../PrecisionAdaptor/Test-PrecisionAdaptor.C | 111 ++++++++ .../Field/PrecisionAdaptor/PrecisionAdaptor.H | 238 +++++++++++------- .../GAMGPreconditioner/GAMGPreconditioner.C | 9 +- 5 files changed, 268 insertions(+), 95 deletions(-) create mode 100644 applications/test/PrecisionAdaptor/Make/files create mode 100644 applications/test/PrecisionAdaptor/Make/options create mode 100644 applications/test/PrecisionAdaptor/Test-PrecisionAdaptor.C diff --git a/applications/test/PrecisionAdaptor/Make/files b/applications/test/PrecisionAdaptor/Make/files new file mode 100644 index 0000000000..d1a36376f0 --- /dev/null +++ b/applications/test/PrecisionAdaptor/Make/files @@ -0,0 +1,3 @@ +Test-PrecisionAdaptor.C + +EXE = $(FOAM_USER_APPBIN)/Test-PrecisionAdaptor diff --git a/applications/test/PrecisionAdaptor/Make/options b/applications/test/PrecisionAdaptor/Make/options new file mode 100644 index 0000000000..18e6fe47af --- /dev/null +++ b/applications/test/PrecisionAdaptor/Make/options @@ -0,0 +1,2 @@ +/* EXE_INC = */ +/* EXE_LIBS = */ diff --git a/applications/test/PrecisionAdaptor/Test-PrecisionAdaptor.C b/applications/test/PrecisionAdaptor/Test-PrecisionAdaptor.C new file mode 100644 index 0000000000..33462b909c --- /dev/null +++ b/applications/test/PrecisionAdaptor/Test-PrecisionAdaptor.C @@ -0,0 +1,111 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | www.openfoam.com + \\/ M anipulation | +------------------------------------------------------------------------------- + Copyright (C) 2021 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 . + +Application + Test-PrecisionAdaptor + +Description + +\*---------------------------------------------------------------------------*/ + +#include "argList.H" +#include "primitiveFields.H" +#include "PrecisionAdaptor.H" + +using namespace Foam; + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +int main(int argc, char *argv[]) +{ + Field content1(8); + Field content2(8); + + forAll(content1, i) + { + content1[i] = 10 * i; + content2[i] = 10 * i; + } + + Foam::reverse(content2); + + ConstPrecisionAdaptor cadaptor1; + + cadaptor1.set(content1); + cadaptor1.commit(); // This is a no-op + Info<< "wrapped: " << cadaptor1() << nl; + + cadaptor1.set(content2); + Info<< "wrapped: " << cadaptor1() << nl; + + Info<< nl; + + PrecisionAdaptor adaptor2; + + adaptor2.set(content1); + adaptor2.ref() *= 2; + adaptor2.commit(); // Propagate changes back to input now + + Info<< "modified wrapped: " << adaptor2() << nl; + + adaptor2.set(content2); + adaptor2.ref() *= 2; + adaptor2.commit(); // Propagate changes back to input now + + Info<< "modified wrapped: " << adaptor2() << nl; + Info<< "source: " << content1 << nl; + Info<< "source: " << content2 << nl; + + + content2 *= 2; + Info<< nl + << "set with " << content2 << nl; + Info<< "wrapped was " << adaptor2() << nl; + adaptor2.set(content2); + Info<< "wrapped now " << adaptor2() << nl; + Info<< "source: " << content2 << nl; + + // Can even do this + Foam::reverse(adaptor2.ref()); + + adaptor2.ref() *= 2; + adaptor2.set(content1); // implicit commit + Info<< "updated: " << content2 << nl; + + Info<< nl + << "input: " << content1 << nl; + + adaptor2.ref() *= 2; + adaptor2.clear(); // discard + adaptor2.commit(); + + Info<< "unchanged: " << content1 << nl; + + Info<< nl << "Done" << nl << endl; + return 0; +} + + +// ************************************************************************* // diff --git a/src/OpenFOAM/fields/Fields/Field/PrecisionAdaptor/PrecisionAdaptor.H b/src/OpenFOAM/fields/Fields/Field/PrecisionAdaptor/PrecisionAdaptor.H index a326c7a403..94372dab7b 100644 --- a/src/OpenFOAM/fields/Fields/Field/PrecisionAdaptor/PrecisionAdaptor.H +++ b/src/OpenFOAM/fields/Fields/Field/PrecisionAdaptor/PrecisionAdaptor.H @@ -5,7 +5,7 @@ \\ / A nd | www.openfoam.com \\/ M anipulation | ------------------------------------------------------------------------------- - Copyright (C) 2019-2020 OpenCFD Ltd. + Copyright (C) 2019-2021 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -39,6 +39,8 @@ Description #ifndef PrecisionAdaptor_H #define PrecisionAdaptor_H +#include // For std::copy +#include // For std::is_same #include "refPtr.H" #include "Field.H" @@ -47,6 +49,10 @@ Description namespace Foam { +/*---------------------------------------------------------------------------*\ + Class ConstPrecisionAdaptor Declaration +\*---------------------------------------------------------------------------*/ + //- A const Field/List wrapper with possible data conversion template class Container = Field> class ConstPrecisionAdaptor @@ -55,107 +61,127 @@ class ConstPrecisionAdaptor { // Private Member Functions - //- Copy in field - void copyInput(const Container& input) - { - this->reset(new Container(input.size())); - std::copy(input.cbegin(), input.cend(), this->ref().begin()); - } - - //- Construct from tmp Field, copy/move as required - void moveInput(tmp>& input) + //- Set adaptor for different input, copying as required + void setInput(const Container& src) { if (std::is_same::value) { - auto& tinput = reinterpret_cast>&>(input); - - if (tinput.is_pointer()) - { - // Acquire control of the managed pointer - this->reset(tinput.ptr()); - } - else - { - // Use const reference - this->cref(tinput.cref()); - } + // Use reference directly + this->cref(reinterpret_cast&>(src)); } else { - this->copyInput(input.cref()); + // Need intermediate buffer + this->reset(new Container(src.size())); + std::copy(src.cbegin(), src.cend(), this->ref().begin()); } - input.clear(); } + //- Set from tmp, steal pointer if possible + void tmpInput(tmp>& tsrc) + { + if (std::is_same::value && tsrc.is_pointer()) + { + // Acquire control of the managed pointer + this->reset(reinterpret_cast*>(tsrc.ptr())); + } + else + { + this->setInput(tsrc.cref()); + } + tsrc.clear(); + } public: - //- The adapted field type + //- The adapted field type. Same as element_type typedef Container FieldType; // Constructors - //- Construct from Container, copying on input as required - ConstPrecisionAdaptor(const Container& input) - : - refPtr>() + //- Default construct, setting content later + ConstPrecisionAdaptor() = default; + + //- Construct from Container of InputType, copying if required + explicit ConstPrecisionAdaptor(const Container& input) { - if (std::is_same::value) - { - // Use const reference directly - this->cref(reinterpret_cast(input)); - } - else - { - this->copyInput(input); - } + this->setInput(input); } - - //- Construct from tmp Container, copy/move as required - ConstPrecisionAdaptor(tmp>&& input) - : - refPtr>() + //- Construct from tmp Container of InputType, copy/move as required + explicit ConstPrecisionAdaptor(tmp>&& input) { - this->moveInput(input); + this->tmpInput(input); } - - //- Construct from tmp Container, copy/move as required - ConstPrecisionAdaptor(const tmp>& input) - : - refPtr>() + //- Construct from tmp Container of InputType, copy/move as required + explicit ConstPrecisionAdaptor(const tmp>& input) { - this->moveInput(const_cast>&>(input)); + this->tmpInput(const_cast>&>(input)); } // Member Functions - // May need in the future: using refPtr>::get; + //- Is precision adaption being used (non-passive adaptor)? + bool active() const noexcept + { + // Same as refPtr::movable() + return (this->is_pointer() && this->valid()); + } - //- Return the field - static const Container& get + //- Commit adapted content changes (no-op for const adaptor) + void commit() + {} + + //- Set adaptor for different input, copying input if required + void set(const Container& input) + { + this->setInput(input); + } + + //- Set adaptor for tmp Container of InputType, copy/move as required + void set(tmp>&& input) + { + this->tmpInput(input); + } + + //- Set adaptor for tmp Container of InputType, copy/move as required + void set(const tmp>& input) + { + this->tmpInput(const_cast>&>(input)); + } + + + // Static Member Functions + + //- Select a reference to the input (if types are identical), + //- or copy into other and return a reference to that + static const Container& select ( const Container& input, - Container& dst + Container& other ) { if (std::is_same::value) { - return reinterpret_cast(input); + return reinterpret_cast&>(input); } else { - dst.resize(input.size()); - std::copy(input.cbegin(), input.cend(), dst.begin()); - return dst; + other.resize(input.size()); + std::copy(input.cbegin(), input.cend(), other.begin()); + return other; } } }; +/*---------------------------------------------------------------------------*\ + Class PrecisionAdaptor Declaration +\*---------------------------------------------------------------------------*/ + //- A non-const Field/List wrapper with possible data conversion template class Container = Field> class PrecisionAdaptor @@ -164,59 +190,94 @@ class PrecisionAdaptor { // Private Data - //- Reference to underlying (input) data - Container& ref_; + //- Reference to underlying external input data + refPtr> orig_; // Private Member Functions - //- Copy in field - void copyInput(const Container& input, const bool copy) + //- Set adaptor for different input, copying as required + void setInput(Container& src, const bool doCopy) { - this->reset(new Container(input.size())); - if (copy) + orig_.ref(src); + if (std::is_same::value) { - std::copy(input.cbegin(), input.cend(), this->ref().begin()); + // Use reference directly + this->ref(reinterpret_cast&>(src)); + } + else + { + // Need intermediate buffer + this->reset(new Container(src.size())); + if (doCopy) + { + std::copy(src.cbegin(), src.cend(), this->ref().begin()); + } } } - public: - //- The adapted field type + //- The adapted field type. Same as element_type typedef Container FieldType; // Constructors - //- Construct from Container, copying on input if required - PrecisionAdaptor(Container& input, const bool copy = true) - : - refPtr>(), - ref_(input) + //- Default construct, setting content later + PrecisionAdaptor() = default; + + //- Construct from Container, + //- copying input if required (and requested) + explicit PrecisionAdaptor + ( + Container& input, + const bool doCopy = true + ) { - if (std::is_same::value) - { - // Use non-const reference directly - this->ref(reinterpret_cast(ref_)); - } - else - { - this->copyInput(input, copy); - } + this->setInput(input, doCopy); } - //- Destructor, copy back on destroy + //- Destructor, copies back content changes (as required) ~PrecisionAdaptor() { - if (this->is_pointer()) - { - const FieldType& store = this->cref(); - ref_.resize(store.size()); // extra safety - std::copy(store.cbegin(), store.cend(), ref_.begin()); - } + this->commit(); // Commit changes + this->clear(); } + + + // Member Functions + + //- Is precision adaption being used (non-passive adaptor)? + bool active() const noexcept + { + // Same as refPtr::movable() + return (this->is_pointer() && this->valid()); + } + + //- Commit adapted content changes back to original input (as required) + void commit() + { + if (this->active() && orig_.valid()) + { + const auto& stored = this->cref(); + auto& input = orig_.ref(); + input.resize(stored.size()); // Extra safety + std::copy(stored.cbegin(), stored.cend(), input.begin()); + } + } + + //- Set adaptor for different input, copying input as required + void set(Container& input, const bool doCopy = true) + { + if (orig_.get() != &input) + { + // Commit changes to old input first + this->commit(); + } + this->setInput(input, doCopy); + } }; @@ -224,7 +285,6 @@ public: } // End namespace Foam - // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // #endif diff --git a/src/OpenFOAM/matrices/lduMatrix/preconditioners/GAMGPreconditioner/GAMGPreconditioner.C b/src/OpenFOAM/matrices/lduMatrix/preconditioners/GAMGPreconditioner/GAMGPreconditioner.C index 40cebad6ea..4619815c81 100644 --- a/src/OpenFOAM/matrices/lduMatrix/preconditioners/GAMGPreconditioner/GAMGPreconditioner.C +++ b/src/OpenFOAM/matrices/lduMatrix/preconditioners/GAMGPreconditioner/GAMGPreconditioner.C @@ -118,15 +118,12 @@ void Foam::GAMGPreconditioner::precondition finestCorrectionScratch ); - - // Storage area when solveScalar != scalar - scalarField rA_s; + // Adapt solveScalarField back to scalarField (as required) + ConstPrecisionAdaptor rA_adaptor(rA_ss); + const scalarField& rA = rA_adaptor.cref(); for (label cycle=0; cycle::get(rA_ss, rA_s); - Vcycle ( smoothers,