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

Description
    A special matrix type and solver, designed for finite volume
    solutions of scalar equations.
    Face addressing is used to make all matrix assembly
    and solution loops vectorise.

SourceFiles
    fvMatrix.C
    fvMatrixSolve.C
    fvScalarMatrix.C

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

#ifndef gpufvMatrix_H
#define gpufvMatrix_H

#include "volgpuFields.H"
#include "surfacegpuFields.H"
#include "gpulduMatrix.H"
#include "tmp.H"
#include "autoPtr.H"
#include "dimensionedTypes.H"
#include "zero.H"
#include "className.H"
#include "gpulduPrimitiveMeshAssembly.H"
#include "gpulduMesh.H"

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

namespace Foam
{

// Forward Declarations
template<class Type> class gpufvMatrix;
template<class T> class UIndirectList;

template<class Type>
Ostream& operator<<(Ostream&, const gpufvMatrix<Type>&);


template<class Type>
tmp<GeometricgpuField<Type, fvPatchgpuField, gpuvolMesh>> operator&
(
    const gpufvMatrix<Type>&,
    const DimensionedgpuField<Type, gpuvolMesh>&
);

template<class Type>
tmp<GeometricgpuField<Type, fvPatchgpuField, gpuvolMesh>> operator&
(
    const gpufvMatrix<Type>&,
    const tmp<DimensionedgpuField<Type, gpuvolMesh>>&
);

template<class Type>
tmp<GeometricgpuField<Type, fvPatchgpuField, gpuvolMesh>> operator&
(
    const gpufvMatrix<Type>&,
    const tmp<GeometricgpuField<Type, fvPatchgpuField, gpuvolMesh>>&
);

template<class Type>
tmp<GeometricgpuField<Type, fvPatchgpuField, gpuvolMesh>> operator&
(
    const tmp<gpufvMatrix<Type>>&,
    const DimensionedgpuField<Type, gpuvolMesh>&
);

template<class Type>
tmp<GeometricgpuField<Type, fvPatchgpuField, gpuvolMesh>> operator&
(
    const tmp<gpufvMatrix<Type>>&,
    const tmp<DimensionedgpuField<Type, gpuvolMesh>>&
);

template<class Type>
tmp<GeometricgpuField<Type, fvPatchgpuField, gpuvolMesh>> operator&
(
    const tmp<gpufvMatrix<Type>>&,
    const tmp<GeometricgpuField<Type, fvPatchgpuField, gpuvolMesh>>&
);



/*---------------------------------------------------------------------------*\
                           Class fvMatrix Declaration
\*---------------------------------------------------------------------------*/

template<class Type>
class gpufvMatrix
:
    public refCount,
    public gpulduMatrix
{
public:

    // Public Types

        //- Field type for psi
        typedef
            GeometricgpuField<Type, fvPatchgpuField, gpuvolMesh>
            psiFieldType;

        //- Field type for face flux (for non-orthogonal correction)
        typedef
            GeometricgpuField<Type, fvsPatchgpuField, gpusurfaceMesh>
            faceFluxFieldType;

private:

    // Private Data

        //- Const reference to field
        //  Converted into a non-const reference at the point of solution.
        const psiFieldType& psi_;

        //- Originating fvMatrices when assembling matrices. Empty if not used.
        PtrList<gpufvMatrix<Type>> subMatrices_;

        //- Is the fvMatrix using implicit formulation
        bool useImplicit_;

        //- Name of the lduAssembly
        word lduAssemblyName_;

        //- Number of fvMatrices added to this
        label nMatrix_;

        //- Dimension set
        dimensionSet dimensions_;

        //- Source term
        gpuField<Type> source_;

        //- Boundary scalar field containing pseudo-matrix coeffs
        //- for internal cells
        FieldField<gpuField, Type> internalCoeffs_;

        //- Boundary scalar field containing pseudo-matrix coeffs
        //- for boundary cells
        FieldField<gpuField, Type> boundaryCoeffs_;

        //- Face flux field for non-orthogonal correction
        mutable faceFluxFieldType* faceFluxCorrectionPtr_;


protected:

    //- Declare friendship with the fvSolver class
    friend class fvSolver;


    // Protected Member Functions

        //- Add patch contribution to internal field
        template<class Type2>
        void addToInternalField
        (
            const labelgpuList& addr,
            const labelgpuList& sort,
            const labelgpuList& sortStart,
            const gpuField<Type2>& pf,
            gpuField<Type2>& intf
        ) const;

        template<class Type2>
        void addToInternalField
        (
            const labelgpuList& addr,
            const labelgpuList& sort,
            const labelgpuList& sortStart,
            const tmp<gpuField<Type2>>& tpf,
            gpuField<Type2>& intf
        ) const;

        //- Subtract patch contribution from internal field
        template<class Type2>
        void subtractFromInternalField
        (
            const labelgpuList& addr,
            const labelgpuList& sort,
            const labelgpuList& sortStart,
            const gpuField<Type2>& pf,
            gpuField<Type2>& intf
        ) const;

        template<class Type2>
        void subtractFromInternalField
        (
            const labelgpuList& addr,
            const labelgpuList& sort,
            const labelgpuList& sortStart,
            const tmp<gpuField<Type2>>& tpf,
            gpuField<Type2>& intf
        ) const;


        // Implicit helper functions

            //- Name the implicit assembly addressing
            label checkImplicit(const label fieldI = 0);


        // Matrix completion functionality

            void addBoundaryDiag
            (
                scalargpuField& diag,
                const direction cmpt
            ) const;

            void addCmptAvBoundaryDiag(scalargpuField& diag) const;

            void addBoundarySource
            (
                gpuField<Type>& source,
                const bool couples=true
            ) const;


        // Matrix manipulation functionality

            //- Set solution in given cells to the specified values
            template<template<class> class ListType>
            void setValuesFromList
            (
                const labelgpuList& cellLabels,
                const ListType<Type>& values
            );


public:

    //- Solver class returned by the solver function
    //- used for systems in which it is useful to cache the solver for reuse.
    //  E.g. if the solver is potentially expensive to construct (AMG) and can
    //  be used several times (PISO)
    class fvSolver
    {
        gpufvMatrix<Type>& fvMat_;

        autoPtr<gpulduMatrix::solver> solver_;

    public:

        // Constructors

            fvSolver(gpufvMatrix<Type>& fvMat, autoPtr<gpulduMatrix::solver>&& sol)
            :
                fvMat_(fvMat),
                solver_(std::move(sol))
            {}


        // Member Functions

            //- Solve returning the solution statistics.
            //  Solver controls read from dictionary
            SolverPerformance<Type> solve(const dictionary& solverControls);

            //- Solve returning the solution statistics.
            //  Solver controls read from fvSolution
            SolverPerformance<Type> solve();
    };


    ClassName("gpufvMatrix");


    // Constructors

        //- Construct given a field to solve for
        gpufvMatrix
        (
            const GeometricgpuField<Type, fvPatchgpuField, gpuvolMesh>& psi,
            const dimensionSet& ds
        );

        //- Copy construct
        gpufvMatrix(const gpufvMatrix<Type>&);

        //- Copy/move construct from tmp<fvMatrix<Type>>
        gpufvMatrix(const tmp<gpufvMatrix<Type>>&);

        //- Construct from Istream given field to solve for
        gpufvMatrix
        (
            const GeometricgpuField<Type, fvPatchgpuField, gpuvolMesh>& psi,
            Istream& is
        );

        //- Clone
        tmp<gpufvMatrix<Type>> clone() const;


    //- Destructor
    virtual ~gpufvMatrix();


    // Member Functions

        // Access

            // Coupling

                label nMatrices() const
                {
                    return (nMatrix_ == 0 ? 1 : nMatrix_);
                }

                const gpufvMatrix<Type>& matrix(const label i) const
                {
                    return (nMatrix_ == 0 ? *this : subMatrices_[i]);
                }

                gpufvMatrix<Type>& matrix(const label i)
                {
                    return (nMatrix_ == 0 ? *this : subMatrices_[i]);
                }

                label globalPatchID
                (
                    const label fieldi,
                    const label patchi
                ) const
                {
                    if (!lduMeshPtr())
                    {
                        return patchi;
                    }
                    else
                    {
                        return lduMeshPtr()->patchMap()[fieldi][patchi];
                    }
                }

                //- Transfer lower, upper, diag and source to this fvMatrix
                void transferFvMatrixCoeffs();

                //- Create or update ldu assembly
                void createOrUpdateLduPrimitiveAssembly();

                //- Access to lduPrimitiveMeshAssembly
                gpulduPrimitiveMeshAssembly* lduMeshPtr();

                //- Const Access to lduPrimitiveMeshAssembly
                const gpulduPrimitiveMeshAssembly* lduMeshPtr() const;

                //- Manipulate matrix
                void manipulateMatrix(direction cmp);

                //- Manipulate boundary/internal coeffs for coupling
                void setBounAndInterCoeffs();

                //- Set interfaces
                void setInterfaces
                (
                    lduInterfacegpuFieldPtrsList&,
                    PtrDynList<lduInterfacegpuField>& newInterfaces
                );

                //- Add internal and boundary contribution to local patches
                void mapContributions
                (
                    label fieldi,
                    const FieldField<gpuField, Type>& fluxContrib,
                    FieldField<gpuField, Type>& contrib,
                    bool internal
                ) const;

                //- Return optional lduAdressing
                const gpulduPrimitiveMeshAssembly& lduMeshAssembly()
                {
                    return *lduMeshPtr();
                }

                //- Return psi
                const GeometricgpuField<Type, fvPatchgpuField, gpuvolMesh>& psi
                (
                    const label i = 0
                ) const
                {
                    return
                    (
                        (i == 0 && nMatrix_ == 0) ? psi_ : matrix(i).psi()
                    );
                }


                GeometricgpuField<Type, fvPatchgpuField, gpuvolMesh>& psi
                (
                    const label i = 0
                )
                {
                    return
                    (
                        (i == 0 && nMatrix_ == 0)
                      ? const_cast
                        <
                            GeometricgpuField<Type, fvPatchgpuField, gpuvolMesh>&
                        >(psi_)
                      : const_cast
                        <
                            GeometricgpuField<Type, fvPatchgpuField, gpuvolMesh>&
                        >
                        (
                            matrix(i).psi()
                        )
                    );
                }

                //- Clear multiple fvMatrices
                void clear()
                {
                    subMatrices_.clear();
                    nMatrix_ = 0;
                }


            const dimensionSet& dimensions() const
            {
                return dimensions_;
            }

            gpuField<Type>& source()
            {
                return source_;
            }

            const gpuField<Type>& source() const
            {
                return source_;
            }

            //- fvBoundary scalar field containing pseudo-matrix coeffs
            //- for internal cells
            const FieldField<gpuField, Type>& internalCoeffs() const
            {
                return internalCoeffs_;
            }

            //- fvBoundary scalar field containing pseudo-matrix coeffs
            //- for internal cells
            FieldField<gpuField, Type>& internalCoeffs()
            {
                return internalCoeffs_;
            }

            //- fvBoundary scalar field containing pseudo-matrix coeffs
            //- for boundary cells
            const FieldField<gpuField, Type>& boundaryCoeffs() const
            {
                return boundaryCoeffs_;
            }

            //- fvBoundary scalar field containing pseudo-matrix coeffs
            //- for boundary cells
            FieldField<gpuField, Type>& boundaryCoeffs()
            {
                return boundaryCoeffs_;
            }

            //- Declare return type of the faceFluxCorrectionPtr() function
            typedef GeometricgpuField<Type, fvsPatchgpuField, gpusurfaceMesh>
                *faceFluxFieldPtrType;

            //- Return pointer to face-flux non-orthogonal correction field
            faceFluxFieldPtrType& faceFluxCorrectionPtr()
            {
                return faceFluxCorrectionPtr_;
            }

            //- True if face-flux non-orthogonal correction field exists
            bool hasFaceFluxCorrection() const noexcept
            {
                return bool(faceFluxCorrectionPtr_);
            }


        // Operations

            //- Set solution in given cells to the specified value
            //- and eliminate the corresponding equations from the matrix.
            void setValues
            (
                const labelgpuList& cellLabels,
                const Type& value
            );

            //- Set solution in given cells to the specified values
            //- and eliminate the corresponding equations from the matrix.
            void setValues
            (
                const labelgpuList& cellLabels,
                const gpuList<Type>& values
            );

            //- Set solution in given cells to the specified values
            //- and eliminate the corresponding equations from the matrix.
            void setValues
            (
                const labelgpuList& cellLabels,
                const UIndirectList<Type>& values
            );

            //- Set reference level for solution
            void setReference
            (
                const label celli,
                const Type& value,
                const bool forceReference = false
            );

            //- Set reference level for solution
            void setReferences
            (
                const labelgpuList& cellLabels,
                const Type& value,
                const bool forceReference = false
            );

            //- Set reference level for solution
            void setReferences
            (
                const labelgpuList& cellLabels,
                const gpuList<Type>& values,
                const bool forceReference = false
            );

            //- Set reference level for a component of the solution
            //- on a given patch face
            void setComponentReference
            (
                const label patchi,
                const label facei,
                const direction cmpt,
                const scalar value
            );

            //- Add fvMatrix
            void addFvMatrix(gpufvMatrix<Type>& matrix);

            //- Relax matrix (for steady-state solution).
            //  alpha = 1 : diagonally equal
            //  alpha < 1 : diagonally dominant
            //  alpha = 0 : do nothing
            //  Note: Requires positive diagonal.
            void relax(const scalar alpha);

            //- Relax matrix (for steady-state solution).
            //  alpha is read from controlDict
            void relax();

            //- Manipulate based on a boundary field
            void boundaryManipulate
            (
                typename GeometricgpuField<Type, fvPatchgpuField, gpuvolMesh>::
                    Boundary& values
            );

            //- Construct and return the solver
            //  Use the given solver controls
            autoPtr<fvSolver> solver(const dictionary&);

            //- Construct and return the solver
            //  Solver controls read from fvSolution
            autoPtr<fvSolver> solver();

            //- Solve segregated or coupled returning the solution statistics.
            //  Use the given solver controls
            SolverPerformance<Type> solveSegregatedOrCoupled(const dictionary&);

            //- Solve segregated returning the solution statistics.
            //  Use the given solver controls
            SolverPerformance<Type> solveSegregated(const dictionary&);

            //- Solve coupled returning the solution statistics.
            //  Use the given solver controls
            SolverPerformance<Type> solveCoupled(const dictionary&);

            //- Solve returning the solution statistics.
            //  Use the given solver controls
            SolverPerformance<Type> solve(const dictionary&);

            //- Solve returning the solution statistics.
            //  Solver controls read from fvSolution
            SolverPerformance<Type> solve();

            //- Return the matrix residual
            tmp<gpuField<Type>> residual() const;

            //- Return the matrix scalar diagonal
            tmp<scalargpuField> D() const;

            //- Return the matrix Type diagonal
            tmp<gpuField<Type>> DD() const;

            //- Return the central coefficient
            tmp<volScalargpuField> A() const;

            //- Return the H operation source
            tmp<GeometricgpuField<Type, fvPatchgpuField, gpuvolMesh>> H() const;

            //- Return H(1)
            tmp<volScalargpuField> H1() const;

            //- Return the face-flux field from the matrix
            tmp<GeometricgpuField<Type, fvsPatchgpuField, gpusurfaceMesh>>
                flux() const;

            //- Return the solver dictionary taking into account finalIteration
            const dictionary& solverDict() const;


    // Member Operators

        void operator=(const gpufvMatrix<Type>&);
        void operator=(const tmp<gpufvMatrix<Type>>&);

        void negate();

        void operator+=(const gpufvMatrix<Type>&);
        void operator+=(const tmp<gpufvMatrix<Type>>&);

        void operator-=(const gpufvMatrix<Type>&);
        void operator-=(const tmp<gpufvMatrix<Type>>&);

        void operator+=
        (
            const DimensionedgpuField<Type, gpuvolMesh>&
        );
        void operator+=
        (
            const tmp<DimensionedgpuField<Type, gpuvolMesh>>&
        );
        void operator+=
        (
            const tmp<GeometricgpuField<Type, fvPatchgpuField, gpuvolMesh>>&
        );

        void operator-=
        (
            const DimensionedgpuField<Type, gpuvolMesh>&
        );
        void operator-=
        (
            const tmp<DimensionedgpuField<Type, gpuvolMesh>>&
        );
        void operator-=
        (
            const tmp<GeometricgpuField<Type, fvPatchgpuField, gpuvolMesh>>&
        );

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

        void operator+=(const zero&);
        void operator-=(const zero&);

        void operator*=(const volScalargpuField::Internal&);
        void operator*=(const tmp<volScalargpuField::Internal>&);
        void operator*=(const tmp<volScalargpuField>&);

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


    // Friend Operators

        friend tmp<GeometricgpuField<Type, fvPatchgpuField, gpuvolMesh>>
        operator& <Type>
        (
            const gpufvMatrix<Type>&,
            const DimensionedgpuField<Type, gpuvolMesh>&
        );

        friend tmp<GeometricgpuField<Type, fvPatchgpuField, gpuvolMesh>>
        operator& <Type>
        (
            const gpufvMatrix<Type>&,
            const tmp<GeometricgpuField<Type, fvPatchgpuField, gpuvolMesh>>&
        );

        friend tmp<GeometricgpuField<Type, fvPatchgpuField, gpuvolMesh>>
        operator& <Type>
        (
            const tmp<gpufvMatrix<Type>>&,
            const DimensionedgpuField<Type, gpuvolMesh>&
        );

        friend tmp<GeometricgpuField<Type, fvPatchgpuField, gpuvolMesh>>
        operator& <Type>
        (
            const tmp<gpufvMatrix<Type>>&,
            const tmp<GeometricgpuField<Type, fvPatchgpuField, gpuvolMesh>>&
        );


    // Ostream Operator

        friend Ostream& operator<< <Type>
        (
            Ostream&,
            const gpufvMatrix<Type>&
        );
};


// * * * * * * * * * * * * * * * Global Functions  * * * * * * * * * * * * * //

template<class Type>
void checkMethod
(
    const gpufvMatrix<Type>&,
    const gpufvMatrix<Type>&,
    const char*
);

template<class Type>
void checkMethod
(
    const gpufvMatrix<Type>&,
    const DimensionedgpuField<Type, gpuvolMesh>&,
    const char*
);

template<class Type>
void checkMethod
(
    const gpufvMatrix<Type>&,
    const dimensioned<Type>&,
    const char*
);


//- Solve returning the solution statistics given convergence tolerance
//  Use the given solver controls
template<class Type>
SolverPerformance<Type> solve(gpufvMatrix<Type>&, const dictionary&);


//- Solve returning the solution statistics given convergence tolerance,
//- deleting temporary matrix after solution.
//  Use the given solver controls
template<class Type>
SolverPerformance<Type> solve
(
    const tmp<gpufvMatrix<Type>>&,
    const dictionary&
);


//- Solve returning the solution statistics given convergence tolerance
//  Solver controls read fvSolution
template<class Type>
SolverPerformance<Type> solve(gpufvMatrix<Type>&);


//- Solve returning the solution statistics given convergence tolerance,
//- deleting temporary matrix after solution.
//  Solver controls read fvSolution
template<class Type>
SolverPerformance<Type> solve(const tmp<gpufvMatrix<Type>>&);


//- Return the correction form of the given matrix
//- by subtracting the matrix multiplied by the current field
template<class Type>
tmp<gpufvMatrix<Type>> correction(const gpufvMatrix<Type>&);


//- Return the correction form of the given temporary matrix
//- by subtracting the matrix multiplied by the current field
template<class Type>
tmp<gpufvMatrix<Type>> correction(const tmp<gpufvMatrix<Type>>&);


// * * * * * * * * * * * * * * * Global Operators  * * * * * * * * * * * * * //

template<class Type>
tmp<gpufvMatrix<Type>> operator==
(
    const gpufvMatrix<Type>&,
    const gpufvMatrix<Type>&
);

template<class Type>
tmp<gpufvMatrix<Type>> operator==
(
    const tmp<gpufvMatrix<Type>>&,
    const gpufvMatrix<Type>&
);

template<class Type>
tmp<gpufvMatrix<Type>> operator==
(
    const gpufvMatrix<Type>&,
    const tmp<gpufvMatrix<Type>>&
);

template<class Type>
tmp<gpufvMatrix<Type>> operator==
(
    const tmp<gpufvMatrix<Type>>&,
    const tmp<gpufvMatrix<Type>>&
);


template<class Type>
tmp<gpufvMatrix<Type>> operator==
(
    const gpufvMatrix<Type>&,
    const DimensionedgpuField<Type, gpuvolMesh>&
);

template<class Type>
tmp<gpufvMatrix<Type>> operator==
(
    const gpufvMatrix<Type>&,
    const tmp<DimensionedgpuField<Type, gpuvolMesh>>&
);

template<class Type>
tmp<gpufvMatrix<Type>> operator==
(
    const gpufvMatrix<Type>&,
    const tmp<GeometricgpuField<Type, fvPatchgpuField, gpuvolMesh>>&
);

template<class Type>
tmp<gpufvMatrix<Type>> operator==
(
    const tmp<gpufvMatrix<Type>>&,
    const DimensionedgpuField<Type, gpuvolMesh>&
);

template<class Type>
tmp<gpufvMatrix<Type>> operator==
(
    const tmp<gpufvMatrix<Type>>&,
    const tmp<DimensionedgpuField<Type, gpuvolMesh>>&
);

template<class Type>
tmp<gpufvMatrix<Type>> operator==
(
    const tmp<gpufvMatrix<Type>>&,
    const tmp<GeometricgpuField<Type, fvPatchgpuField, gpuvolMesh>>&
);

template<class Type>
tmp<gpufvMatrix<Type>> operator==
(
    const gpufvMatrix<Type>&,
    const dimensioned<Type>&
);

template<class Type>
tmp<gpufvMatrix<Type>> operator==
(
    const tmp<gpufvMatrix<Type>>&,
    const dimensioned<Type>&
);


template<class Type>
tmp<gpufvMatrix<Type>> operator==
(
    const gpufvMatrix<Type>&,
    const zero&
);

template<class Type>
tmp<gpufvMatrix<Type>> operator==
(
    const tmp<gpufvMatrix<Type>>&,
    const zero&
);


template<class Type>
tmp<gpufvMatrix<Type>> operator-
(
    const gpufvMatrix<Type>&
);

template<class Type>
tmp<gpufvMatrix<Type>> operator-
(
    const tmp<gpufvMatrix<Type>>&
);


template<class Type>
tmp<gpufvMatrix<Type>> operator+
(
    const gpufvMatrix<Type>&,
    const gpufvMatrix<Type>&
);

template<class Type>
tmp<gpufvMatrix<Type>> operator+
(
    const tmp<gpufvMatrix<Type>>&,
    const gpufvMatrix<Type>&
);

template<class Type>
tmp<gpufvMatrix<Type>> operator+
(
    const gpufvMatrix<Type>&,
    const tmp<gpufvMatrix<Type>>&
);

template<class Type>
tmp<gpufvMatrix<Type>> operator+
(
    const tmp<gpufvMatrix<Type>>&,
    const tmp<gpufvMatrix<Type>>&
);


template<class Type>
tmp<gpufvMatrix<Type>> operator+
(
    const gpufvMatrix<Type>&,
    const DimensionedgpuField<Type, gpuvolMesh>&
);

template<class Type>
tmp<gpufvMatrix<Type>> operator+
(
    const gpufvMatrix<Type>&,
    const tmp<DimensionedgpuField<Type, gpuvolMesh>>&
);

template<class Type>
tmp<gpufvMatrix<Type>> operator+
(
    const gpufvMatrix<Type>&,
    const tmp<GeometricgpuField<Type, fvPatchgpuField, gpuvolMesh>>&
);

template<class Type>
tmp<gpufvMatrix<Type>> operator+
(
    const tmp<gpufvMatrix<Type>>&,
    const DimensionedgpuField<Type, gpuvolMesh>&
);

template<class Type>
tmp<gpufvMatrix<Type>> operator+
(
    const tmp<gpufvMatrix<Type>>&,
    const tmp<DimensionedgpuField<Type, gpuvolMesh>>&
);

template<class Type>
tmp<gpufvMatrix<Type>> operator+
(
    const tmp<gpufvMatrix<Type>>&,
    const tmp<GeometricgpuField<Type, fvPatchgpuField, gpuvolMesh>>&
);

template<class Type>
tmp<gpufvMatrix<Type>> operator+
(
    const DimensionedgpuField<Type, gpuvolMesh>&,
    const gpufvMatrix<Type>&
);

template<class Type>
tmp<gpufvMatrix<Type>> operator+
(
    const tmp<DimensionedgpuField<Type, gpuvolMesh>>&,
    const gpufvMatrix<Type>&
);

template<class Type>
tmp<gpufvMatrix<Type>> operator+
(
    const tmp<GeometricgpuField<Type, fvPatchgpuField, gpuvolMesh>>&,
    const gpufvMatrix<Type>&
);

template<class Type>
tmp<gpufvMatrix<Type>> operator+
(
    const DimensionedgpuField<Type, gpuvolMesh>&,
    const tmp<gpufvMatrix<Type>>&
);

template<class Type>
tmp<gpufvMatrix<Type>> operator+
(
    const tmp<DimensionedgpuField<Type, gpuvolMesh>>&,
    const tmp<gpufvMatrix<Type>>&
);

template<class Type>
tmp<gpufvMatrix<Type>> operator+
(
    const tmp<GeometricgpuField<Type, fvPatchgpuField, gpuvolMesh>>&,
    const tmp<gpufvMatrix<Type>>&
);


template<class Type>
tmp<gpufvMatrix<Type>> operator+
(
    const gpufvMatrix<Type>&,
    const dimensioned<Type>&
);

template<class Type>
tmp<gpufvMatrix<Type>> operator+
(
    const tmp<gpufvMatrix<Type>>&,
    const dimensioned<Type>&
);

template<class Type>
tmp<gpufvMatrix<Type>> operator+
(
    const dimensioned<Type>&,
    const gpufvMatrix<Type>&
);

template<class Type>
tmp<gpufvMatrix<Type>> operator+
(
    const dimensioned<Type>&,
    const tmp<gpufvMatrix<Type>>&
);


template<class Type>
tmp<gpufvMatrix<Type>> operator-
(
    const gpufvMatrix<Type>&,
    const gpufvMatrix<Type>&
);

template<class Type>
tmp<gpufvMatrix<Type>> operator-
(
    const tmp<gpufvMatrix<Type>>&,
    const gpufvMatrix<Type>&
);

template<class Type>
tmp<gpufvMatrix<Type>> operator-
(
    const gpufvMatrix<Type>&,
    const tmp<gpufvMatrix<Type>>&
);

template<class Type>
tmp<gpufvMatrix<Type>> operator-
(
    const tmp<gpufvMatrix<Type>>&,
    const tmp<gpufvMatrix<Type>>&
);


template<class Type>
tmp<gpufvMatrix<Type>> operator-
(
    const gpufvMatrix<Type>&,
    const DimensionedgpuField<Type, gpuvolMesh>&
);

template<class Type>
tmp<gpufvMatrix<Type>> operator-
(
    const gpufvMatrix<Type>&,
    const tmp<DimensionedgpuField<Type, gpuvolMesh>>&
);

template<class Type>
tmp<gpufvMatrix<Type>> operator-
(
    const gpufvMatrix<Type>&,
    const tmp<GeometricgpuField<Type, fvPatchgpuField, gpuvolMesh>>&
);

template<class Type>
tmp<gpufvMatrix<Type>> operator-
(
    const tmp<gpufvMatrix<Type>>&,
    const DimensionedgpuField<Type, gpuvolMesh>&
);

template<class Type>
tmp<gpufvMatrix<Type>> operator-
(
    const tmp<gpufvMatrix<Type>>&,
    const tmp<DimensionedgpuField<Type, gpuvolMesh>>&
);

template<class Type>
tmp<gpufvMatrix<Type>> operator-
(
    const tmp<gpufvMatrix<Type>>&,
    const tmp<GeometricgpuField<Type, fvPatchgpuField, gpuvolMesh>>&
);

template<class Type>
tmp<gpufvMatrix<Type>> operator-
(
    const DimensionedgpuField<Type, gpuvolMesh>&,
    const gpufvMatrix<Type>&
);

template<class Type>
tmp<gpufvMatrix<Type>> operator-
(
    const tmp<DimensionedgpuField<Type, gpuvolMesh>>&,
    const gpufvMatrix<Type>&
);

template<class Type>
tmp<gpufvMatrix<Type>> operator-
(
    const tmp<GeometricgpuField<Type, fvPatchgpuField, gpuvolMesh>>&,
    const gpufvMatrix<Type>&
);

template<class Type>
tmp<gpufvMatrix<Type>> operator-
(
    const DimensionedgpuField<Type, gpuvolMesh>&,
    const tmp<gpufvMatrix<Type>>&
);

template<class Type>
tmp<gpufvMatrix<Type>> operator-
(
    const tmp<DimensionedgpuField<Type, gpuvolMesh>>&,
    const tmp<gpufvMatrix<Type>>&
);

template<class Type>
tmp<gpufvMatrix<Type>> operator-
(
    const tmp<GeometricgpuField<Type, fvPatchgpuField, gpuvolMesh>>&,
    const tmp<gpufvMatrix<Type>>&
);


template<class Type>
tmp<gpufvMatrix<Type>> operator-
(
    const gpufvMatrix<Type>&,
    const dimensioned<Type>&
);

template<class Type>
tmp<gpufvMatrix<Type>> operator-
(
    const tmp<gpufvMatrix<Type>>&,
    const dimensioned<Type>&
);

template<class Type>
tmp<gpufvMatrix<Type>> operator-
(
    const dimensioned<Type>&,
    const gpufvMatrix<Type>&
);

template<class Type>
tmp<gpufvMatrix<Type>> operator-
(
    const dimensioned<Type>&,
    const tmp<gpufvMatrix<Type>>&
);


template<class Type>
tmp<gpufvMatrix<Type>> operator*
(
    const volScalargpuField::Internal&,
    const gpufvMatrix<Type>&
);

template<class Type>
tmp<gpufvMatrix<Type>> operator*
(
    const tmp<volScalargpuField::Internal>&,
    const gpufvMatrix<Type>&
);

template<class Type>
tmp<gpufvMatrix<Type>> operator*
(
    const tmp<volScalargpuField>&,
    const gpufvMatrix<Type>&
);

template<class Type>
tmp<gpufvMatrix<Type>> operator*
(
    const volScalargpuField::Internal&,
    const tmp<gpufvMatrix<Type>>&
);

template<class Type>
tmp<gpufvMatrix<Type>> operator*
(
    const tmp<volScalargpuField::Internal>&,
    const tmp<gpufvMatrix<Type>>&
);

template<class Type>
tmp<gpufvMatrix<Type>> operator*
(
    const tmp<volScalargpuField>&,
    const tmp<gpufvMatrix<Type>>&
);


template<class Type>
tmp<gpufvMatrix<Type>> operator*
(
    const dimensioned<scalar>&,
    const gpufvMatrix<Type>&
);

template<class Type>
tmp<gpufvMatrix<Type>> operator*
(
    const dimensioned<scalar>&,
    const tmp<gpufvMatrix<Type>>&
);


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

} // End namespace Foam

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

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

// Specialisation for scalars
#include "gpufvScalarMatrix.H"

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

#endif

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