3.5-git
DUNE for Multi-{Phase, Component, Scale, Physics, ...} flow and transport in porous media
boundaryflux.hh
Go to the documentation of this file.
1// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2// vi: set et ts=4 sw=4 sts=4:
3/*****************************************************************************
4 * See the file COPYING for full copying permissions. *
5 * *
6 * This program is free software: you can redistribute it and/or modify *
7 * it under the terms of the GNU General Public License as published by *
8 * the Free Software Foundation, either version 3 of the License, or *
9 * (at your option) any later version. *
10 * *
11 * This program is distributed in the hope that it will be useful, *
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14 * GNU General Public License for more details. *
15 * *
16 * You should have received a copy of the GNU General Public License *
17 * along with this program. If not, see <http://www.gnu.org/licenses/>. *
18 *****************************************************************************/
24#ifndef DUMUX_PNM_BOUNDARYFLUX_HH
25#define DUMUX_PNM_BOUNDARYFLUX_HH
26
27#include <algorithm>
28#include <numeric>
29#include <vector>
30#include <type_traits>
31#include <unordered_map>
32#include <string_view>
33#include <iostream>
34
35#include <dune/common/exceptions.hh>
38
39namespace Dumux::PoreNetwork {
40
46template<class GridVariables, class LocalResidual, class SolutionVector>
48{
49 using Problem = std::decay_t<decltype(std::declval<LocalResidual>().problem())>;
50 using GridGeometry = typename ProblemTraits<Problem>::GridGeometry;
51 using GridView = typename GridGeometry::GridView;
52 using Element = typename GridView::template Codim<0>::Entity;
53 using FVElementGeometry = typename GridGeometry::LocalView;
54 using SubControlVolume = typename FVElementGeometry::SubControlVolume;
55 using BoundaryTypes = typename ProblemTraits<Problem>::BoundaryTypes;
57
58 using NumEqVector = typename SolutionVector::block_type;
59 static constexpr auto numEq = NumEqVector::dimension;
60
62 struct Result
63 {
64 NumEqVector totalFlux;
65 std::unordered_map<std::size_t, NumEqVector> fluxPerPore;
66
68 friend std::ostream& operator<< (std::ostream& stream, const Result& result)
69 {
70 stream << result.totalFlux;
71 return stream;
72 }
73
75 const auto& operator[] (int eqIdx) const
76 { return totalFlux[eqIdx]; }
77
79 operator NumEqVector() const
80 { return totalFlux; }
81 };
82
83public:
84 // export the Scalar type
85 using Scalar = typename GridVariables::Scalar;
86
87 BoundaryFlux(const GridVariables& gridVariables,
88 const LocalResidual& localResidual,
89 const SolutionVector& sol)
90 : localResidual_(localResidual)
91 , gridVariables_(gridVariables)
92 , sol_(sol)
93 , isStationary_(localResidual.isStationary())
94 {
95 const auto numDofs = localResidual_.problem().gridGeometry().numDofs();
96 isConsidered_.resize(numDofs, false);
97 boundaryFluxes_.resize(numDofs);
98 }
99
106 template<class Label>
107 Result getFlux(const std::vector<Label>& labels, const bool verbose = false) const
108 {
109 // helper lambda to decide which scvs to consider for flux calculation
110 auto restriction = [&] (const SubControlVolume& scv)
111 {
112 const Label poreLabel = localResidual_.problem().gridGeometry().poreLabel(scv.dofIndex());
113 return std::any_of(labels.begin(), labels.end(),
114 [&](const Label l){ return l == poreLabel; });
115 };
116
117 std::fill(boundaryFluxes_.begin(), boundaryFluxes_.end(), NumEqVector(0.0));
118 std::fill(isConsidered_.begin(), isConsidered_.end(), false);
119
120 // sum up the fluxes
121 for (const auto& element : elements(localResidual_.problem().gridGeometry().gridView()))
122 computeBoundaryFlux_(element, restriction, verbose);
123
124 Result result;
125 result.totalFlux = std::accumulate(boundaryFluxes_.begin(), boundaryFluxes_.end(), NumEqVector(0.0));;
126 for (int i = 0; i < isConsidered_.size(); ++i)
127 {
128 if (isConsidered_[i])
129 result.fluxPerPore[i] = boundaryFluxes_[i];
130 }
131
132 return result;
133 }
134
142 Result getFlux(std::string_view minMax, const int coord, const bool verbose = false) const
143 {
144 if (!(minMax == "min" || minMax == "max"))
145 DUNE_THROW(Dune::InvalidStateException,
146 "second argument must be either 'min' or 'max' (string) !");
147
148 const Scalar eps = 1e-6; //TODO
149 auto onMeasuringBoundary = [&] (const Scalar pos)
150 {
151 return ( (minMax == "min" && pos < localResidual_.problem().gridGeometry().bBoxMin()[coord] + eps) ||
152 (minMax == "max" && pos > localResidual_.problem().gridGeometry().bBoxMax()[coord] - eps) );
153 };
154
155 // helper lambda to decide which scvs to consider for flux calculation
156 auto restriction = [&] (const SubControlVolume& scv)
157 {
158 bool considerAllDirections = coord < 0 ? true : false;
159
160 //only consider SCVs on the boundary
161 bool considerScv = localResidual_.problem().gridGeometry().dofOnBoundary(scv.dofIndex()) && onMeasuringBoundary(scv.dofPosition()[coord]);
162
163 //check whether a vertex lies on a boundary and also check whether this boundary shall be
164 // considered for the flux calculation
165 if (considerScv && !considerAllDirections)
166 {
167 const auto& pos = scv.dofPosition();
168 if (!(pos[coord] < localResidual_.problem().gridGeometry().bBoxMin()[coord] + eps || pos[coord] > localResidual_.problem().gridGeometry().bBoxMax()[coord] -eps ))
169 considerScv = false;
170 }
171
172 return considerScv;
173 };
174
175 std::fill(boundaryFluxes_.begin(), boundaryFluxes_.end(), NumEqVector(0.0));
176 std::fill(isConsidered_.begin(), isConsidered_.end(), false);
177
178 // sum up the fluxes
179 for (const auto& element : elements(localResidual_.problem().gridGeometry().gridView()))
180 computeBoundaryFlux_(element, restriction, verbose);
181
182 Result result;
183 result.totalFlux = std::accumulate(boundaryFluxes_.begin(), boundaryFluxes_.end(), NumEqVector(0.0));;
184 for (int i = 0; i < isConsidered_.size(); ++i)
185 {
186 if (isConsidered_[i])
187 result.fluxPerPore[i] = boundaryFluxes_[i];
188 }
189
190 return result;
191 }
192
193private:
194
202 template<class RestrictingFunction>
203 void computeBoundaryFlux_(const Element& element,
204 RestrictingFunction considerScv,
205 const bool verbose = false) const
206 {
207 // by default, all coordinate directions are considered for the definition of a boundary
208
209 // make sure FVElementGeometry and volume variables are bound to the element
210 const auto fvGeometry = localView(localResidual_.problem().gridGeometry()).bind(element);
211 const auto curElemVolVars = localView(gridVariables_.curGridVolVars()).bind(element, fvGeometry, sol_);
212
213 auto prevElemVolVars = localView(gridVariables_.prevGridVolVars());
214 if (!isStationary_)
215 prevElemVolVars.bindElement(element, fvGeometry, sol_);
216
217 ElementBoundaryTypes elemBcTypes;
218 elemBcTypes.update(localResidual_.problem(), element, fvGeometry);
219 const auto elemFluxVarsCache = localView(gridVariables_.gridFluxVarsCache()).bindElement(element, fvGeometry, curElemVolVars);
220 auto residual = localResidual_.evalFluxAndSource(element, fvGeometry, curElemVolVars, elemFluxVarsCache, elemBcTypes);
221
222 if (!isStationary_)
223 residual += localResidual_.evalStorage(element, fvGeometry, prevElemVolVars, curElemVolVars);
224
225 for (auto&& scv : scvs(fvGeometry))
226 {
227 // compute the boundary flux using the local residual of the element's scv on the boundary
228 if (considerScv(scv))
229 {
230 isConsidered_[scv.dofIndex()] = true;
231
232 // get the type of the boundary condition on the scv
233 const auto& bcTypes = elemBcTypes[scv.localDofIndex()];
234
235 NumEqVector flux(0.0);
236 for (int eqIdx = 0; eqIdx < NumEqVector::dimension; ++eqIdx)
237 {
238 // Check the type of the boudary condition.
239 // If BC is Dirichlet type, the flux is equal to the local residual of the element's scv on the boundary.
240 // Otherwise the flux is either zero or equal to a source term at the element's scv on the boundary.
241 // Technicaly, the PNM considers source terms instead of Neumann BCs.
242 if (!bcTypes.isDirichlet(eqIdx))
243 {
244 auto source = localResidual_.computeSource(localResidual_.problem(), element, fvGeometry, curElemVolVars, scv);
245 source *= scv.volume() * curElemVolVars[scv].extrusionFactor();
246 flux[eqIdx] = source[eqIdx];
247 }
248 else
249 flux[eqIdx] = residual[scv.indexInElement()][eqIdx];
250 }
251
252 // The flux must be substracted:
253 // On an inlet boundary, the flux part of the local residual will be positive, since all fluxes will leave the SCV towards to interior domain.
254 // For the domain itself, however, the sign has to be negative, since mass is entering the system.
255 boundaryFluxes_[scv.dofIndex()] -= flux;
256
257 if (verbose)
258 std::cout << "SCV of element " << scv.elementIndex() << " at vertex " << scv.dofIndex() << " has flux: " << flux << std::endl;
259 }
260 }
261 }
262
263 const LocalResidual localResidual_; // store a copy of the local residual
264 const GridVariables& gridVariables_;
265 const SolutionVector& sol_;
266 bool isStationary_;
267 mutable std::vector<bool> isConsidered_;
268 mutable std::vector<NumEqVector> boundaryFluxes_;
269};
270
271} // end Dumux::PoreNetwork
272
273#endif
GridCache::LocalView localView(const GridCache &gridCache)
Free function to get the local view of a grid cache object.
Definition: localview.hh:38
std::ostream & operator<<(std::ostream &stream, DiscretizationMethod m)
Write discretization method to stream.
Definition: method.hh:171
typename NumEqVectorTraits< PrimaryVariables >::type NumEqVector
A vector with the same size as numbers of equations This is the default implementation and has to be ...
Definition: numeqvector.hh:46
Definition: discretization/porenetwork/fvelementgeometry.hh:34
std::decay_t< decltype(std::declval< Problem >().gridGeometry())> GridGeometry
Definition: common/typetraits/problem.hh:45
typename Detail::template ProblemTraits< Problem, typename GridGeometry::DiscretizationMethod >::BoundaryTypes BoundaryTypes
Definition: common/typetraits/problem.hh:46
This class stores an array of BoundaryTypes objects.
Definition: box/elementboundarytypes.hh:38
Class for the calculation of fluxes at the boundary of pore-network models.
Definition: boundaryflux.hh:48
BoundaryFlux(const GridVariables &gridVariables, const LocalResidual &localResidual, const SolutionVector &sol)
Definition: boundaryflux.hh:87
Result getFlux(std::string_view minMax, const int coord, const bool verbose=false) const
Returns the cumulative flux in , or of several pores at a given location on the boundary.
Definition: boundaryflux.hh:142
typename GridVariables::Scalar Scalar
Definition: boundaryflux.hh:85
Result getFlux(const std::vector< Label > &labels, const bool verbose=false) const
Returns the cumulative flux in , or of several pores for a given list of pore labels to consider.
Definition: boundaryflux.hh:107
Type traits for problem classes.
Boundary types gathered on an element.