// -*- 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