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 NavierStokesModel | ||
10 | * \copydoc Dumux::StaggeredVelocityGradients | ||
11 | */ | ||
12 | #ifndef DUMUX_NAVIERSTOKES_STAGGERED_VELOCITYGRADIENTS_HH | ||
13 | #define DUMUX_NAVIERSTOKES_STAGGERED_VELOCITYGRADIENTS_HH | ||
14 | |||
15 | #include <optional> | ||
16 | #include <dumux/common/exceptions.hh> | ||
17 | #include <dumux/common/parameters.hh> | ||
18 | #include <dune/common/fmatrix.hh> | ||
19 | #include <dumux/common/typetraits/problem.hh> | ||
20 | #include <dumux/discretization/facecentered/staggered/elementboundarytypes.hh> | ||
21 | |||
22 | // forward declare | ||
23 | namespace Dune { | ||
24 | |||
25 | template<class ct, int dim, template< int > class Ref, class Comm> | ||
26 | class SPGrid; | ||
27 | } | ||
28 | |||
29 | namespace Dumux { | ||
30 | |||
31 | namespace Detail | ||
32 | { | ||
33 | |||
34 | template<class Grid> | ||
35 | struct SupportsPeriodicity : public std::false_type {}; | ||
36 | |||
37 | template<class ct, int dim, template< int > class Ref, class Comm> | ||
38 | struct SupportsPeriodicity<Dune::SPGrid<ct, dim, Ref, Comm>> : public std::true_type {}; | ||
39 | } | ||
40 | |||
41 | /*! | ||
42 | * \ingroup NavierStokesModel | ||
43 | * \brief Helper class for calculating the velocity gradients for the Navier-Stokes model using the staggered grid discretization. | ||
44 | */ | ||
45 | class StaggeredVelocityGradients | ||
46 | { | ||
47 | public: | ||
48 | |||
49 | template<class FVElementGeometry, class ElemVolVars> | ||
50 | static auto velocityGradient(const FVElementGeometry& fvGeometry, | ||
51 | const typename FVElementGeometry::SubControlVolumeFace& scvf, | ||
52 | const ElemVolVars& elemVolVars, | ||
53 | bool fullGradient = false) | ||
54 | { | ||
55 | const auto& problem = elemVolVars.gridVolVars().problem(); | ||
56 | using BoundaryTypes = typename ProblemTraits<std::decay_t<decltype(problem)>>::BoundaryTypes; | ||
57 | using ElementBoundaryTypes = FaceCenteredStaggeredElementBoundaryTypes<BoundaryTypes>; | ||
58 | ElementBoundaryTypes elemBcTypes; | ||
59 | elemBcTypes.update(problem, fvGeometry.element(), fvGeometry); | ||
60 | return velocityGradient(fvGeometry, scvf, elemVolVars, elemBcTypes, fullGradient); | ||
61 | } | ||
62 | |||
63 | template<class FVElementGeometry, class ElemVolVars, class BoundaryTypes> | ||
64 | 186226016 | static auto velocityGradient(const FVElementGeometry& fvGeometry, | |
65 | const typename FVElementGeometry::SubControlVolumeFace& scvf, | ||
66 | const ElemVolVars& elemVolVars, | ||
67 | const FaceCenteredStaggeredElementBoundaryTypes<BoundaryTypes>& elemBcTypes, | ||
68 | bool fullGradient = false) | ||
69 | { | ||
70 | using Scalar = typename FVElementGeometry::GridGeometry::GlobalCoordinate::value_type; | ||
71 | static constexpr auto dim = FVElementGeometry::GridGeometry::GlobalCoordinate::dimension; | ||
72 |
0/2✗ Branch 0 not taken.
✗ Branch 1 not taken.
|
186226016 | Dune::FieldMatrix<Scalar, dim, dim> gradient(0.0); |
73 |
2/4✗ Branch 0 not taken.
✓ Branch 1 taken 182152912 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 182152912 times.
|
372452032 | const auto& scv = fvGeometry.scv(scvf.insideScvIdx()); |
74 | |||
75 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 186226016 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
186226016 | if (scvf.isFrontal() && !scvf.boundary()) |
76 | { | ||
77 | ✗ | gradient[scv.dofAxis()][scv.dofAxis()] = velocityGradII(fvGeometry, scvf, elemVolVars); | |
78 | |||
79 | ✗ | if (fullGradient) | |
80 | { | ||
81 | ✗ | Dune::FieldMatrix<std::size_t, dim, dim> counter(0.0); | |
82 | |||
83 | ✗ | for (const auto& otherScvf : scvfs(fvGeometry)) | |
84 | { | ||
85 | ✗ | if (otherScvf.index() == scvf.index()) | |
86 | continue; | ||
87 | |||
88 | ✗ | const auto& otherScv = fvGeometry.scv(otherScvf.insideScvIdx()); | |
89 | |||
90 | ✗ | if (otherScvf.isFrontal() && !otherScvf.boundary()) | |
91 | ✗ | gradient[otherScv.dofAxis()][otherScv.dofAxis()] = velocityGradII(fvGeometry, otherScvf, elemVolVars); | |
92 | ✗ | else if (otherScvf.isLateral()) | |
93 | { | ||
94 | ✗ | assert(otherScv.dofAxis() != otherScvf.normalAxis()); | |
95 | ✗ | const auto i = otherScv.dofAxis(); | |
96 | ✗ | const auto j = otherScvf.normalAxis(); | |
97 | |||
98 | ✗ | if (!fvGeometry.hasBoundaryScvf() || !elemBcTypes[otherScvf.localIndex()].isNeumann(i)) | |
99 | { | ||
100 | ✗ | gradient[i][j] += velocityGradIJ(fvGeometry, otherScvf, elemVolVars); | |
101 | ✗ | ++counter[i][j]; | |
102 | } | ||
103 | ✗ | if (!fvGeometry.hasBoundaryScvf() || !otherScv.boundary() || | |
104 | ✗ | !elemBcTypes[fvGeometry.frontalScvfOnBoundary(otherScv).localIndex()].isNeumann(j)) | |
105 | { | ||
106 | ✗ | gradient[j][i] += velocityGradJI(fvGeometry, otherScvf, elemVolVars); | |
107 | ✗ | ++counter[j][i]; | |
108 | } | ||
109 | } | ||
110 | } | ||
111 | |||
112 | ✗ | for (int i = 0; i < dim; ++i) | |
113 | ✗ | for (int j = 0; j < dim; ++j) | |
114 | ✗ | if (i != j) | |
115 | ✗ | gradient[i][j] /= counter[i][j]; | |
116 | } | ||
117 | } | ||
118 | else | ||
119 | { | ||
120 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 186226016 times.
|
186226016 | if (fullGradient) |
121 | ✗ | DUNE_THROW(Dune::NotImplemented, "Full gradient for lateral staggered faces not implemented"); | |
122 | |||
123 | 186226016 | gradient[scv.dofAxis()][scvf.normalAxis()] = velocityGradIJ(fvGeometry, scvf, elemVolVars); | |
124 | 186226016 | gradient[scvf.normalAxis()][scv.dofAxis()] = velocityGradJI(fvGeometry, scvf, elemVolVars); | |
125 | } | ||
126 | |||
127 | 186226016 | return gradient; | |
128 | } | ||
129 | |||
130 | /*! | ||
131 | * \brief Returns the in-axis velocity gradient. | ||
132 | * | ||
133 | * \verbatim | ||
134 | * ---------======= == and # staggered half-control-volume | ||
135 | * | # | current scv | ||
136 | * | # | # staggered face over which fluxes are calculated | ||
137 | * vel.Opp <~~| O~~> x~~~~> vel.Self | ||
138 | * | # | x dof position | ||
139 | * | # | | ||
140 | * --------======== -- element | ||
141 | * | ||
142 | * O position at which gradient is evaluated (integration point) | ||
143 | * \endverbatim | ||
144 | */ | ||
145 | template<class FVElementGeometry, class ElemVolVars> | ||
146 | 93210817 | static auto velocityGradII(const FVElementGeometry& fvGeometry, | |
147 | const typename FVElementGeometry::SubControlVolumeFace& scvf, | ||
148 | const ElemVolVars& elemVolVars) | ||
149 | { | ||
150 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 93210817 times.
|
93210817 | assert(scvf.isFrontal()); |
151 | // The velocities of the dof at interest and the one of the opposite scvf. | ||
152 | 186421634 | const auto velocitySelf = elemVolVars[scvf.insideScvIdx()].velocity(); | |
153 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 91158249 times.
|
186421634 | const auto velocityOpposite = elemVolVars[scvf.outsideScvIdx()].velocity(); |
154 |
2/4✗ Branch 0 not taken.
✓ Branch 1 taken 91158249 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 91158249 times.
|
279254817 | const auto distance = (fvGeometry.scv(scvf.outsideScvIdx()).dofPosition() - fvGeometry.scv(scvf.insideScvIdx()).dofPosition()).two_norm(); |
155 | |||
156 | 93210817 | return (velocityOpposite - velocitySelf) / distance * scvf.directionSign(); | |
157 | } | ||
158 | |||
159 | /*! | ||
160 | * \brief Returns the velocity gradient perpendicular to the orientation of our current scvf. | ||
161 | * | ||
162 | * \verbatim | ||
163 | * ---------------- | ||
164 | * | |outer || and # staggered half-control-volume (own element) | ||
165 | * | |vel. gradient | ||
166 | * | |~~~~> -------> :: staggered half-control-volume (neighbor element) | ||
167 | * | | ------> | ||
168 | * | lateral scvf | -----> x dof position | ||
169 | * ---------######O::::::::: ----> | ||
170 | * | || | :: ---> -- elements | ||
171 | * | || | :: --> | ||
172 | * | || scv x~~~~> :: -> O position at which gradient is evaluated (integration point) | ||
173 | * | || | inner :: | ||
174 | * | || | vel. :: | ||
175 | * ---------#######::::::::: | ||
176 | * | ||
177 | * | ||
178 | * \endverbatim | ||
179 | */ | ||
180 | template<class FVElementGeometry, class ElemVolVars> | ||
181 | 186594868 | static auto velocityGradIJ(const FVElementGeometry& fvGeometry, | |
182 | const typename FVElementGeometry::SubControlVolumeFace& scvf, | ||
183 | const ElemVolVars& elemVolVars) | ||
184 | { | ||
185 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 186594868 times.
|
186594868 | assert(scvf.isLateral()); |
186 | 373189736 | const auto innerVelocity = elemVolVars[scvf.insideScvIdx()].velocity(); | |
187 | 373189736 | const auto outerVelocity = elemVolVars[scvf.outsideScvIdx()].velocity(); | |
188 | 186594868 | const auto distance = getDistanceIJ_(fvGeometry, scvf); | |
189 | 186594868 | return (outerVelocity - innerVelocity) / distance * scvf.directionSign(); | |
190 | } | ||
191 | |||
192 | /*! | ||
193 | * \brief Returns the velocity gradient in line with our current scvf. | ||
194 | * | ||
195 | * \verbatim | ||
196 | * ^ gradient | ||
197 | * | ^ | ||
198 | * | | ^ | ||
199 | * | | | ^ | ||
200 | * | | | | ^ | ||
201 | * | | | | | ^ || and # staggered half-control-volume (own element) | ||
202 | * | | | | | | | ||
203 | * :: staggered half-control-volume (neighbor element) | ||
204 | * ---------------- | ||
205 | * | inner | outer x dof position (of own scv) | ||
206 | * | vel. | vel. | ||
207 | * | ^ | ^ -- elements | ||
208 | * | | lat. | | | ||
209 | * | | scvf | | O position at which gradient is evaluated (integration point) | ||
210 | * ---------######O::::::::: | ||
211 | * | || ~ :: ~ ~ orthogonal scvf | ||
212 | * | || ~ :: | ||
213 | * | || scv x :: | ||
214 | * | || | :: | ||
215 | * | || | :: | ||
216 | * ---------#######::::::::: | ||
217 | * | ||
218 | * | ||
219 | * \endverbatim | ||
220 | */ | ||
221 | template<class FVElementGeometry, class ElemVolVars> | ||
222 | 186604142 | static auto velocityGradJI(const FVElementGeometry& fvGeometry, | |
223 | const typename FVElementGeometry::SubControlVolumeFace& scvf, | ||
224 | const ElemVolVars& elemVolVars) | ||
225 | { | ||
226 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 186604142 times.
|
186604142 | assert(scvf.isLateral()); |
227 | 186604142 | const auto& orthogonalScvf = fvGeometry.lateralOrthogonalScvf(scvf); | |
228 | 373208284 | const auto innerVelocity = elemVolVars[orthogonalScvf.insideScvIdx()].velocity(); | |
229 | 373208284 | const auto outerVelocity = elemVolVars[orthogonalScvf.outsideScvIdx()].velocity(); | |
230 | 186604142 | const auto distance = getDistanceJI_(fvGeometry, scvf, orthogonalScvf); | |
231 | 186604142 | return (outerVelocity - innerVelocity) / distance * orthogonalScvf.directionSign(); | |
232 | } | ||
233 | |||
234 | private: | ||
235 | |||
236 | template<class FVElementGeometry> | ||
237 | 186594868 | static auto getDistanceIJ_(const FVElementGeometry& fvGeometry, | |
238 | const typename FVElementGeometry::SubControlVolumeFace& scvf) | ||
239 | { | ||
240 |
2/4✗ Branch 0 not taken.
✓ Branch 1 taken 182504516 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 182504516 times.
|
373189736 | const auto& insideScv = fvGeometry.scv(scvf.insideScvIdx()); |
241 | // The scvf is on a boundary, hence there is no outer DOF. | ||
242 | // We take the distance to the boundary instead. | ||
243 |
2/2✓ Branch 0 taken 2490746 times.
✓ Branch 1 taken 184104122 times.
|
186594868 | if (scvf.boundary()) |
244 | 2490746 | return getDistanceToBoundary_(insideScv, scvf); | |
245 | |||
246 | 368208244 | const auto& outsideScv = fvGeometry.scv(scvf.outsideScvIdx()); | |
247 | |||
248 | // The standard case: Our grid does not support periodic boundaries. | ||
249 | if constexpr (!Detail::SupportsPeriodicity<typename FVElementGeometry::GridGeometry::Grid>()) | ||
250 | 184088362 | return getStandardDistance_(insideScv, outsideScv); | |
251 | else | ||
252 | { | ||
253 | 15760 | const auto& gridGeometry = fvGeometry.gridGeometry(); | |
254 | 15760 | const auto& orthogonalScvf = fvGeometry.lateralOrthogonalScvf(scvf); | |
255 |
2/4✓ Branch 0 taken 15760 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 15760 times.
✗ Branch 3 not taken.
|
31520 | const auto& orthogonalInsideScv = fvGeometry.scv(orthogonalScvf.insideScvIdx()); |
256 | |||
257 | // The standard case: Our grid is not periodic or the lateral scvf does not lie on a periodic boundary. | ||
258 |
3/4✓ Branch 0 taken 15760 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 14968 times.
✓ Branch 4 taken 792 times.
|
15760 | if (!gridGeometry.isPeriodic() || !gridGeometry.dofOnPeriodicBoundary(orthogonalInsideScv.dofIndex())) |
259 | 14968 | return getStandardDistance_(insideScv, outsideScv); | |
260 | |||
261 | // Treating periodic boundaries is more involved: | ||
262 | // 1. Consider the outside scv within the element adjacent to the other periodic boundary. | ||
263 | // 2. Iterate over this scv's faces until you find the face parallel to our own scvf. | ||
264 | // This face would lie directly next to our own scvf if the grid was not periodic. | ||
265 | // 3. Calculate the total distance by summing up the distances between the DOFs and the respective integration points | ||
266 | // corresponding to the inside and outside scvfs. | ||
267 | 792 | auto periodicFvGeometry = localView(gridGeometry); | |
268 | 792 | const auto& periodicElement = gridGeometry.element(outsideScv.elementIndex()); | |
269 | 792 | periodicFvGeometry.bindElement(periodicElement); | |
270 | |||
271 |
2/4✓ Branch 1 taken 1980 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1980 times.
✗ Branch 4 not taken.
|
1980 | for (const auto& outsideScvf : scvfs(periodicFvGeometry, outsideScv)) |
272 | { | ||
273 |
4/4✓ Branch 0 taken 1188 times.
✓ Branch 1 taken 792 times.
✓ Branch 2 taken 396 times.
✓ Branch 3 taken 792 times.
|
1980 | if (outsideScvf.normalAxis() == scvf.normalAxis() && outsideScvf.directionSign() != scvf.directionSign()) |
274 | { | ||
275 | 3168 | const auto insideDistance = (insideScv.dofPosition() - scvf.ipGlobal()).two_norm(); | |
276 | 3168 | const auto outsideDistance = (outsideScv.dofPosition() - outsideScvf.ipGlobal()).two_norm(); | |
277 | 792 | return insideDistance + outsideDistance; | |
278 | } | ||
279 | } | ||
280 | ✗ | DUNE_THROW(Dune::InvalidStateException, "Could not find scvf in periodic element"); | |
281 | } | ||
282 | } | ||
283 | |||
284 | //! Get the distance between the DOFs at which the inner and outer velocities are defined. | ||
285 | template<class FVElementGeometry> | ||
286 | 182516254 | static auto getDistanceJI_(const FVElementGeometry& fvGeometry, | |
287 | const typename FVElementGeometry::SubControlVolumeFace& scvf, | ||
288 | const typename FVElementGeometry::SubControlVolumeFace& orthogonalScvf) | ||
289 | { | ||
290 |
2/4✗ Branch 0 not taken.
✓ Branch 1 taken 182516254 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 182516254 times.
|
365032508 | const auto& orthogonalInsideScv = fvGeometry.scv(orthogonalScvf.insideScvIdx()); |
291 | |||
292 | // The orthogonal scvf is on a boundary, hence there is no outer DOF. | ||
293 | // We take the distance to the boundary instead. | ||
294 |
2/2✓ Branch 0 taken 3584 times.
✓ Branch 1 taken 182512670 times.
|
182516254 | if (orthogonalScvf.boundary()) |
295 | 3584 | return getDistanceToBoundary_(orthogonalInsideScv, orthogonalScvf); | |
296 | |||
297 |
2/4✓ Branch 0 taken 16560 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 16560 times.
✗ Branch 3 not taken.
|
365025340 | const auto& orthogonalOutsideScv = fvGeometry.scv(orthogonalScvf.outsideScvIdx()); |
298 | |||
299 | // The standard case: Our grid does not support periodic boundaries. | ||
300 | if constexpr (!Detail::SupportsPeriodicity<typename FVElementGeometry::GridGeometry::Grid>()) | ||
301 | 182496110 | return getStandardDistance_(orthogonalInsideScv, orthogonalOutsideScv); | |
302 | else | ||
303 | { | ||
304 | 16560 | const auto& gridGeometry = fvGeometry.gridGeometry(); | |
305 |
2/4✓ Branch 0 taken 16560 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 16560 times.
✗ Branch 3 not taken.
|
33120 | const auto& insideScv = fvGeometry.scv(scvf.insideScvIdx()); |
306 | |||
307 | // The standard case: Our grid is not periodic or our own DOF does not lie on a periodic boundary. | ||
308 |
3/4✓ Branch 0 taken 16560 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 15696 times.
✓ Branch 4 taken 864 times.
|
16560 | if (!gridGeometry.isPeriodic() || !gridGeometry.dofOnPeriodicBoundary(insideScv.dofIndex())) |
309 | 15696 | return getStandardDistance_(orthogonalInsideScv, orthogonalOutsideScv); | |
310 | |||
311 | // Treating periodic boundaries is more involved: | ||
312 | // 1. Consider the orthogonal outside scv within the element adjacent to the other periodic boundary. | ||
313 | // 2. Iterate over this scv's faces until you find the orthogonal face parallel to our own orthogonal scvf. | ||
314 | // This face would lie directly next to our own orthogonal scvf if the grid was not periodic. | ||
315 | // 3. Calculate the total distance by summing up the distances between the DOFs and the respective integration points | ||
316 | // corresponding to the orthogonal scvfs. | ||
317 | 864 | auto periodicFvGeometry = localView(gridGeometry); | |
318 | 864 | const auto& periodicElement = gridGeometry.element(orthogonalOutsideScv.elementIndex()); | |
319 | 864 | periodicFvGeometry.bindElement(periodicElement); | |
320 | |||
321 |
2/4✓ Branch 1 taken 2160 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2160 times.
✗ Branch 4 not taken.
|
2160 | for (const auto& outsideOrthogonalScvf : scvfs(periodicFvGeometry, orthogonalOutsideScv)) |
322 | { | ||
323 |
4/4✓ Branch 0 taken 1296 times.
✓ Branch 1 taken 864 times.
✓ Branch 2 taken 432 times.
✓ Branch 3 taken 864 times.
|
2160 | if (outsideOrthogonalScvf.normalAxis() == orthogonalScvf.normalAxis() && outsideOrthogonalScvf.directionSign() != orthogonalScvf.directionSign()) |
324 | { | ||
325 | 3456 | const auto insideDistance = (orthogonalInsideScv.dofPosition() - orthogonalScvf.ipGlobal()).two_norm(); | |
326 | 3456 | const auto outsideDistance = (orthogonalOutsideScv.dofPosition() - outsideOrthogonalScvf.ipGlobal()).two_norm(); | |
327 | 864 | return insideDistance + outsideDistance; | |
328 | } | ||
329 | } | ||
330 | ✗ | DUNE_THROW(Dune::InvalidStateException, "Could not find scvf in periodic element"); | |
331 | } | ||
332 | } | ||
333 | |||
334 | template<class SubControlVolume> | ||
335 | 370703024 | static auto getStandardDistance_(const SubControlVolume& insideScv, | |
336 | const SubControlVolume& outsideScv) | ||
337 | { | ||
338 | 1480216144 | return (insideScv.dofPosition() - outsideScv.dofPosition()).two_norm(); | |
339 | } | ||
340 | |||
341 | template<class SubControlVolume, class SubControlVolumeFace> | ||
342 | 2494330 | static auto getDistanceToBoundary_(const SubControlVolume& scv, | |
343 | const SubControlVolumeFace& scvf) | ||
344 | { | ||
345 | 9873040 | return (scv.dofPosition() - scvf.ipGlobal()).two_norm(); | |
346 | } | ||
347 | }; | ||
348 | |||
349 | } // end namespace Dumux | ||
350 | |||
351 | #endif // DUMUX_NAVIERSTOKES_STAGGERED_VELOCITYGRADIENTS_HH | ||
352 |