// -*- 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 * * \brief A simple Darcy test problem (cell-centered finite volume method). */ #ifndef DUMUX_DARCY_SUBPROBLEM_HH #define DUMUX_DARCY_SUBPROBLEM_HH #include #include #include #include namespace Dumux { /*! * \brief The porous medium flow sub problem */ template class PorousMediumSubProblem : public PorousMediumFlowProblem { using ParentType = PorousMediumFlowProblem; using GridView = typename GetPropType::GridView; using Scalar = GetPropType; using PrimaryVariables = GetPropType; using FluidSystem = GetPropType; using NumEqVector = GetPropType; using BoundaryTypes = GetPropType; using FVElementGeometry = typename GetPropType::LocalView; using SubControlVolume = typename FVElementGeometry::SubControlVolume; using SubControlVolumeFace = typename FVElementGeometry::SubControlVolumeFace; using FVGridGeometry = GetPropType; using GridVariables = GetPropType; using ElementFluxVariablesCache = typename GridVariables::GridFluxVariablesCache::LocalView; // copy some indices for convenience using Indices = typename GetPropType::Indices; enum { // grid and world dimension dim = GridView::dimension, dimworld = GridView::dimensionworld, // primary variable indices conti0EqIdx = Indices::conti0EqIdx, pressureIdx = Indices::pressureIdx, #if EXNUMBER >= 3 saturationIdx = Indices::switchIdx, transportCompIdx = Indices::switchIdx #elif EXNUMBER >= 1 transportCompIdx = Indices::switchIdx #else phaseIdx = 0, transportCompIdx = 1 #endif }; using Element = typename GridView::template Codim<0>::Entity; using GlobalPosition = Dune::FieldVector; using CouplingManager = GetPropType; using TimeLoopPtr = std::shared_ptr>; public: PorousMediumSubProblem(std::shared_ptr fvGridGeometry, std::shared_ptr couplingManager) : ParentType(fvGridGeometry, "Darcy"), eps_(1e-7), couplingManager_(couplingManager) { #if EXNUMBER >= 3 saturation_ = getParamFromGroup(this->paramGroup(), "Problem.Saturation"); #else moleFraction_ = getParamFromGroup(this->paramGroup(), "Problem.MoleFraction"); #endif // initialize output file plotFluxes_ = getParamFromGroup(this->paramGroup(), "Problem.PlotFluxes", false); plotStorage_ = getParamFromGroup(this->paramGroup(), "Problem.PlotStorage", false); storageFileName_ = "storage_" + getParam("Problem.Name") + "_" + this->name() + ".csv"; storageFile_.open(storageFileName_); storageFile_ << "#Time[s]" << ";" << "WaterMass[kg]" << ";" << "WaterMassLoss[kg]" << ";" << "EvaporationRate[mm/d]" << std::endl; } /*! * \name Simulation steering */ // \{ /*! * \brief Initialize the problem. */ template void init(const SolutionVector& curSol, const GridVariables& gridVariables) { #if EXNUMBER >= 2 initialWaterContent_ = evaluateWaterMassStorageTerm(curSol, gridVariables); lastWaterMass_ = initialWaterContent_; #endif } template void postTimeStep(const SolutionVector& curSol, const GridVariables& gridVariables) { evaluateWaterMassStorageTerm(curSol, gridVariables); evaluateInterfaceFluxes(curSol, gridVariables); gnuplotStorage_.resetPlot(); gnuplotStorage_.setDatafileSeparator(';'); gnuplotStorage_.setXlabel("time [d]"); gnuplotStorage_.setXRange(0.0, getParam("TimeLoop.TEnd")); gnuplotStorage_.setYlabel("evaporation rate [mm/d]"); gnuplotStorage_.setOption("set yrange [0.0:]"); gnuplotStorage_.setOption("set y2label 'cumulative mass loss'"); gnuplotStorage_.setOption("set y2range [0.0:0.5]"); gnuplotStorage_.setOption("set y2range [0.0:0.5]"); gnuplotStorage_.addFileToPlot(storageFileName_, "using 1:4 with lines title 'evaporation rate'"); gnuplotStorage_.addFileToPlot(storageFileName_, "using 1:3 axes x1y2 with lines title 'cumulative mass loss'"); if (plotStorage_) gnuplotStorage_.plot("temp"); } template Scalar evaluateWaterMassStorageTerm(const SolutionVector& curSol, const GridVariables& gridVariables) { // compute the mass in the entire domain Scalar waterMass = 0.0; for (const auto& element : elements(this->gridGeometry().gridView())) { auto fvGeometry = localView(this->gridGeometry()); fvGeometry.bindElement(element); auto elemVolVars = localView(gridVariables.curGridVolVars()); elemVolVars.bindElement(element, fvGeometry, curSol); for (auto&& scv : scvs(fvGeometry)) { for(int phaseIdx = 0; phaseIdx < FluidSystem::numPhases; ++phaseIdx) { // insert calculation of the water mass here #if EXNUMBER >= 2 const auto& volVars = elemVolVars[scv]; waterMass += volVars.massFraction(phaseIdx, FluidSystem::H2OIdx) * volVars.density(phaseIdx) * volVars.saturation(phaseIdx) * volVars.porosity() * scv.volume() * volVars.extrusionFactor(); #else waterMass += 0.0; #endif } } } #if EXNUMBER >= 2 std::cout << "Mass of water is: " << waterMass << std::endl; #endif Scalar cumMassLoss = initialWaterContent_ - waterMass; Scalar evaporationRate = (lastWaterMass_ - waterMass) * 86400 / (this->gridGeometry().bBoxMax()[0] - this->gridGeometry().bBoxMin()[0]) / timeLoop_->timeStepSize(); lastWaterMass_ = waterMass; storageFile_ << timeLoop_->time() << ";" << waterMass << ";" << cumMassLoss << ";" << evaporationRate << std::endl; return waterMass; } template void evaluateInterfaceFluxes(const SolutionVector& curSol, const GridVariables& gridVariables) { std::vector x; std::vector y; for (const auto& element : elements(this->gridGeometry().gridView())) { auto fvGeometry = localView(this->gridGeometry()); fvGeometry.bindElement(element); auto elemVolVars = localView(gridVariables.curGridVolVars()); elemVolVars.bindElement(element, fvGeometry, curSol); for (auto&& scvf : scvfs(fvGeometry)) { if (!couplingManager().isCoupledEntity(CouplingManager::darcyIdx, scvf)) continue; #if EXNUMBER >= 2 NumEqVector flux = couplingManager().couplingData().massCouplingCondition(element, fvGeometry, elemVolVars, scvf); #else NumEqVector flux(0.0); // add "massCouplingCondition" from the couplingManager here #endif x.push_back(scvf.center()[0]); y.push_back(flux[transportCompIdx]); } } gnuplotInterfaceFluxes_.resetPlot(); gnuplotInterfaceFluxes_.setXlabel("x-position [m]"); gnuplotInterfaceFluxes_.setXRange(this->gridGeometry().bBoxMin()[0], this->gridGeometry().bBoxMax()[0]); gnuplotInterfaceFluxes_.setYlabel("flux [kg/(m^2 s)]"); gnuplotInterfaceFluxes_.setYRange(-5e-4, 0.0); gnuplotInterfaceFluxes_.setOption("set label 'time: " + std::to_string(timeLoop_->time()/86400.) + "d' at graph 0.8,0.8 "); std::string fluxFileName = "flux_" + std::to_string(timeLoop_->timeStepIndex()) + "_" + getParam("Problem.Name") + "_" + this->name() + ".csv"; gnuplotInterfaceFluxes_.addDataSetToPlot(x, y, fluxFileName, "with lines title 'water mass flux'"); if (plotFluxes_) gnuplotInterfaceFluxes_.plot("flux_" + std::to_string(timeLoop_->timeStepIndex())); } /*! * \name Problem parameters */ // \{ /*! * \brief Return the temperature within the domain in [K]. * */ Scalar temperature() const { return 293.15; } // \} /*! * \name Boundary conditions */ // \{ /*! * \brief Specifies which kind of boundary condition should be * used for which equation on a given boundary control volume. * * \param element The element * \param scvf The boundary sub control volume face */ BoundaryTypes boundaryTypes(const Element& element, const SubControlVolumeFace& scvf) const { BoundaryTypes values; values.setAllNeumann(); if (couplingManager().isCoupledEntity(CouplingManager::darcyIdx, scvf)) values.setAllCouplingNeumann(); return values; } /*! * \brief Evaluate the boundary conditions for a Neumann control volume. * * \param element The element for which the Neumann boundary condition is set * \param fvGeomentry The fvGeometry * \param elemVolVars The element volume variables * \param scvf The boundary sub control volume face * * For this method, the \a values variable stores primary variables. */ template NumEqVector neumann(const Element& element, const FVElementGeometry& fvGeometry, const ElementVolumeVariables& elemVolVars, const ElementFluxVariablesCache& elemFluxVarsCache, const SubControlVolumeFace& scvf) const { NumEqVector values(0.0); if (couplingManager().isCoupledEntity(CouplingManager::darcyIdx, scvf)) values = couplingManager().couplingData().massCouplingCondition(element, fvGeometry, elemVolVars, scvf); return values; } // \} /*! * \name Volume terms */ // \{ /*! * \brief Evaluate the source term for all phases within a given * sub-control-volume. * * \param element The element for which the source term is set * \param fvGeomentry The fvGeometry * \param elemVolVars The element volume variables * \param scv The subcontrolvolume */ template NumEqVector source(const Element& element, const FVElementGeometry& fvGeometry, const ElementVolumeVariables& elemVolVars, const SubControlVolume& scv) const { return NumEqVector(0.0); } // \} /*! * \brief Evaluate the initial value for a control volume. * * \param element The element * * For this method, the \a priVars parameter stores primary * variables. */ PrimaryVariables initialAtPos(const GlobalPosition& globalPos) const { static const Scalar stokesPressure = getParamFromGroup("Stokes", "Problem.Pressure"); PrimaryVariables values(0.0); #if EXNUMBER >= 3 values.setState(3/*bothPhases*/); values[saturationIdx] = saturation_; #elif EXNUMBER >= 1 values.setState(2/*secondPhaseOnly*/); values[transportCompIdx] = moleFraction_; #else values[transportCompIdx] = moleFraction_; #endif values[pressureIdx] = stokesPressure; return values; } // \} //! Set the coupling manager void setCouplingManager(std::shared_ptr cm) { couplingManager_ = cm; } //! Get the coupling manager const CouplingManager& couplingManager() const { return *couplingManager_; } void setTimeLoop(TimeLoopPtr timeLoop) { timeLoop_ = timeLoop; } private: bool onLeftBoundary_(const GlobalPosition &globalPos) const { return globalPos[0] < this->gridGeometry().bBoxMin()[0] + eps_; } bool onRightBoundary_(const GlobalPosition &globalPos) const { return globalPos[0] > this->gridGeometry().bBoxMax()[0] - eps_; } bool onLowerBoundary_(const GlobalPosition &globalPos) const { return globalPos[1] < this->gridGeometry().bBoxMin()[1] + eps_; } bool onUpperBoundary_(const GlobalPosition &globalPos) const { return globalPos[1] > this->gridGeometry().bBoxMax()[1] - eps_; } Scalar eps_; #if EXNUMBER >= 3 Scalar saturation_; #else Scalar moleFraction_; #endif Scalar initialWaterContent_ = 0.0; Scalar lastWaterMass_ = 0.0; TimeLoopPtr timeLoop_; std::shared_ptr couplingManager_; std::string storageFileName_; std::ofstream storageFile_; bool plotFluxes_; bool plotStorage_; Dumux::GnuplotInterface gnuplotInterfaceFluxes_; Dumux::GnuplotInterface gnuplotStorage_; }; } //end namespace Dumux #endif