// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- // vi: set et ts=4 sw=4 sts=4: /***************************************************************************** * See the file COPYING for full copying permissions. * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * *****************************************************************************/ /*! * \file * \ingroup ShallowWaterTests * \brief A test for the Shallow water model (rough channel). */ #ifndef DUMUX_ROUGH_CHANNEL_TEST_PROBLEM_HH #define DUMUX_ROUGH_CHANNEL_TEST_PROBLEM_HH #include #include #include "spatialparams.hh" #include #include #include #include namespace Dumux { template class RoughChannelProblem; // Specify the properties for the problem namespace Properties { // Create new type tags namespace TTag { struct RoughChannel { using InheritsFrom = std::tuple; }; } // end namespace TTag template struct Grid { using type = Dune::YaspGrid<2, Dune::TensorProductCoordinates, 2> >; }; // Set the problem property template struct Problem { using type = Dumux::RoughChannelProblem; }; // Set the spatial parameters template struct SpatialParams { private: using GridGeometry = GetPropType; using Scalar = GetPropType; using ElementVolumeVariables = typename GetPropType::LocalView; using VolumeVariables = typename ElementVolumeVariables::VolumeVariables; public: using type = RoughChannelSpatialParams; }; template struct EnableGridGeometryCache { static constexpr bool value = true; }; template struct EnableGridVolumeVariablesCache { static constexpr bool value = false; }; } // end namespace Properties /*! * \ingroup ShallowWaterTests * \brief A simple flow in a rough channel with friction law after Manning. * * The domain is 500 meters long and 5 meters wide. At the left border a discharge * boundary condition is applied and at the right border a water depth boundary condition. * All other boundaries are set to no-flow. Normal flow is assumed, therefor the water depth * at the right border can be calculated with the formular of Gaukler-Manning-Strickler. * * \f[ * v_m = 1/n * R_{hy}^{2/3} * I_s^{1/2} * \f] * * With the mean velocity * \f[ * v_m = \frac{q}/{h} * \f] * the friction value n after Manning * the hydraulic radius R_{hy} equal to the water depth h (because normal flow is assumed) * the bed slope I_s and the unity inflow discharge q. * * Therefore h can be calculated with * * \f[ * h = \left(\frac{n*q}{\sqrt{I_s}} \right)^{3/5} * \f] * * The formula of Gaukler Manning and Strickler is also used to calculate the analytic solution. * * This problem uses the \ref ShallowWaterModel */ template class RoughChannelProblem : public ShallowWaterProblem { using ParentType = ShallowWaterProblem; using PrimaryVariables = GetPropType; using BoundaryTypes = GetPropType; using Scalar = GetPropType; using Indices = typename GetPropType::Indices; using GridGeometry = GetPropType; using NeumannFluxes = GetPropType; using ElementVolumeVariables = typename GetPropType::LocalView; using GridVariables = GetPropType; using ElementFluxVariablesCache = typename GridVariables::GridFluxVariablesCache::LocalView; using VolumeVariables = typename ElementVolumeVariables::VolumeVariables; using FVElementGeometry = typename GetPropType::LocalView; using SubControlVolumeFace = typename FVElementGeometry::SubControlVolumeFace; using GridView = GetPropType; using Element = typename GridView::template Codim<0>::Entity; using GlobalPosition = typename Element::Geometry::GlobalCoordinate; using NumEqVector = GetPropType; using SubControlVolume = typename FVElementGeometry::SubControlVolume; public: RoughChannelProblem(std::shared_ptr gridGeometry) : ParentType(gridGeometry) { name_ = getParam("Problem.Name"); exactWaterDepth_.resize(gridGeometry->numDofs(), 0.0); exactVelocityX_.resize(gridGeometry->numDofs(), 0.0); constManningN_ = getParam("Problem.ManningN"); bedSlope_ = getParam("Problem.BedSlope"); discharge_ = getParam("Problem.Discharge"); hBoundary_ = this->gauklerManningStrickler(discharge_,constManningN_,bedSlope_); } //! Get the analytical water depth const std::vector& getExactWaterDepth() { return exactWaterDepth_; } //! Get the analytical velocity const std::vector& getExactVelocityX() { return exactVelocityX_; } //! Calculate the water depth with Gaukler-Manning-Strickler Scalar gauklerManningStrickler(Scalar discharge, Scalar manningN, Scalar bedSlope) { using std::pow; using std::abs; using std::sqrt; return pow(abs(discharge)*manningN/sqrt(bedSlope), 0.6); } //! Udpate the analytical solution void updateAnalyticalSolution() { using std::abs; for (const auto& element : elements(this->gridGeometry().gridView())) { const Scalar h = this->gauklerManningStrickler(discharge_,constManningN_,bedSlope_); const Scalar u = abs(discharge_)/h; const auto eIdx = this->gridGeometry().elementMapper().index(element); exactWaterDepth_[eIdx] = h; exactVelocityX_[eIdx] = u; } } /*! * \name Problem parameters */ // \{ /*! * \brief The problem name * * This is used as a prefix for files generated by the simulation. */ const std::string& name() const { return name_; } /*! * \brief Evaluate the source term for all balance equations within a given * sub-control-volume. * * This is the method for the case where the source term is * potentially solution dependent and requires some quantities that * are specific to the fully-implicit method. * * \param element The finite element * \param fvGeometry The finite-volume geometry * \param elemVolVars All volume variables for the element * \param scv The sub control volume * * For this method, the \a values parameter stores the conserved quantity rate * generated or annihilate per volume unit. Positive values mean * that the conserved quantity is created, negative ones mean that it vanishes. * E.g. for the mass balance that would be a mass rate in \f$ [ kg / (m^3 \cdot s)] \f$. */ NumEqVector source(const Element& element, const FVElementGeometry& fvGeometry, const ElementVolumeVariables& elemVolVars, const SubControlVolume &scv) const { NumEqVector source (0.0); source += bottomFrictionSource(element, fvGeometry, elemVolVars, scv); return source; } /*! * \brief Compute the source term due to bottom friction * * \param element The finite element * \param fvGeometry The finite-volume geometry * \param elemVolVars All volume variables for the element * \param scv The sub control volume * * \return source */ NumEqVector bottomFrictionSource(const Element& element, const FVElementGeometry& fvGeometry, const ElementVolumeVariables& elemVolVars, const SubControlVolume &scv) const { NumEqVector bottomFrictionSource(0.0); const auto& volVars = elemVolVars[scv]; Dune::FieldVector bottomShearStress = this->spatialParams().frictionLaw(element, scv).shearStress(volVars); bottomFrictionSource[0] = 0.0; bottomFrictionSource[1] =bottomShearStress[0]; bottomFrictionSource[2] =bottomShearStress[1]; return bottomFrictionSource; } // \} /*! * \name Boundary conditions */ // \{ /*! * \brief Specifies which kind of boundary condition should be * used for which equation on a given boundary segment. * * \param globalPos The position for which the boundary type is set */ BoundaryTypes boundaryTypesAtPos(const GlobalPosition &globalPos) const { BoundaryTypes bcTypes; bcTypes.setAllNeumann(); return bcTypes; } /*! * \brief Specifies the neumann boundary * * We need the Riemann invariants to compute the values depending of the boundary type. * Since we use a weak imposition we do not have a dirichlet value. We impose fluxes * based on q, h, etc. computed with the Riemann invariants * * \param element * \param fvGeometry * \param elemVolVars * \param elemFluxVarsCache * \param scvf */ NeumannFluxes neumann(const Element& element, const FVElementGeometry& fvGeometry, const ElementVolumeVariables& elemVolVars, const ElementFluxVariablesCache& elemFluxVarsCache, const SubControlVolumeFace& scvf) const { NeumannFluxes values(0.0); const auto& insideScv = fvGeometry.scv(scvf.insideScvIdx()); const auto& insideVolVars = elemVolVars[insideScv]; const auto& nxy = scvf.unitOuterNormal(); const auto gravity = this->spatialParams().gravity(scvf.center()); std::array boundaryStateVariables; // impose discharge at the left side if (scvf.center()[0] < this->gridGeometry().bBoxMin()[0] + eps_) { boundaryStateVariables = ShallowWater::fixedDischargeBoundary(discharge_, insideVolVars.waterDepth(), insideVolVars.velocity(0), insideVolVars.velocity(1), gravity, nxy); } // impose water depth at the right side else if (scvf.center()[0] > this->gridGeometry().bBoxMax()[0] - eps_) { boundaryStateVariables = ShallowWater::fixedWaterDepthBoundary(hBoundary_, insideVolVars.waterDepth(), insideVolVars.velocity(0), insideVolVars.velocity(1), gravity, nxy); } // no flow boundary else { boundaryStateVariables[0] = insideVolVars.waterDepth(); boundaryStateVariables[1] = -insideVolVars.velocity(0); boundaryStateVariables[2] = -insideVolVars.velocity(1); } auto riemannFlux = ShallowWater::riemannProblem(insideVolVars.waterDepth(), boundaryStateVariables[0], insideVolVars.velocity(0), boundaryStateVariables[1], insideVolVars.velocity(1), boundaryStateVariables[2], insideVolVars.bedSurface(), insideVolVars.bedSurface(), gravity, nxy); values[Indices::massBalanceIdx] = riemannFlux[0]; values[Indices::velocityXIdx] = riemannFlux[1]; values[Indices::velocityYIdx] = riemannFlux[2]; return values; } // \} /*! * \name Volume terms */ // \{ /*! * \brief Evaluate the initial values for a control volume. * * For this method, the \a values parameter stores primary * variables. * * \param globalPos The position for which the boundary type is set */ PrimaryVariables initialAtPos(const GlobalPosition &globalPos) const { PrimaryVariables values(0.0); values[0] = hBoundary_; values[1] = abs(discharge_)/hBoundary_; values[2] = 0.0; return values; }; // \} private: std::vector exactWaterDepth_; std::vector exactVelocityX_; Scalar hBoundary_; Scalar constManningN_; // analytic solution is only available for const friction. Scalar bedSlope_; Scalar discharge_; // discharge at the inflow boundary static constexpr Scalar eps_ = 1.0e-6; std::string name_; }; } //end namespace Dumux #endif