From 29f474637b7c1615e9cc32d3144f206e138cc936 Mon Sep 17 00:00:00 2001 From: Timo Koch <timokoch@uio.no> Date: Mon, 24 Jul 2023 19:48:50 +0200 Subject: [PATCH] [bugfix][energy] Fix energy balance by addition potential energy term --- dumux/porousmediumflow/2p1c/localresidual.hh | 2 +- dumux/porousmediumflow/3p3c/localresidual.hh | 2 +- .../3pwateroil/localresidual.hh | 2 +- .../compositional/localresidual.hh | 2 +- .../immiscible/localresidual.hh | 2 +- .../nonequilibrium/thermal/localresidual.hh | 27 +++++- .../nonisothermal/localresidual.hh | 58 +++++++---- .../localresidual_incompressible.hh | 96 ------------------- dumux/porousmediumflow/nonisothermal/model.hh | 9 +- .../richards/localresidual.hh | 4 +- 10 files changed, 78 insertions(+), 126 deletions(-) delete mode 100644 dumux/porousmediumflow/nonisothermal/localresidual_incompressible.hh diff --git a/dumux/porousmediumflow/2p1c/localresidual.hh b/dumux/porousmediumflow/2p1c/localresidual.hh index 61ecea0b3f..fe41386d16 100644 --- a/dumux/porousmediumflow/2p1c/localresidual.hh +++ b/dumux/porousmediumflow/2p1c/localresidual.hh @@ -59,7 +59,7 @@ public: volVars.porosity() * volVars.saturation(phaseIdx) * volVars.density(phaseIdx); - EnergyLocalResidual::fluidPhaseStorage(storage, scv, volVars, phaseIdx); + EnergyLocalResidual::fluidPhaseStorage(storage, problem, scv, volVars, phaseIdx); } // The energy storage in the solid matrix diff --git a/dumux/porousmediumflow/3p3c/localresidual.hh b/dumux/porousmediumflow/3p3c/localresidual.hh index 61c6d75aa8..506c06f03f 100644 --- a/dumux/porousmediumflow/3p3c/localresidual.hh +++ b/dumux/porousmediumflow/3p3c/localresidual.hh @@ -96,7 +96,7 @@ public: } // The energy storage in the fluid phase with index phaseIdx - EnergyLocalResidual::fluidPhaseStorage(storage, scv, volVars, phaseIdx); + EnergyLocalResidual::fluidPhaseStorage(storage, problem, scv, volVars, phaseIdx); } // The energy storage in the solid matrix diff --git a/dumux/porousmediumflow/3pwateroil/localresidual.hh b/dumux/porousmediumflow/3pwateroil/localresidual.hh index 1ad1d9679e..36467645a0 100644 --- a/dumux/porousmediumflow/3pwateroil/localresidual.hh +++ b/dumux/porousmediumflow/3pwateroil/localresidual.hh @@ -101,7 +101,7 @@ public: } //! The energy storage in the fluid phase with index phaseIdx - EnergyLocalResidual::fluidPhaseStorage(storage, scv, volVars, phaseIdx); + EnergyLocalResidual::fluidPhaseStorage(storage, problem, scv, volVars, phaseIdx); } //! The energy storage in the solid matrix diff --git a/dumux/porousmediumflow/compositional/localresidual.hh b/dumux/porousmediumflow/compositional/localresidual.hh index 3fb810b373..307cf7f7da 100644 --- a/dumux/porousmediumflow/compositional/localresidual.hh +++ b/dumux/porousmediumflow/compositional/localresidual.hh @@ -106,7 +106,7 @@ public: * volVars.saturation(phaseIdx); //! The energy storage in the fluid phase with index phaseIdx - EnergyLocalResidual::fluidPhaseStorage(storage, scv, volVars, phaseIdx); + EnergyLocalResidual::fluidPhaseStorage(storage, problem, scv, volVars, phaseIdx); } //! The energy storage in the solid matrix diff --git a/dumux/porousmediumflow/immiscible/localresidual.hh b/dumux/porousmediumflow/immiscible/localresidual.hh index ca05a02c90..6589f5ba8f 100644 --- a/dumux/porousmediumflow/immiscible/localresidual.hh +++ b/dumux/porousmediumflow/immiscible/localresidual.hh @@ -74,7 +74,7 @@ public: * volVars.saturation(phaseIdx); //! The energy storage in the fluid phase with index phaseIdx - EnergyLocalResidual::fluidPhaseStorage(storage, scv, volVars, phaseIdx); + EnergyLocalResidual::fluidPhaseStorage(storage, problem, scv, volVars, phaseIdx); } //! The energy storage in the solid matrix diff --git a/dumux/porousmediumflow/nonequilibrium/thermal/localresidual.hh b/dumux/porousmediumflow/nonequilibrium/thermal/localresidual.hh index 75c54178af..4fda272afa 100644 --- a/dumux/porousmediumflow/nonequilibrium/thermal/localresidual.hh +++ b/dumux/porousmediumflow/nonequilibrium/thermal/localresidual.hh @@ -37,6 +37,7 @@ class EnergyLocalResidualNonEquilibrium<TypeTag, 1/*numEnergyEqFluid*/> { using Scalar = GetPropType<TypeTag, Properties::Scalar>; using NumEqVector = Dumux::NumEqVector<GetPropType<TypeTag, Properties::PrimaryVariables>>; + using Problem = GetPropType<TypeTag, Properties::Problem>; using VolumeVariables = GetPropType<TypeTag, Properties::VolumeVariables>; using FVElementGeometry = typename GetPropType<TypeTag, Properties::GridGeometry>::LocalView; using SubControlVolume = typename FVElementGeometry::SubControlVolume; @@ -59,17 +60,27 @@ class EnergyLocalResidualNonEquilibrium<TypeTag, 1/*numEnergyEqFluid*/> static constexpr auto numComponents = ModelTraits::numFluidComponents(); public: + static void fluidPhaseStorage(NumEqVector& storage, + const SubControlVolume& scv, + const VolumeVariables& volVars, + int phaseIdx) + { + static_assert("Deprecated interface that has been removed!"); + } + //! The energy storage in the fluid phase with index phaseIdx static void fluidPhaseStorage(NumEqVector& storage, + const Problem&, const SubControlVolume& scv, const VolumeVariables& volVars, int phaseIdx) { - //in case we have one energy equation for more than one fluid phase, add up parts on the one energy equation + // in case we have one energy equation for more than one fluid phase, + // add up parts on the one energy equation storage[energyEq0Idx] += volVars.porosity() * volVars.density(phaseIdx) - * volVars.internalEnergy(phaseIdx) - * volVars.saturation(phaseIdx); + * volVars.saturation(phaseIdx) + * (volVars.internalEnergy(phaseIdx) - gravityPotential); } @@ -213,6 +224,7 @@ class EnergyLocalResidualNonEquilibrium<TypeTag, 2/*numEnergyEqFluid*/> { using Scalar = GetPropType<TypeTag, Properties::Scalar>; using NumEqVector = Dumux::NumEqVector<GetPropType<TypeTag, Properties::PrimaryVariables>>; + using Problem = GetPropType<TypeTag, Properties::Problem>; using VolumeVariables = GetPropType<TypeTag, Properties::VolumeVariables>; using FVElementGeometry = typename GetPropType<TypeTag, Properties::GridGeometry>::LocalView; using SubControlVolume = typename FVElementGeometry::SubControlVolume; @@ -242,9 +254,17 @@ class EnergyLocalResidualNonEquilibrium<TypeTag, 2/*numEnergyEqFluid*/> static constexpr bool enableChemicalNonEquilibrium = ModelTraits::enableChemicalNonEquilibrium(); public: + static void fluidPhaseStorage(NumEqVector& storage, + const SubControlVolume& scv, + const VolumeVariables& volVars, + int phaseIdx) + { + static_assert("Deprecated interface that has been removed!"); + } //! The energy storage in the fluid phase with index phaseIdx static void fluidPhaseStorage(NumEqVector& storage, + const Problem&, const SubControlVolume& scv, const VolumeVariables& volVars, int phaseIdx) @@ -253,6 +273,7 @@ public: * volVars.density(phaseIdx) * volVars.internalEnergy(phaseIdx) * volVars.saturation(phaseIdx); + } //! The advective phase energy fluxes diff --git a/dumux/porousmediumflow/nonisothermal/localresidual.hh b/dumux/porousmediumflow/nonisothermal/localresidual.hh index f203eb31f4..0d5708405d 100644 --- a/dumux/porousmediumflow/nonisothermal/localresidual.hh +++ b/dumux/porousmediumflow/nonisothermal/localresidual.hh @@ -8,7 +8,7 @@ * \file * \ingroup NIModel * \brief Element-wise calculation of the local residual for non-isothermal - * fully implicit models. + * fully implicit models. See NIModel for the detailed description. */ #ifndef DUMUX_ENERGY_LOCAL_RESIDUAL_HH @@ -28,28 +28,33 @@ using EnergyLocalResidual = EnergyLocalResidualImplementation<TypeTag, GetPropTy /*! * \ingroup NIModel - * \brief Element-wise calculation of the energy residual for non-isothermal problems. + * \brief Element-wise calculation of the energy residual for isothermal problems. */ template<class TypeTag> class EnergyLocalResidualImplementation<TypeTag, false> { using Scalar = GetPropType<TypeTag, Properties::Scalar>; using NumEqVector = Dumux::NumEqVector<GetPropType<TypeTag, Properties::PrimaryVariables>>; + using Problem = GetPropType<TypeTag, Properties::Problem>; using VolumeVariables = GetPropType<TypeTag, Properties::VolumeVariables>; using FVElementGeometry = typename GetPropType<TypeTag, Properties::GridGeometry>::LocalView; using SubControlVolume = typename FVElementGeometry::SubControlVolume; using FluxVariables = GetPropType<TypeTag, Properties::FluxVariables>; public: + static void fluidPhaseStorage(NumEqVector& storage, + const SubControlVolume& scv, + const VolumeVariables& volVars, + int phaseIdx) + { + static_assert("Deprecated interface that has been removed!"); + } + /*! * \brief The energy storage in the fluid phase with index phaseIdx. - * - * \param storage The mass of the component within the sub-control volume - * \param scv The sub-control volume - * \param volVars The volume variables - * \param phaseIdx The phase index */ static void fluidPhaseStorage(NumEqVector& storage, + const Problem& problem, const SubControlVolume& scv, const VolumeVariables& volVars, int phaseIdx) @@ -102,13 +107,14 @@ public: /*! * \ingroup NIModel - * \brief TODO docme! + * \brief Element-wise calculation of the energy residual for non-isothermal problems. */ template<class TypeTag> class EnergyLocalResidualImplementation<TypeTag, true> { using Scalar = GetPropType<TypeTag, Properties::Scalar>; using NumEqVector = Dumux::NumEqVector<GetPropType<TypeTag, Properties::PrimaryVariables>>; + using Problem = GetPropType<TypeTag, Properties::Problem>; using VolumeVariables = GetPropType<TypeTag, Properties::VolumeVariables>; using FVElementGeometry = typename GetPropType<TypeTag, Properties::GridGeometry>::LocalView; using SubControlVolume = typename FVElementGeometry::SubControlVolume; @@ -125,22 +131,31 @@ class EnergyLocalResidualImplementation<TypeTag, true> public: /*! - * \brief The energy storage in the fluid phase with index phaseIdx - * - * \param storage The mass of the component within the sub-control volume - * \param scv The sub-control volume - * \param volVars The volume variables - * \param phaseIdx The phase index + * \brief The energy storage in the fluid phase with index phaseIdx. */ static void fluidPhaseStorage(NumEqVector& storage, + const Problem& problem, const SubControlVolume& scv, const VolumeVariables& volVars, int phaseIdx) { + // this implementation of the potential energy contribution is only correct + // if gravity vector is constant in space and time + const auto& x = scv.dofPosition(); + const auto gravityPotential = x*problem.spatialParams().gravity(x); + storage[energyEqIdx] += volVars.porosity() * volVars.density(phaseIdx) - * volVars.internalEnergy(phaseIdx) - * volVars.saturation(phaseIdx); + * volVars.saturation(phaseIdx) + * (volVars.internalEnergy(phaseIdx) - gravityPotential); + } + + static void fluidPhaseStorage(NumEqVector& storage, + const SubControlVolume& scv, + const VolumeVariables& volVars, + int phaseIdx) + { + static_assert("Deprecated interface that has been removed!"); } /*! @@ -171,8 +186,15 @@ public: FluxVariables& fluxVars, int phaseIdx) { - auto upwindTerm = [phaseIdx](const auto& volVars) - { return volVars.density(phaseIdx)*volVars.mobility(phaseIdx)*volVars.enthalpy(phaseIdx); }; + // this implementation of the potential energy contribution is only correct + // if gravity vector is constant in space and time + const auto& x = fluxVars.scvFace().ipGlobal(); + const auto gravityPotential = x*fluxVars.problem().spatialParams().gravity(x); + + auto upwindTerm = [=](const auto& volVars){ + return volVars.density(phaseIdx)*volVars.mobility(phaseIdx) + * (volVars.enthalpy(phaseIdx) - gravityPotential); + }; flux[energyEqIdx] += fluxVars.advectiveFlux(phaseIdx, upwindTerm); } diff --git a/dumux/porousmediumflow/nonisothermal/localresidual_incompressible.hh b/dumux/porousmediumflow/nonisothermal/localresidual_incompressible.hh deleted file mode 100644 index 850d4b474c..0000000000 --- a/dumux/porousmediumflow/nonisothermal/localresidual_incompressible.hh +++ /dev/null @@ -1,96 +0,0 @@ -// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- -// vi: set et ts=4 sw=4 sts=4: -// -// SPDX-FileCopyrightInfo: Copyright © DuMux Project contributors, see AUTHORS.md in root folder -// SPDX-License-Identifier: GPL-3.0-or-later -// -/*! - * \file - * \ingroup NIModel - * \brief Element-wise calculation of the local residual for non-isothermal - * fully implicit models. - */ - -#ifndef DUMUX_ENERGY_LOCAL_RESIDUAL_INCOMPRESSIBLE_HH -#define DUMUX_ENERGY_LOCAL_RESIDUAL_INCOMPRESSIBLE_HH - -#include <dumux/common/properties.hh> -#include <dumux/common/numeqvector.hh> -#include "localresidual.hh" - -namespace Dumux { - -// forward declaration -template<class TypeTag, bool enableEneryBalance> -class EnergyLocalResidualIncompressibleImplementation; - -template<class TypeTag> -using EnergyLocalResidualIncompressible = EnergyLocalResidualIncompressibleImplementation<TypeTag, GetPropType<TypeTag, Properties::ModelTraits>::enableEnergyBalance()>; - -/*! - * \ingroup NIModel - * \brief Element-wise calculation of the energy residual for non-isothermal problems. - */ -template<class TypeTag> -class EnergyLocalResidualIncompressibleImplementation<TypeTag, false>: public EnergyLocalResidualImplementation<TypeTag, false> -{}; //energy balance not enabled - -/*! - * \ingroup NIModel - * \brief TODO docme! - */ -template<class TypeTag> -class EnergyLocalResidualIncompressibleImplementation<TypeTag, true>: public EnergyLocalResidualImplementation<TypeTag, true> -{ - using Scalar = GetPropType<TypeTag, Properties::Scalar>; - using NumEqVector = Dumux::NumEqVector<GetPropType<TypeTag, Properties::PrimaryVariables>>; - using VolumeVariables = GetPropType<TypeTag, Properties::VolumeVariables>; - using FVElementGeometry = typename GetPropType<TypeTag, Properties::GridGeometry>::LocalView; - using SubControlVolume = typename FVElementGeometry::SubControlVolume; - using FluxVariables = GetPropType<TypeTag, Properties::FluxVariables>; - using GridView = typename GetPropType<TypeTag, Properties::GridGeometry>::GridView; - using Element = typename GridView::template Codim<0>::Entity; - using ElementVolumeVariables = typename GetPropType<TypeTag, Properties::GridVolumeVariables>::LocalView; - using ModelTraits = GetPropType<TypeTag, Properties::ModelTraits>; - using Indices = typename ModelTraits::Indices; - - static constexpr int numPhases = ModelTraits::numFluidPhases(); - enum { energyEqIdx = Indices::energyEqIdx }; - -public: - /*! - * \brief The advective phase energy fluxes for incompressible flow. - * - * Using specific internal energy $u$ instead of specific enthalpy \f$h\f$ for incompressible flow in convective flux - * to account for otherwise neglected pressure work term (\f$\nabla p \cdot v\f$). - * - * Compressible formulation in EnergyLocalResidual (neglecting pressure work term (\f$\nabla p \cdot v\f$)) - * is - \f{align*}{ - \frac{\partial}{\partial t} (\rho u) &= -\nabla \cdot (\rho v h) + \nabla \cdot (\lambda \nabla T) - \f} - * - * Incompressible energy formulation is - \f{align*}{ - \frac{\partial}{\partial t} (\rho u) = -\nabla \cdot (\rho v u) + \nabla \cdot (\lambda \nabla T) - \f} - * - * \param flux The flux - * \param fluxVars The flux variables. - * \param phaseIdx The phase index - */ - static void heatConvectionFlux(NumEqVector& flux, - FluxVariables& fluxVars, - int phaseIdx) - { - // internal energy used instead of enthalpy for incompressible flow - auto upwindTerm = [phaseIdx](const auto& volVars) - { return volVars.density(phaseIdx)*volVars.mobility(phaseIdx)*volVars.internalEnergy(phaseIdx); }; - - flux[energyEqIdx] += fluxVars.advectiveFlux(phaseIdx, upwindTerm); - } -}; - -} // end namespace Dumux - -#endif diff --git a/dumux/porousmediumflow/nonisothermal/model.hh b/dumux/porousmediumflow/nonisothermal/model.hh index 18eadc2149..828a1eccbc 100644 --- a/dumux/porousmediumflow/nonisothermal/model.hh +++ b/dumux/porousmediumflow/nonisothermal/model.hh @@ -19,13 +19,13 @@ * results in one energy conservation equation for the porous solid * matrix and the fluids, \f{align*}{ - \frac{\partial (\sum_\alpha \phi \varrho_\alpha u_\alpha S_\alpha )}{\partial t} + \phi \frac{\partial \sum_\alpha \varrho_\alpha S_\alpha (u_\alpha - \mathbf{g}\cdot\mathbf{x})}{\partial t} & + \frac{\partial \left((\left( 1 - \phi \right) \varrho_s c_s T\right)}{\partial t} - \sum_\alpha \nabla \cdot \left\{ - \varrho_\alpha h_\alpha + \varrho_\alpha (h_\alpha - \mathbf{g}\cdot\mathbf{x}) \frac{k_{r\alpha}}{\mu_\alpha} \mathbf{K} \left( \nabla p_\alpha - \varrho_\alpha \mathbf{g} \right) \right\} \\ @@ -49,6 +49,11 @@ * * \f$ \mathbf{g} \f$ is the gravitational acceleration vector, * * \f$ q^h \f$ is a source or sink term. * + * The gravitational potential is approximated by \f$\psi \approx \mathbf{g}\cdot\mathbf{x}\f$ + * with \f$ \mathbf{g} = -\nabla\psi\f$ and where \f$\mathbf{x}\f$ is the position in space. + * The approximation is exact in case the gravitation vector is constant in space. + * The implementation via the gravitational potential in the formulation above also assumes + * that the gravitional potential is independent of time. This formulation is incorrect otherwise. */ #ifndef DUMUX_NONISOTHERMAL_MODEL_HH diff --git a/dumux/porousmediumflow/richards/localresidual.hh b/dumux/porousmediumflow/richards/localresidual.hh index f52fd97e1a..dbfd6d5346 100644 --- a/dumux/porousmediumflow/richards/localresidual.hh +++ b/dumux/porousmediumflow/richards/localresidual.hh @@ -108,8 +108,8 @@ public: * volVars.saturation(liquidPhaseIdx); //! The energy storage in the water, air and solid phase - EnergyLocalResidual::fluidPhaseStorage(storage, scv, volVars, liquidPhaseIdx); - EnergyLocalResidual::fluidPhaseStorage(storage, scv, volVars, gasPhaseIdx); + EnergyLocalResidual::fluidPhaseStorage(storage, problem, scv, volVars, liquidPhaseIdx); + EnergyLocalResidual::fluidPhaseStorage(storage, problem, scv, volVars, gasPhaseIdx); EnergyLocalResidual::solidPhaseStorage(storage, scv, volVars); return storage; -- GitLab