/*---------------------------------------------------------------------------*\ ========= | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | \\ / A nd | www.openfoam.com \\/ M anipulation | ------------------------------------------------------------------------------- Copyright (C) 2019-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 <http://www.gnu.org/licenses/>. Class Foam::PrecisionAdaptor Description Conversion adaptor for Field/List that either wrap the input as a reference, or creates a temporary pointer and copies the values on construction/destruction. This provides, for example, automatic conversion between types for linear solvers able to run mixed precision. \*---------------------------------------------------------------------------*/ #ifndef PrecisionAdaptor_H #define PrecisionAdaptor_H #include <algorithm> // For std::copy #include <type_traits> // For std::is_same #include "refPtr.H" #include "Field.H" // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // namespace Foam { /*---------------------------------------------------------------------------*\ Class ConstPrecisionAdaptor Declaration \*---------------------------------------------------------------------------*/ //- A const Field/List wrapper with possible data conversion template<class Type, class InputType, template<class> class Container = Field> class ConstPrecisionAdaptor : public refPtr<Container<Type>> { // Private Member Functions //- Set adaptor for different input, copying as required void setInput(const Container<InputType>& src) { if (std::is_same<Type, InputType>::value) { // Use reference directly this->cref(reinterpret_cast<const Container<Type>&>(src)); } else { // Need intermediate buffer this->reset(new Container<Type>(src.size())); std::copy(src.cbegin(), src.cend(), this->ref().begin()); } } //- Set from tmp, steal pointer if possible void tmpInput(tmp<Container<InputType>>& tsrc) { if (std::is_same<Type, InputType>::value && tsrc.is_pointer()) { // Acquire control of the managed pointer this->reset(reinterpret_cast<Container<Type>*>(tsrc.ptr())); } else { this->setInput(tsrc.cref()); } tsrc.clear(); } public: //- The adapted field type. Same as element_type typedef Container<Type> FieldType; // Constructors //- Default construct, setting content later ConstPrecisionAdaptor() = default; //- Construct from Container of InputType, copying if required explicit ConstPrecisionAdaptor(const Container<InputType>& input) { this->setInput(input); } //- Construct from tmp Container of InputType, copy/move as required explicit ConstPrecisionAdaptor(tmp<Container<InputType>>&& input) { this->tmpInput(input); } //- Construct from tmp Container of InputType, copy/move as required explicit ConstPrecisionAdaptor(const tmp<Container<InputType>>& input) { this->tmpInput(const_cast<tmp<Container<InputType>>&>(input)); } // Member Functions //- Is precision adaption being used (non-passive adaptor)? bool active() const noexcept { // Same as refPtr::movable() return (this->is_pointer() && this->good()); } //- Commit adapted content changes (no-op for const adaptor) void commit() {} //- Set adaptor for different input, copying input if required void set(const Container<InputType>& input) { this->setInput(input); } //- Set adaptor for tmp Container of InputType, copy/move as required void set(tmp<Container<InputType>>&& input) { this->tmpInput(input); } //- Set adaptor for tmp Container of InputType, copy/move as required void set(const tmp<Container<InputType>>& input) { this->tmpInput(const_cast<tmp<Container<InputType>>&>(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<Type>& select ( const Container<InputType>& input, Container<Type>& other ) { if (std::is_same<Type, InputType>::value) { return reinterpret_cast<const Container<Type>&>(input); } else { 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 Type, class InputType, template<class> class Container = Field> class PrecisionAdaptor : public refPtr<Container<Type>> { // Private Data //- Reference to underlying external input data refPtr<Container<InputType>> orig_; // Private Member Functions //- Set adaptor for different input, copying as required void setInput(Container<InputType>& src, const bool doCopy) { orig_.ref(src); if (std::is_same<Type, InputType>::value) { // Use reference directly this->ref(reinterpret_cast<Container<Type>&>(src)); } else { // Need intermediate buffer this->reset(new Container<Type>(src.size())); if (doCopy) { std::copy(src.cbegin(), src.cend(), this->ref().begin()); } } } public: //- The adapted field type. Same as element_type typedef Container<Type> FieldType; // Constructors //- Default construct, setting content later PrecisionAdaptor() = default; //- Construct from Container<InputType>, //- copying input if required (and requested) explicit PrecisionAdaptor ( Container<InputType>& input, const bool doCopy = true ) { this->setInput(input, doCopy); } //- Destructor, copies back content changes (as required) ~PrecisionAdaptor() { 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->good()); } //- Commit adapted content changes back to original input (as required) void commit() { if (this->active() && orig_.good()) { 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<InputType>& input, const bool doCopy = true) { if (orig_.get() != &input) { // Commit changes to old input first this->commit(); } this->setInput(input, doCopy); } }; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // } // End namespace Foam // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // #endif // ************************************************************************* //