From ed054eac030d9df55ef03779e0e197fe4c963e1a Mon Sep 17 00:00:00 2001
From: Timo Koch <timo.koch@iws.uni-stuttgart.de>
Date: Mon, 13 Feb 2017 20:26:17 +0100
Subject: [PATCH] [test][richardsnc] Add output of tracer mass

---
 .../1p2c_richards2c/richardstestproblem.hh    |  35 ++++--
 .../1p2c_richards2c/rootsystemtestproblem.hh  |  28 +++--
 .../1p2c_richards2c/rositestproblem.hh        | 110 +++++++++++++++++-
 .../1p2c_richards2c/test_rosi2c.input         |   2 +-
 4 files changed, 149 insertions(+), 26 deletions(-)

diff --git a/test/mixeddimension/embedded/1p2c_richards2c/richardstestproblem.hh b/test/mixeddimension/embedded/1p2c_richards2c/richardstestproblem.hh
index d71331baba..e2892d6bd1 100644
--- a/test/mixeddimension/embedded/1p2c_richards2c/richardstestproblem.hh
+++ b/test/mixeddimension/embedded/1p2c_richards2c/richardstestproblem.hh
@@ -29,6 +29,9 @@
 #include <dumux/implicit/cellcentered/tpfa/properties.hh>
 #include <dumux/porousmediumflow/implicit/problem.hh>
 #include <dumux/porousmediumflow/richardsnc/implicit/model.hh>
+#include <dumux/material/components/simpleh2o.hh>
+#include <dumux/material/components/constant.hh>
+#include <dumux/material/fluidsystems/liquidphase2c.hh>
 
 //! get the properties needed for subproblems
 #include <dumux/mixeddimension/subproblemproperties.hh>
@@ -59,6 +62,13 @@ SET_BOOL_PROP(RichardsTestProblem, SolutionDependentHeatConduction, false);
 // Set the problem property
 SET_TYPE_PROP(RichardsTestProblem, Problem, RichardsTestProblem<TypeTag>);
 
+// Set the fluid system
+SET_PROP(RichardsTestProblem, FluidSystem)
+{
+    using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar);
+    using type = FluidSystems::LiquidPhaseTwoC<TypeTag, SimpleH2O<Scalar>, Constant<TypeTag,Scalar>>;
+};
+
 // Set the spatial parameters
 SET_TYPE_PROP(RichardsTestProblem, SpatialParams, RichardsTestSpatialParams<TypeTag>);
 
@@ -70,6 +80,9 @@ SET_BOOL_PROP(RichardsTestProblem, VtkAddVelocity, true);
 
 // Set the grid parameter group
 SET_STRING_PROP(RichardsTestProblem, GridParameterGroup, "SoilGrid");
+
+// Use mass fractions
+SET_BOOL_PROP(RichardsTestProblem, UseMoles, false);
 }
 
 /*!
@@ -132,7 +145,7 @@ public:
     : ParentType(timeManager, gridView)
     {
         name_ = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, std::string, Problem, Name) + "-soil";
-        contaminantMoleFraction_ = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, Problem, ContaminantMoleFraction);
+        contaminantMassFraction_ = GET_RUNTIME_PARAM_FROM_GROUP(TypeTag, Scalar, Problem, ContaminantMassFraction);
 
         // for initial conditions
         const Scalar sw = 0.2; // start with 20% saturation on top
@@ -225,12 +238,14 @@ public:
         // compute correct upwind concentration
         if (sourceValues[conti0EqIdx] > 0)
             sourceValues[transportEqIdx] = sourceValues[conti0EqIdx]
-                                            * lowDimVolVars.moleFraction(wPhaseIdx, transportCompIdx)
-                                            * lowDimVolVars.molarDensity(wPhaseIdx);
+                                            * lowDimVolVars.massFraction(wPhaseIdx, transportCompIdx);
         else
             sourceValues[transportEqIdx] = sourceValues[conti0EqIdx]
-                                            * bulkVolVars.moleFraction(wPhaseIdx, transportCompIdx)
-                                            * bulkVolVars.molarDensity(wPhaseIdx);
+                                            * bulkVolVars.massFraction(wPhaseIdx, transportCompIdx);
+
+        sourceValues[transportEqIdx] += 2* M_PI *rootRadius * 1.0e-8
+                                        *(lowDimVolVars.massFraction(wPhaseIdx, transportCompIdx) - bulkVolVars.massFraction(wPhaseIdx, transportCompIdx))
+                                        *0.5*bulkVolVars.density(wPhaseIdx)*lowDimVolVars.density(wPhaseIdx);
 
         sourceValues *= source.quadratureWeight()*source.integrationElement();
         source = sourceValues;
@@ -282,11 +297,7 @@ public:
     BoundaryTypes boundaryTypesAtPos(const GlobalPosition &globalPos) const
     {
         BoundaryTypes bcTypes;
-
-        // if (globalPos[dimWorld-1] > this->bBoxMax()[dimWorld-1] - eps_)
-        //     bcTypes.setAllDirichlet();
-        // else
-            bcTypes.setAllNeumann();
+        bcTypes.setAllNeumann();
         return bcTypes;
     }
 
@@ -327,7 +338,7 @@ public:
             contaminationPos += this->bBoxMin();
 
             if ((globalPos - contaminationPos).two_norm() < 0.1*(this->bBoxMax()-this->bBoxMin()).two_norm() + eps_)
-                return contaminantMoleFraction_;
+                return contaminantMassFraction_;
             else
                 return 0.0;
         }();
@@ -357,7 +368,7 @@ public:
 private:
     std::string name_;
     std::shared_ptr<CouplingManager> couplingManager_;
-    Scalar contaminantMoleFraction_;
+    Scalar contaminantMassFraction_;
     Scalar pcTop_;
     static constexpr Scalar eps_ = 1e-7;
 };
diff --git a/test/mixeddimension/embedded/1p2c_richards2c/rootsystemtestproblem.hh b/test/mixeddimension/embedded/1p2c_richards2c/rootsystemtestproblem.hh
index 64b0c402a9..3846bbf6e9 100644
--- a/test/mixeddimension/embedded/1p2c_richards2c/rootsystemtestproblem.hh
+++ b/test/mixeddimension/embedded/1p2c_richards2c/rootsystemtestproblem.hh
@@ -76,6 +76,9 @@ SET_BOOL_PROP(RootsystemTestProblem, ProblemEnableGravity, true);
 
 // Enable velocity output
 SET_BOOL_PROP(RootsystemTestProblem, VtkAddVelocity, true);
+
+// Use mass fractions
+SET_BOOL_PROP(RootsystemTestProblem, UseMoles, false);
 }
 
 /*!
@@ -107,7 +110,7 @@ class RootsystemTestProblem : public ImplicitPorousMediaProblem<TypeTag>
         transportCompIdx = Indices::transportCompIdx
     };
 
-    static const int phaseIdx = GET_PROP_VALUE(TypeTag, PhaseIdx);
+    static const int wPhaseIdx = GET_PROP_VALUE(TypeTag, PhaseIdx);
 
     using PrimaryVariables = typename GET_PROP_TYPE(TypeTag, PrimaryVariables);
     using BoundaryTypes = typename GET_PROP_TYPE(TypeTag, BoundaryTypes);
@@ -226,13 +229,12 @@ public:
         PrimaryVariables values(0.0);
         if (scvf.center()[2] + eps_ > this->bBoxMax()[2])
         {
-            const auto r = this->spatialParams().radius(this->gridView().indexSet().index(element));
-            values[conti0EqIdx] = GET_RUNTIME_PARAM(TypeTag, Scalar, BoundaryConditions.TranspirationRate)
-                                  /(M_PI*r*r)/scvf.area();
             const auto& volVars = elemVolvars[scvf.insideScvIdx()];
+            values[conti0EqIdx] = GET_RUNTIME_PARAM(TypeTag, Scalar, BoundaryConditions.TranspirationRate)
+                                  /volVars.extrusionFactor()/scvf.area();
+
             values[transportEqIdx] = values[conti0EqIdx]
-                                      * volVars.moleFraction(phaseIdx, transportCompIdx)
-                                      * volVars.molarDensity(phaseIdx);
+                                      * volVars.massFraction(wPhaseIdx, transportCompIdx);
         }
         return values;
 
@@ -308,18 +310,20 @@ public:
 
         // sink defined as radial flow Jr * density [m^2 s-1]* [kg m-3]
         PrimaryVariables sourceValues(0.0);
-        sourceValues[conti0EqIdx] = 2* M_PI *rootRadius * Kr *(bulkVolVars.pressure(phaseIdx) - lowDimVolVars.pressure(phaseIdx))
-                                    *bulkVolVars.density(phaseIdx);
+        sourceValues[conti0EqIdx] = 2* M_PI *rootRadius * Kr *(bulkVolVars.pressure(wPhaseIdx) - lowDimVolVars.pressure(wPhaseIdx))
+                                    *bulkVolVars.density(wPhaseIdx);
 
         // compute correct upwind concentration
         if (sourceValues[conti0EqIdx] < 0)
             sourceValues[transportEqIdx] = sourceValues[conti0EqIdx]
-                                            * lowDimVolVars.moleFraction(phaseIdx, transportCompIdx)
-                                            * lowDimVolVars.molarDensity(phaseIdx);
+                                            * lowDimVolVars.massFraction(wPhaseIdx, transportCompIdx);
         else
             sourceValues[transportEqIdx] = sourceValues[conti0EqIdx]
-                                            * bulkVolVars.moleFraction(phaseIdx, transportCompIdx)
-                                            * bulkVolVars.molarDensity(phaseIdx);
+                                            * bulkVolVars.massFraction(wPhaseIdx, transportCompIdx);
+
+        sourceValues[transportEqIdx] += 2* M_PI *rootRadius * 1.0e-8
+                                        *(bulkVolVars.massFraction(wPhaseIdx, transportCompIdx) - lowDimVolVars.massFraction(wPhaseIdx, transportCompIdx))
+                                        *0.5*bulkVolVars.density(wPhaseIdx)*lowDimVolVars.density(wPhaseIdx);
 
         sourceValues *= source.quadratureWeight()*source.integrationElement();
         source = sourceValues;
diff --git a/test/mixeddimension/embedded/1p2c_richards2c/rositestproblem.hh b/test/mixeddimension/embedded/1p2c_richards2c/rositestproblem.hh
index d96257a60c..1263c1ed09 100644
--- a/test/mixeddimension/embedded/1p2c_richards2c/rositestproblem.hh
+++ b/test/mixeddimension/embedded/1p2c_richards2c/rositestproblem.hh
@@ -88,6 +88,16 @@ class RosiTestProblem : public MixedDimensionProblem<TypeTag>
     using BulkGridView = typename GET_PROP_TYPE(BulkProblemTypeTag, GridView);
     using LowDimGridView = typename GET_PROP_TYPE(LowDimProblemTypeTag, GridView);
 
+    using BulkIndices = typename GET_PROP_TYPE(BulkProblemTypeTag, Indices);
+    using LowDimIndices = typename GET_PROP_TYPE(LowDimProblemTypeTag, Indices);
+
+    enum {
+        // indices of the primary variables
+        wPhaseIdx = BulkIndices::wPhaseIdx,
+        transportCompIdx = BulkIndices::compMainIdx + 1,
+        transportEqIdx = BulkIndices::conti0EqIdx + 1
+    };
+
 public:
     RosiTestProblem(TimeManager &timeManager, const BulkGridView &bulkGridView, const LowDimGridView &lowDimgridView)
     : ParentType(timeManager, bulkGridView, lowDimgridView)
@@ -96,7 +106,105 @@ public:
     void preTimeStep()
     {
         ParentType::preTimeStep();
-        std::cout << '\n' << "Simulation time in hours: " << this->timeManager().time()/3600 << '\n';
+        std::cout << '\n' << "\033[1m" << "Simulation time in hours: " << this->timeManager().time()/3600 << "\033[0m" << '\n';
+    }
+
+    void init()
+    {
+        ParentType::init();
+
+        // compute the mass in the entire domain to make sure the tracer is conserved
+        Scalar mass = 0.0;
+
+        // low dim elements
+        for (const auto& element : elements(this->lowDimProblem().gridView()))
+        {
+            auto fvGeometry = localView(this->lowDimProblem().model().globalFvGeometry());
+            fvGeometry.bindElement(element);
+
+            auto elemVolVars = localView(this->lowDimProblem().model().curGlobalVolVars());
+            elemVolVars.bindElement(element, fvGeometry, this->lowDimProblem().model().curSol());
+
+            for (auto&& scv : scvs(fvGeometry))
+            {
+                const auto& volVars = elemVolVars[scv];
+                mass += volVars.massFraction(wPhaseIdx, transportCompIdx)*volVars.density(wPhaseIdx)
+                         *scv.volume() * volVars.porosity() * volVars.extrusionFactor();
+            }
+        }
+
+        // bulk elements
+        for (const auto& element : elements(this->bulkProblem().gridView()))
+        {
+            auto fvGeometry = localView(this->bulkProblem().model().globalFvGeometry());
+            fvGeometry.bindElement(element);
+
+            auto elemVolVars = localView(this->bulkProblem().model().curGlobalVolVars());
+            elemVolVars.bindElement(element, fvGeometry, this->bulkProblem().model().curSol());
+
+            for (auto&& scv : scvs(fvGeometry))
+            {
+                const auto& volVars = elemVolVars[scv];
+                mass += volVars.massFraction(wPhaseIdx, transportCompIdx)*volVars.density(wPhaseIdx)
+                         * scv.volume() * volVars.porosity() * volVars.extrusionFactor();
+            }
+        }
+
+        std::cout << "\033[1;36m" << "The domain initially contains " << mass*1e9 << " ug tracer.\033[0m" << '\n';
+    }
+
+    void postTimeStep()
+    {
+        ParentType::postTimeStep();
+
+        // compute the mass in the entire domain to make sure the tracer is conserved
+        Scalar mass = 0.0;
+        Scalar boundaryMass = 0.0;
+
+        // low dim elements
+        for (const auto& element : elements(this->lowDimProblem().gridView()))
+        {
+            auto fvGeometry = localView(this->lowDimProblem().model().globalFvGeometry());
+            fvGeometry.bindElement(element);
+
+            auto elemVolVars = localView(this->lowDimProblem().model().curGlobalVolVars());
+            elemVolVars.bindElement(element, fvGeometry, this->lowDimProblem().model().curSol());
+
+            for (auto&& scv : scvs(fvGeometry))
+            {
+                const auto& volVars = elemVolVars[scv];
+                mass += volVars.massFraction(wPhaseIdx, transportCompIdx)*volVars.density(wPhaseIdx)
+                         *scv.volume() * volVars.porosity() * volVars.extrusionFactor();
+            }
+
+            for (auto&& scvf : scvfs(fvGeometry))
+                if (scvf.boundary())
+                    boundaryMass += this->lowDimProblem().neumann(element, fvGeometry, elemVolVars, scvf)[transportEqIdx]
+                                     * scvf.area() * elemVolVars[scvf.insideScvIdx()].extrusionFactor()
+                                     * this->timeManager().timeStepSize();
+
+        }
+
+        // bulk elements
+        for (const auto& element : elements(this->bulkProblem().gridView()))
+        {
+            auto fvGeometry = localView(this->bulkProblem().model().globalFvGeometry());
+            fvGeometry.bindElement(element);
+
+            auto elemVolVars = localView(this->bulkProblem().model().curGlobalVolVars());
+            elemVolVars.bindElement(element, fvGeometry, this->bulkProblem().model().curSol());
+
+            for (auto&& scv : scvs(fvGeometry))
+            {
+                const auto& volVars = elemVolVars[scv];
+                mass += volVars.massFraction(wPhaseIdx, transportCompIdx)*volVars.density(wPhaseIdx)
+                         *scv.volume() * volVars.porosity() * volVars.extrusionFactor();
+            }
+        }
+
+        std::cout << "\033[1;36m" << "The domain contains " << mass*1e9 << " ug, "
+                  << -boundaryMass*1e9 << " (over the boundary, ug) =  "
+                  << (mass - boundaryMass)*1e9 << " ug balanced.\033[0m" << '\n';
     }
 };
 
diff --git a/test/mixeddimension/embedded/1p2c_richards2c/test_rosi2c.input b/test/mixeddimension/embedded/1p2c_richards2c/test_rosi2c.input
index 7c2bbf4ad2..9db0aeb9bd 100644
--- a/test/mixeddimension/embedded/1p2c_richards2c/test_rosi2c.input
+++ b/test/mixeddimension/embedded/1p2c_richards2c/test_rosi2c.input
@@ -18,7 +18,7 @@ Cells = 20 20 20
 
 [Problem]
 Name = rosi2c
-ContaminantMoleFraction = 1e-9
+ContaminantMassFraction = 1e-6
 
 [Component]
 LiquidDiffusionCoefficient = 1e-9
-- 
GitLab