3.4
DUNE for Multi-{Phase, Component, Scale, Physics, ...} flow and transport in porous media
io/grid/porenetwork/gridmanager.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_PORE_NETWORK_GRID_MANAGER_HH
25#define DUMUX_PORE_NETWORK_GRID_MANAGER_HH
26
27#if HAVE_DUNE_FOAMGRID
28
29#include <iostream>
30#include <algorithm>
31
32#include <dune/common/classname.hh>
33#include <dune/common/exceptions.hh>
34#include <dune/common/timer.hh>
35
36// FoamGrid specific includes
37#include <dune/foamgrid/foamgrid.hh>
38#include <dune/foamgrid/dgffoam.hh>
39
41
42#include "griddata.hh"
44
45namespace Dumux::PoreNetwork {
46
51template<int dimWorld, class GData = Dumux::PoreNetwork::GridData<Dune::FoamGrid<1, dimWorld>>>
52class GridManager
53{
54 using GridType = Dune::FoamGrid<1, dimWorld>;
55 using Element = typename GridType::template Codim<0>::Entity;
56 using GlobalPosition = typename Element::Geometry::GlobalCoordinate;
57
58public:
59 using Grid = GridType;
60 using GridData = GData;
61
62 void init(const std::string& paramGroup = "")
63 {
64 Dune::Timer timer;
65 paramGroup_ = paramGroup;
66
67 // First try to create it from a DGF file in GridParameterGroup.File
68 if (hasParamInGroup(paramGroup, "Grid.File"))
69 {
70 makeGridFromDgfFile(getParamFromGroup<std::string>(paramGroup, "Grid.File"));
71 loadBalance();
72 }
73 else // no grid file found
74 {
75 try
76 {
77 createOneDGrid_();
78 }
79 catch(Dune::RangeError& e)
80 {
81 makeGridFromStructuredLattice();
82 }
83
84 loadBalance();
85 }
86
87 timer.stop();
88 const auto gridType = enableDgfGridPointer_ ? "grid from dgf file" : "generic grid from structured lattice";
89 std::cout << "Creating " << gridType << " with " << grid().leafGridView().size(0) << " elements and "
90 << grid().leafGridView().size(1) << " vertices took " << timer.elapsed() << " seconds." << std::endl;
91 }
92
96 void makeGridFromDgfFile(const std::string& fileName)
97 {
98 // We found a file in the input file...does it have a supported extension?
99 const std::string extension = getFileExtension(fileName);
100 if (extension != "dgf")
101 DUNE_THROW(Dune::IOError, "Grid type " << Dune::className<Grid>() << " only supports DGF (*.dgf) but the specified filename has extension: *."<< extension);
102
103 enableDgfGridPointer_ = true;
104 dgfGridPtr() = Dune::GridPtr<Grid>(fileName.c_str(), Dune::MPIHelper::getCommunicator());
105 gridData_ = std::make_shared<GridData>(dgfGridPtr_, paramGroup_);
106
107 if (getParamFromGroup<bool>(paramGroup_, "Grid.Sanitize", false))
108 sanitizeGrid();
109 }
110
114 void makeGridFromStructuredLattice()
115 {
116 StructuredLatticeGridCreator<dimWorld> creator;
117 creator.init(paramGroup_);
118 gridPtr() = creator.gridPtr();
119
120 gridData_ = std::make_shared<GridData>(gridPtr_, paramGroup_);
121
122 if (getParamFromGroup<bool>(paramGroup_, "Grid.Sanitize", true))
123 sanitizeGrid();
124 else
125 std::cout << "\nWARNING: Set Grid.Sanitize = true in order to remove insular patches of elements not connected to the boundary." << std::endl;
126
127 gridData_->assignParameters();
128 }
129
133 std::string getFileExtension(const std::string& fileName) const
134 {
135 std::size_t i = fileName.rfind('.', fileName.length());
136 if (i != std::string::npos)
137 return(fileName.substr(i+1, fileName.length() - i));
138 else
139 DUNE_THROW(Dune::IOError, "Please provide and extension for your grid file ('"<< fileName << "')!");
140 return "";
141 }
142
146 Grid& grid()
147 { return enableDgfGridPointer_ ? *dgfGridPtr() : *gridPtr(); }
148
152 std::shared_ptr<GridData> getGridData() const
153 {
154 if (!gridData_)
155 DUNE_THROW(Dune::IOError, "No grid data available");
156
157 return gridData_;
158 }
159
163 void loadBalance()
164 {
165 if (Dune::MPIHelper::getCollectiveCommunication().size() > 1)
166 {
167 // if we may have dgf parameters use load balancing of the dgf pointer
168 if (enableDgfGridPointer_)
169 {
170 dgfGridPtr().loadBalance();
171 // update the grid data
172 gridData_ = std::make_shared<GridData>(dgfGridPtr(), paramGroup_);
173 }
174 else
175 gridPtr()->loadBalance();
176 }
177 }
178
182 void sanitizeGrid()
183 {
184 // evaluate the coordination numbers to check if all pores are connected to at least one throat
185 gridData_->getCoordinationNumbers();
186
187 // for dgf grid, copy the data to peristent containers
188 if (enableDgfGridPointer_)
189 gridData_->copyDgfData();
190
191 const auto gridView = grid().leafGridView();
192 static const std::string sanitationMode = getParamFromGroup<std::string>(paramGroup_, "Grid.SanitationMode", "KeepLargestCluster");
193
194 // find the elements to keep, all others will be deleted
195 const auto keepElement = [&]
196 {
197 if (sanitationMode == "UsePoreLabels")
198 return findElementsConnectedToPoreLabel(gridView);
199 else if (sanitationMode == "KeepLargestCluster")
200 return findElementsInLargestCluster(gridView);
201 else
202 DUNE_THROW(Dune::IOError, sanitationMode << "is not a valid sanitation mode. Use KeepLargestCluster or UsePoreLabels.");
203
204
205 }();
206
207 if (std::none_of(keepElement.begin(), keepElement.end(), [](const bool i){return i;}))
208 DUNE_THROW(Dune::InvalidStateException, "sanitize() deleted all elements! Check your boundary face indices. "
209 << "If the problem persisits, add at least one of your boundary face indices to PruningSeedIndices");
210
211 // remove the elements in the grid
212 std::size_t numDeletedElements = 0;
213 grid().preGrow();
214 for (const auto& element : elements(gridView))
215 {
216 const auto eIdx = gridView.indexSet().index(element);
217 if (!keepElement[eIdx])
218 {
219 grid().removeElement(element);
220 ++numDeletedElements;
221 }
222 }
223 // triggers the grid growth process
224 grid().grow();
225 grid().postGrow();
226
227 // resize the parameters for dgf grids
228 if (enableDgfGridPointer_)
229 gridData_->resizeParameterContainers();
230
231 if (numDeletedElements > 0)
232 std::cout << "\nDeleted " << numDeletedElements << " isolated elements.\n" << std::endl;
233 }
234
239 std::vector<bool> findElementsConnectedToPoreLabel(const typename Grid::LeafGridView& gridView) const
240 {
241 // pruning -- remove elements not connected to a Dirichlet boundary (marker == 1)
242 const auto pruningSeedIndices = getParamFromGroup<std::vector<int>>(paramGroup_, "Grid.PruningSeedIndices", std::vector<int>{1});
243 std::vector<bool> elementIsConnected(gridView.size(0), false);
244
245 auto boundaryIdx = [&](const auto& vertex)
246 {
247 if (enableDgfGridPointer_)
248 return static_cast<int>(dgfGridPtr_.parameters(vertex)[gridData_->parameterIndex("PoreLabel")]);
249 else
250 return static_cast<int>(gridData_->poreLabelAtPosForGenericGrid(vertex.geometry().center()));
251 };
252
253 for (const auto& element : elements(gridView))
254 {
255 const auto eIdx = gridView.indexSet().index(element);
256 if (elementIsConnected[eIdx])
257 continue;
258
259 // try to find a seed from which to start the search process
260 bool isSeed = false;
261 bool hasNeighbor = false;
262 for (const auto& intersection : intersections(gridView, element))
263 {
264 auto vertex = element.template subEntity<1>(intersection.indexInInside());
265 // seed found
266 if (std::any_of(pruningSeedIndices.begin(), pruningSeedIndices.end(),
267 [&]( const int i ){ return i == boundaryIdx(vertex); }))
268 {
269 isSeed = true;
270 // break;
271 }
272
273 if (intersection.neighbor())
274 hasNeighbor = true;
275 }
276
277 if (!hasNeighbor)
278 continue;
279
280 if (isSeed)
281 {
282 elementIsConnected[eIdx] = true;
283
284 // use iteration instead of recursion here because the recursion can get too deep
285 recursivelyFindConnectedElements_(gridView, element, elementIsConnected);
286 }
287 }
288
289 return elementIsConnected;
290 }
291
296 std::vector<bool> findElementsInLargestCluster(const typename Grid::LeafGridView& gridView) const
297 {
298 const auto clusteredElements = clusterElements(gridView);
299
300 const auto numClusters = *std::max_element(clusteredElements.begin(), clusteredElements.end()) + 1;
301 std::cout << "\nFound " << numClusters << " unconnected clusters." << std::endl;
302
303 // count number of elements in individual clusters
304 std::vector<std::size_t> clusterFrequency(numClusters);
305 for (const auto clusterIdx : clusteredElements)
306 clusterFrequency[clusterIdx] += 1;
307
308 const auto largestCluster = std::max_element(clusterFrequency.begin(), clusterFrequency.end());
309 const auto largestClusterIdx = std::distance(clusterFrequency.begin(), largestCluster);
310
311 std::vector<bool> elementsInLargestCluster(gridView.size(0));
312
313 for (int eIdx = 0; eIdx < clusteredElements.size(); ++eIdx)
314 if (clusteredElements[eIdx] == largestClusterIdx)
315 elementsInLargestCluster[eIdx] = true;
316
317 return elementsInLargestCluster;
318 }
319
323 std::vector<std::size_t> clusterElements(const typename Grid::LeafGridView& gridView) const
324 {
325 std::vector<std::size_t> elementInCluster(gridView.size(0), 0); // initially, all elements are in pseudo cluster 0
326 std::size_t clusterIdx = 0;
327
328 for (const auto& element : elements(gridView))
329 {
330 const auto eIdx = gridView.indexSet().index(element);
331 if (elementInCluster[eIdx]) // element already is in a cluster
332 continue;
333
334 ++clusterIdx;
335 elementInCluster[eIdx] = clusterIdx;
336
337 recursivelyFindConnectedElements_(gridView, element, elementInCluster, clusterIdx);
338 }
339
340 // make sure the clusters start with index zero
341 for (auto& clusterIdx : elementInCluster)
342 clusterIdx -= 1;
343
344 return elementInCluster;
345 }
346
347protected:
348
352 std::shared_ptr<Grid>& gridPtr()
353 {
354 if(!enableDgfGridPointer_)
355 return gridPtr_;
356 else
357 DUNE_THROW(Dune::InvalidStateException, "You are using DGF. To get the grid pointer use method dgfGridPtr()!");
358 }
359
363 Dune::GridPtr<Grid>& dgfGridPtr()
364 {
365 if(enableDgfGridPointer_)
366 return dgfGridPtr_;
367 else
368 DUNE_THROW(Dune::InvalidStateException, "The DGF grid pointer is only available if the grid was constructed with a DGF file!");
369 }
370
375 bool enableDgfGridPointer_ = false;
376
377 std::shared_ptr<Grid> gridPtr_;
378 Dune::GridPtr<Grid> dgfGridPtr_;
379
380 std::shared_ptr<GridData> gridData_;
381
382 std::string paramGroup_;
383
384private:
385
386 void createOneDGrid_()
387 {
388 const auto lowerLeft = getParamFromGroup<GlobalPosition>(paramGroup_, "Grid.LowerLeft", GlobalPosition(0.0));
389 const auto upperRight = getParamFromGroup<GlobalPosition>(paramGroup_, "Grid.UpperRight");
390 const auto numPores = getParamFromGroup<unsigned int>(paramGroup_, "Grid.NumPores");
391 const auto cells = numPores - 1;
392
393 // create a step vector
394 GlobalPosition step = upperRight;
395 step -= lowerLeft, step /= cells;
396
397 // make the grid (structured interval grid in dimworld space)
398 Dune::GridFactory<Grid> factory;
399
400 // create the vertices
401 GlobalPosition globalPos = lowerLeft;
402 for (unsigned int vIdx = 0; vIdx <= cells; vIdx++, globalPos += step)
403 factory.insertVertex(globalPos);
404
405 // create the cells
406 for(unsigned int eIdx = 0; eIdx < cells; eIdx++)
407 factory.insertElement(Dune::GeometryTypes::line, {eIdx, eIdx+1});
408
409 gridPtr() = std::shared_ptr<Grid>(factory.createGrid());
410 gridData_ = std::make_shared<GridData>(gridPtr_, paramGroup_);
411 gridData_->assignParameters();
412 }
413
414 template<class T>
415 void recursivelyFindConnectedElements_(const typename Grid::LeafGridView& gridView,
416 const Element& element,
417 std::vector<T>& elementIsConnected,
418 T marker = 1) const
419 {
420 // use iteration instead of recursion here because the recursion can get too deep
421 std::stack<Element> elementStack;
422 elementStack.push(element);
423 while (!elementStack.empty())
424 {
425 auto e = elementStack.top();
426 elementStack.pop();
427 for (const auto& intersection : intersections(gridView, e))
428 {
429 if (!intersection.boundary())
430 {
431 auto outside = intersection.outside();
432 auto nIdx = gridView.indexSet().index(outside);
433 if (!elementIsConnected[nIdx])
434 {
435 elementIsConnected[nIdx] = marker;
436 elementStack.push(outside);
437 }
438 }
439 }
440 }
441 }
442};
443
444}
445
446#endif // HAVE_DUNE_FOAMGRID
447
448#endif
The infrastructure to retrieve run-time parameters from Dune::ParameterTrees.
Creates a network grid from a structured lattice. Connections can be randomly deleted.
ctype distance(const Dune::FieldVector< ctype, dimWorld > &a, const Dune::FieldVector< ctype, dimWorld > &b)
Compute the shortest distance between two points.
Definition: distance.hh:138
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:374
Definition: discretization/porenetwork/fvelementgeometry.hh:33
constexpr Line line
Definition: couplingmanager1d3d_line.hh:43