diff --git a/dumux/discretization/facecentered/staggered/fvelementgeometry.hh b/dumux/discretization/facecentered/staggered/fvelementgeometry.hh index 58d008df61c90e894a5d3a32b1080a7a9569912d..357cdd273da04a3cee8bd98277a3ce197007040b 100644 --- a/dumux/discretization/facecentered/staggered/fvelementgeometry.hh +++ b/dumux/discretization/facecentered/staggered/fvelementgeometry.hh @@ -197,6 +197,14 @@ public: const Element& element() const { return *elementPtr_; } + //! Returns true if the IP of an scvf lies on a concave corner + bool scvfIntegrationPointInConcaveCorner(const SubControlVolumeFace& scvf) const + { return GG::GeometryHelper::scvfIntegrationPointInConcaveCorner(*this, scvf); } + + //! Returns the the scvf of neighbor element with the same integration point and unit outer normal + const SubControlVolumeFace& outsideScvfWithSameIntegrationPoint(const SubControlVolumeFace& scvf) const + { return GG::GeometryHelper::outsideScvfWithSameIntegrationPoint(*this, scvf); } + private: const auto& scvfIndices_() const @@ -249,7 +257,7 @@ public: , geometryHelper_(gridGeometry.gridView()) {} - //! Get a sub control volume face with a local scv index + //! Get a sub control volume face with a global scvf index const SubControlVolumeFace& scvf(const GridIndexType scvfIdx) const { return scvfs_[findLocalIndex_(scvfIdx, scvfIndices_())]; } @@ -385,6 +393,14 @@ public: return *gridGeometry_; } + //! Returns true if the IP of an scvf lies on a concave corner + bool scvfIntegrationPointInConcaveCorner(const SubControlVolumeFace& scvf) const + { return GG::GeometryHelper::scvfIntegrationPointInConcaveCorner(*this, scvf); } + + //! Returns the the scvf of neighbor element with the same integration point and unit outer normal + const SubControlVolumeFace& outsideScvfWithSameIntegrationPoint(const SubControlVolumeFace& scvf) const + { return GG::GeometryHelper::outsideScvfWithSameIntegrationPoint(*this, scvf); } + private: //! Binding of an element preparing the geometries of the whole stencil //! called by the local jacobian to prepare element assembly diff --git a/dumux/discretization/facecentered/staggered/fvgridgeometry.hh b/dumux/discretization/facecentered/staggered/fvgridgeometry.hh index 7158921f2f9422e6dd2374b5962dab42fa882074..5830e63d591f15ed4db3dc6f39d0c54ce8924883 100644 --- a/dumux/discretization/facecentered/staggered/fvgridgeometry.hh +++ b/dumux/discretization/facecentered/staggered/fvgridgeometry.hh @@ -61,7 +61,7 @@ struct FaceCenteredStaggeredDefaultGridGeometryTraits : public DefaultMapperTrai using SubControlVolumeFace = FaceCenteredStaggeredSubControlVolumeFace<GridView>; using IntersectionMapper = ConformingGridIntersectionMapper<GridView>; using LocalIntersectionMapper = FaceCenteredStaggeredLocalIntersectionIndexMapper<GridView>; - using GeometryHelper = FaceCenteredStaggeredGeometryHelper<GridView, typename GridView::Grid>; + using GeometryHelper = FaceCenteredStaggeredGeometryHelper<GridView>; template<class GridGeometry> using ConnectivityMap = FaceCenteredStaggeredConnectivityMap<GridGeometry>; diff --git a/dumux/discretization/facecentered/staggered/geometryhelper.hh b/dumux/discretization/facecentered/staggered/geometryhelper.hh index 80bb2433f5c055adcdfe8065dd93267c12aeb127..73013f44f2b392f1b1c2d4bae3e93aec627c8b8d 100644 --- a/dumux/discretization/facecentered/staggered/geometryhelper.hh +++ b/dumux/discretization/facecentered/staggered/geometryhelper.hh @@ -24,25 +24,15 @@ #ifndef DUMUX_DISCRETIZATION_FACECENTERED_STAGGERED_GEOMETRY_HELPER_HH #define DUMUX_DISCRETIZATION_FACECENTERED_STAGGERED_GEOMETRY_HELPER_HH -#include <dune/common/float_cmp.hh> +#include <array> +#include <cassert> #include <dumux/common/indextraits.hh> -#include <dune/common/reservedvector.hh> -#include <dumux/common/math.hh> -#include <dumux/discretization/basegridgeometry.hh> -#include <dumux/discretization/checkoverlapsize.hh> -#include <dumux/discretization/method.hh> - -namespace Dune { - -template<int dim, class Coordinates> -class YaspGrid; - -} +#include <dumux/discretization/facecentered/staggered/gridsupportsconcavecorners.hh> namespace Dumux { -template<class GridView, class Implementation> -class FaceCenteredStaggeredGeometryHelperBase +template<class GridView> +class FaceCenteredStaggeredGeometryHelper { using GridIndexType = typename IndexTraits<GridView>::GridIndex; using SmallLocalIndexType = typename IndexTraits<GridView>::SmallLocalIndex; @@ -54,15 +44,15 @@ public: static constexpr auto numElementFaces = dim * 2; static constexpr auto numLateralFacesPerScv = 2 * (dim - 1); - FaceCenteredStaggeredGeometryHelperBase(const GridView& gridView) : gridView_(gridView) {} + FaceCenteredStaggeredGeometryHelper(const GridView& gridView) : gridView_(gridView) {} //! Returns the local index of the opposing face. static constexpr SmallLocalIndexType localOppositeIdx(const SmallLocalIndexType ownLocalFaceIndex) { - return (ownLocalFaceIndex % 2) ? (ownLocalFaceIndex - 1) : (ownLocalFaceIndex + 1); + return isOdd_(ownLocalFaceIndex) ? (ownLocalFaceIndex - 1) : (ownLocalFaceIndex + 1); } - //! Return the local index of a lateral orthogonal scvf + //! Return the local index of a lateral orthogonal scvf static constexpr int lateralOrthogonalScvfLocalIndex(const SmallLocalIndexType ownLocalScvfIndex) { if constexpr(GridView::Grid::dimension == 1) @@ -128,7 +118,6 @@ public: } } - //! Returns the local indices of the faces lateral to the own one. static constexpr auto localLaterFaceIndices(const SmallLocalIndexType ownLocalFaceIndex) { @@ -163,144 +152,60 @@ public: DUNE_THROW(Dune::InvalidStateException, "localFacetIdx " << localFacetIdx << " out of range"); } - //! Map two local facet indices to a local common entity index (vertex in 2D or edge in 3D) - SmallLocalIndexType localCommonEntityIndex(SmallLocalIndexType localFacetIdx0, SmallLocalIndexType localFacetIdx1) const + //! Returns true if the IP of an scvf lies on a concave corner + template<class FVElementGeometry, class SubControlVolumeFace> + static bool scvfIntegrationPointInConcaveCorner(const FVElementGeometry& fvGeometry, const SubControlVolumeFace& scvf) { - constexpr auto table = [] - { - using Table = std::array<std::array<SmallLocalIndexType, 2>, dim == 2 ? 4 : 12>; - if constexpr (dim == 2) - return Table {{ {0,2}, {1,2}, {0,3}, {1,3} }}; - else - return Table {{ {0,2}, {1,2}, {0,3}, {1,3}, {0,4}, {1,4}, {2,4}, {3,4}, {0,5}, {1,5}, {2,5}, {3,5} }}; - }(); - - using std::swap; - if (localFacetIdx0 > localFacetIdx1) - swap(localFacetIdx0, localFacetIdx1); - - const auto idx = std::find(table.begin(), table.end(), std::array<SmallLocalIndexType, 2>{localFacetIdx0, localFacetIdx1}); + assert (scvf.isLateral()); - if (idx != std::end(table)) - return std::distance(table.begin(), idx); + using Grid = typename FVElementGeometry::GridGeometry::Grid; + if constexpr (!GridSupportsConcaveCorners<Grid>::value) + return false; else - DUNE_THROW(Dune::InvalidStateException, "Faces with local indices " << localFacetIdx0 << " and " << localFacetIdx1 << " do not intersect"); - } - - //! Map two facets to a global common entity index (vertex in 2D or edge in 3D) - auto globalCommonEntityIndex(const Element& element, SmallLocalIndexType localFacetIdx0, SmallLocalIndexType localFacetIdx1) const - { - const auto refElement = referenceElement(element); - - assert(refElement.size(localFacetIdx0, 1, 2) == refElement.size(localFacetIdx1, 1, 2)); - std::array<GridIndexType, dim == 2 ? 2 : 4> cache = {}; - std::bitset<dim == 2 ? 2 : 4> valueCached = {}; - - for (int i = 0; i < refElement.size(localFacetIdx0, 1, 2); ++i) { - // get local sub index of subentity w.r.t. to the element - const auto localEntityIdx0 = refElement.subEntity(localFacetIdx0, 1, i, 2); - // and with the local index we can get the global index of the vertex/edge (2d/3d) - const auto globalEntityIdx0 = gridView().indexSet().subIndex(element, localEntityIdx0, 2); - for (int j = 0; j < refElement.size(localFacetIdx1, 1, 2); ++j) - { - const auto globalEntityIdx1 = valueCached[j] ? cache[j] - : gridView().indexSet().subIndex(element, refElement.subEntity(localFacetIdx1, 1, j, 2), 2); - - if (globalEntityIdx0 == globalEntityIdx1) - return globalEntityIdx0; - else - { - cache[j] = globalEntityIdx1; - valueCached.set(j); - } - } + if (scvf.boundary()) + return false; + + const auto& insideScv = fvGeometry.scv(scvf.insideScvIdx()); + const auto& outsideScv = fvGeometry.scv(scvf.outsideScvIdx()); + int onBoundaryCounter = 0; + onBoundaryCounter += static_cast<int>(insideScv.boundary()); + onBoundaryCounter += static_cast<int>(outsideScv.boundary()); + return onBoundaryCounter == 1; } - DUNE_THROW(Dune::InvalidStateException, "No common entity found"); } - const GridView& gridView() const - { return gridView_; } - -protected: - - Implementation &asImp() - { return *static_cast<Implementation*>(this); } - - const Implementation &asImp() const - { return *static_cast<const Implementation*>(this); } - -private: - GridView gridView_; -}; + template<class FVElementGeometry, class SubControlVolumeFace> + static const SubControlVolumeFace& outsideScvfWithSameIntegrationPoint(const FVElementGeometry& fvGeometry, const SubControlVolumeFace& scvf) + { + const auto& lateralOrthogonalScvf = fvGeometry.lateralOrthogonalScvf(scvf); + assert(!lateralOrthogonalScvf.boundary()); + const int offset = (dim == 2) ? 3 : 5; + const auto otherLocalIdx = isOdd_(scvf.localIndex()) ? scvf.localIndex() - offset : scvf.localIndex() + offset; -template<class GridView, class Grid = typename GridView::Grid> -class FaceCenteredStaggeredGeometryHelper - : public FaceCenteredStaggeredGeometryHelperBase<GridView, FaceCenteredStaggeredGeometryHelper<GridView, Grid>> -{ - using ParentType = FaceCenteredStaggeredGeometryHelperBase<GridView, FaceCenteredStaggeredGeometryHelper<GridView, Grid>>; - using SmallLocalIndexType = typename IndexTraits<GridView>::SmallLocalIndex; - using Element = typename GridView::template Codim<0>::Entity; -public: - - using ParentType::ParentType; + auto outsideFVGeometry = localView(fvGeometry.gridGeometry()); + const auto outsideElementIdx = fvGeometry.scv(lateralOrthogonalScvf.outsideScvIdx()).elementIndex(); + outsideFVGeometry.bindElement(fvGeometry.gridGeometry().element(outsideElementIdx)); - //! Update the map for getting the corresponding local face indices in another element. - void update(const Element& ownElement, const Element& otherElement) - { - SmallLocalIndexType ownIdx = 0; - SmallLocalIndexType otherIdx = 0; - for (const auto& ownIs : intersections(this->gridView(), ownElement)) + for (const auto& otherScvf : scvfs(outsideFVGeometry)) { - // helper lambda to make sure the inner loop stops once the other index is found - const auto computeOtherIdx = [&] (const auto& ownOuterNormal) - { - for (const auto& otherIs : intersections(this->gridView(), otherElement)) - { - const auto& otherOuterNormal = otherIs.centerUnitOuterNormal(); - if (Dune::FloatCmp::eq<typename GridView::ctype>(ownOuterNormal*otherOuterNormal, 1.0)) - { - map_[ownIdx++] = otherIdx++; - return; - } - } - }; - - computeOtherIdx(ownIs.centerUnitOuterNormal()); + if (otherScvf.localIndex() == otherLocalIdx) + return otherScvf; } - if (ownIdx != map_.size()) - DUNE_THROW(Dune::InvalidStateException, "Index could not be mapped"); + + DUNE_THROW(Dune::InvalidStateException, "No outside scvf found"); } - //! Return the local index of the other element's facet with the same position as the own element's facet. - SmallLocalIndexType localFaceIndexInOtherElement(const SmallLocalIndexType localFaceIndexInOwnElement) const - { return map_[localFaceIndexInOwnElement]; } + const GridView& gridView() const + { return gridView_; } private: - std::array<SmallLocalIndexType, ParentType::numElementFaces> map_; -}; -template<class GridView, class YaspCoordinates> -class FaceCenteredStaggeredGeometryHelper<GridView, Dune::YaspGrid<GridView::Grid::dimension, YaspCoordinates>> - : public FaceCenteredStaggeredGeometryHelperBase<GridView, FaceCenteredStaggeredGeometryHelper<GridView, typename GridView::Grid>> -{ - using ParentType = FaceCenteredStaggeredGeometryHelperBase<GridView, FaceCenteredStaggeredGeometryHelper<GridView, typename GridView::Grid>>; - using SmallLocalIndexType = typename IndexTraits<GridView>::SmallLocalIndex; - using Element = typename GridView::template Codim<0>::Entity; -public: + static constexpr bool isOdd_(int number) + { return number % 2; } - using ParentType::ParentType; - - //! Update the map for getting the corresponding local face indices in another element. - //! Nothing needs to be done here. - void update(const Element& ownElement, const Element& otherElement) - {} - - //! Return the local index of the other element's facet with the same position as the own element's facet. - //! For Yasp grids, this is just the same index. - SmallLocalIndexType localFaceIndexInOtherElement(const SmallLocalIndexType localFaceIndexInOwnElement) const - { return localFaceIndexInOwnElement; } + GridView gridView_; }; } // end namespace Dumux diff --git a/dumux/discretization/facecentered/staggered/gridsupportsconcavecorners.hh b/dumux/discretization/facecentered/staggered/gridsupportsconcavecorners.hh new file mode 100644 index 0000000000000000000000000000000000000000..60584593dbdee4cfccd2d1df66767ff45d99babf --- /dev/null +++ b/dumux/discretization/facecentered/staggered/gridsupportsconcavecorners.hh @@ -0,0 +1,50 @@ +// -*- 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 Typetraits + * \copydoc Type trait to determine if a grid supports concave corners (e.g. by cutting out a hole from the domain interior) + */ +#ifndef DUMUX_DISCRETIZATION_FACECENTERED_STAGGERED_GRID_SUPPORTS_CONCAVE_CORNERS_HH +#define DUMUX_DISCRETIZATION_FACECENTERED_STAGGERED_GRID_SUPPORTS_CONCAVE_CORNERS_HH + +#include <type_traits> + +// forward declare +namespace Dune { + +template<int dim, class Coordinates> +class YaspGrid; + +} + +namespace Dumux { + +/*! + * \brief Type trait to determine if a grid supports concave corners (e.g. by cutting out a hole from the domain interior) + */ +template<class T> +struct GridSupportsConcaveCorners : public std::true_type {}; + +template<int dim, class Coords> +struct GridSupportsConcaveCorners<Dune::YaspGrid<dim, Coords>> : public std::false_type {}; + +} // end namespace Dumux + +#endif diff --git a/dumux/freeflow/navierstokes/momentum/fluxhelper.hh b/dumux/freeflow/navierstokes/momentum/fluxhelper.hh index 7a023de98d7d18dfddf5a5d54747345108aec2ff..2bbc646ca02d512600aefa1f3e5e736909c0ccc3 100644 --- a/dumux/freeflow/navierstokes/momentum/fluxhelper.hh +++ b/dumux/freeflow/navierstokes/momentum/fluxhelper.hh @@ -127,28 +127,39 @@ struct NavierStokesMomentumBoundaryFluxHelper const auto& orthogonalScvf = fvGeometry.lateralOrthogonalScvf(scvf); const auto innerTransportingVelocity = elemVolVars[orthogonalScvf.insideScvIdx()].velocity(); - if (scvf.boundary()) + static const bool useOldScheme = getParam<bool>("FreeFlow.UseOldTransportingVelocity", true); // TODO how to deprecate? + + if (useOldScheme) { - if (const auto bcTypes = problem.boundaryTypes(element, scvf); bcTypes.isDirichlet(scvf.normalAxis())) - return problem.dirichlet(element, scvf)[scvf.normalAxis()]; + if (scvf.boundary()) + { + if (problem.boundaryTypes(element, scvf).isDirichlet(scvf.normalAxis())) + return problem.dirichlet(element, scvf)[scvf.normalAxis()]; + } else - return - innerTransportingVelocity; // fallback + return innerTransportingVelocity; } - else + + // use the Dirichlet velocity as transporting velocity if the lateral face is on a Dirichlet boundary + if (scvf.boundary()) { - static const bool useOldScheme = getParam<bool>("FreeFlow.UseOldTransportingVelocity", true); // TODO how to deprecate? - if (useOldScheme) - return innerTransportingVelocity; + if (problem.boundaryTypes(element, scvf).isDirichlet(scvf.normalAxis())) + return 0.5*(problem.dirichlet(element, scvf)[scvf.normalAxis()] + innerTransportingVelocity); + } + + if (orthogonalScvf.boundary()) + { + if (problem.boundaryTypes(element, orthogonalScvf).isDirichlet(scvf.normalAxis())) + return 0.5*(problem.dirichlet(element, scvf)[scvf.normalAxis()] + innerTransportingVelocity); else - { - // average the transporting velocity by weighting with the scv volumes - const auto insideVolume = fvGeometry.scv(orthogonalScvf.insideScvIdx()).volume(); - const auto outsideVolume = fvGeometry.scv(orthogonalScvf.outsideScvIdx()).volume(); - const auto outerTransportingVelocity = elemVolVars[orthogonalScvf.outsideScvIdx()].velocity(); - return (insideVolume*innerTransportingVelocity + outsideVolume*outerTransportingVelocity) / (insideVolume + outsideVolume); - } + return innerTransportingVelocity; // fallback value, should actually never be called } + + // average the transporting velocity by weighting with the scv volumes + const auto insideVolume = fvGeometry.scv(orthogonalScvf.insideScvIdx()).volume(); + const auto outsideVolume = fvGeometry.scv(orthogonalScvf.outsideScvIdx()).volume(); + const auto outerTransportingVelocity = elemVolVars[orthogonalScvf.outsideScvIdx()].velocity(); + return (insideVolume*innerTransportingVelocity + outsideVolume*outerTransportingVelocity) / (insideVolume + outsideVolume); }(); // lateral face normal to boundary (integration point touches boundary) diff --git a/dumux/freeflow/navierstokes/momentum/fluxvariables.hh b/dumux/freeflow/navierstokes/momentum/fluxvariables.hh index 765620d2fe55aee919111fe35cfd917e865e48b3..590d1380996331a42d0a28dac66846251acd8ad1 100644 --- a/dumux/freeflow/navierstokes/momentum/fluxvariables.hh +++ b/dumux/freeflow/navierstokes/momentum/fluxvariables.hh @@ -391,25 +391,33 @@ public: // get the transporting velocity which is perpendicular to our own (inner) velocity const Scalar transportingVelocity = [&]() { + const auto& orthogonalScvf = fvGeometry.lateralOrthogonalScvf(scvf); + const Scalar innerTransportingVelocity = elemVolVars[orthogonalScvf.insideScvIdx()].velocity(); + static const bool useOldScheme = getParam<bool>("FreeFlow.UseOldTransportingVelocity", true); // TODO how to deprecate? + if (useOldScheme) + { + if (scvf.boundary() && fvGeometry.scv(scvf.insideScvIdx()).boundary()) + { + if (this->elemBcTypes()[scvf.localIndex()].isDirichlet(scvf.normalAxis())) + return problem.dirichlet(this->element(), scvf)[scvf.normalAxis()]; + } + else + return innerTransportingVelocity; + } + // use the Dirichlet velocity as transporting velocity if the lateral face is on a Dirichlet boundary - if (!useOldScheme && scvf.boundary()) + if (scvf.boundary()) { if (this->elemBcTypes()[scvf.localIndex()].isDirichlet(scvf.normalAxis())) - return problem.dirichlet(this->element(), scvf)[scvf.normalAxis()]; + return 0.5*(problem.dirichlet(this->element(), scvf)[scvf.normalAxis()] + innerTransportingVelocity); } - const auto& orthogonalScvf = fvGeometry.lateralOrthogonalScvf(scvf); - const Scalar innerTransportingVelocity = elemVolVars[orthogonalScvf.insideScvIdx()].velocity(); - - if (useOldScheme) - return innerTransportingVelocity; - if (orthogonalScvf.boundary()) { if (this->elemBcTypes()[orthogonalScvf.localIndex()].isDirichlet(scvf.normalAxis())) - return problem.dirichlet(this->element(), scvf)[scvf.normalAxis()]; + return 0.5*(problem.dirichlet(this->element(), scvf)[scvf.normalAxis()] + innerTransportingVelocity); else return innerTransportingVelocity; // fallback value, should actually never be called } @@ -423,13 +431,32 @@ public: const Scalar transportedMomentum = [&]() { + const auto& insideScv = fvGeometry.scv(scvf.insideScvIdx()); + + auto getDirichletMomentumFlux = [&]() + { + return problem.dirichlet(this->element(), scvf)[insideScv.dofAxis()] * this->problem().density(this->element(), insideScv); + }; + // use the Dirichlet velocity as for transported momentum if the lateral face is on a Dirichlet boundary if (scvf.boundary()) { - if (const auto& scv = fvGeometry.scv(scvf.insideScvIdx()); this->elemBcTypes()[scvf.localIndex()].isDirichlet(scv.dofAxis())) - return problem.dirichlet(this->element(), scvf)[scv.dofAxis()] * this->problem().density(this->element(), scv); - else + if (!this->elemBcTypes()[scvf.localIndex()].isDirichlet(insideScv.dofAxis())) DUNE_THROW(Dune::InvalidStateException, "Neither Dirichlet nor Neumann BC set at " << scvf.ipGlobal()); + + return getDirichletMomentumFlux(); + } + else + { + if (fvGeometry.scvfIntegrationPointInConcaveCorner(scvf)) + { + // TODO: we could put this into an assert, as the construction of outsideScvfWithSameIntegrationPoint is quite expensive + const auto& outsideScvfWithSameIntegrationPoint = fvGeometry.outsideScvfWithSameIntegrationPoint(scvf); + if (!this->problem().boundaryTypes(this->element(), outsideScvfWithSameIntegrationPoint).isDirichlet(insideScv.dofAxis())) + DUNE_THROW(Dune::InvalidStateException, "Neither Dirichlet nor Neumann BC set at " << outsideScvfWithSameIntegrationPoint.ipGlobal()); + + return getDirichletMomentumFlux(); + } } const bool selfIsUpstream = scvf.directionSign() == sign(transportingVelocity); diff --git a/test/freeflow/navierstokes/channel/3d/CMakeLists.txt b/test/freeflow/navierstokes/channel/3d/CMakeLists.txt index 11356d3181361dc964ea2ff5cdb069fedf48ba23..97f0fcd0f6f79580d3913cc0ec83fcbbfb9296c9 100644 --- a/test/freeflow/navierstokes/channel/3d/CMakeLists.txt +++ b/test/freeflow/navierstokes/channel/3d/CMakeLists.txt @@ -25,6 +25,19 @@ dumux_add_test(NAME test_ff_stokes_channel_3d_staircase -Problem.Name test_ff_stokes_channel_3d_staircase -Problem.IsStaircaseGeometry true" --zeroThreshold {"velocity_liq \(m/s\)":1e-12}) +dumux_add_test(NAME test_ff_stokes_channel_3d_staircase_nocaching + SOURCES main.cc + LABELS freeflow navierstokes + CMAKE_GUARD HAVE_UMFPACK dune-subgrid_FOUND + COMPILE_DEFINITIONS ENABLECACHING=0 + COMMAND ${CMAKE_SOURCE_DIR}/bin/testing/runtest.py + CMD_ARGS --script fuzzy + --files ${CMAKE_SOURCE_DIR}/test/references/test_ff_stokes_channel_3d_staircase-reference.vtu + ${CMAKE_CURRENT_BINARY_DIR}/test_ff_stokes_channel_3d_staircase_nocaching-00001.vtu + --command "${CMAKE_CURRENT_BINARY_DIR}/test_ff_stokes_channel_3d params.input + -Problem.Name test_ff_stokes_channel_3d_staircase_nocaching -Problem.IsStaircaseGeometry true" + --zeroThreshold {"velocity_liq \(m/s\)":1e-12}) + dumux_add_test(NAME test_ff_stokes_channel_pseudo3d LABELS freeflow navierstokes SOURCES main.cc diff --git a/test/freeflow/navierstokes/channel/3d/main.cc b/test/freeflow/navierstokes/channel/3d/main.cc index 2ba127d34d836c842abf182306e09bfc635bb747..4a3e6912a1fe19f6a4b254071bd1710b24645301 100644 --- a/test/freeflow/navierstokes/channel/3d/main.cc +++ b/test/freeflow/navierstokes/channel/3d/main.cc @@ -29,20 +29,22 @@ #include <dune/common/parallel/mpihelper.hh> #include <dune/common/timer.hh> -#include <dune/grid/io/file/vtk.hh> -#include <dune/istl/io.hh> -#include <dumux/assembly/staggeredfvassembler.hh> -#include <dumux/assembly/diffmethod.hh> #include <dumux/common/dumuxmessage.hh> #include <dumux/common/parameters.hh> #include <dumux/common/properties.hh> -#include <dumux/freeflow/navierstokes/staggered/fluxoversurface.hh> + +#include <dumux/io/vtkoutputmodule.hh> #include <dumux/io/grid/gridmanager_sub.hh> #include <dumux/io/grid/gridmanager_yasp.hh> -#include <dumux/io/staggeredvtkoutputmodule.hh> + #include <dumux/linear/seqsolverbackend.hh> -#include <dumux/nonlinear/newtonsolver.hh> +#include <dumux/multidomain/fvassembler.hh> +#include <dumux/multidomain/traits.hh> +#include <dumux/multidomain/newtonsolver.hh> + +#include <dumux/freeflow/navierstokes/velocityoutput.hh> +#include <dumux/freeflow/navierstokes/fluxoveraxisalignedsurface.hh> #include "properties.hh" @@ -51,7 +53,8 @@ int main(int argc, char** argv) using namespace Dumux; // define the type tag for this problem - using TypeTag = Properties::TTag::ThreeDChannelTest; + using MomentumTypeTag = Properties::TTag::ThreeDChannelTestMomentum; + using MassTypeTag = Properties::TTag::ThreeDChannelTestMass; // initialize MPI, finalize is done automatically on exit const auto& mpiHelper = Dune::MPIHelper::instance(argc, argv); @@ -64,8 +67,8 @@ int main(int argc, char** argv) Parameters::init(argc, argv); // create a grid - using Scalar = GetPropType<TypeTag, Properties::Scalar>; - using Grid = GetPropType<TypeTag, Properties::Grid>; + using Scalar = GetPropType<MassTypeTag, Properties::Scalar>; + using Grid = GetPropType<MassTypeTag, Properties::Grid>; Dumux::GridManager<Grid> gridManager; #if HAVE_DUNE_SUBGRID && GRID_DIM == 3 @@ -87,6 +90,8 @@ int main(int argc, char** argv) }; gridManager.init(selector, "Internal"); +#elif HAVE_DUNE_SUBGRID + gridManager.init([&](const auto& element) { return true; }); #else gridManager.init(); #endif @@ -99,110 +104,110 @@ int main(int argc, char** argv) const auto& leafGridView = gridManager.grid().leafGridView(); // create the finite volume grid geometry - using GridGeometry = GetPropType<TypeTag, Properties::GridGeometry>; - auto gridGeometry = std::make_shared<GridGeometry>(leafGridView); - - // the problem (boundary conditions) - using Problem = GetPropType<TypeTag, Properties::Problem>; - auto problem = std::make_shared<Problem>(gridGeometry); + using MomentumGridGeometry = GetPropType<MomentumTypeTag, Properties::GridGeometry>; + auto momentumGridGeometry = std::make_shared<MomentumGridGeometry>(leafGridView); + using MassGridGeometry = GetPropType<MassTypeTag, Properties::GridGeometry>; + auto massGridGeometry = std::make_shared<MassGridGeometry>(leafGridView); + + // the coupling manager + using Traits = MultiDomainTraits<MomentumTypeTag, MassTypeTag>; + using CouplingManager = StaggeredFreeFlowCouplingManager<Traits>; + auto couplingManager = std::make_shared<CouplingManager>(); + + // the problems (boundary conditions) + using MomentumProblem = GetPropType<MomentumTypeTag, Properties::Problem>; + auto momentumProblem = std::make_shared<MomentumProblem>(momentumGridGeometry, couplingManager); + using MassProblem = GetPropType<MassTypeTag, Properties::Problem>; + auto massProblem = std::make_shared<MassProblem>(massGridGeometry, couplingManager); // the solution vector - using SolutionVector = GetPropType<TypeTag, Properties::SolutionVector>; + constexpr auto momentumIdx = CouplingManager::freeFlowMomentumIndex; + constexpr auto massIdx = CouplingManager::freeFlowMassIndex; + using SolutionVector = typename Traits::SolutionVector; SolutionVector x; - x[GridGeometry::cellCenterIdx()].resize(gridGeometry->numCellCenterDofs()); - x[GridGeometry::faceIdx()].resize(gridGeometry->numFaceDofs()); + x[momentumIdx].resize(momentumGridGeometry->numDofs()); + x[massIdx].resize(massGridGeometry->numDofs()); // the grid variables - using GridVariables = GetPropType<TypeTag, Properties::GridVariables>; - auto gridVariables = std::make_shared<GridVariables>(problem, gridGeometry); - gridVariables->init(x); - - // intialize the vtk output module - using IOFields = GetPropType<TypeTag, Properties::IOFields>; - StaggeredVtkOutputModule<GridVariables, SolutionVector> vtkWriter(*gridVariables, x, problem->name()); + using MomentumGridVariables = GetPropType<MomentumTypeTag, Properties::GridVariables>; + auto momentumGridVariables = std::make_shared<MomentumGridVariables>(momentumProblem, momentumGridGeometry); + using MassGridVariables = GetPropType<MassTypeTag, Properties::GridVariables>; + auto massGridVariables = std::make_shared<MassGridVariables>(massProblem, massGridGeometry); + + // compute coupling stencil and afterwards initialize grid variables (need coupling information) + couplingManager->init(momentumProblem, massProblem, std::make_tuple(momentumGridVariables, massGridVariables), x); + massGridVariables->init(x[massIdx]); + momentumGridVariables->init(x[momentumIdx]); + + using Assembler = MultiDomainFVAssembler<Traits, CouplingManager, DiffMethod::numeric>; + auto assembler = std::make_shared<Assembler>(std::make_tuple(momentumProblem, massProblem), + std::make_tuple(momentumGridGeometry, massGridGeometry), + std::make_tuple(momentumGridVariables, massGridVariables), + couplingManager); + + // initialize the vtk output module + using IOFields = GetPropType<MassTypeTag, Properties::IOFields>; + VtkOutputModule vtkWriter(*massGridVariables, x[massIdx], massProblem->name()); IOFields::initOutputModule(vtkWriter); // Add model specific output fields + vtkWriter.addVelocityOutput(std::make_shared<NavierStokesVelocityOutput<MassGridVariables>>()); vtkWriter.write(0.0); - // the assembler with time loop for instationary problem - using Assembler = StaggeredFVAssembler<TypeTag, DiffMethod::numeric>; - auto assembler = std::make_shared<Assembler>(problem, gridGeometry, gridVariables); - // the linear solver using LinearSolver = Dumux::UMFPackBackend; auto linearSolver = std::make_shared<LinearSolver>(); // the non-linear solver - using NewtonSolver = Dumux::NewtonSolver<Assembler, LinearSolver>; - NewtonSolver nonLinearSolver(assembler, linearSolver); - - // set up two planes over which fluxes are calculated - FluxOverSurface<GridVariables, - SolutionVector, - GetPropType<TypeTag, Properties::ModelTraits>, - GetPropType<TypeTag, Properties::LocalResidual>> flux(*gridVariables, x); - using GridView = typename GetPropType<TypeTag, Properties::GridGeometry>::GridView; + using NewtonSolver = MultiDomainNewtonSolver<Assembler, LinearSolver, CouplingManager>; + NewtonSolver nonLinearSolver(assembler, linearSolver, couplingManager); + + // set up three planes over which fluxes are calculated + FluxOverAxisAlignedSurface flux(*massGridVariables, x[massIdx], assembler->localResidual(massIdx)); + + using GridView = typename GetPropType<MassTypeTag, Properties::GridGeometry>::GridView; using GlobalPosition = Dune::FieldVector<Scalar, GridView::dimensionworld>; - const Scalar xMin = gridGeometry->bBoxMin()[0]; - const Scalar xMax = gridGeometry->bBoxMax()[0]; - const Scalar yMin = gridGeometry->bBoxMin()[1]; - const Scalar yMax = gridGeometry->bBoxMax()[1]; + const Scalar xMin = massGridGeometry->bBoxMin()[0]; + const Scalar xMax = massGridGeometry->bBoxMax()[0]; + const Scalar yMin = massGridGeometry->bBoxMin()[1]; + const Scalar yMax = massGridGeometry->bBoxMax()[1]; #if GRID_DIM == 3 - const Scalar zMin = gridGeometry->bBoxMin()[2]; - const Scalar zMax = gridGeometry->bBoxMax()[2]; + const Scalar zMin = massGridGeometry->bBoxMin()[2]; + const Scalar zMax = massGridGeometry->bBoxMax()[2]; #endif - // The first plane shall be placed at the middle of the channel. - // If we have an odd number of cells in x-direction, there would not be any cell faces - // at the postion of the plane (which is required for the flux calculation). - // In this case, we add half a cell-width to the x-position in order to make sure that - // the cell faces lie on the plane. This assumes a regular cartesian grid. - // The second plane is placed at the outlet of the channel. + // the first plane is at the inlet #if GRID_DIM == 3 - const auto p0inlet = GlobalPosition{xMin, yMin, zMin}; - const auto p1inlet = GlobalPosition{xMin, yMax, zMin}; - const auto p2inlet = GlobalPosition{xMin, yMin, zMax}; - const auto p3inlet = GlobalPosition{xMin, yMax, zMax}; - flux.addSurface("inlet", p0inlet, p1inlet, p2inlet, p3inlet); + const auto inletLowerLeft = GlobalPosition{xMin, yMin, zMin}; + const auto inletUpperRight = GlobalPosition{xMin, yMax, zMax}; + flux.addAxisAlignedSurface("inlet", inletLowerLeft, inletUpperRight); #else - const auto p0inlet = GlobalPosition{xMin, yMin}; - const auto p1inlet = GlobalPosition{xMin, yMax}; - flux.addSurface("inlet", p0inlet, p1inlet); + const auto inletLowerLeft = GlobalPosition{xMin, yMin}; + const auto inletUpperRight = GlobalPosition{xMin, yMax}; + flux.addAxisAlignedSurface("inlet", inletLowerLeft, inletUpperRight); #endif + // the second plane is at the middle of the channel const Scalar planePosMiddleX = xMin + 0.5*(xMax - xMin); - int numCellsX = getParam<std::vector<int>>("Grid.Cells")[0]; - - const unsigned int refinement = getParam<unsigned int>("Grid.Refinement", 0); - - numCellsX *= (1<<refinement); - - const Scalar offsetX = (numCellsX % 2 == 0) ? 0.0 : 0.5*((xMax - xMin) / numCellsX); - #if GRID_DIM == 3 - const auto p0middle = GlobalPosition{planePosMiddleX + offsetX, yMin, zMin}; - const auto p1middle = GlobalPosition{planePosMiddleX + offsetX, yMax, zMin}; - const auto p2middle = GlobalPosition{planePosMiddleX + offsetX, yMin, zMax}; - const auto p3middle = GlobalPosition{planePosMiddleX + offsetX, yMax, zMax}; - flux.addSurface("middle", p0middle, p1middle, p2middle, p3middle); + const auto middleLowerLeft = GlobalPosition{planePosMiddleX, yMin, zMin}; + const auto middleUpperRight = GlobalPosition{planePosMiddleX, yMax, zMax}; + flux.addAxisAlignedSurface("middle", middleLowerLeft, middleUpperRight); #else - const auto p0middle = GlobalPosition{planePosMiddleX + offsetX, yMin}; - const auto p1middle = GlobalPosition{planePosMiddleX + offsetX, yMax}; -flux.addSurface("middle", p0middle, p1middle); + const auto middleLowerLeft = GlobalPosition{planePosMiddleX, yMin}; + const auto middleUpperRight = GlobalPosition{planePosMiddleX, yMax}; + flux.addAxisAlignedSurface("middle", middleLowerLeft, middleUpperRight); #endif - // The second plane is placed at the outlet of the channel. + // The last plane is placed at the outlet of the channel. #if GRID_DIM == 3 - const auto p0outlet = GlobalPosition{xMax, yMin, zMin}; - const auto p1outlet = GlobalPosition{xMax, yMax, zMin}; - const auto p2outlet = GlobalPosition{xMax, yMin, zMax}; - const auto p3outlet = GlobalPosition{xMax, yMax, zMax}; - flux.addSurface("outlet", p0outlet, p1outlet, p2outlet, p3outlet); + const auto outletLowerLeft = GlobalPosition{xMax, yMin, zMin}; + const auto outletUpperRight = GlobalPosition{xMax, yMax, zMax}; + flux.addAxisAlignedSurface("outlet", outletLowerLeft, outletUpperRight); #else - const auto p0outlet = GlobalPosition{xMax, yMin}; - const auto p1outlet = GlobalPosition{xMax, yMax}; - flux.addSurface("outlet", p0outlet, p1outlet); + const auto outletLowerLeft = GlobalPosition{xMax, yMin}; + const auto outletUpperRight = GlobalPosition{xMax, yMax}; + flux.addAxisAlignedSurface("outlet", outletLowerLeft, outletUpperRight); #endif // linearize & solve @@ -213,37 +218,19 @@ flux.addSurface("middle", p0middle, p1middle); vtkWriter.write(1.0); // calculate and print mass fluxes over the planes - flux.calculateMassOrMoleFluxes(); - if(GetPropType<TypeTag, Properties::ModelTraits>::enableEnergyBalance()) - { - std::cout << "mass / energy flux at inlet is: " << flux.netFlux("inlet") << std::endl; - std::cout << "mass / energy flux at middle is: " << flux.netFlux("middle") << std::endl; - std::cout << "mass / energy flux at outlet is: " << flux.netFlux("outlet") << std::endl; - } - else - { - std::cout << "mass flux at inlet is: " << flux.netFlux("inlet") << std::endl; - std::cout << "mass flux at middle is: " << flux.netFlux("middle") << std::endl; - std::cout << "mass flux at outlet is: " << flux.netFlux("outlet") << std::endl; - } - - // calculate and print volume fluxes over the planes - flux.calculateVolumeFluxes(); - std::cout << "volume flux at inlet is: " << flux.netFlux("inlet")[0] << std::endl; - std::cout << "volume flux at middle is: " << flux.netFlux("middle")[0] << std::endl; - std::cout << "volume flux at outlet is: " << flux.netFlux("outlet")[0] << std::endl; - + flux.calculateAllFluxes(); + std::cout << "mass flux at inlet is: " << flux.flux("inlet") << std::endl; + std::cout << "mass flux at middle is: " << flux.flux("middle") << std::endl; + std::cout << "mass flux at outlet is: " << flux.flux("outlet") << std::endl; + std::cout << "analyticalFlux: " << massProblem->analyticalFlux()*1e3 << std::endl; timer.stop(); - std::cout << "analyticalFlux: " << problem->analyticalFlux() << std::endl; - const auto& comm = Dune::MPIHelper::getCommunication(); std::cout << "Simulation took " << timer.elapsed() << " seconds on " << comm.size() << " processes.\n" << "The cumulative CPU time was " << timer.elapsed()*comm.size() << " seconds.\n"; - //////////////////////////////////////////////////////////// // finalize, print dumux message to say goodbye //////////////////////////////////////////////////////////// @@ -256,4 +243,4 @@ flux.addSurface("middle", p0middle, p1middle); } return 0; -} // end main +} diff --git a/test/freeflow/navierstokes/channel/3d/params.input b/test/freeflow/navierstokes/channel/3d/params.input index 9d69b20680936982cff50c6b10a510ab97fca632..f64ce1a23b32cea2955ddbf023f6c31e1276901c 100644 --- a/test/freeflow/navierstokes/channel/3d/params.input +++ b/test/freeflow/navierstokes/channel/3d/params.input @@ -1,9 +1,9 @@ [Grid] UpperRight = 0.005 0.002 0.00025 -Cells = 3 20 10 +Cells = 3 20 10 [Problem] -Name = test_stokes_channel_3d # name passed to the output routines +Name = test_stokes_channel_3d DeltaP = 10 EnableGravity = false Height = 0.00025 diff --git a/test/freeflow/navierstokes/channel/3d/params_pseudo.input b/test/freeflow/navierstokes/channel/3d/params_pseudo.input index e450d1a3b7a0259b60763e538f19c8350e47af73..e2f1b9c5717addb68f9982ccc960b600fda822b1 100644 --- a/test/freeflow/navierstokes/channel/3d/params_pseudo.input +++ b/test/freeflow/navierstokes/channel/3d/params_pseudo.input @@ -3,7 +3,7 @@ UpperRight = 0.005 0.002 Cells = 3 20 [Problem] -Name = test_stokes_channel_pseudo3d # name passed to the output routines +Name = test_stokes_channel_pseudo3d DeltaP = 10 EnableGravity = false Height = 0.00025 @@ -12,13 +12,12 @@ Height = 0.00025 LiquidDensity = 1e3 LiquidKinematicViscosity = 1e-6 -[Newton] -MaxSteps = 10 -MaxRelativeShift = 1e-8 - [Assembly] NumericDifference.BaseEpsilon = 1e-8 +[Newton] +MaxSteps = 10 +MaxRelativeShift = 1e-8 [Vtk] WriteFaceData = false diff --git a/test/freeflow/navierstokes/channel/3d/problem.hh b/test/freeflow/navierstokes/channel/3d/problem.hh index c64755be9ea1dabf014c4f5241f5c801aa1f537a..b342843bd783a7e10978410397665864047049f7 100644 --- a/test/freeflow/navierstokes/channel/3d/problem.hh +++ b/test/freeflow/navierstokes/channel/3d/problem.hh @@ -25,17 +25,17 @@ * to mimic the 3D behavior of the flow. */ -#ifndef DUMUX_3D_CHANNEL_PROBLEM_HH -#define DUMUX_3D_CHANNEL_PROBLEM_HH +#ifndef DUMUX_TEST_FREEFLOW_NAVIERSTOKES_3D_CHANNEL_PROBLEM_HH +#define DUMUX_TEST_FREEFLOW_NAVIERSTOKES_3D_CHANNEL_PROBLEM_HH #include <dune/common/float_cmp.hh> #include <dumux/common/properties.hh> #include <dumux/common/parameters.hh> -#include <dumux/common/numeqvector.hh> -#include <dumux/freeflow/navierstokes/boundarytypes.hh> #include <dumux/freeflow/navierstokes/problem.hh> +#include <dumux/freeflow/navierstokes/momentum/fluxhelper.hh> +#include <dumux/freeflow/navierstokes/scalarfluxhelper.hh> #ifndef GRID_DIM #define GRID_DIM 3 @@ -59,33 +59,31 @@ class ThreeDChannelTestProblem : public NavierStokesProblem<TypeTag> { using ParentType = NavierStokesProblem<TypeTag>; - using GridView = typename GetPropType<TypeTag, Properties::GridGeometry>::GridView; - using Scalar = GetPropType<TypeTag, Properties::Scalar>; - - using Indices = typename GetPropType<TypeTag, Properties::ModelTraits>::Indices; - using BoundaryTypes = Dumux::NavierStokesBoundaryTypes<GetPropType<TypeTag, Properties::ModelTraits>::numEq()>; - using Element = typename GridView::template Codim<0>::Entity; - + using BoundaryTypes = typename ParentType::BoundaryTypes; using GridGeometry = GetPropType<TypeTag, Properties::GridGeometry>; using FVElementGeometry = typename GridGeometry::LocalView; using SubControlVolume = typename FVElementGeometry::SubControlVolume; using SubControlVolumeFace = typename FVElementGeometry::SubControlVolumeFace; + using Indices = typename GetPropType<TypeTag, Properties::ModelTraits>::Indices; + using NumEqVector = typename ParentType::NumEqVector; + using ModelTraits = GetPropType<TypeTag, Properties::ModelTraits>; + using PrimaryVariables = typename ParentType::PrimaryVariables; + using Scalar = GetPropType<TypeTag, Properties::Scalar>; + using SolutionVector = GetPropType<TypeTag, Properties::SolutionVector>; - static constexpr int dim = GridView::dimension; - static constexpr int dimWorld = GridView::dimensionworld; + static constexpr auto dimWorld = GridGeometry::GridView::dimensionworld; + using Element = typename GridGeometry::GridView::template Codim<0>::Entity; using GlobalPosition = typename Element::Geometry::GlobalCoordinate; + using VelocityVector = Dune::FieldVector<Scalar, dimWorld>; - using CellCenterPrimaryVariables = GetPropType<TypeTag, Properties::CellCenterPrimaryVariables>; - using FacePrimaryVariables = GetPropType<TypeTag, Properties::FacePrimaryVariables>; - - using PrimaryVariables = GetPropType<TypeTag, Properties::PrimaryVariables>; - using NumEqVector = Dumux::NumEqVector<PrimaryVariables>; + using CouplingManager = GetPropType<TypeTag, Properties::CouplingManager>; static constexpr bool enablePseudoThreeDWallFriction = !(GRID_DIM == 3); + static constexpr int dim = GridGeometry::GridView::dimension; public: - ThreeDChannelTestProblem(std::shared_ptr<const GridGeometry> gridGeometry) - : ParentType(gridGeometry) + ThreeDChannelTestProblem(std::shared_ptr<const GridGeometry> gridGeometry, std::shared_ptr<CouplingManager> couplingManager) + : ParentType(gridGeometry, couplingManager) { deltaP_ = getParam<Scalar>("Problem.DeltaP"); height_ = getParam<Scalar>("Problem.Height"); @@ -95,7 +93,7 @@ public: if(dim == 3 && !Dune::FloatCmp::eq(height_, this->gridGeometry().bBoxMax()[2])) DUNE_THROW(Dune::InvalidStateException, "z-dimension must equal height"); - if(enablePseudoThreeDWallFriction) + if constexpr (enablePseudoThreeDWallFriction) extrusionFactor_ = 2.0/3.0 * height_; else extrusionFactor_ = 1.0; @@ -117,31 +115,30 @@ public: /*! * \brief Evaluates the source term for all phases within a given - * sub-control volume face. + * sub-control volume */ - using ParentType::source; - template<class ElementVolumeVariables, class ElementFaceVariables> - NumEqVector source(const Element &element, + template<class ElementVolumeVariables> + NumEqVector source(const Element& element, const FVElementGeometry& fvGeometry, const ElementVolumeVariables& elemVolVars, - const ElementFaceVariables& elemFaceVars, - const SubControlVolumeFace &scvf) const + const SubControlVolume& scv) const { auto source = NumEqVector(0.0); -#if GRID_DIM != 3 + if constexpr (ParentType::isMomentumProblem() && enablePseudoThreeDWallFriction) + { static const Scalar height = getParam<Scalar>("Problem.Height"); static const Scalar factor = getParam<Scalar>("Problem.PseudoWallFractionFactor", 8.0); - source[scvf.directionIndex()] = this->pseudo3DWallFriction(scvf, elemVolVars, elemFaceVars, height, factor); -#endif + source[scv.dofAxis()] = this->pseudo3DWallFriction(element, fvGeometry, elemVolVars, scv, height, factor); + } return source; } + //! the domain's extrusion factor Scalar extrusionFactorAtPos(const GlobalPosition& pos) const { return extrusionFactor_; } - // \} /*! * \name Boundary conditions @@ -158,16 +155,14 @@ public: { BoundaryTypes values; - // set a fixed pressure at the inlet and outlet - if (isOutlet_(globalPos) || isInlet_(globalPos)) - values.setDirichlet(Indices::pressureIdx); - else + if constexpr (ParentType::isMomentumProblem()) { - values.setDirichlet(Indices::velocityXIdx); - values.setDirichlet(Indices::velocityYIdx); - if(dim == 3) - values.setDirichlet(Indices::velocityZIdx); + values.setAllDirichlet(); + if (isOutlet_(globalPos) || isInlet_(globalPos)) + values.setAllNeumann(); } + else + values.setNeumann(Indices::conti0EqIdx); return values; } @@ -179,30 +174,48 @@ public: */ PrimaryVariables dirichletAtPos(const GlobalPosition &globalPos) const { - PrimaryVariables values(0.0); - - if(isInlet_(globalPos)) - values[Indices::pressureIdx] = 1e5 + deltaP_; - - if(isOutlet_(globalPos)) - values[Indices::pressureIdx] = 1e5; - - return values; + // no-flow/no-slip + return PrimaryVariables(0.0); } - // \} - /*! - * \brief Evaluates the initial value for a control volume. + * \brief Evaluates the boundary conditions for a Neumann control volume. * - * \param globalPos The global position + * \param element The element for which the Neumann boundary condition is set + * \param fvGeometry The fvGeometry + * \param elemVolVars The element volume variables + * \param elemFaceVars The element face variables + * \param scvf The boundary sub control volume face */ - PrimaryVariables initialAtPos(const GlobalPosition &globalPos) const + template<class ElementVolumeVariables, class ElementFluxVariablesCache> + NumEqVector neumann(const Element& element, + const FVElementGeometry& fvGeometry, + const ElementVolumeVariables& elemVolVars, + const ElementFluxVariablesCache& elemFluxVarsCache, + const SubControlVolumeFace& scvf) const { - PrimaryVariables values(0.0); + NumEqVector values(0.0); + const auto& globalPos = scvf.ipGlobal(); + + if constexpr (ParentType::isMomentumProblem()) + { + const auto p = isInlet_(globalPos) ? 1e5 + deltaP_ : 1e5; + values = NavierStokesMomentumBoundaryFluxHelper::fixedPressureMomentumFlux( + *this, fvGeometry, scvf, elemVolVars, elemFluxVarsCache, p + ); + } + else + { + values = NavierStokesScalarBoundaryFluxHelper<AdvectiveFlux<ModelTraits>>::scalarOutflowFlux( + *this, element, fvGeometry, scvf, elemVolVars + ); + } + return values; } + // \} + //! Returns the analytical solution for the flux through the rectangular channel Scalar analyticalFlux() const { @@ -218,14 +231,10 @@ public: private: bool isInlet_(const GlobalPosition& globalPos) const - { - return globalPos[0] < eps_; - } + { return globalPos[0] < eps_; } bool isOutlet_(const GlobalPosition& globalPos) const - { - return globalPos[0] > this->gridGeometry().bBoxMax()[0] - eps_; - } + { return globalPos[0] > this->gridGeometry().bBoxMax()[0] - eps_; } static constexpr Scalar eps_=1e-6; Scalar deltaP_; @@ -234,6 +243,7 @@ private: Scalar rho_; Scalar nu_; }; + } // end namespace Dumux #endif diff --git a/test/freeflow/navierstokes/channel/3d/properties.hh b/test/freeflow/navierstokes/channel/3d/properties.hh index 8a0e341762c58163a599c0fe8041c9f0aa22816b..cb23a1d3976de04f9da26d9c860e98621616373e 100644 --- a/test/freeflow/navierstokes/channel/3d/properties.hh +++ b/test/freeflow/navierstokes/channel/3d/properties.hh @@ -21,27 +21,38 @@ * \ingroup NavierStokesTests * \brief The properties for the channel flow test for the staggered grid (Navier-)Stokes model. */ -#ifndef DUMUX_3D_CHANNEL_PROPERTIES_HH -#define DUMUX_3D_CHANNEL_PROPERTIES_HH +#ifndef DUMUX_TEST_FREEFLOW_NAVIERSTOKES_3D_CHANNEL_PROPERTIES_HH +#define DUMUX_TEST_FREEFLOW_NAVIERSTOKES_3D_CHANNEL_PROPERTIES_HH + +#ifndef ENABLECACHING +#define ENABLECACHING 1 +#endif #include <dune/grid/yaspgrid.hh> -#include <dumux/discretization/staggered/freeflow/properties.hh> -#include <dumux/freeflow/navierstokes/model.hh> +#if HAVE_DUNE_SUBGRID +#include <dune/subgrid/subgrid.hh> +#endif + +#include <dumux/discretization/fcstaggered.hh> +#include <dumux/discretization/cctpfa.hh> +#include <dumux/multidomain/staggeredfreeflow/couplingmanager.hh> + +#include <dumux/freeflow/navierstokes/momentum/model.hh> +#include <dumux/freeflow/navierstokes/mass/1p/model.hh> + #include <dumux/material/components/constant.hh> #include <dumux/material/fluidsystems/1pliquid.hh> #include "problem.hh" -#if HAVE_DUNE_SUBGRID && GRID_DIM == 3 -#include <dune/subgrid/subgrid.hh> -#endif - namespace Dumux::Properties { // Create new type tags namespace TTag { -struct ThreeDChannelTest { using InheritsFrom = std::tuple<NavierStokes, StaggeredFreeFlowModel>; }; +struct ThreeDChannelTest {}; +struct ThreeDChannelTestMomentum { using InheritsFrom = std::tuple<ThreeDChannelTest, NavierStokesMomentum, FaceCenteredStaggeredModel>; }; +struct ThreeDChannelTestMass { using InheritsFrom = std::tuple<ThreeDChannelTest, NavierStokesMassOneP, CCTpfaModel>; }; } // end namespace TTag // the fluid system @@ -60,7 +71,7 @@ struct Grid<TypeTag, TTag::ThreeDChannelTest> using HostGrid = Dune::YaspGrid<dim, Dune::EquidistantOffsetCoordinates<GetPropType<TypeTag, Properties::Scalar>, dim> >; -#if HAVE_DUNE_SUBGRID && GRID_DIM == 3 +#if HAVE_DUNE_SUBGRID using type = Dune::SubGrid<HostGrid::dimension, HostGrid>; #else using type = HostGrid; @@ -72,11 +83,18 @@ template<class TypeTag> struct Problem<TypeTag, TTag::ThreeDChannelTest> { using type = ThreeDChannelTestProblem<TypeTag> ; }; template<class TypeTag> -struct EnableGridGeometryCache<TypeTag, TTag::ThreeDChannelTest> { static constexpr bool value = true; }; +struct EnableGridGeometryCache<TypeTag, TTag::ThreeDChannelTest> { static constexpr bool value = ENABLECACHING; }; template<class TypeTag> -struct EnableGridFluxVariablesCache<TypeTag, TTag::ThreeDChannelTest> { static constexpr bool value = true; }; +struct EnableGridFluxVariablesCache<TypeTag, TTag::ThreeDChannelTest> { static constexpr bool value = ENABLECACHING; }; template<class TypeTag> -struct EnableGridVolumeVariablesCache<TypeTag, TTag::ThreeDChannelTest> { static constexpr bool value = true; }; +struct EnableGridVolumeVariablesCache<TypeTag, TTag::ThreeDChannelTest> { static constexpr bool value = ENABLECACHING; }; + +template<class TypeTag> +struct CouplingManager<TypeTag, TTag::ThreeDChannelTest> +{ + using Traits = MultiDomainTraits<TTag::ThreeDChannelTestMomentum, TTag::ThreeDChannelTestMass>; + using type = StaggeredFreeFlowCouplingManager<Traits>; +}; } // end namespace Dumux::Properties