3.5-git
DUNE for Multi-{Phase, Component, Scale, Physics, ...} flow and transport in porous media
structuredlatticegridcreator.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_IO_STRUCTURED_LATTICE_GRID_CREATOR_HH
25#define DUMUX_IO_STRUCTURED_LATTICE_GRID_CREATOR_HH
26
27#if HAVE_DUNE_FOAMGRID
28
29#include <vector>
30#include <memory>
31#include <type_traits>
32#include <random>
33
34#include <dune/common/concept.hh>
35#include <dune/common/exceptions.hh>
36#include <dune/common/version.hh>
37#include <dune/grid/common/gridfactory.hh>
38#include <dune/grid/yaspgrid.hh>
39#include <dune/geometry/referenceelements.hh>
40
41// FoamGrid specific includes
42#include <dune/foamgrid/foamgrid.hh>
43#include <dune/foamgrid/dgffoam.hh>
44
48
49namespace Dumux::PoreNetwork {
50
51namespace Concept {
52
57template<class GlobalPosition>
58struct LowDimElementSelector
59{
60 template<class F>
61 auto require(F&& f) -> decltype(
62 bool(f(std::declval<const GlobalPosition&>(), std::declval<const GlobalPosition&>()))
63 );
64};
65} // end namespace Concept
66
71template<int dimWorld>
72class StructuredLatticeGridCreator
73{
74 using GridType = Dune::FoamGrid<1, dimWorld>;
75 using Element = typename GridType::template Codim<0>::Entity;
76 using GlobalPosition = typename Element::Geometry::GlobalCoordinate;
77 using CoordScalar = typename GridType::ctype;
79 using HostGridView = typename HostGrid::LeafGridView;
80 using ReferenceElements = typename Dune::ReferenceElements<CoordScalar, dimWorld>;
81 // grid factory expects unsigned int, will yield compiler warning for std::size_t
82 using VertexIndexPair = std::pair<unsigned int, unsigned int>;
83
84 struct Diagonal
85 {
86 VertexIndexPair localVertexIndices;
87 unsigned int directionNumber;
88 };
89public:
90
91 using Grid = GridType;
92
93 void init(const std::string& paramGroup = "")
94 {
95 auto useAllElements = [](const GlobalPosition& a, const GlobalPosition& b){ return true; };
96 init(useAllElements, paramGroup);
97 }
98
99 template<class LowDimElementSelector,
100 typename std::enable_if_t<Dune::models<Concept::LowDimElementSelector<GlobalPosition>, LowDimElementSelector>(), int> = 0>
101 void init(const LowDimElementSelector& lowDimElementSelector,
102 const std::string& paramGroup = "")
103 {
104 paramGroup_ = paramGroup;
105 removeThroatsOnBoundary_ = getParamFromGroup<std::vector<std::size_t>>(paramGroup, "Grid.RemoveThroatsOnBoundary",
106 std::vector<std::size_t>());
107
108 setElementSelector_(lowDimElementSelector);
109 initRandomNumberGenerator_();
110
111 // create the host grid
112 const auto hostGridInputData = getHostGridInputData_();
114 using HastGridManager = GridManager<HostGrid>;
115 HastGridManager hostGridManager;
116 hostGridManager.init(hostGridInputData.positions, hostGridInputData.cells, hostGridInputData.grading, paramGroup_);
117 hostGridView_ = std::make_unique<HostGridView>(hostGridManager.grid().leafGridView());
118
119 hostGridLowerLeft_ = hostGridInputData.lowerLeft;
120 hostGridUpperRight_ = hostGridInputData.upperRight;
121
122 convertHostGridToLowDimGrid_();
123 }
124
128 void loadBalance()
129 {
130 if (Dune::MPIHelper::getCommunication().size() > 1)
131 gridPtr_->loadBalance();
132 }
133
137 Grid& grid()
138 { return *gridPtr(); }
139
143 std::shared_ptr<Grid>& gridPtr()
144 { return gridPtr_; }
145
146private:
147
148 template<class LowDimElementSelector>
149 void setElementSelector_(const LowDimElementSelector& lowDimElementSelector)
150 {
151 if (!removeThroatsOnBoundary_.empty())
152 {
153 auto finalSelector = [&](const GlobalPosition& a, const GlobalPosition& b)
154 {
155 static const auto lambdaToRemoveThroatsOnBoundary = getLambdaToRemoveThroatsOnBoundary_();
156 using std::min;
157 return min(lambdaToRemoveThroatsOnBoundary(a, b), lowDimElementSelector(a, b));
158 };
159 considerLowDimElement_ = finalSelector;
160 }
161 else
162 considerLowDimElement_ = lowDimElementSelector;
163 }
164
165 void initRandomNumberGenerator_()
166 {
167 directionProbability_ = getDirectionProbability_();
168
169 if (hasParamInGroup(paramGroup_, "Grid.DeletionRandomNumberSeed"))
170 {
171 const auto seed = getParamFromGroup<std::size_t>(paramGroup_, "Grid.DeletionRandomNumberSeed");
172 generator_.seed(seed);
173 }
174 else
175 {
176 std::random_device rd;
177 generator_.seed(rd());
178 }
179 }
180
181 void convertHostGridToLowDimGrid_()
182 {
183 resizeContainers_();
184
185 for (const auto& element : elements(*hostGridView_))
186 {
187 const auto& geometry = element.geometry();
188 const auto& refElement = ReferenceElements::general(geometry.type());
189 treatEdges_(element, refElement, geometry);
190 treatDiagonals_(element, refElement, geometry);
191 }
192 if (elementSet_.empty())
193 DUNE_THROW(Dune::GridError, "Trying to create pore network with zero elements!");
194
195 // make the actual network grid
196 Dune::GridFactory<Grid> factory;
197 for (auto&& vertex : vertexSet_)
198 factory.insertVertex(vertex);
199 for (auto&& element : elementSet_)
200#if DUNE_VERSION_NEWER(DUNE_COMMON,2,7)
201 factory.insertElement(Dune::GeometryTypes::cube(1), {element.first, element.second});
202#else
203 factory.insertElement(Dune::GeometryType(1), {element.first, element.second});
204#endif
205
206 gridPtr_ = std::shared_ptr<Grid>(factory.createGrid());
207 }
208
209 void resizeContainers_()
210 {
211 vertexInserted_.resize(hostGridView_->size(dimWorld), false);
212 hostVertexIdxToVertexIdx_.resize(hostGridView_->size(dimWorld));
213 edgeInserted_.resize(hostGridView_->size(dimWorld-1), false);
214 faceInserted_.resize(hostGridView_->size(dimWorld-2), false);
215 }
216
217 template<class HostGridElement>
218 bool maybeInsertElementAndVertices_(const HostGridElement& hostGridElement,
219 const typename HostGridElement::Geometry& hostGridElementGeometry,
220 std::size_t localVertexIdx0,
221 std::size_t localVertexIdx1,
222 double directionProbability)
223 {
224 if (neglectElement_(hostGridElementGeometry, localVertexIdx0, localVertexIdx1, directionProbability))
225 return false;
226 else
227 {
228 insertElementAndVertices_(hostGridElement, hostGridElementGeometry, localVertexIdx0, localVertexIdx1, directionProbability);
229 return true;
230 }
231 }
232
233 template<class HostGridElement>
234 void insertElementAndVertices_(const HostGridElement& hostGridElement,
235 const typename HostGridElement::Geometry& hostGridElementGeometry,
236 std::size_t localVertexIdx0,
237 std::size_t localVertexIdx1,
238 double directionProbability)
239 {
240 // get global vertex indices w.r.t. host grid
241 const auto vIdxGlobal0 = hostGridView_->indexSet().subIndex(hostGridElement, localVertexIdx0, dimWorld);
242 const auto vIdxGlobal1 = hostGridView_->indexSet().subIndex(hostGridElement, localVertexIdx1, dimWorld);
243
244 auto getGobalVertexIdx = [&](auto globalIdx, auto localIdx)
245 {
246 if (!vertexInserted_[globalIdx])
247 {
248 vertexInserted_[globalIdx] = true;
249 hostVertexIdxToVertexIdx_[globalIdx] = vertexSet_.size();
250 vertexSet_.push_back(hostGridElementGeometry.corner(localIdx));
251 }
252
253 return hostVertexIdxToVertexIdx_[globalIdx];
254 };
255
256 const auto newVertexIdx0 = getGobalVertexIdx(vIdxGlobal0, localVertexIdx0);
257 const auto newVertexIdx1 = getGobalVertexIdx(vIdxGlobal1, localVertexIdx1);
258 elementSet_.emplace_back(newVertexIdx0, newVertexIdx1);
259 }
260
261 auto getDirectionProbability_() const
262 {
263 std::array<double, numDirections_()> directionProbability;
264 directionProbability.fill(0.0); // do not delete any throats
265
266 // convenience option to delete all diagonal throats
267 if (getParamFromGroup<bool>(paramGroup_, "Grid.RegularLattice", false))
268 {
269 directionProbability.fill(1.0); // delete all throats ...
270 for (int i = 0; i < dimWorld; ++i)
271 directionProbability[i] = 0.0; // ... but not the ones parallel to the main axes
272
273 return directionProbability;
274 }
275
276 // get user input or print out help message explaining correct usage
277 if (hasParamInGroup(paramGroup_, "Grid.DeletionProbability"))
278 {
279 try
280 {
281 directionProbability = getParamFromGroup<decltype(directionProbability)>(paramGroup_, "Grid.DeletionProbability");
282 }
284 {
285 throwDirectionError_();
286 }
287 }
288
289 return directionProbability;
290 }
291
292 void throwDirectionError_() const
293 {
294 // define directions (to be used for user specified anisotropy)
295 Dune::FieldVector<std::string, numDirections_()> directions;
296 if (dimWorld < 3) // 2D
297 {
298 // x, y
299 directions[0] = "1: (1, 0)\n";
300 directions[1] = "2: (0, 1)\n";
301 // diagonals through cell midpoint
302 directions[2] = "3: (1, 1)\n";
303 directions[3] = "4: (1, -1)\n";
304 }
305 else // 3D
306 {
307 // x, y, z
308 directions[0] = " 1: (1, 0, 0)\n";
309 directions[1] = "2: (0, 1, 0)\n";
310 directions[2] = "3: (0, 0, 1)\n";
311 //face diagonals
312 directions[3] = "4: (1, 1, 0)\n";
313 directions[4] = "5: (1, -1, 0)\n";
314 directions[5] = "6: (1, 0, 1)\n";
315 directions[6] = "7: (1, 0, -1)\n";
316 directions[7] = "8: (0, 1, 1)\n";
317 directions[8] = "9: (0, 1, -1)\n";
318 // diagonals through cell midpoint
319 directions[9] = "10: (1, 1, 1)\n";
320 directions[10] = "11: (1, 1, -1)\n";
321 directions[11] = "12: (-1, 1, 1)\n";
322 directions[12] = "13: (-1, -1, 1)\n";
323 }
324 DUNE_THROW(ParameterException, "You must specifiy probabilities for all directions (" << numDirections_() << ") \n" << directions << "\nExample (3D):\n\n"
325 << "DeletionProbability = 0.5 0.5 0 0 0 0 0 0 0 0 0 0 0 \n\n"
326 << "deletes approximately 50% of all throats in x and y direction, while no deletion in any other direction takes place.\n" );
327 }
328
329 static constexpr std::size_t numDirections_()
330 { return (dimWorld < 3) ? 4 : 13; }
331
332 template<class Geometry>
333 bool neglectElement_(Geometry& hostGridElementGeometry,
334 std::size_t localVertexIdx0,
335 std::size_t localVertexIdx1,
336 double directionProbability)
337 {
338 if (randomNumer_() < directionProbability)
339 return true;
340
341 // TODO Change order of execution: check considerLowDimElement_ before checking the random number
342 // TODO This change will alter the reference solution because randomNumer_() gets called less, therefore the random numbers are different
343 if (!considerLowDimElement_(hostGridElementGeometry.corner(localVertexIdx0), hostGridElementGeometry.corner(localVertexIdx1)))
344 return true;
345
346 return false;
347 }
348
349 auto randomNumer_()
350 { return uniformdist_(generator_); }
351
352 auto getHostGridInputData_() const
353 {
354 struct InputData
355 {
356 std::array<std::vector<int>, dimWorld> cells;
357 std::array<std::vector<CoordScalar>, dimWorld> positions;
358 std::array<std::vector<CoordScalar>, dimWorld> grading;
359 GlobalPosition lowerLeft;
360 GlobalPosition upperRight;
361 } inputData;
362
363 // convenience references
364 auto& cells = inputData.cells;
365 auto& positions = inputData.positions;
366 auto& grading = inputData.grading;
367 auto& lowerLeft = inputData.lowerLeft;
368 auto& upperRight = inputData.upperRight;
369
370 // try to get the pore positions explicitly ...
371 for (int i = 0; i < dimWorld; ++i)
372 positions[i] = getParamFromGroup<std::vector<CoordScalar>>(paramGroup_, "Grid.Positions" + std::to_string(i), std::vector<CoordScalar>{});
373 if (std::none_of(positions.begin(), positions.end(), [&](auto& v){ return v.empty(); }))
374 {
375 for (int i = 0; i < dimWorld; ++i)
376 {
377 cells[i].resize(positions[i].size()-1, 1.0);
378 grading[i].resize(positions[i].size()-1, 1.0);
379 }
380 }
381 else // .. or calculate positions from input data
382 {
383 const auto lowerLeft = getParamFromGroup<GlobalPosition>(paramGroup_, "Grid.LowerLeft", GlobalPosition(0.0));
384 const auto upperRight = getParamFromGroup<GlobalPosition>(paramGroup_, "Grid.UpperRight");
385 const auto numPores = getParamFromGroup<std::vector<unsigned int>>(paramGroup_, "Grid.NumPores");
386 if (numPores.size() != dimWorld)
387 DUNE_THROW(ParameterException, "Grid.NumPores has to be a space-separated list of " << dimWorld << " integers!");
388
389 for (int i = 0; i < dimWorld; ++i)
390 {
391 positions[i].push_back(lowerLeft[i]);
392 positions[i].push_back(upperRight[i]);
393 cells[i].push_back(numPores[i] - 1);
394 grading[i].resize(positions[i].size()-1, 1.0);
395 grading[i] = getParamFromGroup<std::vector<CoordScalar>>(paramGroup_, "Grid.Grading" + std::to_string(i), grading[i]);
396 }
397 }
398
399 // get the lower left position
400 lowerLeft = [&]()
401 {
402 GlobalPosition result;
403 for (int i = 0; i < dimWorld; ++i)
404 result[i] = positions[i][0];
405 return result;
406 }();
407
408 // get the upper right position
409 upperRight = [&]()
410 {
411 GlobalPosition result;
412 for (int i = 0; i < dimWorld; ++i)
413 result[i] = positions[i].back();
414 return result;
415 }();
416
417 return inputData;
418 }
419
420 template<class HostGridElement, class ReferenceElement, class Geometry>
421 void treatEdges_(const HostGridElement& element, const ReferenceElement& refElement, const Geometry& geometry)
422 {
423 // go over all edges and add them as elements if they passed all the tests
424 for (unsigned int edgeIdx = 0; edgeIdx < element.subEntities(dimWorld-1); ++edgeIdx)
425 {
426 const auto vIdxLocal0 = refElement.subEntity(edgeIdx, dimWorld-1, 0, dimWorld);
427 const auto vIdxLocal1 = refElement.subEntity(edgeIdx, dimWorld-1, 1, dimWorld);
428 const auto edgeIdxGlobal = hostGridView_->indexSet().subIndex(element, edgeIdx, dimWorld-1);
429
430 if (edgeInserted_[edgeIdxGlobal])
431 continue;
432 else
433 edgeInserted_[edgeIdxGlobal] = true;
434
435 std::size_t directionNumber = 0;
436
437 if(dimWorld == 2 ) // 2D
438 {
439 if(edgeIdx < 2) // y-direction
440 directionNumber = 0;
441 else // x-direction
442 directionNumber = 1;
443 }
444 else // 3D
445 {
446 if(edgeIdx < 4) // z-direction
447 directionNumber = 2;
448 else if(edgeIdx == 4 || edgeIdx == 5 || edgeIdx == 8 || edgeIdx == 9) // y-direction
449 directionNumber = 1;
450 else if(edgeIdx == 6 || edgeIdx == 7 || edgeIdx == 10 || edgeIdx == 11) // x-direction
451 directionNumber = 0;
452 }
453 maybeInsertElementAndVertices_(element, geometry, vIdxLocal0, vIdxLocal1, directionProbability_[directionNumber]);
454 }
455 }
456
457 template<class HostGridElement, class ReferenceElement, class Geometry>
458 void treatDiagonals_(const HostGridElement& element, const ReferenceElement& refElement, const Geometry& geometry)
459 {
460 if constexpr (dimWorld < 3)
461 treatDiagonalConnections2D_(element, geometry);
462 else
463 treatDiagonalConnections3D_(element, refElement, geometry);
464 }
465
466 template<class HostGridElement, class Geometry>
467 void treatDiagonalConnections2D_(const HostGridElement& element,
468 const Geometry& geometry)
469 {
470 static constexpr std::array<Diagonal, 2> diagonals{ Diagonal{std::make_pair(0, 3), 2},
471 Diagonal{std::make_pair(1, 2), 3} };
472
473 treatIntersectingDiagonals_(element, geometry, diagonals);
474 }
475
476 template<class HostGridElement, class ReferenceElement, class Geometry>
477 void treatDiagonalConnections3D_(const HostGridElement& element,
478 const ReferenceElement& refElement,
479 const Geometry& geometry)
480 {
481 // set diagonals on host grid element faces
482 for (auto faceIdx = 0; faceIdx < element.subEntities(dimWorld-2); ++faceIdx)
483 {
484 const auto faceIdxGlobal = hostGridView_->indexSet().subIndex(element, faceIdx, dimWorld-2);
485 // face already checked?
486 if (faceInserted_[faceIdxGlobal])
487 continue;
488 else
489 faceInserted_[faceIdxGlobal] = true;
490
491 // get local vertex indices
492 std::array<unsigned int, 4> vIdxLocal;
493 for (int i = 0; i < 4; i++)
494 vIdxLocal[i] = refElement.subEntity(faceIdx, dimWorld-2, i, dimWorld);
495
496 const auto directionNumbers = [&]()
497 {
498 if (faceIdx < 2)
499 return std::make_pair<unsigned int, unsigned int>(8,7);
500 else if (faceIdx < 4)
501 return std::make_pair<unsigned int, unsigned int>(6,5);
502 else
503 return std::make_pair<unsigned int, unsigned int>(4,3);
504 }();
505
506 const std::array<Diagonal, 2> diagonals{ Diagonal{std::make_pair(vIdxLocal[1], vIdxLocal[2]), directionNumbers.first},
507 Diagonal{std::make_pair(vIdxLocal[0], vIdxLocal[3]), directionNumbers.second} };
508
509 treatIntersectingDiagonals_(element, geometry, diagonals);
510 }
511
512 static constexpr std::array<Diagonal, 4> diagonals{ Diagonal{std::make_pair(0, 7), 9},
513 Diagonal{std::make_pair(3, 4), 10},
514 Diagonal{std::make_pair(1, 6), 11},
515 Diagonal{std::make_pair(2, 5), 12}, };
516
517 treatIntersectingDiagonals_(element, geometry, diagonals);
518 }
519
520 template<class HostGridElement, class Geometry, class Diagonals>
521 void treatIntersectingDiagonals_(const HostGridElement& element,
522 const Geometry& geometry,
523 const Diagonals& diagonals)
524 {
525 static const bool allowIntersectingDiagonals = getParamFromGroup<bool>(paramGroup_, "Grid.AllowIntersectingDiagonals", true);
526
527 auto treat = [&](const Diagonal& diagonal)
528 {
529 return maybeInsertElementAndVertices_(element, geometry,
530 diagonal.localVertexIndices.first, diagonal.localVertexIndices.second,
531 directionProbability_[diagonal.directionNumber]);
532 };
533
534 if (allowIntersectingDiagonals)
535 {
536 // insert all diagonals
537 for (const auto& diagonal : diagonals)
538 treat(diagonal);
539 }
540 else
541 {
542 auto order = createOrderedList_(diagonals.size());
543 std::shuffle(order.begin(), order.end(), generator_);
544 for (auto i : order)
545 {
546 const auto& diagonal = diagonals[i];
547 const bool inserted = treat(diagonal);
548 if (inserted)
549 return;
550 }
551 }
552 }
553
554 std::vector<std::size_t> createOrderedList_(const std::size_t size) const
555 {
556 std::vector<std::size_t> result(size);
557 std::iota(result.begin(), result.end(), 0);
558 return result;
559 }
560
561 auto getLambdaToRemoveThroatsOnBoundary_() const
562 {
563 auto negletElementsOnBoundary = [&](const GlobalPosition& a, const GlobalPosition& b)
564 {
565 const auto center = 0.5 * (a + b);
566 const auto eps = (a-b).two_norm() * 1e-5;
567
568 bool neglectElement = false;
569 for (auto i : removeThroatsOnBoundary_)
570 {
571 switch(i)
572 {
573 case 0: neglectElement = center[0] < hostGridLowerLeft_[0] + eps; break;
574 case 1: neglectElement = center[0] > hostGridUpperRight_[0] - eps; break;
575 case 2: if constexpr (dimWorld > 1)
576 {
577 neglectElement = center[1] < hostGridLowerLeft_[1] + eps;
578 break;
579 }
580 case 3: if constexpr (dimWorld > 1)
581 {
582 neglectElement = center[1] > hostGridUpperRight_[1] - eps;
583 break;
584 }
585 case 4: if constexpr (dimWorld > 2)
586 {
587 neglectElement = center[2] < hostGridLowerLeft_[2] + eps;
588 break;
589 }
590 case 5: if constexpr (dimWorld > 2)
591 {
592 neglectElement = center[2] > hostGridUpperRight_[2] - eps;
593 break;
594 }
595 }
596
597 if (neglectElement)
598 return false;
599 }
600
601 return true;
602 };
603
604 return negletElementsOnBoundary;
605 }
606
607 std::string paramGroup_;
608 GlobalPosition hostGridLowerLeft_;
609 GlobalPosition hostGridUpperRight_;
610 std::vector<std::size_t> removeThroatsOnBoundary_;
611 std::unique_ptr<const HostGridView> hostGridView_;
612 std::function<bool(const GlobalPosition&, const GlobalPosition&)> considerLowDimElement_;
613
614 std::vector<GlobalPosition> vertexSet_;
615 std::vector<VertexIndexPair> elementSet_;
616
617 std::vector<bool> vertexInserted_;
618 std::vector<std::size_t> hostVertexIdxToVertexIdx_;
619 std::vector<std::size_t> edgeInserted_;
620 std::vector<std::size_t> faceInserted_;
621
622 mutable std::mt19937 generator_;
623 std::uniform_real_distribution<> uniformdist_{0, 1};
624 std::array<double, numDirections_()> directionProbability_;
625
626 std::shared_ptr<Grid> gridPtr_;
627};
628
629} // end namespace Dumux::PoreNetwork
630
631#endif // HAVE_DUNE_FOAMGRID
632
633#endif
Some exceptions thrown in DuMux
The infrastructure to retrieve run-time parameters from Dune::ParameterTrees.
Grid manager specialization for YaspGrid.
T getParamFromGroup(Args &&... args)
A free function to get a parameter from the parameter tree singleton with a model group.
Definition: parameters.hh:161
bool hasParamInGroup(const std::string &paramGroup, const std::string &param)
Check whether a key exists in the parameter tree with a model group prefix.
Definition: parameters.hh:177
Definition: discretization/porenetwork/fvelementgeometry.hh:34
Exception thrown if a run-time parameter is not specified correctly.
Definition: exceptions.hh:60
Definition: consistentlyorientedgrid.hh:32