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