// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- // vi: set et ts=4 sw=4 sts=4: /***************************************************************************** * See the file COPYING for full copying permissions. * * * * This program 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 2 of the License, or * * (at your option) any later version. * * * * This program 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 this program. If not, see . * *****************************************************************************/ /*! * \file * \ingroup NavierStokesModel * \copydoc Dumux::NavierStokesFluxVariablesImpl */ #ifndef DUMUX_NAVIERSTOKES_STAGGERED_FLUXVARIABLES_HH #define DUMUX_NAVIERSTOKES_STAGGERED_FLUXVARIABLES_HH #include #include #include namespace Dumux { // forward declaration template class NavierStokesFluxVariablesImpl; /*! * \ingroup NavierStokesModel * \brief The flux variables class for the Navier-Stokes model using the staggered grid discretization. */ template class NavierStokesFluxVariablesImpl : public FluxVariablesBase { using GridView = typename GET_PROP_TYPE(TypeTag, GridView); using Problem = typename GET_PROP_TYPE(TypeTag, Problem); using Element = typename GridView::template Codim<0>::Entity; using FVElementGeometry = typename GET_PROP_TYPE(TypeTag, FVElementGeometry); using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar); using Indices = typename GET_PROP_TYPE(TypeTag, Indices); using ElementVolumeVariables = typename GET_PROP_TYPE(TypeTag, ElementVolumeVariables); using SubControlVolumeFace = typename GET_PROP_TYPE(TypeTag, SubControlVolumeFace); using FluxVariablesCache = typename GET_PROP_TYPE(TypeTag, FluxVariablesCache); using CellCenterPrimaryVariables = typename GET_PROP_TYPE(TypeTag, CellCenterPrimaryVariables); using FacePrimaryVariables = typename GET_PROP_TYPE(TypeTag, FacePrimaryVariables); using IndexType = typename GridView::IndexSet::IndexType; using Stencil = std::vector; using ElementFaceVariables = typename GET_PROP_TYPE(TypeTag, ElementFaceVariables); using FaceVariables = typename GET_PROP_TYPE(TypeTag, FaceVariables); static constexpr bool navierStokes = GET_PROP_VALUE(TypeTag, EnableInertiaTerms); enum { // grid and world dimension dim = GridView::dimension, dimWorld = GridView::dimensionworld, massBalanceIdx = Indices::massBalanceIdx, }; using GlobalPosition = Dune::FieldVector; using DofTypeIndices = typename GET_PROP(TypeTag, DofTypeIndices); typename DofTypeIndices::CellCenterIdx cellCenterIdx; typename DofTypeIndices::FaceIdx faceIdx; public: /*! * \brief Returns the advective flux over a sub control volume face * \param elemVolVars All volume variables for the element * \param elemFaceVars The face variables * \param scvf The sub control volume face * \param upwindTerm The uwind term (i.e. the advectively transported quantity) * \param isOutflow Determines if we are on an outflow boundary */ template static Scalar advectiveFluxForCellCenter(const ElementVolumeVariables& elemVolVars, const ElementFaceVariables& elemFaceVars, const SubControlVolumeFace &scvf, UpwindTerm upwindTerm, bool isOutflow = false) { const Scalar velocity = elemFaceVars[scvf].velocitySelf(); const bool insideIsUpstream = scvf.directionSign() == sign(velocity); static const Scalar upWindWeight = getParamFromGroup(GET_PROP_VALUE(TypeTag, ModelParameterGroup), "Implicit.UpwindWeight"); const auto& insideVolVars = elemVolVars[scvf.insideScvIdx()]; const auto& outsideVolVars = isOutflow ? insideVolVars : elemVolVars[scvf.outsideScvIdx()]; const auto& upstreamVolVars = insideIsUpstream ? insideVolVars : outsideVolVars; const auto& downstreamVolVars = insideIsUpstream ? outsideVolVars : insideVolVars; const Scalar flux = (upWindWeight * upwindTerm(upstreamVolVars) + (1.0 - upWindWeight) * upwindTerm(downstreamVolVars)) * velocity * scvf.area() * scvf.directionSign(); return flux; } /*! * \brief Computes the flux for the cell center residual. */ CellCenterPrimaryVariables computeFluxForCellCenter(const Problem& problem, const Element &element, const FVElementGeometry& fvGeometry, const ElementVolumeVariables& elemVolVars, const ElementFaceVariables& elemFaceVars, const SubControlVolumeFace &scvf, const FluxVariablesCache& fluxVarsCache) { auto upwindTerm = [](const auto& volVars) { return volVars.density(); }; const Scalar flux = advectiveFluxForCellCenter(elemVolVars, elemFaceVars, scvf, upwindTerm, false); CellCenterPrimaryVariables result(0.0); result[massBalanceIdx] = flux; return result; } /*! * \brief Returns the normal part of the momentum flux */ FacePrimaryVariables computeNormalMomentumFlux(const Problem& problem, const Element& element, const SubControlVolumeFace& scvf, const FVElementGeometry& fvGeometry, const ElementVolumeVariables& elemVolVars, const ElementFaceVariables& elementFaceVars) { const auto insideScvIdx = scvf.insideScvIdx(); const auto& insideVolVars = elemVolVars[insideScvIdx]; const Scalar velocitySelf = elementFaceVars[scvf].velocitySelf() ; const Scalar velocityOpposite = elementFaceVars[scvf].velocityOpposite(); FacePrimaryVariables normalFlux(0.0); if(navierStokes) { // advective part const Scalar vAvg = (velocitySelf + velocityOpposite) * 0.5; const Scalar vUp = (scvf.directionSign() == sign(vAvg)) ? velocityOpposite : velocitySelf; normalFlux += vAvg * vUp * insideVolVars.density(); } // diffusive part const Scalar deltaV = scvf.normalInPosCoordDir() ? (velocitySelf - velocityOpposite) : (velocityOpposite - velocitySelf); const Scalar deltaX = scvf.selfToOppositeDistance(); normalFlux -= insideVolVars.viscosity() * 2.0 * deltaV/deltaX; // account for the orientation of the face const Scalar sgn = -1.0 * scvf.directionSign(); Scalar result = normalFlux * sgn * scvf.area(); // treat outflow conditions if(navierStokes && scvf.boundary()) { const auto& upVolVars = (scvf.directionSign() == sign(velocitySelf)) ? elemVolVars[insideScvIdx] : elemVolVars[scvf.outsideScvIdx()] ; result += velocitySelf * velocitySelf * upVolVars.density() * scvf.directionSign() * scvf.area() ; } return result; } /*! * \brief Returns the tangential part of the momentum flux */ FacePrimaryVariables computeTangetialMomentumFlux(const Problem& problem, const Element& element, const SubControlVolumeFace& scvf, const FVElementGeometry& fvGeometry, const ElementVolumeVariables& elemVolVars, const ElementFaceVariables& elementFaceVars) { FacePrimaryVariables tangentialFlux(0.0); auto& faceVars = elementFaceVars[scvf]; const int numSubFaces = scvf.pairData().size(); // account for all sub-faces for(int localSubFaceIdx = 0; localSubFaceIdx < numSubFaces; ++localSubFaceIdx) { const auto eIdx = scvf.insideScvIdx(); const auto& normalFace = fvGeometry.scvf(eIdx, scvf.pairData()[localSubFaceIdx].localNormalFaceIdx); // Check if we have a symmetry boundary condition. If yes, the tangental part of the momentum flux can be neglected. if(scvf.pairData()[localSubFaceIdx].outerParallelFaceDofIdx < 0) { // lambda to conveniently create a ghost face which is outside the domain, parallel to the scvf of interest auto makeGhostFace = [eIdx] (const GlobalPosition& pos) { return SubControlVolumeFace(pos, std::vector{eIdx,eIdx}); }; // use the ghost face to check if there is a symmetry boundary condition and skip any further steps if yes const auto bcTypes = problem.boundaryTypes(element, makeGhostFace(scvf.pairData()[localSubFaceIdx].virtualOuterParallelFaceDofPos)); if(bcTypes.isSymmetry()) continue; } // if there is no symmetry boundary condition, proceed to calculate the tangential momentum flux if(navierStokes) tangentialFlux += computeAdvectivePartOfTangentialMomentumFlux_(problem, element, scvf, normalFace, elemVolVars, faceVars, localSubFaceIdx); tangentialFlux += computeDiffusivePartOfTangentialMomentumFlux_(problem, element, scvf, normalFace, elemVolVars, faceVars, localSubFaceIdx); } return tangentialFlux; } private: FacePrimaryVariables computeAdvectivePartOfTangentialMomentumFlux_(const Problem& problem, const Element& element, const SubControlVolumeFace& scvf, const SubControlVolumeFace& normalFace, const ElementVolumeVariables& elemVolVars, const FaceVariables& faceVars, const int localSubFaceIdx) { const Scalar transportingVelocity = faceVars.velocityNormalInside(localSubFaceIdx); const auto insideScvIdx = normalFace.insideScvIdx(); const auto outsideScvIdx = normalFace.outsideScvIdx(); const bool innerElementIsUpstream = ( normalFace.directionSign() == sign(transportingVelocity) ); const auto& upVolVars = innerElementIsUpstream ? elemVolVars[insideScvIdx] : elemVolVars[outsideScvIdx]; const Scalar transportedVelocity = innerElementIsUpstream ? faceVars.velocitySelf() : faceVars.velocityParallel(localSubFaceIdx); const Scalar momentum = upVolVars.density() * transportedVelocity; return transportingVelocity * momentum * normalFace.directionSign() * normalFace.area() * 0.5; } FacePrimaryVariables computeDiffusivePartOfTangentialMomentumFlux_(const Problem& problem, const Element& element, const SubControlVolumeFace& scvf, const SubControlVolumeFace& normalFace, const ElementVolumeVariables& elemVolVars, const FaceVariables& faceVars, const int localSubFaceIdx) { FacePrimaryVariables tangentialDiffusiveFlux(0.0); const auto insideScvIdx = normalFace.insideScvIdx(); const auto outsideScvIdx = normalFace.outsideScvIdx(); const auto& insideVolVars = elemVolVars[insideScvIdx]; const auto& outsideVolVars = elemVolVars[outsideScvIdx]; // the averaged viscosity at the face normal to our face of interest (where we assemble the face residual) const Scalar muAvg = (insideVolVars.viscosity() + outsideVolVars.viscosity()) * 0.5; // the normal derivative const Scalar innerNormalVelocity = faceVars.velocityNormalInside(localSubFaceIdx); const Scalar outerNormalVelocity = faceVars.velocityNormalOutside(localSubFaceIdx); const Scalar normalDeltaV = scvf.normalInPosCoordDir() ? (outerNormalVelocity - innerNormalVelocity) : (innerNormalVelocity - outerNormalVelocity); const Scalar normalDerivative = normalDeltaV / scvf.pairData(localSubFaceIdx).normalDistance; tangentialDiffusiveFlux -= muAvg * normalDerivative; // the parallel derivative const Scalar innerParallelVelocity = faceVars.velocitySelf(); const Scalar outerParallelVelocity = faceVars.velocityParallel(localSubFaceIdx); const Scalar parallelDeltaV = normalFace.normalInPosCoordDir() ? (outerParallelVelocity - innerParallelVelocity) : (innerParallelVelocity - outerParallelVelocity); const Scalar parallelDerivative = parallelDeltaV / scvf.pairData(localSubFaceIdx).parallelDistance; tangentialDiffusiveFlux -= muAvg * parallelDerivative; return tangentialDiffusiveFlux * normalFace.directionSign() * normalFace.area() * 0.5; } }; } // end namespace #endif