diff --git a/dumux/common/properties.hh b/dumux/common/properties.hh index 3dd939e20fa50aa13515efb2e0a7546a73c87068..22673a693bc40003054f8640d51ea25e3cb03099 100644 --- a/dumux/common/properties.hh +++ b/dumux/common/properties.hh @@ -233,9 +233,9 @@ struct BoundaryValues { using type = UndefinedProperty; }; template<class TypeTag, class MyTypeTag> struct StaggeredFaceSolution { using type = UndefinedProperty; }; //!< A vector containing the solution for a face (similar to ElementSolution) template<class TypeTag, class MyTypeTag> -struct EnableGridFaceVariablesCache { using type = UndefinedProperty; }; //!< Switch on/off caching of face variables +struct EnableGridFaceVariablesCache { using type = UndefinedProperty; }; //!< Switch on/off caching of face variables template<class TypeTag, class MyTypeTag> -struct UpwindGeometryOrder { using type = UndefinedProperty; }; //!< Specifies the order of the upwinding scheme (1 == basic upwinding, 2 == higher order) +struct UpwindSchemeOrder { using type = UndefinedProperty; }; //!< Specifies the order of the upwinding scheme (1 == basic upwinding, 2 == higher order) ///////////////////////////////////////////////////////////// // Properties used by the mpnc model diff --git a/dumux/discretization/staggered/freeflow/connectivitymap.hh b/dumux/discretization/staggered/freeflow/connectivitymap.hh index 7622726ec6d57549af77c80c9f673438f3020509..3fe85d132fd8ba6577c31d61d2acad16c99285fd 100644 --- a/dumux/discretization/staggered/freeflow/connectivitymap.hh +++ b/dumux/discretization/staggered/freeflow/connectivitymap.hh @@ -54,7 +54,8 @@ class StaggeredFreeFlowConnectivityMap using Stencil = std::vector<GridIndexType>; - static constexpr bool useHigherOrder = false; //TODO + static constexpr int upwindSchemeOrder = FVGridGeometry::upwindSchemeOrder; + static constexpr bool useHigherOrder = upwindSchemeOrder > 1; public: @@ -94,13 +95,6 @@ public: } } - //! \brief Set the order needed by the scheme - void setStencilOrder(const int stencilOrder) - { - stencilOrder_ = stencilOrder; - } - - //! Returns the stencil of a cell center dof w.r.t. other cell center dofs const std::vector<GridIndexType>& operator() (CellCenterIdxType, CellCenterIdxType, const GridIndexType globalI) const { @@ -190,7 +184,7 @@ private: { stencil.push_back(scvf.axisData().selfDof); stencil.push_back(scvf.axisData().oppositeDof); - addHigherOrderInAxisDofs(std::integral_constant<bool, useHigherOrder>{}, scvf, stencil); + addHigherOrderInAxisDofs_(scvf, stencil, std::integral_constant<bool, useHigherOrder>{}); } for(const auto& data : scvf.pairData()) @@ -201,31 +195,25 @@ private: stencil.push_back(data.normalPair.second); // add parallel dofs - for (int i = 0; i < stencilOrder_ /*data.parallelDofs.size()*/; i++) + for (int i = 0; i < upwindSchemeOrder; i++) { if(!(data.parallelDofs[i] < 0)) - { stencil.push_back(data.parallelDofs[i]); - } } } } - void addHigherOrderInAxisDofs(std::false_type, const SubControlVolumeFace& scvf, Stencil& stencil) {} + void addHigherOrderInAxisDofs_(const SubControlVolumeFace& scvf, Stencil& stencil, std::false_type) {} - void addHigherOrderInAxisDofs(std::true_type, const SubControlVolumeFace& scvf, Stencil& stencil) + void addHigherOrderInAxisDofs_(const SubControlVolumeFace& scvf, Stencil& stencil, std::true_type) { - for (int i = 0; i < stencilOrder_ - 1; i++) + for (int i = 0; i < upwindSchemeOrder - 1; i++) { if (scvf.hasBackwardNeighbor(i)) - { stencil.push_back(scvf.axisData().inAxisBackwardDofs[i]); - } if (scvf.hasForwardNeighbor(i)) - { stencil.push_back(scvf.axisData().inAxisForwardDofs[i]); - } } } @@ -234,7 +222,6 @@ private: CellCenterToFaceMap cellCenterToFaceMap_; FaceToCellCenterMap faceToCellCenterMap_; FaceToFaceMap faceToFaceMap_; - int stencilOrder_; }; } // end namespace Dumux diff --git a/dumux/discretization/staggered/freeflow/facevariables.hh b/dumux/discretization/staggered/freeflow/facevariables.hh index 2f1943d9b8681707302d7256eaac675638582f7d..159018d08c95c92185f58858e8d6dce942458346 100644 --- a/dumux/discretization/staggered/freeflow/facevariables.hh +++ b/dumux/discretization/staggered/freeflow/facevariables.hh @@ -31,40 +31,37 @@ namespace Dumux { namespace Detail { -template<class Scalar, int geometryOrder> -struct InAxisVelocities; - -template<class Scalar> -struct InAxisVelocities<Scalar, 1> +template<class Scalar, int upwindSchemeOrder> +struct InAxisVelocities { Scalar self = 0.0; Scalar opposite = 0.0; + std::array<Scalar, upwindSchemeOrder-1> forward{}; + std::array<Scalar, upwindSchemeOrder-1> backward{}; }; -template<class Scalar, int geometryOrder> -struct InAxisVelocities +template<class Scalar> +struct InAxisVelocities<Scalar, 1> { Scalar self = 0.0; Scalar opposite = 0.0; - std::array<Scalar, geometryOrder-1> forward{}; - std::array<Scalar, geometryOrder-1> backward{}; }; -} +} // end namespace Detail /*! * \ingroup StaggeredDiscretization * \brief The face variables class for free flow staggered grid models. * Contains all relevant velocities for the assembly of the momentum balance. */ -template<class FacePrimaryVariables, int dim, int geometryOrder> // TODO doc +template<class FacePrimaryVariables, int dim, int upwindSchemeOrder> // TODO doc class StaggeredFaceVariables { static constexpr int numPairs = (dim == 2) ? 2 : 4; - static constexpr bool useHigherOrder = geometryOrder > 1; + static constexpr bool useHigherOrder = upwindSchemeOrder > 1; using Scalar = typename FacePrimaryVariables::block_type; - using InAxisVelocities = Detail::InAxisVelocities<Scalar, geometryOrder>; + using InAxisVelocities = Detail::InAxisVelocities<Scalar, upwindSchemeOrder>; public: @@ -99,7 +96,7 @@ public: inAxisVelocities_.self = faceSol[scvf.dofIndex()]; inAxisVelocities_.opposite = faceSol[scvf.dofIndexOpposingFace()]; - addHigherOrderInAxisVelocities(std::integral_constant<bool, useHigherOrder>{}, faceSol, scvf); + addHigherOrderInAxisVelocities_(faceSol, scvf, std::integral_constant<bool, useHigherOrder>{}); // handle all sub faces for (int i = 0; i < velocityParallel_.size(); ++i) @@ -116,7 +113,7 @@ public: velocityNormalOutside_[i] = faceSol[subFaceData.normalPair.second]; // treat the velocities parallel to the self face - for (int j = 0; j < geometryOrder; j++) + for (int j = 0; j < upwindSchemeOrder; j++) { if (scvf.hasParallelNeighbor(i,j)) velocityParallel_[i][j] = faceSol[subFaceData.parallelDofs[j]]; @@ -196,10 +193,10 @@ public: private: template<class SolVector, class SubControlVolumeFace> - void addHigherOrderInAxisVelocities(std::false_type, const SolVector& faceSol, const SubControlVolumeFace& scvf) {} + void addHigherOrderInAxisVelocities_(const SolVector& faceSol, const SubControlVolumeFace& scvf, std::false_type) {} template<class SolVector, class SubControlVolumeFace> - void addHigherOrderInAxisVelocities(std::true_type, const SolVector& faceSol, const SubControlVolumeFace& scvf) + void addHigherOrderInAxisVelocities_(const SolVector& faceSol, const SubControlVolumeFace& scvf, std::true_type) { // treat the velocity forward of the self face i.e. the face that is @@ -220,7 +217,7 @@ private: } InAxisVelocities inAxisVelocities_; - std::array<std::array<Scalar, geometryOrder>, numPairs> velocityParallel_; + std::array<std::array<Scalar, upwindSchemeOrder>, numPairs> velocityParallel_; std::array<Scalar, numPairs> velocityNormalInside_; std::array<Scalar, numPairs> velocityNormalOutside_; diff --git a/dumux/discretization/staggered/freeflow/fvgridgeometrytraits.hh b/dumux/discretization/staggered/freeflow/fvgridgeometrytraits.hh index c882c55a39329be5f23bc15cebc8817e44351c64..d07a0780e01fdc866ebf2e950ddd2ad4688a0b71 100644 --- a/dumux/discretization/staggered/freeflow/fvgridgeometrytraits.hh +++ b/dumux/discretization/staggered/freeflow/fvgridgeometrytraits.hh @@ -39,14 +39,15 @@ namespace Dumux { * \ingroup StaggeredDiscretization * \brief Default traits for the finite volume grid geometry. */ -template<class GridView, int geometryOrder, class MapperTraits = DefaultMapperTraits<GridView>> +template<class GridView, int upwindSchemeOrder, class MapperTraits = DefaultMapperTraits<GridView>> struct StaggeredFreeFlowDefaultFVGridGeometryTraits : public MapperTraits { using SubControlVolume = CCSubControlVolume<GridView>; - using SubControlVolumeFace = FreeFlowStaggeredSubControlVolumeFace<GridView, geometryOrder>; + using SubControlVolumeFace = FreeFlowStaggeredSubControlVolumeFace<GridView, upwindSchemeOrder>; using IntersectionMapper = ConformingGridIntersectionMapper<GridView>; - using GeometryHelper = FreeFlowStaggeredGeometryHelper<GridView, geometryOrder>; + using GeometryHelper = FreeFlowStaggeredGeometryHelper<GridView, upwindSchemeOrder>; + static constexpr int UpwindSchemeOrder = upwindSchemeOrder; struct DofTypeIndices { diff --git a/dumux/discretization/staggered/freeflow/properties.hh b/dumux/discretization/staggered/freeflow/properties.hh index 3c568c7d291cc30181e05c91cc85088ab0633eed..1588ca21e4783c0eeeee8c980c99fe339ccce426 100644 --- a/dumux/discretization/staggered/freeflow/properties.hh +++ b/dumux/discretization/staggered/freeflow/properties.hh @@ -81,10 +81,10 @@ template<class TypeTag> struct FVGridGeometry<TypeTag, TTag::StaggeredFreeFlowModel> { private: - static constexpr auto upwindGeometryOrder = getPropValue<TypeTag, Properties::UpwindGeometryOrder>(); + static constexpr auto upwindSchemeOrder = getPropValue<TypeTag, Properties::UpwindSchemeOrder>(); static constexpr bool enableCache = getPropValue<TypeTag, Properties::EnableFVGridGeometryCache>(); using GridView = GetPropType<TypeTag, Properties::GridView>; - using Traits = StaggeredFreeFlowDefaultFVGridGeometryTraits<GridView, upwindGeometryOrder>; + using Traits = StaggeredFreeFlowDefaultFVGridGeometryTraits<GridView, upwindSchemeOrder>; public: using type = StaggeredFVGridGeometry<GridView, enableCache, Traits>; }; @@ -96,9 +96,9 @@ struct FaceVariables<TypeTag, TTag::StaggeredFreeFlowModel> private: using FacePrimaryVariables = GetPropType<TypeTag, Properties::FacePrimaryVariables>; using GridView = GetPropType<TypeTag, Properties::GridView>; - static constexpr auto upwindGeometryOrder = getPropValue<TypeTag, Properties::UpwindGeometryOrder>(); + static constexpr auto upwindSchemeOrder = getPropValue<TypeTag, Properties::UpwindSchemeOrder>(); public: - using type = StaggeredFaceVariables<FacePrimaryVariables, GridView::dimension, upwindGeometryOrder>; + using type = StaggeredFaceVariables<FacePrimaryVariables, GridView::dimension, upwindSchemeOrder>; }; //! Set the default global volume variables cache vector class @@ -134,7 +134,7 @@ struct VelocityOutput<TypeTag, TTag::StaggeredFreeFlowModel> * \brief Set the order of the upwinding scheme to 1 by default. */ template<class TypeTag> -struct UpwindGeometryOrder<TypeTag, TTag::StaggeredFreeFlowModel> { static constexpr int value = 1; }; +struct UpwindSchemeOrder<TypeTag, TTag::StaggeredFreeFlowModel> { static constexpr int value = 1; }; } // namespace Properties } // namespace Dumux diff --git a/dumux/discretization/staggered/freeflow/staggeredgeometryhelper.hh b/dumux/discretization/staggered/freeflow/staggeredgeometryhelper.hh index 0872330b0ed598713c0941f2a1e42da64242b8e1..09c568e7a904c736034d3f6442fa90d9d5f3a3a0 100644 --- a/dumux/discretization/staggered/freeflow/staggeredgeometryhelper.hh +++ b/dumux/discretization/staggered/freeflow/staggeredgeometryhelper.hh @@ -39,11 +39,11 @@ namespace Dumux * \ingroup StaggeredDiscretization * \brief Parallel Data stored per sub face */ -template<class Scalar, class GlobalPosition, int geometryOrder> +template<class Scalar, class GlobalPosition, int upwindSchemeOrder> struct PairData { - std::array<int, geometryOrder> parallelDofs; - std::array<Scalar, geometryOrder+1/*geometryOrder*/> parallelDistances; // TODO + std::array<int, upwindSchemeOrder> parallelDofs; + std::array<Scalar, upwindSchemeOrder+1> parallelDistances; // TODO store only two distances, not three. std::pair<signed int, signed int> normalPair; int localNormalFaceIdx; Scalar normalDistance; @@ -55,13 +55,13 @@ struct PairData * \ingroup StaggeredDiscretization * \brief In Axis Data stored per sub face */ -template<class Scalar, int geometryOrder> +template<class Scalar, int upwindSchemeOrder> struct AxisData; /*! * \ingroup StaggeredDiscretization * \brief In Axis Data stored per sub face */ -template<class Scalar, int geometryOrder> +template<class Scalar, int upwindSchemeOrder> struct AxisData { AxisData() @@ -74,11 +74,11 @@ struct AxisData int selfDof; int oppositeDof; - std::array<int, geometryOrder-1> inAxisForwardDofs; - std::array<int, geometryOrder-1> inAxisBackwardDofs; + std::array<int, upwindSchemeOrder-1> inAxisForwardDofs; + std::array<int, upwindSchemeOrder-1> inAxisBackwardDofs; Scalar selfToOppositeDistance; - std::array<Scalar, geometryOrder-1> inAxisForwardDistances; - std::array<Scalar, geometryOrder-1> inAxisBackwardDistances; + std::array<Scalar, upwindSchemeOrder-1> inAxisForwardDistances; + std::array<Scalar, upwindSchemeOrder-1> inAxisBackwardDistances; }; /*! @@ -93,7 +93,7 @@ struct AxisData<Scalar, 1> Scalar selfToOppositeDistance; }; -// template<class Scalar, int geometryOrder> +// template<class Scalar, int upwindSchemeOrder> // using AxisData /*! @@ -113,7 +113,7 @@ inline static unsigned int directionIndex(Vector&& vector) * \brief Helper class constructing the dual grid finite volume geometries * for the free flow staggered discretization method. */ -template<class GridView, int geometryOrder> +template<class GridView, int upwindSchemeOrder> class FreeFlowStaggeredGeometryHelper { using Scalar = typename GridView::ctype; @@ -137,7 +137,7 @@ class FreeFlowStaggeredGeometryHelper static constexpr int numfacets = dimWorld * 2; static constexpr int numPairs = 2 * (dimWorld - 1); - static constexpr bool useHigherOrder = geometryOrder > 1; + static constexpr bool useHigherOrder = upwindSchemeOrder > 1; public: @@ -150,9 +150,8 @@ public: void updateLocalFace(const IntersectionMapper& intersectionMapper_, const Intersection& intersection) { intersection_ = intersection; - innerNormalFacePos_.clear(); - fillPairData_(std::integral_constant<bool, true>{}); //TODO - fillAxisData_(std::integral_constant<bool, useHigherOrder>{}); + fillAxisData_(); + fillPairData_(); } /*! @@ -197,8 +196,11 @@ public: private: - - void fillAxisData_(std::false_type) + /*! + * \brief Fills all entries of the in axis data + * Calls a function to extend the axis data for higher order upwind methods if required. + */ + void fillAxisData_() { const auto inIdx = intersection_.indexInInside(); const auto oppIdx = localOppositeIdx_(inIdx); @@ -213,29 +215,27 @@ private: const auto self = getFacet_(inIdx, element_); const auto opposite = getFacet_(oppIdx, element_); axisData_.selfToOppositeDistance = (self.geometry().center() - opposite.geometry().center()).two_norm(); + + fillOuterAxisData_(std::integral_constant<bool, useHigherOrder>{}); } + /*! - * \brief Fills all entries of the in axis data + * \brief Fills the axis data with the outer Dofs and Distances. + * For first order upwind methods no outer information is required. */ - void fillAxisData_(std::true_type) + void fillOuterAxisData_(std::false_type) {} + + /*! + * \brief Fills the axis data with the outer dofs and distances. + * This function builds and extended stencil and is therefore only called when higher order upwinding methods are prescribed. + */ + void fillOuterAxisData_(std::true_type) { const auto numForwardBackwardAxisDofs = axisData_.inAxisForwardDofs.size(); - const auto inIdx = intersection_.indexInInside(); - const auto oppIdx = localOppositeIdx_(inIdx); - - // Set the self Dof - axisData_.selfDof = gridView_.indexSet().subIndex(intersection_.inside(), inIdx, codimIntersection); - - // Set the opposite Dof - axisData_.oppositeDof = gridView_.indexSet().subIndex(intersection_.inside(), oppIdx, codimIntersection); - - // Set the Self to Opposite Distance - const auto self = getFacet_(inIdx, element_); - const auto opposite = getFacet_(oppIdx, element_); - axisData_.selfToOppositeDistance = (self.geometry().center() - opposite.geometry().center()).two_norm(); // Set the Forward Dofs std::stack<Element> inAxisForwardElementStack; + const auto inIdx = intersection_.indexInInside(); auto selfFace = getFacet_(inIdx, element_); if(intersection_.neighbor()) { @@ -266,26 +266,28 @@ private: while(!inAxisForwardElementStack.empty()) { int forwardIdx = inAxisForwardElementStack.size()-1; - this->axisData_.inAxisForwardDofs[forwardIdx] = gridView_.indexSet().subIndex(inAxisForwardElementStack.top(), inIdx, codimIntersection); + axisData_.inAxisForwardDofs[forwardIdx] = gridView_.indexSet().subIndex(inAxisForwardElementStack.top(), inIdx, codimIntersection); selfFace = getFacet_(inIdx, inAxisForwardElementStack.top()); forwardFaceCoordinates[forwardIdx] = selfFace.geometry().center(); inAxisForwardElementStack.pop(); } + const auto self = getFacet_(inIdx, element_); for(int i = 0; i< forwardFaceCoordinates.size(); i++) { if(i == 0) { - this->axisData_.inAxisForwardDistances[i] = (forwardFaceCoordinates[i] - self.geometry().center()).two_norm(); + axisData_.inAxisForwardDistances[i] = (forwardFaceCoordinates[i] - self.geometry().center()).two_norm(); } else { - this->axisData_.inAxisForwardDistances[i] = (forwardFaceCoordinates[i]- forwardFaceCoordinates[i-1]).two_norm(); + axisData_.inAxisForwardDistances[i] = (forwardFaceCoordinates[i]- forwardFaceCoordinates[i-1]).two_norm(); } } // Set the Backward Dofs std::stack<Element> inAxisBackwardElementStack; + const auto oppIdx = localOppositeIdx_(inIdx); auto oppFace = getFacet_(oppIdx, element_); for(const auto& intersection : intersections(gridView_, element_)) { @@ -329,6 +331,7 @@ private: inAxisBackwardElementStack.pop(); } + const auto opposite = getFacet_(oppIdx, element_); for(int i = 0; i< backwardFaceCoordinates.size(); i++) { if(i == 0) @@ -342,33 +345,19 @@ private: } } - void fillPairData_(std::false_type) - { - - // TODO - - } - /*! - * \brief Fills all entries of the pair data + * \brief Fills the pair data with the normal dofs and distances + * and calls a further function to collect the parallel dofs and distances */ - void fillPairData_(std::true_type) + void fillPairData_() { // initialize values that could remain unitialized if the intersection lies on a boundary for(auto& data : pairData_) { - // parallel Dofs - data.parallelDofs.fill(-1); - - // parallel Distances - data.parallelDistances.fill(0.0); - // outer normals data.normalPair.second = -1; } - unsigned int numParallelFaces = pairData_[0].parallelDistances.size(); - // set basic global positions const auto& elementCenter = this->element_.geometry().center(); const auto& FacetCenter = intersection_.geometry().center(); @@ -382,10 +371,6 @@ private: const int innerElementIntersectionIdx = innerElementIntersection.indexInInside(); setNormalPairFirstInfo_(innerElementIntersectionIdx, element_, numPairInnerNormalIdx); numPairInnerNormalIdx++; - - innerNormalFacePos_.reserve(numPairs); - const auto& innerNormalFacet = getFacet_(innerElementIntersection.indexInInside(), element_); - innerNormalFacePos_.emplace_back(innerNormalFacet.geometry().center()); } } @@ -419,6 +404,76 @@ private: } } + fillParallelPairData_(std::integral_constant<bool, useHigherOrder>{}); + } + + /*! + * \brief Fills the pair data with the parallel dofs and distances + * This function is only called when the simple first order upwind methods are used. + */ + void fillParallelPairData_(std::false_type) + { + // initialize values that could remain unitialized if the intersection lies on a boundary + for(auto& data : pairData_) + { + // parallel Dofs and Distances + data.parallelDofs.fill(-1); + data.parallelDistances.fill(0.0); + } + + // set basic global positions and stencil size definitions + const auto& elementCenter = element_.geometry().center(); + + // get the parallel Dofs + const int parallelLocalIdx = intersection_.indexInInside(); + int numPairParallelIdx = 0; + for(const auto& intersection : intersections(gridView_, element_)) + { + if( facetIsNormal_(intersection.indexInInside(), parallelLocalIdx) ) + { + // Store the parallel dimension of self cell in the direction of the axis + auto parallelAxisIdx = directionIndex(intersection); + pairData_[numPairParallelIdx].parallelDistances[0] = setParallelPairDistances_(element_, parallelAxisIdx); + + if( intersection.neighbor() ) + { + // If the normal intersection has a neighboring cell, go in and store the parallel information. + auto outerElement = intersection.outside(); + pairData_[numPairParallelIdx].parallelDofs[0] = gridView_.indexSet().subIndex(outerElement, parallelLocalIdx, codimIntersection); + pairData_[numPairParallelIdx].parallelDistances[1] = setParallelPairDistances_(outerElement, parallelAxisIdx); + } + else // No parallel neighbor available + { + // If the intersection has no neighbor we have to deal with the virtual outer parallel dof + const auto& boundaryFacetCenter = intersection.geometry().center(); + const auto distance = boundaryFacetCenter - elementCenter; + const auto virtualFirstParallelFaceDofPos = this->intersection_.geometry().center() + distance; + + pairData_[numPairParallelIdx].virtualFirstParallelFaceDofPos = std::move(virtualFirstParallelFaceDofPos); + } + numPairParallelIdx++; + } + } + } + + /*! + * \brief Fills the pair data with the parallel dofs and distances. + * This function builds and extended stencil and is therefore only called when higher order upwinding methods are prescribed. + */ + void fillParallelPairData_(std::true_type) + { + // initialize values that could remain unitialized if the intersection lies on a boundary + for(auto& data : pairData_) + { + // parallel Dofs and Distances + data.parallelDofs.fill(-1); + data.parallelDistances.fill(0.0); + } + + // set basic global positions and stencil size definitions + unsigned int numParallelFaces = pairData_[0].parallelDistances.size(); + const auto& elementCenter = this->element_.geometry().center(); + // get the parallel Dofs const int parallelLocalIdx = intersection_.indexInInside(); int numPairParallelIdx = 0; @@ -468,7 +523,6 @@ private: else { // If the intersection has no neighbor we have to deal with the virtual outer parallel dof - const auto& elementCenter = this->element_.geometry().center(); const auto& boundaryFacetCenter = intersection.geometry().center(); const auto distance = boundaryFacetCenter - elementCenter; @@ -577,9 +631,8 @@ private: const Element element_; //!< The respective element const typename Element::Geometry elementGeometry_; //!< Reference to the element geometry const GridView gridView_; //!< The grid view - AxisData<Scalar, geometryOrder> axisData_; //!< Data related to forward and backward faces - std::array<PairData<Scalar, GlobalPosition, geometryOrder>, numPairs> pairData_; //!< Collection of pair information related to normal and parallel faces - std::vector<GlobalPosition> innerNormalFacePos_; + AxisData<Scalar, upwindSchemeOrder> axisData_; //!< Data related to forward and backward faces + std::array<PairData<Scalar, GlobalPosition, upwindSchemeOrder>, numPairs> pairData_; //!< Collection of pair information related to normal and parallel faces }; } // end namespace Dumux diff --git a/dumux/discretization/staggered/freeflow/subcontrolvolumeface.hh b/dumux/discretization/staggered/freeflow/subcontrolvolumeface.hh index 8dcc9b93275a7cf3600785e17b11d22fb954e784..a8123938996a7c721e396414e7536e7f758333dc 100644 --- a/dumux/discretization/staggered/freeflow/subcontrolvolumeface.hh +++ b/dumux/discretization/staggered/freeflow/subcontrolvolumeface.hh @@ -42,12 +42,12 @@ namespace Dumux { * of a sub control volume we compute fluxes on. This is a specialization for free flow models. */ template<class GV, - int geometryOrder, + int upwindSchemeOrder, class T = StaggeredDefaultScvfGeometryTraits<GV> > class FreeFlowStaggeredSubControlVolumeFace -: public SubControlVolumeFaceBase<FreeFlowStaggeredSubControlVolumeFace<GV, geometryOrder, T>, T> +: public SubControlVolumeFaceBase<FreeFlowStaggeredSubControlVolumeFace<GV, upwindSchemeOrder, T>, T> { - using ThisType = FreeFlowStaggeredSubControlVolumeFace<GV, geometryOrder, T>; + using ThisType = FreeFlowStaggeredSubControlVolumeFace<GV, upwindSchemeOrder, T>; using ParentType = SubControlVolumeFaceBase<ThisType, T>; using Geometry = typename T::Geometry; using GridIndexType = typename IndexTraits<GV>::GridIndex; @@ -59,7 +59,7 @@ class FreeFlowStaggeredSubControlVolumeFace static constexpr int numPairs = 2 * (dimworld - 1); - static constexpr bool useHigherOrder = geometryOrder > 1; + static constexpr bool useHigherOrder = upwindSchemeOrder > 1; public: using GlobalPosition = typename T::GlobalPosition; @@ -207,7 +207,7 @@ public: } //! Returns the data for one sub face - const PairData<Scalar, GlobalPosition, geometryOrder>& pairData(const int idx) const + const PairData<Scalar, GlobalPosition, upwindSchemeOrder>& pairData(const int idx) const { return pairData_[idx]; } @@ -328,8 +328,8 @@ private: int dofIdx_; Scalar selfToOppositeDistance_; - AxisData<Scalar, geometryOrder> axisData_; - std::array<PairData<Scalar, GlobalPosition, geometryOrder>, numPairs> pairData_; + AxisData<Scalar, upwindSchemeOrder> axisData_; + std::array<PairData<Scalar, GlobalPosition, upwindSchemeOrder>, numPairs> pairData_; int localFaceIdx_; unsigned int dirIdx_; diff --git a/dumux/discretization/staggered/fvgridgeometry.hh b/dumux/discretization/staggered/fvgridgeometry.hh index 2b7e9b22f68f18f07a9b1d2ebc413ddc95f69d7b..c38909a6995cbd36e0103c682923f3e1a13344d4 100644 --- a/dumux/discretization/staggered/fvgridgeometry.hh +++ b/dumux/discretization/staggered/fvgridgeometry.hh @@ -157,8 +157,6 @@ public: { return this->fvGridGeometry_->numFaceDofs(); } }; - - /*! * \ingroup StaggeredDiscretization * \brief Base class for the finite volume geometry vector for staggered models @@ -193,6 +191,8 @@ class StaggeredFVGridGeometry<GV, true, Traits> public: //! export discretization method static constexpr DiscretizationMethod discMethod = DiscretizationMethod::staggered; + static constexpr int upwindSchemeOrder = Traits::UpwindSchemeOrder; + static constexpr bool useHigherOrder = upwindSchemeOrder > 1; //! export the type of the fv element geometry (the local view type) using LocalView = typename Traits::template LocalView<ThisType, true>; @@ -213,6 +213,10 @@ public: static constexpr auto faceIdx() { return typename DofTypeIndices::FaceIdx{}; } + //! The order of the stencil built + static constexpr std::size_t order() + { return upwindSchemeOrder; } + using CellCenterFVGridGeometryType = CellCenterFVGridGeometry<ThisType>; using FaceFVGridGeometryType = FaceFVGridGeometry<ThisType>; @@ -227,10 +231,6 @@ public: if (!CheckOverlapSize<DiscretizationMethod::staggered>::isValid(gridView)) DUNE_THROW(Dune::InvalidStateException, "The staggered discretization method needs at least an overlap of 1 for parallel computations. " << " Set the parameter \"Grid.Overlap\" in the input file."); - if (hasParamInGroup(paramGroup, "Discretization.TvdApproach")) - stencilOrder_ = 2; - else - stencilOrder_ = 1; } //! The total number of sub control volumes @@ -251,11 +251,6 @@ public: return numBoundaryScvf_; } - //! The order of the stencil built - std::size_t order() const - { - return stencilOrder_; - } //! The total number of intersections std::size_t numIntersections() const @@ -351,7 +346,6 @@ public: } // build the connectivity map for an effecient assembly - connectivityMap_.setStencilOrder(stencilOrder_); connectivityMap_.update(*this); } @@ -423,7 +417,6 @@ private: // mappers ConnectivityMap connectivityMap_; IntersectionMapper intersectionMapper_; - int stencilOrder_; std::vector<SubControlVolume> scvs_; std::vector<SubControlVolumeFace> scvfs_; @@ -455,6 +448,8 @@ class StaggeredFVGridGeometry<GV, false, Traits> public: //! export discretization method static constexpr DiscretizationMethod discMethod = DiscretizationMethod::staggered; + static constexpr int upwindSchemeOrder = Traits::UpwindSchemeOrder; + static constexpr bool useHigherOrder = upwindSchemeOrder > 1; using GeometryHelper = typename Traits::GeometryHelper; @@ -477,6 +472,10 @@ public: static constexpr auto faceIdx() { return typename DofTypeIndices::FaceIdx{}; } + //! The order of the stencil built + static constexpr std::size_t order() + { return upwindSchemeOrder; } + using CellCenterFVGridGeometryType = CellCenterFVGridGeometry<ThisType>; using FaceFVGridGeometryType = FaceFVGridGeometry<ThisType>; @@ -491,10 +490,6 @@ public: if (!CheckOverlapSize<DiscretizationMethod::staggered>::isValid(gridView)) DUNE_THROW(Dune::InvalidStateException, "The staggered discretization method needs at least an overlap of 1 for parallel computations. " << " Set the parameter \"Grid.Overlap\" in the input file."); - if (hasParamInGroup(paramGroup, "Discretization.TvdApproach")) - stencilOrder_ = 2; - else - stencilOrder_ = 1; } //! update all fvElementGeometries (do this again after grid adaption) @@ -547,7 +542,6 @@ public: } // build the connectivity map for an effecient assembly - connectivityMap_.setStencilOrder(stencilOrder_); connectivityMap_.update(*this); } @@ -563,12 +557,6 @@ public: return numScvf_; } - //! The order of the stencil built - std::size_t order() const - { - return stencilOrder_; - } - //! The total number of boundary sub control volume faces std::size_t numBoundaryScvf() const { @@ -652,7 +640,6 @@ private: // mappers ConnectivityMap connectivityMap_; IntersectionMapper intersectionMapper_; - int stencilOrder_; //! vectors that store the global data std::vector<std::vector<GridIndexType>> scvfIndicesOfScv_; diff --git a/dumux/discretization/staggered/gridfluxvariablescache.hh b/dumux/discretization/staggered/gridfluxvariablescache.hh index 3c87372776d2c95922574fc8730da2ef0249097d..44d9550018bb3870a888bf77e61bf95077ed816d 100644 --- a/dumux/discretization/staggered/gridfluxvariablescache.hh +++ b/dumux/discretization/staggered/gridfluxvariablescache.hh @@ -28,7 +28,7 @@ #include <dumux/discretization/localview.hh> #include <dumux/discretization/staggered/elementfluxvariablescache.hh> -#include <dumux/freeflow/higherorderapproximation.hh> +#include <dumux/freeflow/upwindingmethods.hh> namespace Dumux { @@ -86,7 +86,7 @@ public: StaggeredGridFluxVariablesCache(const Problem& problem, const std::string& paramGroup = "") : problemPtr_(&problem) - , higherOrderApproximation_(paramGroup) + , upwindingMethods_(paramGroup) {} // When global caching is enabled, precompute transmissibilities and stencils for all the scv faces @@ -123,10 +123,10 @@ public: } } - //! Return the HigherOrderApproximation - const HigherOrderApproximation<Scalar>& higherOrderApproximation() const + //! Return the UpwindingMethods + const UpwindingMethods<Scalar>& upwindingMethods() const { - return higherOrderApproximation_; + return upwindingMethods_; } const Problem& problem() const @@ -141,7 +141,7 @@ public: private: const Problem* problemPtr_; - HigherOrderApproximation<Scalar> higherOrderApproximation_; + UpwindingMethods<Scalar> upwindingMethods_; std::vector<FluxVariablesCache> fluxVarsCache_; std::vector<std::size_t> globalScvfIndices_; @@ -174,7 +174,7 @@ public: StaggeredGridFluxVariablesCache(const Problem& problem, const std::string& paramGroup = "") : problemPtr_(&problem) - , higherOrderApproximation_(paramGroup) + , upwindingMethods_(paramGroup) {} // When global caching is enabled, precompute transmissibilities and stencils for all the scv faces @@ -187,15 +187,15 @@ public: const Problem& problem() const { return *problemPtr_; } - //! Return the HigherOrderApproximation - const HigherOrderApproximation<Scalar>& higherOrderApproximation() const + //! Return the UpwindingMethods + const UpwindingMethods<Scalar>& upwindingMethods() const { - return higherOrderApproximation_; + return upwindingMethods_; } private: const Problem* problemPtr_; - HigherOrderApproximation<Scalar> higherOrderApproximation_; + UpwindingMethods<Scalar> upwindingMethods_; }; diff --git a/dumux/freeflow/CMakeLists.txt b/dumux/freeflow/CMakeLists.txt index 5f16de206e056526ae1a904c3c0261c80257a51b..d63c7b093702e9de86eea7d1dae52ade5f280b7a 100644 --- a/dumux/freeflow/CMakeLists.txt +++ b/dumux/freeflow/CMakeLists.txt @@ -7,5 +7,5 @@ install(FILES properties.hh turbulenceproperties.hh volumevariables.hh -higherorderapproximation.hh +upwindingmethods.hh DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/dumux/freeflow) diff --git a/dumux/freeflow/navierstokes/staggered/CMakeLists.txt b/dumux/freeflow/navierstokes/staggered/CMakeLists.txt index 5d59b30262acbbf8b4c049b917e591cdc1a74ba7..1f1987ff9f454cac7d604da5aa0205d1822f1077 100644 --- a/dumux/freeflow/navierstokes/staggered/CMakeLists.txt +++ b/dumux/freeflow/navierstokes/staggered/CMakeLists.txt @@ -2,4 +2,5 @@ install(FILES fluxoversurface.hh fluxvariables.hh localresidual.hh +staggeredupwindfluxvariables.hh DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/dumux/freeflow/navierstokes/staggered) diff --git a/dumux/freeflow/navierstokes/staggered/fluxvariables.hh b/dumux/freeflow/navierstokes/staggered/fluxvariables.hh index 37ad3fd3cb934e99cc37bc97477844e4ab205136..08d03621846d337fdc7ffa1e80b9e24a0b2fe4ec 100644 --- a/dumux/freeflow/navierstokes/staggered/fluxvariables.hh +++ b/dumux/freeflow/navierstokes/staggered/fluxvariables.hh @@ -31,11 +31,11 @@ #include <dumux/common/parameters.hh> #include <dumux/common/properties.hh> -#include <dumux/freeflow/higherorderapproximation.hh> - #include <dumux/flux/fluxvariablesbase.hh> #include <dumux/discretization/method.hh> +#include "upwindvariables.hh" + namespace Dumux { // forward declaration @@ -86,7 +86,8 @@ class NavierStokesFluxVariablesImpl<TypeTag, DiscretizationMethod::staggered> static constexpr auto cellCenterIdx = FVGridGeometry::cellCenterIdx(); static constexpr auto faceIdx = FVGridGeometry::faceIdx(); - static constexpr bool useHigherOrder = false; // TODO + static constexpr int upwindSchemeOrder = getPropValue<TypeTag, Properties::UpwindSchemeOrder>(); + static constexpr bool useHigherOrder = upwindSchemeOrder > 1; public: @@ -203,8 +204,6 @@ public: const Scalar velocitySelf = elemFaceVars[scvf].velocitySelf(); const Scalar velocityOpposite = elemFaceVars[scvf].velocityOpposite(); - // const bool selfIsUpstream = scvf.directionSign() != sign(transportingVelocity); - // The volume variables within the current element. We only require those (and none of neighboring elements) // because the fluxes are calculated over the staggered face at the center of the element. const auto& insideVolVars = elemVolVars[scvf.insideScvIdx()]; @@ -214,42 +213,9 @@ public: { // Get the average velocity at the center of the element (i.e. the location of the staggered face). const Scalar transportingVelocity = (velocitySelf + velocityOpposite) * 0.5; - - const auto upwindingMomenta = getFrontalUpwindingMomenta_(scvf, elemFaceVars, insideVolVars.density(), transportingVelocity); - const Scalar momentum = doMomentumUpwinding_(scvf, upwindingMomenta, gridFluxVarsCache); - - // Variables that will store the velocities of interest: - // velocities[0]: downstream velocity - // velocities[1]: upstream velocity - // velocities[2]: upstream-upstream velocity - // std::array<Scalar, 3> velocities{0.0, 0.0, 0.0}; - - // Variables that will store the distances between the dofs of interest: - // distances[0]: upstream to downstream distance - // distances[1]: upstream-upstream to upstream distance - // distances[2]: downstream staggered cell size - // std::array<Scalar, 3> distances{0.0, 0.0, 0.0}; - - // const auto& highOrder = gridFluxVarsCache.higherOrderApproximation(); - - // If a Tvd approach has been specified and I am not too near to the boundary I can use a second order - // approximation for the velocity. In this frontal flux I use for the density always the value that I have on the scvf. - // if (highOrder.tvdApproach() != TvdApproach::none) - // { - // if (canFrontalSecondOrder_(scvf, selfIsUpstream, velocities, distances, elemFaceVars[scvf])) - // { - // momentum = highOrder.tvd(velocities[0], velocities[1], velocities[2], distances[0], distances[1], distances[2], selfIsUpstream, insideVolVars.density(), highOrder.tvdApproach()); - // } - // else - // momentum = highOrder.upwind(velocities[0], velocities[1], insideVolVars.density()); - // } - // else - // momentum = selfIsUpstream ? highOrder.upwind(velocityOpposite, velocitySelf, insideVolVars.density()) - // : highOrder.upwind(velocitySelf, velocityOpposite, insideVolVars.density()); - - // Account for the orientation of the staggered face's normal outer normal vector - // (pointing in opposite direction of the scvf's one). - frontalFlux += transportingVelocity * momentum * -1.0 * scvf.directionSign(); + Dumux::UpwindVariables<TypeTag, upwindSchemeOrder> upwindVariables; + frontalFlux += upwindVariables.computeUpwindedFrontalMomentum(scvf, elemFaceVars, elemVolVars, gridFluxVarsCache, transportingVelocity) + * transportingVelocity * -1.0 * scvf.directionSign(); } // Diffusive flux. @@ -368,9 +334,15 @@ public: // If there is no symmetry or Neumann boundary condition for the given sub face, proceed to calculate the tangential momentum flux. if (problem.enableInertiaTerms()) - normalFlux += computeAdvectivePartOfLateralMomentumFlux_(problem, fvGeometry, element, scvf, normalFace, elemVolVars, faceVars, gridFluxVarsCache, localSubFaceIdx, lateralFaceHasDirichletPressure, lateralFaceHasBJS); - - normalFlux += computeDiffusivePartOfLateralMomentumFlux_(problem, fvGeometry, element, scvf, normalFace, elemVolVars, faceVars, localSubFaceIdx, lateralFaceHasDirichletPressure, lateralFaceHasBJS); + normalFlux += computeAdvectivePartOfLateralMomentumFlux_(problem, fvGeometry, element, + scvf, normalFace, elemVolVars, faceVars, + gridFluxVarsCache, localSubFaceIdx, + lateralFaceHasDirichletPressure, lateralFaceHasBJS); + + normalFlux += computeDiffusivePartOfLateralMomentumFlux_(problem, fvGeometry, element, + scvf, normalFace, elemVolVars, faceVars, + localSubFaceIdx, + lateralFaceHasDirichletPressure, lateralFaceHasBJS); } return normalFlux; } @@ -414,49 +386,11 @@ private: // of interest is located. const Scalar transportingVelocity = faceVars.velocityNormalInside(localSubFaceIdx); - const auto parallelUpwindingMomenta = getParallelUpwindingMomenta_(problem, element, scvf, normalFace, elemVolVars, faceVars, - transportingVelocity, localSubFaceIdx, - lateralFaceHasDirichletPressure, lateralFaceHasBJS); - - const Scalar momentum = doMomentumUpwinding_(scvf, parallelUpwindingMomenta, gridFluxVarsCache); - - // Variables that will store the velocities of interest: - // velocities[0]: downstream velocity - // velocities[1]: upsteram velocity - // velocities[2]: upstream-upstream velocity - // std::array<Scalar, 3> velocities{0.0, 0.0, 0.0}; - - // Variables that will store the distances between the dofs of interest: - // distances[0]: upstream to downstream distance - // distances[1]: upstream-upstream to upstream distance - // distances[2]: downstream staggered cell size - // std::array<Scalar, 3> distances{0.0, 0.0, 0.0}; - - // const auto& highOrder = gridFluxVarsCache.higherOrderApproximation(); - // - // // If a Tvd approach has been specified and I am not too near to the boundary I can use a second order approximation. - // if (highOrder.tvdApproach() != TvdApproach::none) - // { - // if (canLateralSecondOrder_(scvf, fvGeometry, selfIsUpstream, localSubFaceIdx, velocities, distances, problem, element, faceVars, lateralFaceHasDirichletPressure, lateralFaceHasBJS)) - // { - // momentum = highOrder.tvd(velocities[0], velocities[1], velocities[2], distances[0], distances[1], distances[2], selfIsUpstream, selfIsUpstream ? insideVolVars.density() : outsideVolVars.density(), highOrder.tvdApproach()); - // } - // else - // momentum = highOrder.upwind(velocities[0], velocities[1], selfIsUpstream ? insideVolVars.density() : outsideVolVars.density()); - // } - // else - // { - // const Scalar velocityFirstParallel = scvf.hasParallelNeighbor(localSubFaceIdx, 0) - // ? faceVars.velocityParallel(localSubFaceIdx, 0) - // : getParallelVelocityFromBoundary_(problem, scvf, normalFace, faceVars.velocitySelf(), localSubFaceIdx, element, lateralFaceHasDirichletPressure, lateralFaceHasBJS); - // - // momentum = selfIsUpstream ? highOrder.upwind(velocityFirstParallel, faceVars.velocitySelf(), insideVolVars.density()) - // : highOrder.upwind(faceVars.velocitySelf(), velocityFirstParallel, outsideVolVars.density()); - // } - - // Account for the orientation of the staggered normal face's outer normal vector - // and its area (0.5 of the coinciding scfv). - return transportingVelocity * momentum * normalFace.directionSign() * normalFace.area() * 0.5 * extrusionFactor_(elemVolVars, normalFace); + Dumux::UpwindVariables<TypeTag, upwindSchemeOrder> upwindVariables; + return upwindVariables.computeUpwindedLateralMomentum(problem, fvGeometry, element, scvf, normalFace, elemVolVars, faceVars, + gridFluxVarsCache, localSubFaceIdx, lateralFaceHasDirichletPressure, + lateralFaceHasBJS) + * transportingVelocity * normalFace.directionSign() * normalFace.area() * 0.5 * extrusionFactor_(elemVolVars, normalFace); } /*! @@ -543,7 +477,9 @@ private: const Scalar velocityFirstParallel = scvf.hasParallelNeighbor(localSubFaceIdx,0) ? faceVars.velocityParallel(localSubFaceIdx,0) - : getParallelVelocityFromBoundary_(problem, scvf, normalFace, innerParallelVelocity, localSubFaceIdx, element, false, lateralFaceHasBJS); + : getParallelVelocityFromBoundary_(problem, scvf, normalFace, + innerParallelVelocity, localSubFaceIdx, + element, false, lateralFaceHasBJS); // The velocity gradient already accounts for the orientation // of the staggered face's outer normal vector. @@ -609,7 +545,8 @@ private: //! helper function to conveniently create a ghost face used to retrieve boundary values from the problem SubControlVolumeFace makeGhostFace_(const SubControlVolumeFace& ownScvf, const GlobalPosition& pos) const { - return SubControlVolumeFace(pos, std::vector<unsigned int>{ownScvf.insideScvIdx(), ownScvf.outsideScvIdx()}, ownScvf.directionIndex(), ownScvf.axisData().selfDof, ownScvf.index()); + return SubControlVolumeFace(pos, std::vector<unsigned int>{ownScvf.insideScvIdx(), ownScvf.outsideScvIdx()}, + ownScvf.directionIndex(), ownScvf.axisData().selfDof, ownScvf.index()); }; //! helper function to conveniently create a ghost face which is outside the domain, parallel to the scvf of interest @@ -652,186 +589,6 @@ private: return false; } - /*! - * \brief Check if a second order approximation for the frontal part of the advective term can be used - * - * This helper function checks if the scvf of interest is not too near to the - * boundary so that a dof upstream with respect to the upstream dof is available. - * - * \param ownScvf The SubControlVolumeFace we are considering - * \param selfIsUpstream @c true if the velocity ownScvf is upstream wrt the transporting velocity - * \param velocities Variable that will store the velocities of interest - * \param distances Variable that will store the distances of interest - * \param faceVars The face variables related to ownScvf - */ - bool canFrontalSecondOrder_(const SubControlVolumeFace& ownScvf, - const bool selfIsUpstream, - std::array<Scalar, 3>& velocities, - std::array<Scalar, 3>& distances, - const FaceVariables& faceVars) const - { - // Depending on selfIsUpstream I can assign the downstream and the upstream velocities, - // then I have to check if I have a forward or a backward neighbor to retrieve - // an "upstream-upstream velocity" and be able to use a second order scheme. - if (selfIsUpstream) - { - velocities[0] = faceVars.velocityOpposite(); - velocities[1] = faceVars.velocitySelf(); - - if (ownScvf.hasForwardNeighbor(0)) - { - velocities[2] = faceVars.velocityForward(0); - distances[0] = ownScvf.selfToOppositeDistance(); - distances[1] = ownScvf.axisData().inAxisForwardDistances[0]; - distances[2] = 0.5 * (ownScvf.axisData().selfToOppositeDistance + ownScvf.axisData().inAxisBackwardDistances[0]); - return true; - } - else - return false; - } - else - { - velocities[0] = faceVars.velocitySelf(); - velocities[1] = faceVars.velocityOpposite(); - - if (ownScvf.hasBackwardNeighbor(0)) - { - velocities[2] = faceVars.velocityBackward(0); - distances[0] = ownScvf.selfToOppositeDistance(); - distances[1] = ownScvf.axisData().inAxisBackwardDistances[0]; - distances[2] = 0.5 * (ownScvf.axisData().selfToOppositeDistance + ownScvf.axisData().inAxisForwardDistances[0]); - return true; - } - else - return false; - } - } - - /*! - * \brief Check if a second order approximation for the lateral part of the advective term can be used - * - * This helper function checks if the scvf of interest is not too near to the - * boundary so that a dof upstream with respect to the upstream dof is available. - * - * \param ownScvf The SubControlVolumeFace we are considering - * \param selfIsUpstream @c true if the velocity ownScvf is upstream wrt the transporting velocity - * \param localSubFaceIdx The local subface index - * \param velocities Variable that will store the velocities of interest - * \param distances Variable that will store the distances of interest - */ - bool canLateralSecondOrder_(const SubControlVolumeFace& ownScvf, - const FVElementGeometry& fvGeometry, - const bool selfIsUpstream, - const int localSubFaceIdx, - std::array<Scalar, 3>& velocities, - std::array<Scalar, 3>& distances, - const Problem& problem, - const Element& element, - const FaceVariables& faceVars, - const bool lateralFaceHasDirichletPressure, - const bool lateralFaceHasBJS) const - { - const SubControlVolumeFace& normalFace = fvGeometry.scvf(ownScvf.insideScvIdx(), ownScvf.pairData(localSubFaceIdx).localNormalFaceIdx); - - // The local index of the faces that is opposite to localSubFaceIdx - const int oppositeSubFaceIdx = localSubFaceIdx % 2 ? localSubFaceIdx - 1 : localSubFaceIdx + 1; - - if (selfIsUpstream) - { - // I can assign the upstream velocity. The downstream velocity can be assigned or retrieved - // from the boundary if there is no parallel neighbor. - velocities[1] = faceVars.velocitySelf(); - - if(ownScvf.hasParallelNeighbor(localSubFaceIdx, 0)) - { - velocities[0] = faceVars.velocityParallel(localSubFaceIdx, 0); - distances[2] = ownScvf.pairData(localSubFaceIdx).parallelDistances[1]; - } - else - { - velocities[0] = getParallelVelocityFromBoundary_(problem, ownScvf, normalFace, faceVars.velocitySelf(), localSubFaceIdx, element, lateralFaceHasDirichletPressure, lateralFaceHasBJS); - distances[2] = ownScvf.area() / 2.0; - } - - // The "upstream-upsteram" velocity is retrieved from the other parallel neighbor - // or from the boundary. - if (ownScvf.hasParallelNeighbor(oppositeSubFaceIdx, 0)) - velocities[2] = faceVars.velocityParallel(oppositeSubFaceIdx, 0); - else - velocities[2] = getParallelVelocityFromOtherBoundary_(problem, fvGeometry, ownScvf, oppositeSubFaceIdx, element, velocities[1]); - - distances[0] = ownScvf.cellCenteredParallelDistance(localSubFaceIdx, 0); - distances[1] = ownScvf.cellCenteredParallelDistance(oppositeSubFaceIdx, 0); - - return true; - } - else - { - // The self velocity is downstream, then if there is no parallel neighbor I can not use - // a second order approximation beacuse I have only two velocities. - velocities[0] = faceVars.velocitySelf(); - - if (!ownScvf.hasParallelNeighbor(localSubFaceIdx, 0)) - { - velocities[1] = getParallelVelocityFromBoundary_(problem, ownScvf, normalFace, faceVars.velocitySelf(), localSubFaceIdx, element, lateralFaceHasDirichletPressure, lateralFaceHasBJS); - return false; - } - - velocities[1] = faceVars.velocityParallel(localSubFaceIdx, 0); - - // If there is another parallel neighbor I can assign the "upstream-upstream" - // velocity, otherwise I retrieve it from the boundary. - if (ownScvf.hasParallelNeighbor(localSubFaceIdx, 1)) - velocities[2] = faceVars.velocityParallel(localSubFaceIdx, 1); - else - { - const Element& elementParallel = fvGeometry.fvGridGeometry().element(fvGeometry.scv(normalFace.outsideScvIdx())); - const SubControlVolumeFace& firstParallelScvf = fvGeometry.scvf(normalFace.outsideScvIdx(), ownScvf.localFaceIdx()); - velocities[2] = getParallelVelocityFromOtherBoundary_(problem, fvGeometry, firstParallelScvf, localSubFaceIdx, elementParallel, velocities[1]); - } - - distances[0] = ownScvf.cellCenteredParallelDistance(localSubFaceIdx, 0); - distances[1] = ownScvf.cellCenteredParallelDistance(localSubFaceIdx, 1); - distances[2] = ownScvf.area(); - - return true; - } - } - - /*! - * \brief Return the outer parallel velocity for normal faces that are on the boundary and therefore have no neighbor. - * - * Calls the problem to retrieve a fixed value set on the boundary. - * - * \param problem The problem - * \param scvf The SubControlVolumeFace that is normal to the boundary - * \param normalFace The face at the boundary - * \param velocitySelf the velocity at scvf - * \param localSubFaceIdx The local index of the face that is on the boundary - * \param element The element that is on the boundary - * \param lateralFaceHasDirichletPressure @c true if there is a dirichlet condition for the pressure on the boundary - * \param lateralFaceHasBJS @c true if there is a BJS condition fot the velocity on the boundary - */ - Scalar getParallelVelocityFromBoundary_(const Problem& problem, - const SubControlVolumeFace& scvf, - const SubControlVolumeFace& normalFace, - const Scalar velocitySelf, - const int localSubFaceIdx, - const Element& element, - const bool lateralFaceHasDirichletPressure, - const bool lateralFaceHasBJS) const - { - // If there is a Dirichlet condition for the pressure we assume zero gradient for the velocity, - // so the velocity at the boundary equal to that on the scvf. - if (lateralFaceHasDirichletPressure) - return velocitySelf; - - const auto ghostFace = makeParallelGhostFace_(scvf, localSubFaceIdx); - if (lateralFaceHasBJS) - return problem.bjsVelocity(element, scvf, normalFace, localSubFaceIdx, velocitySelf); - return problem.dirichlet(element, ghostFace)[Indices::velocity(scvf.directionIndex())]; - } - /*! * \brief Return a velocity value from a boundary for which the boundary conditions have to be checked. * @@ -877,58 +634,38 @@ private: DUNE_THROW(Dune::InvalidStateException, "Something went wrong with the boundary conditions for the momentum equations at global position " << boundarySubFaceCenter); } } - - Scalar doMomentumUpwinding_(const SubControlVolumeFace& scvf, - const std::array<Scalar, 2>& momentum, - const GridFluxVariablesCache& gridFluxVarsCache) const - { - const auto& upwindScheme = gridFluxVarsCache.higherOrderApproximation(); - return upwindScheme.upwind(momentum[0], momentum[1]); - } - - std::array<Scalar, 2> getFrontalUpwindingMomenta_(const SubControlVolumeFace& scvf, - const ElementFaceVariables& elemFaceVars, - const Scalar density, - const Scalar transportingVelocity) const - { - const Scalar momentumSelf = elemFaceVars[scvf].velocitySelf() * density; - const Scalar momentumOpposite = elemFaceVars[scvf].velocityOpposite() * density; - const bool selfIsUpstream = scvf.directionSign() != sign(transportingVelocity); - - return selfIsUpstream ? std::array<Scalar, 2>{momentumOpposite, momentumSelf} - : std::array<Scalar, 2>{momentumSelf, momentumOpposite}; - } - - std::array<Scalar, 2> getParallelUpwindingMomenta_(const Problem& problem, - const Element& element, - const SubControlVolumeFace& ownFace, - const SubControlVolumeFace& lateralFace, - const ElementVolumeVariables& elemVolVars, - const FaceVariables& faceVars, - const Scalar transportingVelocity, - const int localSubFaceIdx, - bool lateralFaceHasDirichletPressure, - bool lateralFaceHasBJS) const + /*! + * \brief Return the outer parallel velocity for normal faces that are on the boundary and therefore have no neighbor. + * + * Calls the problem to retrieve a fixed value set on the boundary. + * + * \param problem The problem + * \param scvf The SubControlVolumeFace that is normal to the boundary + * \param normalFace The face at the boundary + * \param velocitySelf the velocity at scvf + * \param localSubFaceIdx The local index of the face that is on the boundary + * \param element The element that is on the boundary + * \param lateralFaceHasDirichletPressure @c true if there is a dirichlet condition for the pressure on the boundary + * \param lateralFaceHasBJS @c true if there is a BJS condition fot the velocity on the boundary + */ + Scalar getParallelVelocityFromBoundary_(const Problem& problem, + const SubControlVolumeFace& scvf, + const SubControlVolumeFace& normalFace, + const Scalar velocitySelf, + const int localSubFaceIdx, + const Element& element, + const bool lateralFaceHasDirichletPressure, + const bool lateralFaceHasBJS) const { + // If there is a Dirichlet condition for the pressure we assume zero gradient for the velocity, + // so the velocity at the boundary equal to that on the scvf. + if (lateralFaceHasDirichletPressure) + return velocitySelf; - // Check whether the own or the neighboring element is upstream. - const bool selfIsUpstream = lateralFace.directionSign() == sign(transportingVelocity); - - // Get the volume variables of the own and the neighboring element - const auto& insideVolVars = elemVolVars[lateralFace.insideScvIdx()]; - const auto& outsideVolVars = elemVolVars[lateralFace.outsideScvIdx()]; - - const Scalar momentumSelf = faceVars.velocitySelf() * insideVolVars.density(); - - const Scalar momentumParallel = ownFace.hasParallelNeighbor(localSubFaceIdx, 0) - ? faceVars.velocityParallel(localSubFaceIdx, 0) * outsideVolVars.density() - : (getParallelVelocityFromBoundary_(problem, ownFace, lateralFace, - faceVars.velocitySelf(), localSubFaceIdx, element, - lateralFaceHasDirichletPressure, lateralFaceHasBJS) - * insideVolVars.density()); - - return selfIsUpstream ? std::array<Scalar, 2>{momentumParallel, momentumSelf} - : std::array<Scalar, 2>{momentumSelf, momentumParallel}; + const auto ghostFace = makeParallelGhostFace_(scvf, localSubFaceIdx); + if (lateralFaceHasBJS) + return problem.bjsVelocity(element, scvf, normalFace, localSubFaceIdx, velocitySelf); + return problem.dirichlet(element, ghostFace)[Indices::velocity(scvf.directionIndex())]; } }; diff --git a/dumux/freeflow/navierstokes/staggered/upwindvariables.hh b/dumux/freeflow/navierstokes/staggered/upwindvariables.hh new file mode 100644 index 0000000000000000000000000000000000000000..b5a7bc6d05dccd55d120c14c2b3e851ef5cd269e --- /dev/null +++ b/dumux/freeflow/navierstokes/staggered/upwindvariables.hh @@ -0,0 +1,656 @@ +// -*- 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 3 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 <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * \ingroup NavierStokesModel + * \copydoc Dumux::NavierStokesUpwindVariables + */ +#ifndef DUMUX_NAVIERSTOKES_STAGGERED_UPWINDVARIABLES_HH +#define DUMUX_NAVIERSTOKES_STAGGERED_UPWINDVARIABLES_HH + +#include <array> + +#include <dumux/common/math.hh> +#include <dumux/common/exceptions.hh> +#include <dumux/common/parameters.hh> +#include <dumux/common/properties.hh> + +#include <dumux/discretization/method.hh> +#include <dumux/freeflow/upwindingmethods.hh> + +namespace Dumux { + +/*! + * \ingroup NavierStokesModel + * \brief The upwinding variables class for the Navier-Stokes model using the staggered grid discretization. + */ +template<class TypeTag, int upwindSchemeOrder> +class UpwindVariables +{ + using GridVariables = GetPropType<TypeTag, Properties::GridVariables>; + + using GridVolumeVariables = typename GridVariables::GridVolumeVariables; + using ElementVolumeVariables = typename GridVolumeVariables::LocalView; + using VolumeVariables = typename GridVolumeVariables::VolumeVariables; + + using GridFluxVariablesCache = typename GridVariables::GridFluxVariablesCache; + using FluxVariablesCache = typename GridFluxVariablesCache::FluxVariablesCache; + + using GridFaceVariables = typename GridVariables::GridFaceVariables; + using ElementFaceVariables = typename GridFaceVariables::LocalView; + using FaceVariables = typename GridFaceVariables::FaceVariables; + + using Problem = GetPropType<TypeTag, Properties::Problem>; + using FVGridGeometry = GetPropType<TypeTag, Properties::FVGridGeometry>; + using FVElementGeometry = typename FVGridGeometry::LocalView; + using GridView = typename FVGridGeometry::GridView; + using Element = typename GridView::template Codim<0>::Entity; + using Scalar = GetPropType<TypeTag, Properties::Scalar>; + using ModelTraits = GetPropType<TypeTag, Properties::ModelTraits>; + using Indices = typename ModelTraits::Indices; + using SubControlVolumeFace = typename FVElementGeometry::SubControlVolumeFace; + using CellCenterPrimaryVariables = GetPropType<TypeTag, Properties::CellCenterPrimaryVariables>; + using FacePrimaryVariables = GetPropType<TypeTag, Properties::FacePrimaryVariables>; + using BoundaryTypes = GetPropType<TypeTag, Properties::BoundaryTypes>; + + using GlobalPosition = typename Element::Geometry::GlobalCoordinate; + static constexpr bool useHigherOrder = upwindSchemeOrder > 1; + + static constexpr auto cellCenterIdx = FVGridGeometry::cellCenterIdx(); + static constexpr auto faceIdx = FVGridGeometry::faceIdx(); + +public: + UpwindVariables() {}; + + /*! + * \brief + * + * TODO: DOC + * + */ + FacePrimaryVariables computeUpwindedFrontalMomentum(const SubControlVolumeFace& scvf, + const ElementFaceVariables& elemFaceVars, + const ElementVolumeVariables& elemVolVars, + const GridFluxVariablesCache& gridFluxVarsCache, + const Scalar transportingVelocity) + { + Scalar momentum(0.0); + const bool canHigherOrder = canFrontalSecondOrder_(scvf, transportingVelocity, std::integral_constant<bool, useHigherOrder>{}); + + if (canHigherOrder) + { + const auto upwindingMomenta = getFrontalUpwindingMomenta_(scvf, elemFaceVars, elemVolVars[scvf.insideScvIdx()].density(), + transportingVelocity, std::integral_constant<bool, useHigherOrder>{}); + momentum = doFrontalMomentumUpwinding_(scvf, upwindingMomenta, transportingVelocity, gridFluxVarsCache); + } + else + { + const auto upwindingMomenta = getFrontalUpwindingMomenta_(scvf, elemFaceVars, elemVolVars[scvf.insideScvIdx()].density(), + transportingVelocity, std::integral_constant<bool, false>{}); + momentum = doFrontalMomentumUpwinding_(scvf, upwindingMomenta, transportingVelocity, gridFluxVarsCache); + } + + return momentum; + } + + /*! + * \brief + * + * TODO: DOC + * + */ + FacePrimaryVariables computeUpwindedLateralMomentum(const Problem& problem, + const FVElementGeometry& fvGeometry, + const Element& element, + const SubControlVolumeFace& scvf, + const SubControlVolumeFace& normalFace, + const ElementVolumeVariables& elemVolVars, + const FaceVariables& faceVars, + const GridFluxVariablesCache& gridFluxVarsCache, + const int localSubFaceIdx, + const bool lateralFaceHasDirichletPressure, + const bool lateralFaceHasBJS) + { + // Get the transporting velocity, located at the scvf perpendicular to the current scvf where the dof + // of interest is located. + const Scalar transportingVelocity = faceVars.velocityNormalInside(localSubFaceIdx); + + + // Check whether the own or the neighboring element is upstream. + const bool selfIsUpstream = ( normalFace.directionSign() == sign(transportingVelocity) ); + + Scalar momentum(0.0); + const bool canHigherOrder = canLateralSecondOrder_(scvf, selfIsUpstream, localSubFaceIdx, std::integral_constant<bool, useHigherOrder>{}); + + if (canHigherOrder) + { + const auto parallelUpwindingMomenta = getLateralUpwindingMomenta_(problem, fvGeometry, element, scvf, elemVolVars, faceVars, + transportingVelocity, localSubFaceIdx, + lateralFaceHasDirichletPressure, lateralFaceHasBJS, + std::integral_constant<bool, useHigherOrder>{}); + momentum = doLateralMomentumUpwinding_(scvf, normalFace, parallelUpwindingMomenta, transportingVelocity, localSubFaceIdx, gridFluxVarsCache); + } + else + { + const auto parallelUpwindingMomenta = getLateralUpwindingMomenta_(problem, fvGeometry, element, scvf, elemVolVars, faceVars, + transportingVelocity, localSubFaceIdx, + lateralFaceHasDirichletPressure, lateralFaceHasBJS, + std::integral_constant<bool, false>{}); + momentum = doLateralMomentumUpwinding_(scvf, normalFace, parallelUpwindingMomenta, transportingVelocity, localSubFaceIdx, gridFluxVarsCache); + } + + return momentum; + } + +private: + + /*! + * \brief + * + * TODO: DOC + * + */ + bool canFrontalSecondOrder_(const SubControlVolumeFace& ownScvf, + const Scalar transportingVelocity, + std::true_type) const + { + const bool selfIsUpstream = ownScvf.directionSign() != sign(transportingVelocity); + // Depending on selfIsUpstream I have to check if I have a forward or a backward neighbor to retrieve + if (selfIsUpstream) + { + if (ownScvf.hasForwardNeighbor(0)) + return true; + else + return false; + } + else + { + if (ownScvf.hasBackwardNeighbor(0)) + return true; + else + return false; + } + } + + /*! + * \brief + * + * TODO: DOC + * + */ + bool canFrontalSecondOrder_(const SubControlVolumeFace& ownScvf, + const Scalar transportingVelocity, + std::false_type) const + { + return false; + } + + /*! + * \brief + * + * TODO: DOC + * + */ + std::array<Scalar, 3> getFrontalUpwindingMomenta_(const SubControlVolumeFace& scvf, + const ElementFaceVariables& elemFaceVars, + const Scalar density, + const Scalar transportingVelocity, + std::true_type) const + { + const bool selfIsUpstream = scvf.directionSign() != sign(transportingVelocity); + std::array<Scalar, 3> momenta{0.0, 0.0, 0.0}; + + if (selfIsUpstream) + { + momenta[0] = elemFaceVars[scvf].velocityOpposite() * density; + momenta[1] = elemFaceVars[scvf].velocitySelf() * density; + momenta[2] = elemFaceVars[scvf].velocityForward(upwindSchemeOrder-2) * density; + } + else + { + momenta[0] = elemFaceVars[scvf].velocitySelf() * density; + momenta[1] = elemFaceVars[scvf].velocityOpposite() * density; + momenta[2] = elemFaceVars[scvf].velocityBackward(upwindSchemeOrder-2) * density; + } + + return momenta; + } + + /*! + * \brief + * + * TODO: DOC + * + */ + std::array<Scalar, 2> getFrontalUpwindingMomenta_(const SubControlVolumeFace& scvf, + const ElementFaceVariables& elemFaceVars, + const Scalar density, + const Scalar transportingVelocity, + std::false_type) const + { + const Scalar momentumSelf = elemFaceVars[scvf].velocitySelf() * density; + const Scalar momentumOpposite = elemFaceVars[scvf].velocityOpposite() * density; + const bool selfIsUpstream = scvf.directionSign() != sign(transportingVelocity); + + return selfIsUpstream ? std::array<Scalar, 2>{momentumOpposite, momentumSelf} + : std::array<Scalar, 2>{momentumSelf, momentumOpposite}; + } + + /*! + * \brief + * + * TODO: DOC + * + */ + Scalar doFrontalMomentumUpwinding_(const SubControlVolumeFace& scvf, + const std::array<Scalar, 2>& momenta, + const Scalar transportingVelocity, + const GridFluxVariablesCache& gridFluxVarsCache) const + { + const auto& upwindScheme = gridFluxVarsCache.upwindingMethods(); + return upwindScheme.upwind(momenta[0], momenta[1]); + } + + /*! + * \brief + * + * TODO: DOC + * + */ + template<bool enable = useHigherOrder, std::enable_if_t<enable, int> = 0> + Scalar doFrontalMomentumUpwinding_(const SubControlVolumeFace& scvf, + const std::array<Scalar, 3>& momenta, + const Scalar transportingVelocity, + const GridFluxVariablesCache& gridFluxVarsCache) const + { + const auto& upwindScheme = gridFluxVarsCache.upwindingMethods(); + const bool selfIsUpstream = scvf.directionSign() != sign(transportingVelocity); + std::array<Scalar,3> distances = getFrontalDistances_(scvf, selfIsUpstream); + return upwindScheme.tvd(momenta, distances, selfIsUpstream, upwindScheme.tvdApproach()); + } + + /*! + * \brief + * + * TODO: DOC + * + */ + template<bool enable = useHigherOrder, std::enable_if_t<enable, int> = 0> + std::array<Scalar, 3> getFrontalDistances_(const SubControlVolumeFace& ownScvf, + const bool selfIsUpstream) const + { + // Depending on selfIsUpstream the downstream and the (up)upstream distances are saved. + // distances {upstream to downstream distance, up-upstream to upstream distance, downstream staggered cell size} + std::array<Scalar, 3> distances{0.0, 0.0, 0.0}; + + if (selfIsUpstream) + { + distances[0] = ownScvf.selfToOppositeDistance(); + distances[1] = ownScvf.axisData().inAxisForwardDistances[upwindSchemeOrder-2]; + distances[2] = 0.5 * (ownScvf.axisData().selfToOppositeDistance + ownScvf.axisData().inAxisBackwardDistances[0]); + } + else + { + distances[0] = ownScvf.selfToOppositeDistance(); + distances[1] = ownScvf.axisData().inAxisBackwardDistances[upwindSchemeOrder-2]; + distances[2] = 0.5 * (ownScvf.axisData().selfToOppositeDistance + ownScvf.axisData().inAxisForwardDistances[0]); + } + return distances; + } + + /*! + * TODO: DOC + * + * \brief Check if a second order approximation for the lateral part of the advective term can be used + * + * This helper function checks if the scvf of interest is not too near to the + * boundary so that a dof upstream with respect to the upstream dof is available. + * + * \param ownScvf The SubControlVolumeFace we are considering + * \param selfIsUpstream @c true if the velocity at ownScvf is upstream wrt the transporting velocity + * \param localSubFaceIdx The local subface index + */ + bool canLateralSecondOrder_(const SubControlVolumeFace& ownScvf, + const bool selfIsUpstream, + const int localSubFaceIdx, + std::true_type) const + { + if (selfIsUpstream) + { + // The self velocity is upstream. The downstream velocity can be assigned or retrieved + // from the boundary, even if there is no parallel neighbor. + return true; + } + else + { + // The self velocity is downstream. If there is no parallel neighbor I cannot use a second order approximation. + if (!ownScvf.hasParallelNeighbor(localSubFaceIdx, 0)) + return false; + else + return true; + } + } + + /*! + * \brief Check if a second order approximation for the lateral part of the advective term can be used + * If higher order methods are not prescribed, this is called and false is returned. + */ + bool canLateralSecondOrder_(const SubControlVolumeFace& ownScvf, + const bool selfIsUpstream, + const int localSubFaceIdx, + std::false_type) const + { return false; } + + + /*! + * \brief + * + * TODO: DOC + * + */ + std::array<Scalar, 3> getLateralUpwindingMomenta_(const Problem& problem, + const FVElementGeometry& fvGeometry, + const Element& element, + const SubControlVolumeFace& ownScvf, + const ElementVolumeVariables& elemVolVars, + const FaceVariables& faceVars, + const Scalar transportingVelocity, + const int localSubFaceIdx, + bool lateralFaceHasDirichletPressure, + bool lateralFaceHasBJS, + std::true_type) const + { + std::array<Scalar, 3> momenta{0.0, 0.0, 0.0}; + const SubControlVolumeFace& lateralFace = fvGeometry.scvf(ownScvf.insideScvIdx(), ownScvf.pairData(localSubFaceIdx).localNormalFaceIdx); + + // Check whether the own or the neighboring element is upstream. + const bool selfIsUpstream = lateralFace.directionSign() == sign(transportingVelocity); + + // Get the volume variables of the own and the neighboring element + const auto& insideVolVars = elemVolVars[lateralFace.insideScvIdx()]; + const auto& outsideVolVars = elemVolVars[lateralFace.outsideScvIdx()]; + + // The local index of the faces that is opposite to localSubFaceIdx + const int oppositeSubFaceIdx = localSubFaceIdx % 2 ? localSubFaceIdx - 1 : localSubFaceIdx + 1; + + if (selfIsUpstream) + { + momenta[1] = faceVars.velocitySelf() * insideVolVars.density(); + + if(ownScvf.hasParallelNeighbor(localSubFaceIdx, 0)) + momenta[0] = faceVars.velocityParallel(localSubFaceIdx, 0) * insideVolVars.density(); + else + momenta[0] = getParallelVelocityFromBoundary_(problem, ownScvf, lateralFace, + faceVars.velocitySelf(), localSubFaceIdx, + element, lateralFaceHasDirichletPressure, + lateralFaceHasBJS) * insideVolVars.density(); + + // The "upstream-upstream" velocity is retrieved from the other parallel neighbor or from the boundary. + if (ownScvf.hasParallelNeighbor(oppositeSubFaceIdx, 0)) + momenta[2] = faceVars.velocityParallel(oppositeSubFaceIdx, 0) * insideVolVars.density(); + else + momenta[2] = getParallelVelocityFromOtherBoundary_(problem, fvGeometry, ownScvf, + oppositeSubFaceIdx, element, + (momenta[1]/insideVolVars.density())) * insideVolVars.density(); + } + else + { + momenta[0] = faceVars.velocitySelf() * outsideVolVars.density(); + + if (!ownScvf.hasParallelNeighbor(localSubFaceIdx, 0)) + { + std::cout << "how did we get here \n"; + momenta[1] = getParallelVelocityFromBoundary_(problem, ownScvf, lateralFace, + faceVars.velocitySelf(), localSubFaceIdx, + element, lateralFaceHasDirichletPressure, + lateralFaceHasBJS) * outsideVolVars.density(); + return momenta; + } + + momenta[1] = faceVars.velocityParallel(localSubFaceIdx, 0) * outsideVolVars.density(); + + // If there is another parallel neighbor I can assign the "upstream-upstream" velocity, otherwise I retrieve it from the boundary. + if (ownScvf.hasParallelNeighbor(localSubFaceIdx, 1)) + momenta[2] = faceVars.velocityParallel(localSubFaceIdx, 1) * outsideVolVars.density(); + else + { + const Element& elementParallel = fvGeometry.fvGridGeometry().element(fvGeometry.scv(lateralFace.outsideScvIdx())); + const SubControlVolumeFace& firstParallelScvf = fvGeometry.scvf(lateralFace.outsideScvIdx(), ownScvf.localFaceIdx()); + momenta[2] = getParallelVelocityFromOtherBoundary_(problem, fvGeometry, firstParallelScvf, + localSubFaceIdx, elementParallel, + (momenta[1]/outsideVolVars.density())) * outsideVolVars.density(); + } + } + return momenta; + + } + + /*! + * \brief + * + * TODO: DOC + * + */ + std::array<Scalar, 2> getLateralUpwindingMomenta_(const Problem& problem, + const FVElementGeometry& fvGeometry, + const Element& element, + const SubControlVolumeFace& ownScvf, + const ElementVolumeVariables& elemVolVars, + const FaceVariables& faceVars, + const Scalar transportingVelocity, + const int localSubFaceIdx, + bool lateralFaceHasDirichletPressure, + bool lateralFaceHasBJS, + std::false_type) const + { + // Check whether the own or the neighboring element is upstream. + const SubControlVolumeFace& lateralFace = fvGeometry.scvf(ownScvf.insideScvIdx(), ownScvf.pairData(localSubFaceIdx).localNormalFaceIdx); + const bool selfIsUpstream = lateralFace.directionSign() == sign(transportingVelocity); + + // Get the volume variables of the own and the neighboring element + const auto& insideVolVars = elemVolVars[lateralFace.insideScvIdx()]; + const auto& outsideVolVars = elemVolVars[lateralFace.outsideScvIdx()]; + + const Scalar momentumSelf = faceVars.velocitySelf() * insideVolVars.density(); + + const Scalar momentumParallel = ownScvf.hasParallelNeighbor(localSubFaceIdx, 0) + ? faceVars.velocityParallel(localSubFaceIdx, 0) * outsideVolVars.density() + : (getParallelVelocityFromBoundary_(problem, ownScvf, lateralFace, + faceVars.velocitySelf(), localSubFaceIdx, element, + lateralFaceHasDirichletPressure, lateralFaceHasBJS) + * insideVolVars.density()); + + return selfIsUpstream ? std::array<Scalar, 2>{momentumParallel, momentumSelf} + : std::array<Scalar, 2>{momentumSelf, momentumParallel}; + } + + /*! + * \brief + * + * TODO: DOC + * + */ + Scalar doLateralMomentumUpwinding_(const SubControlVolumeFace& scvf, + const SubControlVolumeFace& normalScvf, + const std::array<Scalar, 2>& momenta, + const Scalar transportingVelocity, + const int localSubFaceIdx, + const GridFluxVariablesCache& gridFluxVarsCache) const + { + return doFrontalMomentumUpwinding_(scvf, momenta, transportingVelocity, gridFluxVarsCache); + } + + /*! + * \brief + * + * TODO: DOC + * + * \param scvf The sub control volume face + * \param normalScvf The normal sub control volume face + * \param momenta The momenta to be upwinded + * \param transportingVelocity The average of the self and opposite velocities. + * \param localSubFaceIdx The local index of the subface + * \param gridFluxVarsCache The grid flux variables cache + */ + Scalar doLateralMomentumUpwinding_(const SubControlVolumeFace& scvf, + const SubControlVolumeFace& normalScvf, + const std::array<Scalar, 3>& momenta, + const Scalar transportingVelocity, + const int localSubFaceIdx, + const GridFluxVariablesCache& gridFluxVarsCache) const + { + const bool selfIsUpstream = ( normalScvf.directionSign() == sign(transportingVelocity) ); + std::array<Scalar,3> distances = getLateralDistances_(scvf, localSubFaceIdx, selfIsUpstream); + return upwindScheme.tvd(momenta, distances, selfIsUpstream, upwindScheme.tvdApproach()); + } + + /*! + * \brief + * + * TODO: DOC + * + */ + template<bool enable = useHigherOrder, std::enable_if_t<enable, int> = 0> + std::array<Scalar, 3> getLateralDistances_(const SubControlVolumeFace& ownScvf, + const int localSubFaceIdx, + const bool selfIsUpstream) const + { + // distances {upstream to downstream distance, up-upstream to upstream distance, downstream staggered cell size} + std::array<Scalar, 3> distances{0.0, 0.0, 0.0}; + + // The local index of the faces that is opposite to localSubFaceIdx + const int oppositeSubFaceIdx = localSubFaceIdx % 2 ? localSubFaceIdx - 1 : localSubFaceIdx + 1; + + if (selfIsUpstream) + { + distances[0] = ownScvf.cellCenteredParallelDistance(localSubFaceIdx, 0); + distances[1] = ownScvf.cellCenteredParallelDistance(oppositeSubFaceIdx, 0); + if(ownScvf.hasParallelNeighbor(localSubFaceIdx, 0)) + distances[2] = ownScvf.pairData(localSubFaceIdx).parallelDistances[1]; + else + distances[2] = ownScvf.area() / 2.0; + } + else + { + distances[0] = ownScvf.cellCenteredParallelDistance(localSubFaceIdx, 0); + distances[1] = ownScvf.cellCenteredParallelDistance(localSubFaceIdx, 1); + distances[2] = ownScvf.area(); + } + + return distances; + } + + /*! + * \brief Return the outer parallel velocity for normal faces that are on the boundary and therefore have no neighbor. + * + * Calls the problem to retrieve a fixed value set on the boundary. + * + * \param problem The problem + * \param scvf The SubControlVolumeFace that is normal to the boundary + * \param normalFace The face at the boundary + * \param velocitySelf the velocity at scvf + * \param localSubFaceIdx The local index of the face that is on the boundary + * \param element The element that is on the boundary + * \param lateralFaceHasDirichletPressure @c true if there is a dirichlet condition for the pressure on the boundary + * \param lateralFaceHasBJS @c true if there is a BJS condition fot the velocity on the boundary + */ + Scalar getParallelVelocityFromBoundary_(const Problem& problem, + const SubControlVolumeFace& scvf, + const SubControlVolumeFace& normalFace, + const Scalar velocitySelf, + const int localSubFaceIdx, + const Element& element, + const bool lateralFaceHasDirichletPressure, + const bool lateralFaceHasBJS) const + { + // If there is a Dirichlet condition for the pressure we assume zero gradient for the velocity, + // so the velocity at the boundary equal to that on the scvf. + if (lateralFaceHasDirichletPressure) + return velocitySelf; + + const auto ghostFace = makeParallelGhostFace_(scvf, localSubFaceIdx); + if (lateralFaceHasBJS) + return problem.bjsVelocity(element, scvf, normalFace, localSubFaceIdx, velocitySelf); + return problem.dirichlet(element, ghostFace)[Indices::velocity(scvf.directionIndex())]; + } + + /*! + * \brief Return a velocity value from a boundary for which the boundary conditions have to be checked. + * + * \param problem The problem + * \param scvf The SubControlVolumeFace that is normal to the boundary + * \param localIdx The local index of the face that is on the boundary + * \param boundaryElement The element that is on the boundary + * \param parallelVelocity The velocity over scvf + */ + Scalar getParallelVelocityFromOtherBoundary_(const Problem& problem, + const FVElementGeometry& fvGeometry, + const SubControlVolumeFace& scvf, + const int localIdx, + const Element& boundaryElement, + const Scalar parallelVelocity) const + { + // A ghost subface at the boundary is created, featuring the location of the sub face's center + const SubControlVolumeFace& boundaryNormalFace = fvGeometry.scvf(scvf.insideScvIdx(), scvf.pairData(localIdx).localNormalFaceIdx); + GlobalPosition boundarySubFaceCenter = scvf.pairData(localIdx).virtualFirstParallelFaceDofPos + boundaryNormalFace.center(); + boundarySubFaceCenter *= 0.5; + const SubControlVolumeFace boundarySubFace = makeGhostFace_(boundaryNormalFace, boundarySubFaceCenter); + + // The boundary condition is checked, in case of symmetry or Dirichlet for the pressure + // a gradient of zero is assumed in the direction normal to the bounadry, while if there is + // Dirichlet of BJS for the velocity the related values are exploited. + const auto bcTypes = problem.boundaryTypes(boundaryElement, boundarySubFace); + + if (bcTypes.isDirichlet(Indices::velocity(scvf.directionIndex()))) + { + const SubControlVolumeFace ghostFace = makeParallelGhostFace_(scvf, localIdx); + return problem.dirichlet(boundaryElement, ghostFace)[Indices::velocity(scvf.directionIndex())]; + } + else if (bcTypes.isSymmetry() || bcTypes.isDirichlet(Indices::pressureIdx)) + return parallelVelocity; + else if (bcTypes.isBJS(Indices::velocity(scvf.directionIndex()))) + { + const SubControlVolumeFace ghostFace = makeParallelGhostFace_(scvf, localIdx); + return problem.bjsVelocity(boundaryElement, scvf, boundaryNormalFace, localIdx, parallelVelocity); + } + else + { + // Neumann conditions are not well implemented + DUNE_THROW(Dune::InvalidStateException, "Something went wrong with the boundary conditions for the momentum equations at global position " << boundarySubFaceCenter); + } + } + + + //! helper function to conveniently create a ghost face used to retrieve boundary values from the problem + SubControlVolumeFace makeGhostFace_(const SubControlVolumeFace& ownScvf, const GlobalPosition& pos) const + { + return SubControlVolumeFace(pos, std::vector<unsigned int>{ownScvf.insideScvIdx(), ownScvf.outsideScvIdx()}, + ownScvf.directionIndex(), ownScvf.axisData().selfDof, ownScvf.index()); + }; + + //! helper function to conveniently create a ghost face which is outside the domain, parallel to the scvf of interest + SubControlVolumeFace makeParallelGhostFace_(const SubControlVolumeFace& ownScvf, const int localSubFaceIdx) const + { + return makeGhostFace_(ownScvf, ownScvf.pairData(localSubFaceIdx).virtualFirstParallelFaceDofPos); + }; +}; + +} // end namespace Dumux + +#endif // DUMUX_NAVIERSTOKES_STAGGERED_UPWINDVARIABLES_HH diff --git a/dumux/freeflow/higherorderapproximation.hh b/dumux/freeflow/upwindingmethods.hh similarity index 66% rename from dumux/freeflow/higherorderapproximation.hh rename to dumux/freeflow/upwindingmethods.hh index 3e983074159dd35546353aafa47b63029e281bb4..18f985de6ae9d96814bb2dbe7eae4f3f4d4bdae1 100644 --- a/dumux/freeflow/higherorderapproximation.hh +++ b/dumux/freeflow/upwindingmethods.hh @@ -20,8 +20,8 @@ * \brief This file contains different higher order methods for approximating the velocity. */ -#ifndef DUMUX_HIGHER_ORDER_VELOCITY_APPROXIMATION_HH -#define DUMUX_HIGHER_ORDER_VELOCITY_APPROXIMATION_HH +#ifndef DUMUX_UPWINDING_METHODS_HH +#define DUMUX_UPWINDING_METHODS_HH #include <cmath> #include <functional> @@ -48,18 +48,18 @@ enum class DifferencingScheme * \brief This file contains different higher order methods for approximating the velocity. */ template<class Scalar> -class HigherOrderApproximation +class UpwindingMethods { public: - HigherOrderApproximation(const std::string& paramGroup = "") + UpwindingMethods(const std::string& paramGroup = "") { upwindWeight_ = getParamFromGroup<Scalar>(paramGroup, "Flux.UpwindWeight"); - if (hasParamInGroup(paramGroup, "Discretization.TvdApproach")) + if (hasParamInGroup(paramGroup, "Flux.TvdApproach")) { // Read the runtime parameters - tvdApproach_ = tvdApproachFromString(getParamFromGroup<std::string>(paramGroup, "Discretization.TvdApproach")); - differencingScheme_ = differencingSchemeFromString(getParamFromGroup<std::string>(paramGroup, "Discretization.DifferencingScheme")); + tvdApproach_ = tvdApproachFromString(getParamFromGroup<std::string>(paramGroup, "Flux.TvdApproach")); + differencingScheme_ = differencingSchemeFromString(getParamFromGroup<std::string>(paramGroup, "Flux.DifferencingScheme")); // Assign the limiter_ depending on the differencing scheme switch (differencingScheme_) @@ -116,6 +116,8 @@ public: tvdApproach_ = TvdApproach::none; differencingScheme_ = DifferencingScheme::none; } + std::cout << "Using the tvdApproach " << tvdApproachToString(tvdApproach_) + << " and the differencing Scheme " << differencingSchemeToString(differencingScheme_) << "\n"; } /** @@ -129,9 +131,9 @@ public: if (tvd == "Hou") return TvdApproach::hou; DUNE_THROW(ParameterException, "\nThis tvd approach : \"" << tvd << "\" is not implemented.\n" << "The available TVD approaches for uniform (and nonuniform) grids are as follows: \n" - << tvdApproachToString(TvdApproach::uniform) << ": Assumes a uniform cell size distribution\n" - << tvdApproachToString(TvdApproach::li) << ": Li's approach for nonuniform cell sizes\n" - << tvdApproachToString(TvdApproach::hou) << ": Hou's approach for nonuniform cell sizes"); + << tvdApproachToString(TvdApproach::uniform) << ": assumes a uniform cell size distribution\n" + << tvdApproachToString(TvdApproach::li) << ": li's approach for nonuniform cell sizes\n" + << tvdApproachToString(TvdApproach::hou) << ": hou's approach for nonuniform cell sizes"); } /** @@ -154,13 +156,13 @@ public: */ DifferencingScheme differencingSchemeFromString(const std::string& differencingScheme) { - if (differencingScheme == "vanleer") return DifferencingScheme::vanleer; - if (differencingScheme == "vanalbada") return DifferencingScheme::vanalbada; - if (differencingScheme == "minmod") return DifferencingScheme::minmod; - if (differencingScheme == "superbee") return DifferencingScheme::superbee; - if (differencingScheme == "umist") return DifferencingScheme::umist; - if (differencingScheme == "mclimiter") return DifferencingScheme::mclimiter; - if (differencingScheme == "wahyd") return DifferencingScheme::wahyd; + if (differencingScheme == "Vanleer") return DifferencingScheme::vanleer; + if (differencingScheme == "Vanalbada") return DifferencingScheme::vanalbada; + if (differencingScheme == "Minmod") return DifferencingScheme::minmod; + if (differencingScheme == "Superbee") return DifferencingScheme::superbee; + if (differencingScheme == "Umist") return DifferencingScheme::umist; + if (differencingScheme == "Mclimiter") return DifferencingScheme::mclimiter; + if (differencingScheme == "Wahyd") return DifferencingScheme::wahyd; DUNE_THROW(ParameterException, "\nThis differencing scheme: \"" << differencingScheme << "\" is not implemented.\n" << "The available differencing schemes are as follows: \n" << differencingSchemeToString(DifferencingScheme::vanleer) << ": The Vanleer flux limiter\n" @@ -179,18 +181,17 @@ public: { switch (differencingScheme) { - case DifferencingScheme::vanleer: return "vanleer"; - case DifferencingScheme::vanalbada: return "vanalbada"; - case DifferencingScheme::minmod: return "minmod"; - case DifferencingScheme::superbee: return "superbee"; - case DifferencingScheme::umist: return "umist"; - case DifferencingScheme::mclimiter: return "mclimiter"; - case DifferencingScheme::wahyd: return "wahyd"; + case DifferencingScheme::vanleer: return "Vanleer"; + case DifferencingScheme::vanalbada: return "Vanalbada"; + case DifferencingScheme::minmod: return "Minmod"; + case DifferencingScheme::superbee: return "Superbee"; + case DifferencingScheme::umist: return "Umist"; + case DifferencingScheme::mclimiter: return "Mclimiter"; + case DifferencingScheme::wahyd: return "Wahyd"; default: return "Invalid"; // should never be reached } } - /** * \brief Upwind Method */ @@ -200,60 +201,13 @@ public: return (upwindWeight_ * upstreamMomentum + (1.0 - upwindWeight_) * downstreamMomentum); } - /** - * \brief Central Differencing Method - */ - Scalar centralDifference(const Scalar downstreamVelocity, - const Scalar upstreamVelocity, - const Scalar density) const - { - return (0.5 * (upstreamVelocity + downstreamVelocity)) * density; - } - - /** - * \brief Linear Upwind Method - */ - Scalar linearUpwind(const Scalar upstreamVelocity, - const Scalar upUpstreamVelocity, - const Scalar upstreamToDownstreamDistance, - const Scalar upUpstreamToUpstreamDistance, - const Scalar density) const - { - Scalar zeroOrder = upstreamVelocity; - Scalar firstOrder = -1.0 * ((upstreamVelocity - upUpstreamVelocity) / upUpstreamToUpstreamDistance) * ( upstreamToDownstreamDistance / -2.0); - return (zeroOrder + firstOrder) * density; - } - - /** - * \brief QUICK upwinding Scheme: Quadratic Upstream Interpolation for Convective Kinematics - */ - Scalar upwindQUICK(const Scalar downstreamVelocity, - const Scalar upstreamVelocity, - const Scalar upUpstreamVelocity, - const Scalar upstreamToDownstreamDistance, - const Scalar upUpstreamToUpstreamDistance, - const Scalar density) const - { - Scalar normalDistance = (upUpstreamToUpstreamDistance + upstreamToDownstreamDistance) / 2.0; - Scalar zeroOrder = upstreamVelocity; - Scalar firstOrder = ((downstreamVelocity - upstreamVelocity) / 2.0); - Scalar secondOrder = -(((downstreamVelocity - upstreamVelocity) / upstreamToDownstreamDistance) - ((upstreamVelocity - upUpstreamVelocity) / upUpstreamToUpstreamDistance)) - * upstreamToDownstreamDistance * upstreamToDownstreamDistance / (8.0 * normalDistance); - return (zeroOrder + firstOrder + secondOrder) * density; - } - /** * \brief Tvd Scheme: Total Variation Diminishing * */ - Scalar tvd(const Scalar downstreamVelocity, - const Scalar upstreamVelocity, - const Scalar upUpstreamVelocity, - const Scalar upstreamToDownstreamDistance, - const Scalar upUpstreamToUpstreamDistance, - const Scalar downstreamStaggeredCellSize, + Scalar tvd(const std::array<Scalar,3>& momenta, + const std::array<Scalar,3>& distances, const bool selfIsUpstream, - const Scalar density, const TvdApproach tvdApproach) const { Scalar momentum = 0.0; @@ -261,17 +215,17 @@ public: { case TvdApproach::uniform : { - momentum += tvdUniform(downstreamVelocity, upstreamVelocity, upUpstreamVelocity, density); + momentum += tvdUniform(momenta, distances, selfIsUpstream); break; } case TvdApproach::li : { - momentum += tvdLi(downstreamVelocity, upstreamVelocity, upUpstreamVelocity, upstreamToDownstreamDistance, upUpstreamToUpstreamDistance, selfIsUpstream, density); + momentum += tvdLi(momenta, distances, selfIsUpstream); break; } case TvdApproach::hou : { - momentum += tvdHou(downstreamVelocity, upstreamVelocity, upUpstreamVelocity, upstreamToDownstreamDistance, upUpstreamToUpstreamDistance, downstreamStaggeredCellSize, density); + momentum += tvdHou(momenta, distances, selfIsUpstream); break; } default: @@ -288,22 +242,24 @@ public: * * This function assumes the cell size distribution to be uniform. */ - Scalar tvdUniform(const Scalar downstreamVelocity, - const Scalar upstreamVelocity, - const Scalar upUpstreamVelocity, - const Scalar density) const + Scalar tvdUniform(const std::array<Scalar,3>& momenta, + const std::array<Scalar,3>& distances, + const bool selfIsUpstream) const { using std::isfinite; - const Scalar ratio = (upstreamVelocity - upUpstreamVelocity) / (downstreamVelocity - upstreamVelocity); + const Scalar downstreamMomentum = momenta[0]; + const Scalar upstreamMomentum = momenta[1]; + const Scalar upUpstreamMomentum = momenta[2]; + const Scalar ratio = (upstreamMomentum - upUpstreamMomentum) / (downstreamMomentum - upstreamMomentum); // If the velocity field is uniform (like at the first newton step) we get a NaN if(ratio > 0.0 && isfinite(ratio)) { - const Scalar secondOrderTerm = 0.5 * limiter_(ratio, 2.0) * (downstreamVelocity - upstreamVelocity); - return density * (upstreamVelocity + secondOrderTerm); + const Scalar secondOrderTerm = 0.5 * limiter_(ratio, 2.0) * (downstreamMomentum - upstreamMomentum); + return (upstreamMomentum + secondOrderTerm); } else - return density * upstreamVelocity; + return upstreamMomentum; } /** @@ -313,31 +269,33 @@ public: * It tries to reconstruct the value for the velocity at the upstream-upstream point * if the grid was uniform. */ - Scalar tvdLi(const Scalar downstreamVelocity, - const Scalar upstreamVelocity, - const Scalar upUpstreamVelocity, - const Scalar upstreamToDownstreamDistance, - const Scalar upUpstreamToUpstreamDistance, - const bool selfIsUpstream, - const Scalar density) const + Scalar tvdLi(const std::array<Scalar,3>& momenta, + const std::array<Scalar,3>& distances, + const bool selfIsUpstream) const { using std::isfinite; - // I need the information of selfIsUpstream to get the correct sign because upUpstreamToUpstreamDistance is always positive - const Scalar upUpstreamGradient = (upstreamVelocity - upUpstreamVelocity) / upUpstreamToUpstreamDistance * selfIsUpstream; + const Scalar downstreamMomentum = momenta[0]; + const Scalar upstreamMomentum = momenta[1]; + const Scalar upUpstreamMomentum = momenta[2]; + const Scalar upstreamToDownstreamDistance = distances[0]; + const Scalar upUpstreamToUpstreamDistance = distances[1]; + + // selfIsUpstream is required to get the correct sign because upUpstreamToUpstreamDistance is always positive + const Scalar upUpstreamGradient = (upstreamMomentum - upUpstreamMomentum) / upUpstreamToUpstreamDistance * selfIsUpstream; // Distance between the upUpstream node and the position where it should be if the grid were uniform. const Scalar correctionDistance = upUpstreamToUpstreamDistance - upstreamToDownstreamDistance; - const Scalar reconstrutedUpUpstreamVelocity = upUpstreamVelocity + upUpstreamGradient * correctionDistance; - const Scalar ratio = (upstreamVelocity - reconstrutedUpUpstreamVelocity) / (downstreamVelocity - upstreamVelocity); + const Scalar reconstrutedUpUpstreamVelocity = upUpstreamMomentum + upUpstreamGradient * correctionDistance; + const Scalar ratio = (upstreamMomentum - reconstrutedUpUpstreamVelocity) / (downstreamMomentum - upstreamMomentum); // If the velocity field is uniform (like at the first newton step) we get a NaN if(ratio > 0.0 && isfinite(ratio)) { - const Scalar secondOrderTerm = 0.5 * limiter_(ratio, 2.0) * (downstreamVelocity - upstreamVelocity); - return density * (upstreamVelocity + secondOrderTerm); + const Scalar secondOrderTerm = 0.5 * limiter_(ratio, 2.0) * (downstreamMomentum - upstreamMomentum); + return (upstreamMomentum + secondOrderTerm); } else - return density * upstreamVelocity; + return upstreamMomentum; } /** @@ -346,16 +304,18 @@ public: * This function manages the non uniformities of the grid according to [Hou, Simons, Hinkelmann 2007]. * It should behave better then the Li's version in very stretched grids. */ - Scalar tvdHou(const Scalar downstreamVelocity, - const Scalar upstreamVelocity, - const Scalar upUpstreamVelocity, - const Scalar upstreamToDownstreamDistance, - const Scalar upUpstreamToUpstreamDistance, - const Scalar downstreamStaggeredCellSize, - const Scalar density) const + Scalar tvdHou(const std::array<Scalar,3>& momenta, + const std::array<Scalar,3>& distances, + const bool selfIsUpstream) const { using std::isfinite; - const Scalar ratio = (upstreamVelocity - upUpstreamVelocity) / (downstreamVelocity - upstreamVelocity) + const Scalar downstreamMomentum = momenta[0]; + const Scalar upstreamMomentum = momenta[1]; + const Scalar upUpstreamMomentum = momenta[2]; + const Scalar upstreamToDownstreamDistance = distances[0]; + const Scalar upUpstreamToUpstreamDistance = distances[1]; + const Scalar downstreamStaggeredCellSize = distances[2]; + const Scalar ratio = (upstreamMomentum - upUpstreamMomentum) / (downstreamMomentum - upstreamMomentum) * upstreamToDownstreamDistance / upUpstreamToUpstreamDistance; // If the velocity field is uniform (like at the first newton step) we get a NaN @@ -363,11 +323,11 @@ public: { const Scalar upstreamStaggeredCellSize = 0.5 * (upstreamToDownstreamDistance + upUpstreamToUpstreamDistance); const Scalar R = (upstreamStaggeredCellSize + downstreamStaggeredCellSize) / upstreamStaggeredCellSize; - const Scalar secondOrderTerm = limiter_(ratio, R) / R * (downstreamVelocity - upstreamVelocity); - return density * (upstreamVelocity + secondOrderTerm); + const Scalar secondOrderTerm = limiter_(ratio, R) / R * (downstreamMomentum - upstreamMomentum); + return (upstreamMomentum + secondOrderTerm); } else - return density * upstreamVelocity; + return upstreamMomentum; } /** @@ -459,4 +419,4 @@ private: } // end namespace Dumux -#endif // DUMUX_HIGHER_ORDER_VELOCITY_APPROXIMATION_HH +#endif // DUMUX_UPWINDING_METHODS_HH diff --git a/test/freeflow/navierstokes/kovasznay/CMakeLists.txt b/test/freeflow/navierstokes/kovasznay/CMakeLists.txt index f549843b312c1e46c5a53c91d6af8746889a7d06..3b2f353413d0744c1d8f05116eb4fbadeee4c1e0 100644 --- a/test/freeflow/navierstokes/kovasznay/CMakeLists.txt +++ b/test/freeflow/navierstokes/kovasznay/CMakeLists.txt @@ -2,6 +2,7 @@ dune_add_test(NAME test_ff_navierstokes_kovasznay LABELS freeflow navierstokes SOURCES main.cc CMAKE_GUARD HAVE_UMFPACK + COMPILE_DEFINITIONS UPWINDSCHEMEORDER=1 COMMAND ${CMAKE_SOURCE_DIR}/bin/testing/runtest.py CMD_ARGS --script fuzzy --files ${CMAKE_SOURCE_DIR}/test/references/test_ff_navierstokes_kovasznay-reference.vtu @@ -10,14 +11,17 @@ dune_add_test(NAME test_ff_navierstokes_kovasznay -Problem.Name test_ff_navierstokes_kovasznay") dune_add_test(NAME test_ff_navierstokes_kovasznay_higherorder - TARGET test_ff_navierstokes_kovasznay - LABELS freeflow navierstokes + SOURCES main.cc + LABELS freeflow + CMAKE_GUARD HAVE_UMFPACK + COMPILE_DEFINITIONS UPWINDSCHEMEORDER=2 COMMAND ${CMAKE_SOURCE_DIR}/bin/testing/runtest.py CMD_ARGS --script fuzzy --files ${CMAKE_SOURCE_DIR}/test/references/test_ff_navierstokes_kovasznay_higherorder-reference.vtu ${CMAKE_CURRENT_BINARY_DIR}/test_ff_navierstokes_kovasznay_higherorder-00001.vtu - --command "${CMAKE_CURRENT_BINARY_DIR}/test_ff_navierstokes_kovasznay params.input + --command "${CMAKE_CURRENT_BINARY_DIR}/test_ff_navierstokes_kovasznay_higherorder params.input -Problem.Name test_ff_navierstokes_kovasznay_higherorder - -Discretization.TvdApproach Hou - -Discretization.DifferencingScheme vanleer") + -Flux.TvdApproach Hou + -Flux.DifferencingScheme Vanleer") + dune_symlink_to_source_files(FILES "params.input") diff --git a/test/freeflow/navierstokes/kovasznay/params.input b/test/freeflow/navierstokes/kovasznay/params.input index 0f47acc145b5673a089a530996221e5acf63b58a..e3dc1f6b2da8eab3b09ec58d6342113be4c52043 100644 --- a/test/freeflow/navierstokes/kovasznay/params.input +++ b/test/freeflow/navierstokes/kovasznay/params.input @@ -19,3 +19,7 @@ MaxRelativeShift = 1e-5 [Vtk] WriteFaceData = false + +[Flux] +TvdApproach = Hou +DifferencingScheme = Vanleer diff --git a/test/freeflow/navierstokes/kovasznay/problem.hh b/test/freeflow/navierstokes/kovasznay/problem.hh index bbbb7ab1ec1dab8c282c93977486df2f1cbda0d9..8a228736b3221b7e6c934b2e9ea984593a73e970 100644 --- a/test/freeflow/navierstokes/kovasznay/problem.hh +++ b/test/freeflow/navierstokes/kovasznay/problem.hh @@ -25,6 +25,10 @@ #ifndef DUMUX_KOVASZNAY_TEST_PROBLEM_HH #define DUMUX_KOVASZNAY_TEST_PROBLEM_HH +#ifndef UPWINDSCHEMEORDER +#define UPWINDSCHEMEORDER 0 +#endif + #include <dune/grid/yaspgrid.hh> #include <dumux/material/fluidsystems/1pliquid.hh> @@ -68,6 +72,9 @@ template<class TypeTag> struct EnableGridFluxVariablesCache<TypeTag, TTag::KovasznayTest> { static constexpr bool value = true; }; template<class TypeTag> struct EnableGridVolumeVariablesCache<TypeTag, TTag::KovasznayTest> { static constexpr bool value = true; }; + +template<class TypeTag> +struct UpwindSchemeOrder<TypeTag, TTag::KovasznayTest> { static constexpr int value = UPWINDSCHEMEORDER; }; } // end namespace Properties /*! @@ -99,12 +106,14 @@ class KovasznayTestProblem : public NavierStokesProblem<TypeTag> using GlobalPosition = typename Element::Geometry::GlobalCoordinate; using VelocityVector = Dune::FieldVector<Scalar, dimWorld>; + static constexpr auto upwindSchemeOrder = getPropValue<TypeTag, Properties::UpwindSchemeOrder>(); + public: KovasznayTestProblem(std::shared_ptr<const FVGridGeometry> fvGridGeometry) : ParentType(fvGridGeometry), eps_(1e-6) { printL2Error_ = getParam<bool>("Problem.PrintL2Error"); - + std::cout<< "upwindSchemeOrder is: " << FVGridGeometry::order() << "\n"; kinematicViscosity_ = getParam<Scalar>("Component.LiquidKinematicViscosity", 1.0); Scalar reynoldsNumber = 1.0 / kinematicViscosity_; lambda_ = 0.5 * reynoldsNumber