/*---------------------------------------------------------------------------*\
  =========                 |
  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
   \\    /   O peration     |
    \\  /    A nd           | www.openfoam.com
     \\/     M anipulation  |
-------------------------------------------------------------------------------
    Copyright (C) 2011-2016 OpenFOAM Foundation
    Copyright (C) 2015-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::DimensionedField

Description
    Field with dimensions and associated with geometry type GeoMesh which is
    used to size the field and a reference to it is maintained.

SourceFiles
    DimensionedFieldI.H
    DimensionedField.C
    DimensionedFieldIO.C

\*---------------------------------------------------------------------------*/

#ifndef DimensionedgpuField_H
#define DimensionedgpuField_H

#include "regIOobject.H"
#include "gpuField.H"
#include "dimensionedType.H"
#include "orientedType.H"
#include "DimensionedField.H"

// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

namespace Foam
{

// Forward declarations
template<class Type, class GeoMesh> class DimensionedgpuField;

template<class Type, class GeoMesh>
Ostream& operator<<
(
    Ostream& os,
    const DimensionedgpuField<Type, GeoMesh>& df
);

template<class Type, class GeoMesh>
Ostream& operator<<
(
    Ostream& os,
    const tmp<DimensionedgpuField<Type, GeoMesh>>& tdf
);


/*---------------------------------------------------------------------------*\
                      Class DimensionedgpuField Declaration
\*---------------------------------------------------------------------------*/

template<class Type, class GeoMesh>
class DimensionedgpuField
:
    public regIOobject,
    public gpuField<Type>
{
public:

    // Public Typedefs

        //- Type of mesh on which this DimensionedField is instantiated
        typedef typename GeoMesh::Mesh Mesh;

        //- Type of the field from which this DimensionedField is derived
        typedef gpuField<Type> FieldType;

        //- Component type of the elements of the field
        typedef typename gpuField<Type>::cmptType cmptType;

		typedef DimensionedField<Type,  typename GeoMesh::hostType> DimField;

private:

    // Private data

        //- Reference to mesh
        const Mesh& mesh_;

        //- Dimension set for this field
        dimensionSet dimensions_;

        //- Oriented flag
        orientedType oriented_;
		
    // Private Member Functions

        //- Assert that non-zero field size == mesh size
        void checkFieldSize() const;

        void readIfPresent(const word& fieldDictEntry = "value");


public:

    //- Runtime type information
    TypeName("DimensionedgpuField");


    // Static Member Functions

        //- Return a NullObjectRef DimensionedField
        inline static const DimensionedgpuField<Type, GeoMesh>& null();


    // Constructors
    
        //- Construct from components
        DimensionedgpuField
        (
            const IOobject& io,
            const Mesh& mesh,
            const dimensionSet& dims,
            const Field<Type>& field
        );

        //- Construct from components
        DimensionedgpuField
        (
            const IOobject& io,
            const Mesh& mesh,
            const dimensionSet& dims,
            const gpuField<Type>& field
        );

        //- Construct from components, transferring initial field content
        DimensionedgpuField
        (
            const IOobject& io,
            const Mesh& mesh,
            const dimensionSet& dims,
            Field<Type>&& field
        );

        //- Construct from components, transferring initial field content
        DimensionedgpuField
        (
            const IOobject& io,
            const Mesh& mesh,
            const dimensionSet& dims,
            List<Type>&& field
        );

        //- Construct from components, copy or transfer tmp content
        DimensionedgpuField
        (
            const IOobject& io,
            const Mesh& mesh,
            const dimensionSet& dims,
            const tmp<Field<Type>>& tfield
        );

        //- Construct from components, copy or transfer tmp content
        DimensionedgpuField
        (
            const IOobject& io,
            const Mesh& mesh,
            const dimensionSet& dims,
            const tmp<gpuField<Type>>& tfield
        );

        //- Construct from components, setting the initial size and assigning
        //- the dimensions, but not initialising any field values.
        //  Used for temporary fields which are initialised after construction
        DimensionedgpuField
        (
            const IOobject& io,
            const Mesh& mesh,
            const dimensionSet& dims,
            const bool checkIOFlags = true
        );

        //- Construct from components, setting the initial size and assigning
        //- both dimensions and values.
        //  The internal name for the dimensioned\<Type\> has no influence.
        DimensionedgpuField
        (
            const IOobject& io,
            const Mesh& mesh,
            const dimensioned<Type>& dt,
            const bool checkIOFlags = true
        );
		
        //- Construct from Istream.
        DimensionedgpuField
        (
            const IOobject& io,
            const Mesh& mesh,
            const word& fieldDictEntry = "value"
        );

        //- Construct from dictionary
        DimensionedgpuField
        (
            const IOobject& io,
            const Mesh& mesh,
            const dictionary& fieldDict,
            const word& fieldDictEntry = "value"
        );


        //- Copy construct
        DimensionedgpuField(const DimensionedgpuField<Type, GeoMesh>& df);

        //- Move construct
        DimensionedgpuField(DimensionedgpuField<Type, GeoMesh>&& df);

        //- Copy construct or reuse (move) as specified.
        DimensionedgpuField(DimensionedgpuField<Type, GeoMesh>& df, bool reuse);

        //- Construct from tmp\<DimensionedField\> deleting argument
        DimensionedgpuField
        (
            const tmp<DimensionedgpuField<Type, GeoMesh>>& tdf
        );

        //- Copy construct, resetting IO parameters
        DimensionedgpuField
        (
            const IOobject& io,
            const DimensionedgpuField<Type, GeoMesh>& df
        );

        //- Move construct, resetting IO parameters
        DimensionedgpuField
        (
            const IOobject& io,
            DimensionedgpuField<Type, GeoMesh>&& df
        );

        //- Copy or move construct, resetting IO parameters.
        DimensionedgpuField
        (
            const IOobject& io,
            DimensionedgpuField<Type, GeoMesh>& df,
            bool reuse
        );

        //- Construct from tmp\<DimensionedField\> deleting argument,
        //- resetting IO parameters.
        DimensionedgpuField
        (
            const IOobject& io,
            const tmp<DimensionedgpuField<Type, GeoMesh>>& tdf
        );

        //- Copy construct with a new name
        DimensionedgpuField
        (
            const word& newName,
            const DimensionedgpuField<Type, GeoMesh>& df
        );

        //- Move construct with a new name
        DimensionedgpuField
        (
            const word& newName,
            DimensionedgpuField<Type, GeoMesh>&& df
        );

        //- Copy or move construct, resetting name.
        DimensionedgpuField
        (
            const word& newName,
            DimensionedgpuField<Type, GeoMesh>& df,
            bool reuse
        );

        //- Construct with a new name from tmp\<DimensionedField\>
        DimensionedgpuField
        (
            const word& newName,
            const tmp<DimensionedgpuField<Type, GeoMesh>>& tdf
        );


        //- Clone
        tmp<DimensionedgpuField<Type, GeoMesh>> clone() const;


    //- Destructor
    virtual ~DimensionedgpuField() = default;


    // Member Functions
    
        void readField
        (
            const dictionary& fieldDict,
            const word& fieldDictEntry = "value"
        );

        //- Return mesh
        inline const Mesh& mesh() const;

        //- Return dimensions
        inline const dimensionSet& dimensions() const;

        //- Return non-const access to dimensions
        inline dimensionSet& dimensions();

        //- Return oriented type
        inline const orientedType& oriented() const noexcept;

        //- Return non-const access to the oriented type
        inline orientedType& oriented() noexcept;

        //- Set the oriented flag
        inline void setOriented(const bool oriented = true) noexcept;

        //- Return field
        inline const gpuField<Type>& field() const;

        //- Return field
        inline gpuField<Type>& field();

        //- Return a component field of the field
        tmp<DimensionedgpuField<cmptType, GeoMesh>> component
        (
            const direction d
        ) const;

        //- Replace a component field of the field
        void replace
        (
            const direction d,
            const DimensionedgpuField<cmptType, GeoMesh>& df
        );

        //- Replace a component field of the field
        void replace
        (
            const direction d,
            const tmp<DimensionedgpuField<cmptType, GeoMesh>>& tdf
        );

        //- Return the field transpose (only defined for second rank tensors)
        tmp<DimensionedgpuField<Type, GeoMesh>> T() const;

        //- Calculate and return arithmetic average
        dimensioned<Type> average() const;

        //- Calculate and return weighted average
        dimensioned<Type> weightedAverage
        (
            const DimensionedgpuField<scalar, GeoMesh>& weightField
        ) const;

        //- Calculate and return weighted average
        dimensioned<Type> weightedAverage
        (
            const tmp<DimensionedgpuField<scalar, GeoMesh>>& tweightField
        ) const;

        // Write

            bool writeData(Ostream& os, const word& fieldDictEntry) const;

            bool writeData(Ostream& os) const;

    // Member Operators

        void operator=(const DimensionedgpuField<Type, GeoMesh>& df);
        void operator=(const tmp<DimensionedgpuField<Type, GeoMesh>>& tdf);
        void operator=(const DimField& df);
        void operator=(const tmp<DimField>& tdf);

        //- Assign dimensions and value.
        void operator=(const dimensioned<Type>& dt);

        void operator+=(const DimensionedgpuField<Type, GeoMesh>& df);
        void operator+=(const tmp<DimensionedgpuField<Type, GeoMesh>>& tdf);

        void operator-=(const DimensionedgpuField<Type, GeoMesh>& df);
        void operator-=(const tmp<DimensionedgpuField<Type, GeoMesh>>& tdf);

        void operator*=(const DimensionedgpuField<scalar, GeoMesh>& df);
        void operator*=(const tmp<DimensionedgpuField<scalar, GeoMesh>>& tdf);

        void operator/=(const DimensionedgpuField<scalar, GeoMesh>& df);
        void operator/=(const tmp<DimensionedgpuField<scalar, GeoMesh>>& tdf);

        void operator+=(const dimensioned<Type>& dt);
        void operator-=(const dimensioned<Type>& dt);

        void operator*=(const dimensioned<scalar>& dt);
        void operator/=(const dimensioned<scalar>& dt);

		// Ostream Operators
		
			friend Ostream& operator<< <Type, GeoMesh>
			(
				Ostream& os,
				const DimensionedField<Type, GeoMesh>& df
			);
		
			friend Ostream& operator<< <Type, GeoMesh>
			(
				Ostream& os,
				const tmp<DimensionedField<Type, GeoMesh>>& tdf
			);
};


// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

} // End namespace Foam

// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

#include "DimensionedgpuFieldI.H"
#include "DimensionedgpuFieldFunctions.H"

#ifdef NoRepository
    #include "DimensionedgpuField.C"
#endif

// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

#endif

// ************************************************************************* //
