Line | Branch | Exec | Source |
---|---|---|---|
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 | // SPDX-FileCopyrightInfo: Copyright © DuMux Project contributors, see AUTHORS.md in root folder | ||
5 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
6 | // | ||
7 | /*! | ||
8 | * \file | ||
9 | * \ingroup PNMTwoPModel | ||
10 | * \ingroup SpatialParameters | ||
11 | * \brief The two-phase spatial parameters for pore-network models. | ||
12 | */ | ||
13 | #ifndef DUMUX_PNM_2P_SPATIAL_PARAMS_HH | ||
14 | #define DUMUX_PNM_2P_SPATIAL_PARAMS_HH | ||
15 | |||
16 | #include <dumux/material/fluidmatrixinteractions/fluidmatrixinteraction.hh> | ||
17 | #include <dumux/material/fluidmatrixinteractions/porenetwork/throat/thresholdcapillarypressures.hh> | ||
18 | |||
19 | #include <dumux/porenetwork/common/spatialparams.hh> | ||
20 | #include <dumux/porenetwork/common/poreproperties.hh> | ||
21 | #include <dumux/porenetwork/common/throatproperties.hh> | ||
22 | |||
23 | namespace Dumux::PoreNetwork { | ||
24 | |||
25 | /*! | ||
26 | * \ingroup PNMTwoPModel | ||
27 | * \ingroup SpatialParameters | ||
28 | * \brief The base class for spatial parameters for pore-network models. | ||
29 | */ | ||
30 | template<class GridGeometry, class Scalar, class LocalRules, class Implementation> | ||
31 | class TwoPSpatialParams | ||
32 | : public SpatialParams<GridGeometry, Scalar, Implementation> | ||
33 | { | ||
34 | using ParentType = SpatialParams<GridGeometry, Scalar, Implementation>; | ||
35 | using GridView = typename GridGeometry::GridView; | ||
36 | using SubControlVolume = typename GridGeometry::SubControlVolume; | ||
37 | using Element = typename GridView::template Codim<0>::Entity; | ||
38 | using GlobalPosition = typename Element::Geometry::GlobalCoordinate; | ||
39 | |||
40 | public: | ||
41 | |||
42 | 4 | TwoPSpatialParams(std::shared_ptr<const GridGeometry> gridGeometry) | |
43 |
3/12✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 4 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
|
8 | : ParentType(gridGeometry) |
44 | { | ||
45 |
2/4✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
|
8 | if (!gridGeometry->useSameGeometryForAllPores() && LocalRules::supportsMultipleGeometries()) |
46 | ✗ | DUNE_THROW(Dune::InvalidStateException, "Your MaterialLaw does not support multiple pore body shapes."); | |
47 | |||
48 |
2/4✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
|
8 | if (this->gridGeometry().useSameShapeForAllThroats()) |
49 | { | ||
50 |
1/2✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
|
4 | cornerHalfAngles_.resize(1); |
51 |
2/4✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
|
8 | const auto& shape = this->gridGeometry().throatCrossSectionShape(/*eIdx*/0); |
52 |
1/2✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
|
4 | cornerHalfAngles_[0] = Throat::cornerHalfAngles<Scalar>(shape); |
53 | } | ||
54 | else | ||
55 | { | ||
56 | ✗ | cornerHalfAngles_.resize(this->gridGeometry().gridView().size(0)); | |
57 | ✗ | for (auto&& element : elements(this->gridGeometry().gridView())) | |
58 | { | ||
59 | ✗ | const auto eIdx = this->gridGeometry().elementMapper().index(element); | |
60 | ✗ | const auto& shape = this->gridGeometry().throatCrossSectionShape(eIdx); | |
61 | ✗ | cornerHalfAngles_[eIdx] = Throat::cornerHalfAngles<Scalar>(shape); | |
62 | } | ||
63 | } | ||
64 | 4 | } | |
65 | |||
66 | /*! | ||
67 | * \brief The index of the wetting phase within a pore throat | ||
68 | */ | ||
69 | template<class FS, class ElementVolumeVariables> | ||
70 | ✗ | int wettingPhase(const Element& element, | |
71 | const ElementVolumeVariables& elemVolVars) const | ||
72 |
7/10✗ Branch 1 not taken.
✓ Branch 2 taken 417 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 417 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 417 times.
✓ Branch 8 taken 959820 times.
✓ Branch 9 taken 959820 times.
✓ Branch 10 taken 959820 times.
✓ Branch 11 taken 959820 times.
|
2879877 | { return this->asImp_().template wettingPhaseAtPos<FS>(element.geometry().center()); } |
73 | |||
74 | /*! | ||
75 | * \brief The index of the wetting phase within a pore body | ||
76 | */ | ||
77 | template<class FS, class ElementSolutionVector> | ||
78 | ✗ | int wettingPhase(const Element& element, | |
79 | const SubControlVolume& scv, | ||
80 | const ElementSolutionVector& elemSol) const | ||
81 |
5/6✓ Branch 0 taken 2401 times.
✓ Branch 1 taken 1528997 times.
✓ Branch 2 taken 2401 times.
✓ Branch 3 taken 39259 times.
✓ Branch 4 taken 1489738 times.
✗ Branch 5 not taken.
|
3062796 | { return this->asImp_().template wettingPhaseAtPos<FS>(scv.center()); } |
82 | |||
83 | /*! | ||
84 | * \brief Function for defining which phase is to be considered as the wetting phase. | ||
85 | * | ||
86 | * \return the wetting phase index | ||
87 | * \param globalPos The global position | ||
88 | */ | ||
89 | template<class FluidSystem> | ||
90 | int wettingPhaseAtPos(const GlobalPosition& globalPos) const | ||
91 | { | ||
92 | DUNE_THROW(Dune::InvalidStateException, | ||
93 | "The spatial parameters do not provide " | ||
94 | "a wettingPhaseAtPos() method."); | ||
95 | } | ||
96 | |||
97 | /*! | ||
98 | * \brief The contact angle within a pore throat \f$[rad]\f$. | ||
99 | * \note Overload for solution-dependent values. | ||
100 | * | ||
101 | * \param element The element | ||
102 | * \param elemVolVars The element volume variables | ||
103 | */ | ||
104 | template<class ElementVolumeVariables> | ||
105 | ✗ | Scalar contactAngle(const Element& element, | |
106 | const ElementVolumeVariables& elemVolVars) const | ||
107 | 5852211 | { return this->asImp_().contactAngleAtPos(element.geometry().center()); } | |
108 | |||
109 | /*! | ||
110 | * \brief The contact angle within a pore body \f$[rad]\f$. | ||
111 | * \note Overload for solution-dependent values. | ||
112 | * | ||
113 | * \param element The element | ||
114 | * \param scv The sub-control volume | ||
115 | * \param elemSol The element solution | ||
116 | */ | ||
117 | template<class ElementSolutionVector> | ||
118 | Scalar contactAngle(const Element& element, | ||
119 | const SubControlVolume& scv, | ||
120 | const ElementSolutionVector& elemSol) const | ||
121 | { return this->asImp_().contactAngleAtPos(scv.center()); } | ||
122 | |||
123 | //! \brief Function for defining the Contact Angle | ||
124 | int contactAngleAtPos(const GlobalPosition& globalPos) const | ||
125 | { | ||
126 | DUNE_THROW(Dune::InvalidStateException, | ||
127 | "The spatial parameters do not provide " | ||
128 | "a contactAngleAtPos() method."); | ||
129 | } | ||
130 | |||
131 | /*! | ||
132 | * \brief Returns the surface tension \f$ [N/m] \f$ | ||
133 | * | ||
134 | * \param element The current element | ||
135 | * \param scv The sub-control volume inside the element. | ||
136 | * \param elemSol The solution at the dofs connected to the element. | ||
137 | */ | ||
138 | template<class ElementSolution> | ||
139 | ✗ | Scalar surfaceTension(const Element& element, | |
140 | const SubControlVolume& scv, | ||
141 | const ElementSolution& elemSol) const | ||
142 | 9188362 | { return this->asImp_().surfaceTensionAtPos(scv.center()); } | |
143 | |||
144 | //! \brief Function for defining the surface Tension | ||
145 | Scalar surfaceTensionAtPos(const GlobalPosition& globalPos) const | ||
146 | { | ||
147 | DUNE_THROW(Dune::InvalidStateException, | ||
148 | "The spatial parameters do not provide " | ||
149 | "a surfaceTensionAtPos() method."); | ||
150 | } | ||
151 | |||
152 | /*! | ||
153 | * \brief Return the element (throat) specific entry capillary pressure \f$ Pa\f$ | ||
154 | * \param element The current element | ||
155 | * \param elemVolVars The element volume variables | ||
156 | */ | ||
157 | template<class ElementVolumeVariables> | ||
158 | 960111 | const Scalar pcEntry(const Element& element, | |
159 | const ElementVolumeVariables& elemVolVars) const | ||
160 | { | ||
161 | 2880333 | const auto eIdx = this->gridGeometry().elementMapper().index(element); | |
162 | // take the average of both adjacent pores TODO: is this correct? | ||
163 |
2/4✓ Branch 0 taken 960111 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 960111 times.
✗ Branch 3 not taken.
|
1920222 | const Scalar surfaceTension = 0.5*(elemVolVars[0].surfaceTension() + elemVolVars[1].surfaceTension()); |
164 | 960111 | return ThresholdCapillaryPressures::pcEntry(surfaceTension, | |
165 | 960111 | this->asImp_().contactAngle(element, elemVolVars), | |
166 | 960111 | this->asImp_().throatInscribedRadius(element, elemVolVars), | |
167 |
2/4✓ Branch 0 taken 960111 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 960111 times.
✗ Branch 3 not taken.
|
1920222 | this->gridGeometry().throatShapeFactor(eIdx)); |
168 | } | ||
169 | |||
170 | /*! | ||
171 | * \brief Return the element (throat) specific snap-off capillary pressure \f$ Pa\f$ | ||
172 | * \param element The current element | ||
173 | * \param elemVolVars The element volume variables | ||
174 | */ | ||
175 | template<class ElementVolumeVariables> | ||
176 | 959946 | const Scalar pcSnapoff(const Element& element, | |
177 | const ElementVolumeVariables& elemVolVars) const | ||
178 | { | ||
179 | // take the average of both adjacent pores TODO: is this correct? | ||
180 |
2/4✓ Branch 0 taken 959946 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 959946 times.
✗ Branch 3 not taken.
|
1919892 | const Scalar surfaceTension = 0.5*(elemVolVars[0].surfaceTension() + elemVolVars[1].surfaceTension()); |
181 | 959946 | return ThresholdCapillaryPressures::pcSnapoff(surfaceTension, | |
182 | 959946 | this->asImp_().contactAngle(element, elemVolVars), | |
183 | 959946 | this->asImp_().throatInscribedRadius(element, elemVolVars), | |
184 |
2/4✓ Branch 0 taken 959946 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 959946 times.
✗ Branch 3 not taken.
|
1919892 | this->gridGeometry().throatCrossSectionShape(/*eIdx*/ 0)); |
185 | } | ||
186 | |||
187 | /*! | ||
188 | * \brief Returns the parameter object for the pore-local pc-Sw law | ||
189 | * | ||
190 | * \param element The current element | ||
191 | * \param scv The sub-control volume inside the element. | ||
192 | * \param elemSol The solution at the dofs connected to the element. | ||
193 | * \return The material parameters object | ||
194 | */ | ||
195 | template<class ElementSolution> | ||
196 | 6042272 | auto fluidMatrixInteraction(const Element& element, | |
197 | const SubControlVolume& scv, | ||
198 | const ElementSolution& elemSol) const | ||
199 | { | ||
200 |
0/2✗ Branch 1 not taken.
✗ Branch 2 not taken.
|
6042272 | const auto params = typename LocalRules::BasicParams(this->asImp_(), element, scv, elemSol); |
201 |
6/14✓ Branch 1 taken 3062796 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3062796 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 3062796 times.
✗ Branch 8 not taken.
✗ Branch 11 not taken.
✓ Branch 12 taken 3062796 times.
✓ Branch 13 taken 3062796 times.
✗ Branch 14 not taken.
✓ Branch 15 taken 3062796 times.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
|
24169088 | return makeFluidMatrixInteraction(LocalRules(params, "SpatialParams")); |
202 | } | ||
203 | |||
204 | 2879460 | const Dune::ReservedVector<Scalar, 4>& cornerHalfAngles(const Element& element) const | |
205 | { | ||
206 |
2/4✓ Branch 0 taken 2879460 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2879460 times.
✗ Branch 3 not taken.
|
5758920 | if (this->gridGeometry().useSameShapeForAllThroats()) |
207 | 2879460 | return cornerHalfAngles_[0]; | |
208 | else | ||
209 | { | ||
210 | ✗ | const auto eIdx = this->gridGeometry().gridView().indexSet().index(element); | |
211 | ✗ | return cornerHalfAngles_[eIdx]; | |
212 | } | ||
213 | } | ||
214 | |||
215 | private: | ||
216 | std::vector<Dune::ReservedVector<Scalar, 4>> cornerHalfAngles_; | ||
217 | }; | ||
218 | |||
219 | /*! | ||
220 | * \ingroup PNMTwoPModel | ||
221 | * \ingroup SpatialParameters | ||
222 | * \brief The default class for spatial parameters for two-phase pore-network models. | ||
223 | */ | ||
224 | template<class GridGeometry, class Scalar, class LocalRules> | ||
225 | class TwoPDefaultSpatialParams | ||
226 | : public TwoPSpatialParams<GridGeometry, Scalar, LocalRules, TwoPDefaultSpatialParams<GridGeometry, Scalar, LocalRules>> | ||
227 | { | ||
228 | using ParentType = TwoPSpatialParams<GridGeometry, Scalar, LocalRules, | ||
229 | TwoPDefaultSpatialParams<GridGeometry, Scalar, LocalRules>>; | ||
230 | using GridView = typename GridGeometry::GridView; | ||
231 | using Element = typename GridView::template Codim<0>::Entity; | ||
232 | using GlobalPosition = typename Element::Geometry::GlobalCoordinate; | ||
233 | public: | ||
234 | using ParentType::ParentType; | ||
235 | |||
236 | TwoPDefaultSpatialParams(std::shared_ptr<const GridGeometry> gridGeometry) | ||
237 | : ParentType(gridGeometry) | ||
238 | { } | ||
239 | |||
240 | /*! | ||
241 | * \brief Function for defining which phase is to be considered as the wetting phase. | ||
242 | * | ||
243 | * \return the wetting phase index | ||
244 | * \param globalPos The global position | ||
245 | */ | ||
246 | template<class FluidSystem> | ||
247 | int wettingPhaseAtPos(const GlobalPosition& globalPos) const | ||
248 | { return FluidSystem::phase0Idx; } | ||
249 | |||
250 | /*! | ||
251 | * \brief Function for defining the Contact Angle | ||
252 | * | ||
253 | * \return the contact angle | ||
254 | * \param globalPos The global position | ||
255 | */ | ||
256 | int contactAngleAtPos(const GlobalPosition& globalPos) const | ||
257 | { | ||
258 | static const Scalar theta = getParam<Scalar>("SpatialParams.ContactAngle", 0.0); | ||
259 | return theta; | ||
260 | } | ||
261 | |||
262 | /*! | ||
263 | * \brief Function for defining the surface Tension | ||
264 | * | ||
265 | * \return the surface tension | ||
266 | * \param globalPos The global position | ||
267 | */ | ||
268 | Scalar surfaceTensionAtPos(const GlobalPosition& globalPos) const | ||
269 | { | ||
270 | static const Scalar gamma = getParam<Scalar>("SpatialParams.SurfaceTension", 0.0725); // default to surface tension of water/air | ||
271 | return gamma; | ||
272 | } | ||
273 | |||
274 | }; | ||
275 | |||
276 | } // namespace Dumux::PoreNetwork | ||
277 | |||
278 | #endif | ||
279 |