Dune-Fufem 2.11-git
Loading...
Searching...
No Matches
elementcoloring.hh
Go to the documentation of this file.
1// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2// vi: set et ts=4 sw=2 sts=2:
3
4// SPDX-FileCopyrightText: Copyright © DUNE-FUFEM Project contributors, see file AUTHORS.md
5// SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later
6
7#ifndef DUNE_FUFEM_PARALLEL_ELEMENTCOLORING_HH
8#define DUNE_FUFEM_PARALLEL_ELEMENTCOLORING_HH
9
10#include <cstddef>
11#include <type_traits>
12#include <utility>
13#if __has_include(<execution>)
14#include <execution>
15#else
16#include <version>
17#endif
18#include <vector>
19#include <unordered_map>
20#include <queue>
21#include <algorithm>
22#include <limits>
23#include <iostream>
24#include <initializer_list>
25
26#include <dune/common/timer.hh>
28
30
33
34
35
36namespace Dune::Fufem::Impl {
37
38
39
40/* A class for building a container that looks like std::vector<std::vector<T>>
41 * but uses flat storage. The class should be used as follows:
42 *
43 * // Create with given outer size
44 * auto vec = FlatBlockedVector<T>(size);
45 *
46 * // Reserve size for all entries.
47 * // It's allowed to do this in arbitrary order.
48 * vec.capacity(2) = ...;
49 * vec.capacity(1) = ...;
50 * vec.capacity(2) += ...;
51 *
52 * // Initialize blocks
53 * vec.initialize();
54 *
55 * // Fill blocks in arbitrary order
56 * vec[i].push_back(value);
57 * vec[i].push_back(value);
58 *
59 * // Swap or access entries using operator[] or iterators
60 * // or resize within reserved capacity
61 * vec[i][0] = ...;
62 * auto&& vec_i = vec[i];
63 * std::sort(vec_i.begin(), vec_i.end());
64 * vec[i].resize(1);
65 *
66 * // Export a range of ranges. This will steal the internal data.
67 * auto r = std::move(vec).toRange();
68 *
69 * // Use r like a nested range
70 * for (auto&& ri : r)
71 * for (auto&& rij : ri)
72 * foo(rij);
73 */
74template<class T, class st = std::size_t>
75class FlatBlockedVector
76{
77public:
78 using size_type = st;
79
80private:
81
82 struct MutableBlock
83 {
84 void push_back(const T& t)
85 {
86 data_[end_] = t;
87 ++end_;
88 }
89
90 T& operator[](size_type i)
91 {
92 return data_[begin_+i];
93 }
94
95 auto begin()
96 {
97 return data_.begin() + begin_;
98 }
99
100 auto end()
101 {
102 return data_.begin() + end_;
103 }
104
105 void resize(size_type size)
106 {
107 end_ = begin_+size;
108 }
109
110 std::vector<T>& data_;
111 size_type& begin_;
112 size_type& end_;
113 };
114
115public:
116
117 FlatBlockedVector(size_type size):
118 blockBegin_(size, 0),
119 blockEnd_(size, 0)
120 {
121 blockEnd_.clear();
122 blockEnd_.resize(size, 0);
123 }
124
125 size_type size() const
126 {
127 return blockBegin_.size();
128 }
129
130 size_type& capacity(size_type i)
131 {
132 return blockEnd_[i];
133 }
134
135 void initialize()
136 {
137 auto size = blockBegin_.size();
138 blockBegin_[0] = 0;
139 for(auto i : Dune::range(std::size_t(1), size))
140 {
141 blockBegin_[i] = blockBegin_[i-1] + capacity(i-1);
142 blockEnd_[i-1] = blockBegin_[i-1];
143 }
144 data_.resize(blockBegin_[size-1] + capacity(size-1));
145 blockEnd_[size-1] = blockBegin_[size-1];
146 }
147
148 auto operator[] (size_type i) const
149 {
150 return Dune::transformedRangeView(Dune::range(blockBegin_[i], blockEnd_[i]), [&](auto j) {
151 return data_[j];
152 });
153 }
154
155 auto operator[] (size_type i)
156 {
157 return MutableBlock{data_, blockBegin_[i], blockEnd_[i]};
158 }
159
160 auto toRange() &&
161 {
162 auto size = blockBegin_.size();
164 [setVector=std::move(*this)](auto i) {
165 return setVector[i];
166 });
167 }
168
169 std::vector<T> data_;
170 std::vector<size_type> blockBegin_;
171 std::vector<size_type> blockEnd_;
172};
173
174
175
176template<class V>
177void sortAndUnifyVector(V&& v)
178{
179 std::sort(v.begin(), v.end());
180 auto lastUnique = std::unique(v.begin(), v.end());
181 v.resize(lastUnique-v.begin());
182}
183
184
185
186}
187
188
189
190namespace Dune {
191namespace Fufem {
192
193
194
221template <class Adjacency, class SeedNodesContainer>
222auto computeColoring(const Adjacency& adjacency, SeedNodesContainer&& seedNodes)
223{
224
225 // Hack to access i-th entry without adjacency[i].
226 // This is necessary to support TransformedRangeView,
227 // because the latter does not provide operator[]
228 // in dune-2.9 although the range is random-access.
229 auto adjacentNodes = [&](auto i) {
230 return adjacency.begin()[i];
231 };
232
233 using size_type = std::decay_t<decltype(*adjacentNodes(0).begin())>;
234
235 // Use magic values to tag fully unprocessed and queued (but non-colored) entries.
236 size_type nonProcessedStatus = std::numeric_limits<size_type>::max();
237 size_type queuedStatus = std::numeric_limits<size_type>::max()-1;
238
239 // Initialize result vector with unprocessed state for all entries.
240 std::vector<size_type> color(adjacency.size(), nonProcessedStatus);
241
242 // Queue for the advancing front method
243 auto queue = std::queue<size_type>();
244
245 // Some helper methods to make code more expressive
246 auto isColored = [&](const auto& i) {
247 return color[i]<queuedStatus;
248 };
249 auto isNonProcessed = [&](const auto& i) {
250 return color[i]==nonProcessedStatus;
251 };
252 auto queueNode = [&](const auto& i) {
253 color[i] = queuedStatus;
254 queue.push(i);
255 };
256
257 // Fill queue with seed nodes (or {0} if seed node container is empty)
258 for(size_type i : seedNodes)
259 queueNode(i);
260 if (queue.empty())
261 queueNode(0);
262
263 // A temporary data structure to store the colors of already
264 // colored neighbors.
265 //
266 // There's various possible data structures here. The most
267 // obvious would be a set-like container storing the used
268 // color indices. This could be (with increasing performance)
269 // std::set, std::unordered_set, std::vector (keep sorted and unique),
270 // std::vector (just append, sort and remove duplicates afterwards).
271 // Since we expect that the total number of colors is small,
272 // it is significantly faster to simply mark used colors in
273 // a flag vector. Because memory usage is not an issue here,
274 // we use std::vector<char> which measurably outperformes
275 // std::vector<bool> due to faster bit access.
276 std::vector<char> colorIsUsed;
277 colorIsUsed.resize(32);
278
279 // If the graph has several connected components that are not
280 // reachable by the seed nodes, then we need to do several sweeps
281 // of the advancing front method.
282 while (not queue.empty())
283 {
284 // Use advancing front method to traverse graph
285 while (not queue.empty())
286 {
287 // Process next entry from queue
288 auto i = queue.front();
289 queue.pop();
290
291 // Collect colors of already colored neighbors,
292 // put nonprocessed neighbors into queue,
293 // and do nothing with already queued neighbors.
294 colorIsUsed.assign(colorIsUsed.size(), false);
295 for (const auto& neighbor: adjacentNodes(i))
296 {
297 if (isColored(neighbor))
298 colorIsUsed[color[neighbor]] = true;
299 else if (isNonProcessed(neighbor))
300 queueNode(neighbor);
301 }
302
303 // Assign first color that is not used by any processed neighbor to the current node
304 color[i] = std::find(colorIsUsed.begin(), colorIsUsed.end(), false) - colorIsUsed.begin();
305
306 // If necessary: Resize flag vector
307 if (color[i]==colorIsUsed.size())
308 colorIsUsed.resize(color[i]+1);
309 }
310
311 // If there are still some uncolored nodes, then the graph has connected
312 // components that are not reachable by the provided seed nodes. Hence we
313 // do another advancing front sweep starting from the first uncolored node.
314 auto firstNonProcessedIt = std::find(color.begin(), color.end(), nonProcessedStatus);
315 if (firstNonProcessedIt!=color.end())
316 queueNode(firstNonProcessedIt - color.begin());
317 }
318
319 return color;
320}
321
322
323
348template <class Adjacency, class T>
349auto computeColoring(const Adjacency& adjacency, std::initializer_list<T> seedIndices)
350{
351 return computeColoring(adjacency, std::vector<T>{seedIndices});
352}
353
354
355
389template <class Adjacency>
390auto computeColoring(const Adjacency& adjacency)
391{
392 return computeColoring(adjacency, {0});
393}
394
395
396
424template <class CliqueList>
425auto cliqueListToAdjacency(const CliqueList& cliqueList, std::size_t size, bool verbose=false)
426{
427 using size_type = std::decay_t<decltype(*(cliqueList.begin()->begin()))>;
428
429 // In verbode mode: Compute size of the largest provided clique.
430 // This is a lower bound for the clique number (largest existing clique)
431 // which itself is a lower bound for the chromatic number.
432 // Notice that even for planar 2d grids the element graph is not planar
433 // and thus we may need more than 4 colors.
434 if (verbose)
435 {
436 size_type maxCliqueSize = 0;
437 for(const auto& clique : cliqueList)
438 maxCliqueSize = std::max(size_type(maxCliqueSize), size_type(clique.size()));
439 std::cout << "Largest found element clique (lower bound of required colors) has size " << maxCliqueSize << std::endl;
440 }
441
442 // Theoretically we could use a std::set for each adjacency[k]
443 // and simply insert all entries from the cliques into this set.
444 // However, simply pushing back entries and removing duplicates
445 // afterwards is faster.
446 // Furthermore using windows into a flat std::vector<size_type>
447 // is faster than using a std::vector<std::vector<size_type>>.
448 // The latter is provided by the FlatBlockedVector helper class.
449 auto adjacency = Impl::FlatBlockedVector<size_type, size_type>(size);
450
451 // We first must compute an upper bound for the
452 // degree of each vertex to reserve the appropriate
453 // block in the flat vector.
454 for (const auto& clique : cliqueList)
455 for (auto k : clique)
456 adjacency.capacity(k) += clique.size()-1;
457 adjacency.initialize();
458
459 // Add edges to adjacency list
460 for (const auto& clique : cliqueList)
461 for (auto k : clique)
462 for (auto l : clique)
463 if (k!=l)
464 adjacency[k].push_back(l);
465
466 // Remove duplicate entries in inner container
467 {
468 auto indices = Dune::range(adjacency.size());
469#if __cpp_lib_execution >= 201603L
470 std::for_each(std::execution::par, indices.begin(), indices.end(), [&](auto i) {
471#else
472 std::for_each(indices.begin(), indices.end(), [&](auto i) {
473#endif
474 Impl::sortAndUnifyVector(adjacency[i]);
475 });
476 }
477
478 return std::move(adjacency).toRange();
479}
480
481
482
512template <class GridView, class ElementMapper, class size_type=uint32_t>
513auto vertexBasedElementAdjacency(const GridView& gridView, const ElementMapper& elementMapper, bool verbose=false)
514{
515 static const auto dim = GridView::dimension;
516
517 auto subEntityMapper = Dune::SingleCodimSingleGeomTypeMapper<GridView, dim>(gridView);
518
519 // Compute a map of vertex index to vector of adjacent elements indices.
520 auto vertexCliques = Impl::FlatBlockedVector<size_type>(subEntityMapper.size());
521
522 // Precompute and reserve block sizes
523 for (const auto& element : elements(gridView))
524 for (std::size_t i = 0; i<element.subEntities(dim); ++i)
525 ++vertexCliques.capacity(subEntityMapper.subIndex(element, i, dim));
526 vertexCliques.initialize();
527
528 for (const auto& element : elements(gridView))
529 {
530 auto elementIndex = elementMapper.index(element);
531 for (std::size_t i = 0; i<element.subEntities(dim); ++i)
532 vertexCliques[subEntityMapper.subIndex(element, i, dim)].push_back(elementIndex);
533 }
534
535 return cliqueListToAdjacency(std::move(vertexCliques).toRange(), elementMapper.size(), verbose);
536}
537
538
539
566template <class GridView, class size_type=uint32_t>
567auto vertexBasedElementAdjacency(const GridView& gridView, bool verbose=false)
568{
570 return vertexBasedElementAdjacency(gridView, elementMapper, verbose);
571}
572
573
574
598template <class GridView, class SubEntityLayout, class ElementMapper, class size_type=uint32_t>
599auto subEntityBasedElementAdjacency(const GridView& gridView, const SubEntityLayout& subEntityLayout, const ElementMapper& elementMapper)
600{
601 static const auto dim = GridView::dimension;
602
603 auto subEntityMapper = Dune::MultipleCodimMultipleGeomTypeMapper<GridView>(gridView, subEntityLayout);
604
605 std::vector<std::vector<size_type>> subEntityCliques(subEntityMapper.size());
606 for (const auto& element : elements(gridView))
607 {
608 auto elementIndex = elementMapper.index(element);
609 auto re = Dune::referenceElement<double,dim>(element.type());
610 for (std::size_t codim = 1; codim <= dim; ++codim)
611 for (std::size_t i = 0; i<re.size(codim); ++i)
612 if (subEntityLayout(re.type(i, codim), GridView::dimension))
613 subEntityCliques[subEntityMapper.subIndex(element, i, codim)].push_back(elementIndex);
614 }
615
616 return cliqueListToAdjacency(subEntityCliques, elementMapper.size());
617}
618
619
620
639template <class GridView, class SubEntityLayout, class size_type=uint32_t>
640auto subEntityBasedElementAdjacency(const GridView& gridView, const SubEntityLayout& subEntityLayout)
641{
643 return subEntityBasedElementAdjacency(gridView, elementMapper, subEntityLayout);
644}
645
646
647
663template<class GridView, class size_type>
664auto coloredGridViewPartition(const GridView& gridView, const std::vector<size_type>& coloring)
665{
666 size_type colorCount = *std::max_element(coloring.begin(), coloring.end()) + 1;
667
668 using ElementSeed = typename GridView::Grid::template Codim<0>::EntitySeed;
669 using ElementSeedRange = std::vector<ElementSeed>;
670
671 std::vector<ElementSeedRange> elementSeedPartition(colorCount);
673 for (const auto& element : elements(gridView))
674 elementSeedPartition[coloring[elementMapper.index(element)]].push_back(element.seed());
675
676 // For convenience transform from range of seeds to range of entities
677 // This is save because transformedRangeView() stores copies of passed
678 // r-values. Furthermore it transforms random access ranges to random
679 // access ranges, such that the resulting entity range can be split
680 // cheaply.
681 auto seedToElement = [gridPtr = &gridView.grid()] (const auto& seed) {
682 return gridPtr->entity(seed);
683 };
684 return Dune::transformedRangeView(std::move(elementSeedPartition), [seedToElement](const auto& seedRange) {
685 return Dune::transformedRangeView(seedRange, seedToElement);
686 });
687}
688
689
690
726template<class GridView, class size_type=uint32_t>
727auto coloredGridViewPartition(const GridView& gridView, bool verbose=false)
728{
729 Dune::Timer timer;
730
731 timer.reset();
732 auto adjacency = Dune::Fufem::vertexBasedElementAdjacency(gridView, verbose);
733 if (verbose)
734 std::cout << "Building adjacency map took " << timer.elapsed() << "s." << std::endl;
735
736 timer.reset();
737 auto coloring = Dune::Fufem::computeColoring(adjacency);
738 if (verbose)
739 std::cout << "Building coloring took " << timer.elapsed() << "s." << std::endl;
740
741 timer.reset();
742 auto elementPartition = Dune::Fufem::coloredGridViewPartition(gridView, coloring);
743 if (verbose)
744 {
745 std::cout << "Building colored element partition took " << timer.elapsed() << "s." << std::endl;
746 std::cout << "Number of used colors is " << elementPartition.size() << std::endl;
747 std::size_t elementCount = 0;
748 for(const auto& color : elementPartition)
749 elementCount += color.size();
750 std::cout << "Total number of elements in the partition is " << elementCount << std::endl;
751 }
752
753 return elementPartition;
754}
755
756
757
770template<class ElementRange, class Basis>
771bool checkOverlapFree(const ElementRange& elementRange, const Basis& basis) {
772
773 bool overlapFree = true;
774
776
777 using MI = typename Basis::MultiIndex;
779
780 auto localView = basis.localView();
781 for (const auto& element : elementRange)
782 {
783 localView.bind(element);
784
785 // Check if any of the DOFs of the present element showed up earlier.
786 for (size_t i=0; i<localView.tree().size(); ++i)
787 {
788 auto localIndex = localView.tree().localIndex(i);
789 auto globalIndex = localView.index(localIndex);
790 auto it = processedDOFs.find(globalIndex);
791 if (it != processedDOFs.end())
792 {
793 std::cout << "Overlap detected! Index [" << globalIndex << "] shows up in element "
794 << it->second << " and " << elementMapper.index(element) << std::endl;
795 overlapFree =false;
796 }
797 }
798
799 for (size_t i=0; i<localView.tree().size(); ++i)
800 {
801 auto localIndex = localView.tree().localIndex(i);
802 auto globalIndex = localView.index(localIndex);
803 processedDOFs[globalIndex] = elementMapper.index(element);
804 }
805 }
806 return overlapFree;
807}
808
809
810
811} // namespace Fufem
812} // namespace Dune
813
814
815#endif // DUNE_FUFEM_PARALLEL_ELEMENTCOLORING_HH
BCRSMatrix< FieldMatrix< T, n, m >, A >::size_type size_type
void seed(const Vertex &vertex)
reference operator[](size_type i)
int size() const
auto transformedRangeView(R &&range, F &&f)
static constexpr IntegralRange< std::decay_t< T > > range(T &&from, U &&to) noexcept
size_type dim() const
size_t() const
constexpr std::integer_sequence< T, II..., T(IN)> push_back(std::integer_sequence< T, II... >, std::integral_constant< T, IN >={})
STL namespace.
auto coloredGridViewPartition(const GridView &gridView, const std::vector< size_type > &coloring)
Compute a colored partition of the elements in the gridView from coloring.
Definition elementcoloring.hh:664
auto subEntityBasedElementAdjacency(const GridView &gridView, const SubEntityLayout &subEntityLayout, const ElementMapper &elementMapper)
Compute a layout based adjacency list.
Definition elementcoloring.hh:599
auto cliqueListToAdjacency(const CliqueList &cliqueList, std::size_t size, bool verbose=false)
Compute an adjacency list from a clique list.
Definition elementcoloring.hh:425
auto vertexBasedElementAdjacency(const GridView &gridView, const ElementMapper &elementMapper, bool verbose=false)
Compute a vertex based adjacency list.
Definition elementcoloring.hh:513
auto computeColoring(const Adjacency &adjacency, SeedNodesContainer &&seedNodes)
Compute a graph coloring from neighboring information.
Definition elementcoloring.hh:222
bool checkOverlapFree(const ElementRange &elementRange, const Basis &basis)
Check if given element range is overlap free.
Definition elementcoloring.hh:771
const Grid & grid() const
MCMGLayout mcmgElementLayout()
void reset() noexcept
double elapsed() const noexcept
T assign(T... args)
T begin(T... args)
T end(T... args)
T endl(T... args)
T find(T... args)
T for_each(T... args)
T forward(T... args)
T max_element(T... args)
T max(T... args)
T push_back(T... args)
T resize(T... args)
T size(T... args)
T sort(T... args)
T unique(T... args)