Dune-Fufem 2.12-git
Loading...
Searching...
No Matches
affineconstraints.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_CONSTRAINTS_AFFINECONSTRAINTS_HH
8#define DUNE_FUFEM_CONSTRAINTS_AFFINECONSTRAINTS_HH
9
10#include <iostream>
11#include <map>
12#include <unordered_map>
13#include <utility>
14#include <type_traits>
15
19
21
23
24namespace Dune::Fufem {
25
26namespace Impl {
27
28 // This class stores a range of indices and wraps it in
29 // a LocalView-like interface. This allows to pass a
30 // generic set of indices to the scatter() methods
31 // of the MatrixBackend implementations in dune-assembler.
32 template<class Index>
33 class FakeLocalView
34 {
35 class FakeTree
36 {
37 std::size_t size_;
38
39 public:
40
41 FakeTree(std::size_t size)
42 : size_(size)
43 {}
44
45 constexpr std::size_t size() const
46 {
47 return size_;
48 }
49
50 constexpr std::size_t localIndex(std::size_t nodeLocalIndex) const
51 {
52 return nodeLocalIndex;
53 }
54 };
55
56 using Indices = std::vector<Index>;
57
58 Indices indices_;
59
60 public:
61
62 constexpr FakeTree tree() const
63 {
64 return FakeTree{size()};
65 }
66
67 constexpr std::size_t size() const
68 {
69 return indices_.size();
70 }
71
72 constexpr const Index& index(std::size_t localIndex) const
73 {
74 return indices_[localIndex];
75 }
76
77 constexpr const auto& indices() const
78 {
79 return indices_;
80 }
81
82 constexpr auto& indices()
83 {
84 return indices_;
85 }
86
87 template<class Range>
88 constexpr void fillFromRange(Range&& range)
89 {
90 indices_.resize(range.size());
91 std::copy(range.begin(), range.end(), indices_.begin());
92 }
93
94 constexpr void clear()
95 {
96 indices_.clear();
97 }
98
99 };
100
101} // end namespace Impl
102
103
104
194template<class BV, class V, class MI, class C = double>
196{
197
198 template<class OtherMultiIndex>
199 static decltype(auto) toMultiIndex(const OtherMultiIndex& omi)
200 {
202 return omi;
204 return MultiIndex(omi);
205 else if constexpr (requires { MultiIndex{{omi}}; })
206 return MultiIndex{{omi}};
207 else if constexpr (requires(MultiIndex mi){ mi.push_back(0); omi.size(); })
208 {
209 MultiIndex mi;
210 for(auto i : omi)
211 mi.push_back(i);
212 return mi;
213 }
214 else
215 static_assert(Dune::AlwaysFalse<OtherMultiIndex>::value, "Value cannot be converted to MultiIndex type");
216 }
217
218 // The following two helpers exist, because the vector backends support non-matching
219 // container types, where a `FieldVector<T,1>` is used instead of a plain `T`. Then
220 // traversing the container will produce a trailing multi-index digit 0 which does
221 // not show up in the basis. Hence, a search in the unordered_map fails if the
222 // multi-index has been inserted as key without the trailing 0. To avoid this pitfall
223 // the following helpers ignore a trailing multi-index digit 0 when computing hashes
224 // and during equality comparison. Notice that this does not produce false positives
225 // because a basis cannot contain the multi-indices `a=(a_0,...,a_n,0)` and `b=(a_0,...a_n)`
226 // at the same time, since the latter would correspond to an inner node in the index
227 // tree if the former exists.
228 //
229 // These custom functors can be removed once we require that the container nesting
230 // structure must exactly match the basis indices and check for this during index access.
231 // Until then, using these functors avoids that some constraints are silently
232 // overlooked, because they are not found by `std::unordered_map::find` due
233 // to the mismatch.
234 struct MultiIndexHash
235 {
236 std::size_t operator()(const MI& mi) const
237 {
238 std::size_t seed = 0;
239 for (auto i : Dune::range(mi.size()-1))
240 Dune::hash_combine(seed, mi[i]);
241 if (mi.back() != 0)
242 Dune::hash_combine(seed, mi.back());
243 return seed;
244 }
245 };
246
247 struct MultiIndexEquals
248 {
249 bool operator()(const MI& mi1, const MI& mi2) const
250 {
251 auto compareDigits = [&](auto size) {
252 for (auto i : Dune::range(size))
253 if (mi1[i] != mi2[i])
254 return false;
255 return true;
256 };
257 if (mi1.size() == mi2.size())
258 return compareDigits(mi1.size());
259 else if (mi1.size()+1 == mi2.size())
260 {
261 if (mi2.back() == 0)
262 return compareDigits(mi1.size());
263 }
264 else if (mi2.size()+1 == mi1.size())
265 {
266 if (mi1.back() == 0)
267 return compareDigits(mi2.size());
268 }
269 return false;
270 }
271 };
272
273public:
274
275 using BitVector = BV;
276 using Vector = V;
277 using MultiIndex = MI;
278 using Coefficient = C;
279
283
284
285
286 AffineConstraints() = default;
289
294 : isConstrained_(std::move(isConstrained))
295 , constantTerm_(std::move(constantTerm))
296 {}
297
302 : isConstrained_(isConstrained)
303 , constantTerm_(constantTerm)
304 {}
305
309 template<class MultiIndexType>
310 bool isConstrained(const MultiIndexType& i) const
311 {
312 return Dune::Functions::istlVectorBackend(isConstrained_)[toMultiIndex(i)];
313 }
314
319 {
320 return isConstrained_;
321 }
322
327 {
328 return isConstrained_;
329 }
330
339 const LinearTerm& linearTerm() const
340 {
341 return interpolation_;
342 }
343
353 {
354 return interpolation_;
355 }
356
368 template<class MultiIndexType>
369 const LinearTermRow& linearTerm(const MultiIndexType& i) const
370 {
371 auto it = interpolation_.find(toMultiIndex(i));
372 if (it != interpolation_.end())
373 return it->second;
374 else
375 return emptyInterpolationRow_;
376 }
377
385 {
386 return constantTerm_;
387 }
388
396 {
397 return constantTerm_;
398 }
399
406 template<class MultiIndexType>
407 const auto& constantTerm(const MultiIndexType& i) const
408 {
409 return Dune::Functions::istlVectorBackend(constantTerm_)[toMultiIndex(i)];
410 }
411
418 template<class MultiIndexType>
419 auto& constantTerm(const MultiIndexType& i)
420 {
421 return Dune::Functions::istlVectorBackend(constantTerm_)[i];
422 }
423
435 template<class Vector>
436 void interpolate(Vector& v) const
437 {
438 auto vectorBackend = Dune::Functions::istlVectorBackend(v);
439 Dune::Fufem::recursiveForEachVectorEntry(v, [&](auto&& vector_i, const auto& i)
440 {
441 if (isConstrained(i))
442 {
443 vector_i = constantTerm(i);
444 for(const auto& [j, L_ij] : linearTerm(i))
445 vector_i += L_ij * vectorBackend[j];
446 }
447 });
448 }
449
463 template<class Vector>
465 {
466 auto vectorBackend = Dune::Functions::istlVectorBackend(v);
467 Dune::Fufem::recursiveForEachVectorEntry(v, [&](auto&& vector_i, const auto& i)
468 {
469 if (isConstrained(i))
470 {
471 vector_i = 0;
472 for(const auto& [j, L_ij] : linearTerm(i))
473 vector_i += L_ij * vectorBackend[j];
474 }
475 });
476 }
477
485 template<class MatrixPatternBackend, class Basis>
486 void extendMatrixPattern(MatrixPatternBackend& patternBackend, const Basis& basis) const
487 {
488 // Check if the MatrixPatternBackend supports the dune-assembler interface.
489 if constexpr (requires() { typename MatrixPatternBackend::LocalPattern; })
490 {
491 // This functions extracts the global indices from a linear combination
492 auto globalIndices = [](const auto& linearTerm) {
495 [](const auto& coeff) -> decltype(auto) { return coeff.first; }
496 );
497 };
498
499 // We need to pass LocalViews and a LocalPattern to the scatter
500 // methods of dune-assembler MatrixBackends.
501 auto localPattern = typename MatrixPatternBackend::LocalPattern();
502 auto newRowIndices = Impl::FakeLocalView<typename Basis::MultiIndex>();
503 auto newColIndices = Impl::FakeLocalView<typename Basis::MultiIndex>();
504 auto accumulatedRow = typename Basis::MultiIndex();
505 auto accumulatedColIndices = std::vector<typename Basis::MultiIndex>();
506 patternBackend.forEachEntry([&](const auto& row, const auto& col)
507 {
508 if (isConstrained(row))
509 {
510 newRowIndices.fillFromRange(globalIndices(linearTerm(toMultiIndex(row))));
511 if (isConstrained(col))
512 newColIndices.fillFromRange(globalIndices(linearTerm(toMultiIndex(col))));
513 else
514 newColIndices.fillFromRange(std::array{toMultiIndex(col)});
515 localPattern.resize(newRowIndices.size(), newColIndices.size());
516 localPattern.addAll();
517 patternBackend.scatter(newRowIndices, newColIndices, localPattern);
518 }
519 else
520 {
521 if (isConstrained(col))
522 {
523 if (accumulatedRow != toMultiIndex(row))
524 {
525 if (accumulatedColIndices.size()>0)
526 {
527 newRowIndices.fillFromRange(std::array{accumulatedRow});
528 std::swap(newColIndices.indices(), accumulatedColIndices);
529 localPattern.resize(newRowIndices.size(), newColIndices.size());
530 localPattern.addAll();
531 patternBackend.scatter(newRowIndices, newColIndices, localPattern);
532 }
533 accumulatedColIndices.clear();
534 accumulatedRow = toMultiIndex(row);
535 }
536 for(const auto& newColIndex : globalIndices(linearTerm(toMultiIndex(col))))
537 accumulatedColIndices.push_back(newColIndex);
538 }
539 }
540 });
541 if (accumulatedColIndices.size()>0)
542 {
543 newRowIndices.fillFromRange(std::array{accumulatedRow});
544 std::swap(newColIndices.indices(), accumulatedColIndices);
545 localPattern.resize(newRowIndices.size(), newColIndices.size());
546 localPattern.addAll();
547 patternBackend.scatter(newRowIndices, newColIndices, localPattern);
548 }
549 }
550 else
551 {
552 auto localView = basis.localView();
553 for(const auto& element : elements(basis.gridView()))
554 {
555 localView.bind(element);
556 for(auto i_local : Dune::range(localView.tree().size()))
557 {
558 auto i = localView.index(localView.tree().localIndex(i_local));
559 for(auto j_local : Dune::range(localView.tree().size()))
560 {
561 auto j = localView.index(localView.tree().localIndex(j_local));
562 if (isConstrained(i))
563 {
564 if (isConstrained(j))
565 for(const auto& [k, L_ik] : linearTerm(i))
566 for(const auto& [l, L_jl] : linearTerm(j))
567 patternBackend.insertEntry(k, l);
568 else
569 for(const auto& [k, L_ik] : linearTerm(i))
570 patternBackend.insertEntry(k, j);
571 }
572 else if (isConstrained(j))
573 {
574 for(const auto& [l, L_jl] : linearTerm(j))
575 patternBackend.insertEntry(i, l);
576 }
577 }
578 }
579 }
580 }
581 }
582
591 template<class Matrix>
592 void constrainMatrix(Matrix& A) const
593 {
594 auto matrixBackend = Dune::Assembler::ISTLMatrixBackend(A);
595 Dune::Fufem::recursiveForEachMatrixEntry(A, [&](auto&& matrix_ij, const auto& i, const auto& j)
596 {
597 auto equals = [](const auto& i, const auto& j) {
598 if constexpr (std::is_same_v<decltype(i), decltype(j)>)
599 return i==j;
600 else
601 return false;
602 };
603 if (isConstrained(i))
604 {
605 if (isConstrained(j))
606 for(const auto& [k, L_ik] : linearTerm(i))
607 for(const auto& [l, L_jl] : linearTerm(j))
608 matrixBackend(k, l) += L_ik*L_jl * matrix_ij;
609 else
610 for(const auto& [k, L_ik] : linearTerm(i))
611 matrixBackend(k, j) += L_ik * matrix_ij;
612 matrix_ij = equals(i, j) ? 1 : 0;
613 }
614 else if (isConstrained(j))
615 {
616 for(const auto& [l, L_jl] : linearTerm(j))
617 matrixBackend(i, l) += L_jl * matrix_ij;
618 matrix_ij = equals(i, j) ? 1 : 0;
619 }
620 });
621 }
622
629 template<class Vector>
630 void constrainVector(Vector& b) const
631 {
632 auto vectorBackend = Dune::Functions::istlVectorBackend(b);
633 Dune::Fufem::recursiveForEachVectorEntry(b, [&](auto&& vector_i, const auto& i)
634 {
635 if (isConstrained(i))
636 {
637 for(const auto& [k, L_ik] : linearTerm(i))
638 vectorBackend[k] += L_ik * vector_i;
639 vector_i = constantTerm(i);
640 }
641 });
642 }
643
652 template<class Vector>
654 {
655 auto vectorBackend = Dune::Functions::istlVectorBackend(b);
656 Dune::Fufem::recursiveForEachVectorEntry(b, [&](auto&& vector_i, const auto& i)
657 {
658 if (isConstrained(i))
659 {
660 for(const auto& [k, L_ik] : linearTerm(i))
661 vectorBackend[k] += L_ik * vector_i;
662 vector_i = 0;
663 }
664 });
665 }
666
675 template<class Matrix, class Vector>
677 {
678 A.mmv(constantTerm_, b);
681 }
682
693 template<class Matrix, class Vector>
695 {
698 }
699
703 void report() const
704 {
705 Dune::Fufem::recursiveForEachVectorEntry(isConstrained_, [&](auto&& isConstrained_i, const auto& i)
706 {
707 if (isConstrained(i))
708 {
709 std::cout << "x_" << i << " = ";
710 for(const auto& [j, L_ij] : linearTerm(i))
711 std::cout << L_ij << " * x_" << j << " + " ;
713 }
714 });
715 std::cout << "interpolated dofs : " << interpolation_.size() << std::endl;
716 }
717
723 bool check() const
724 {
725 bool pass = true;
726 for(const auto& [i, L_i] : interpolation_)
727 {
728 for(const auto& [j, L_ij] : L_i)
729 {
730 if (isConstrained(j))
731 {
732 std::cout << "Error: interpolation for constrained DOF " << i << " depends on constrained DOF " << j << std::endl;
733 pass = false;
734 }
735 }
736 }
737 return pass;
738 }
739
752 {
753 auto dependenciesResolved = isConstrained_;
754 auto dependenciesResolvedBackend = Dune::Functions::istlVectorBackend(dependenciesResolved);
755 dependenciesResolvedBackend = false;
756 for(auto& [i, L_i] : interpolation_)
757 if (not(dependenciesResolvedBackend[i]))
758 resolveDOF(dependenciesResolved, i, L_i);
759 }
760
761private:
762
763 void resolveDOF(BitVector& dependenciesResolved, const MultiIndex& i, LinearTermRow& L_i)
764 {
765 auto dependenciesResolvedBackend = Dune::Functions::istlVectorBackend(dependenciesResolved);
766 auto c = Dune::Functions::istlVectorBackend(constantTerm_);
767 auto it = L_i.begin();
768 while (it != L_i.end())
769 {
770 auto& [j, L_ij] = *it;
771 // If interpolation weight is itself constraint,
772 // first resolve it recursively (if needed)
773 // and then insert all indirect dependencies directly
774 if (isConstrained(j))
775 {
776 c[i] += L_ij * c[j];
777 auto L_j_it = interpolation_.find(j);
778 if (L_j_it != interpolation_.end())
779 {
780 auto& L_j = L_j_it->second;
781 if (not(dependenciesResolvedBackend[j]))
782 resolveDOF(dependenciesResolved, j, L_j);
783 for (const auto& [k, L_jk] : L_j)
784 L_i[k] += L_ij * L_jk;
785 }
786 else
787 dependenciesResolvedBackend[j] = true;
788 it = L_i.erase(it);
789 }
790 else
791 ++it;
792 }
793 dependenciesResolvedBackend[i] = true;
794 }
795
796 BitVector isConstrained_;
797 LinearTerm interpolation_;
798 LinearTermRow emptyInterpolationRow_;
799 ConstantTerm constantTerm_;
800};
801
802
803
819template<class BitVector=std::monostate, class Vector=std::monostate, class Basis>
820auto makeAffineConstraints(const Basis& basis)
821{
822 using MultiIndex = typename Basis::MultiIndex;
823 using Coefficient = double;
824
825 auto generateBitVector = [&] {
827 return Dune::Functions::makeContainer<char>(basis.rootBasis().preBasis().containerDescriptor(), false);
828 else
829 {
830 auto v = BitVector();
831 Dune::Functions::istlVectorBackend(v).resize(basis);
833 return v;
834 }
835 };
836 auto generateVector = [&] {
838 return Dune::Functions::makeISTLVector<Coefficient>(basis.rootBasis().preBasis().containerDescriptor());
839 else
840 {
841 auto v = Vector();
842 Dune::Functions::istlVectorBackend(v).resize(basis);
844 return v;
845 }
846 };
847 using GeneratedBitVector = decltype(generateBitVector());
848 using GeneratedVector = decltype(generateVector());
849 static_assert(not std::is_same_v<GeneratedBitVector, std::monostate>, "Deducing a BitVector-type for non-flat indices in makeAffineConstraints requires dune-functions>=2.11.");
850 static_assert(not std::is_same_v<GeneratedVector, std::monostate>, "Deducing a Vector-type for non-flat indices in makeAffineConstraints requires dune-functions>=2.11.");
852}
853
854
855
856} // namespace Dune::Fufem
857
858
859
860#endif // DUNE_FUFEM_CONSTRAINTS_AFFINECONSTRAINTS_HH
Col col
void seed(const Vertex &vertex)
int size() const
void clear()
bool equals(const SLListConstIterator< T, A > &other) const
auto transformedRangeView(R &&range, F &&f)
static constexpr IntegralRange< T > range(T from, T to) noexcept
std::ptrdiff_t index() const
virtual void operator()()=0
void hash_combine(std::size_t &seed, const T &arg)
void mmv(const X &x, Y &y) const
Class to handle affine constraints on a subset of DOFs.
Definition affineconstraints.hh:196
void constrainVectorHomogeneously(Vector &b) const
Constrain vector to a constrained basis.
Definition affineconstraints.hh:653
AffineConstraints(const BitVector &isConstrained, const Vector &constantTerm)
Construct from bit vector and vector.
Definition affineconstraints.hh:301
AffineConstraints(const AffineConstraints &)=default
LinearTerm & linearTerm()
Mutable access to linear part of affine map.
Definition affineconstraints.hh:352
void resolveDependencies()
Resolve dependencies of constrained DOFs on other constrained DOFs.
Definition affineconstraints.hh:751
void constrainMatrix(Matrix &A) const
Constrain matrix to a constrained basis.
Definition affineconstraints.hh:592
const auto & constantTerm(const MultiIndexType &i) const
Const access to i-th row of constant part of affine map.
Definition affineconstraints.hh:407
void report() const
Report the constraints to std::cout.
Definition affineconstraints.hh:703
AffineConstraints(BitVector &&isConstrained, Vector &&constantTerm)
Construct from bit vector and vector.
Definition affineconstraints.hh:293
std::map< MI, Coefficient > LinearTermRow
Definition affineconstraints.hh:280
const LinearTermRow & linearTerm(const MultiIndexType &i) const
Const access to i-th row of linear part of affine map.
Definition affineconstraints.hh:369
const LinearTerm & linearTerm() const
Const access to linear part of affine map.
Definition affineconstraints.hh:339
void extendMatrixPattern(MatrixPatternBackend &patternBackend, const Basis &basis) const
Extend matrix pattern for a constrained basis.
Definition affineconstraints.hh:486
void constrainVector(Vector &b) const
Constrain vector to a constrained basis.
Definition affineconstraints.hh:630
V Vector
Definition affineconstraints.hh:276
bool check() const
Check constraints for consistency.
Definition affineconstraints.hh:723
bool isConstrained(const MultiIndexType &i) const
Check if i-th DOF is constrained.
Definition affineconstraints.hh:310
const BitVector & isConstrained() const
Const access to bit-vector marking constrained DOFs.
Definition affineconstraints.hh:318
const ConstantTerm & constantTerm() const
Const access to constant part of affine map.
Definition affineconstraints.hh:384
BitVector & isConstrained()
Mutable access to bit-vector marking constrained DOFs.
Definition affineconstraints.hh:326
void interpolate(Vector &v) const
Interpolate vector according to affine constraints.
Definition affineconstraints.hh:436
void constrainLinearSystem(Matrix &A, Vector &b) const
Constrain linear system affine subspace satisfying the constraints.
Definition affineconstraints.hh:676
void interpolateHomogeneously(Vector &v) const
Interpolate vector according to affine constraints.
Definition affineconstraints.hh:464
std::unordered_map< MultiIndex, LinearTermRow, MultiIndexHash, MultiIndexEquals > LinearTerm
Definition affineconstraints.hh:281
MI MultiIndex
Definition affineconstraints.hh:277
void constrainLinearSystemHomogeneously(Matrix &A, Vector &b) const
Constrain linear system affine subspace satisfying the constraints.
Definition affineconstraints.hh:694
BV BitVector
Definition affineconstraints.hh:275
ConstantTerm & constantTerm()
Mutable access to constant part of affine map.
Definition affineconstraints.hh:395
AffineConstraints(AffineConstraints &&)=default
C Coefficient
Definition affineconstraints.hh:278
Vector ConstantTerm
Definition affineconstraints.hh:282
auto & constantTerm(const MultiIndexType &i)
Mutable access to i-th row of constant part of affine map.
Definition affineconstraints.hh:419
T copy(T... args)
T end(T... args)
T endl(T... args)
T find(T... args)
T forward(T... args)
auto makeAffineConstraints(const Basis &basis)
Create and initialize an AffineConstraints object for a basis.
Definition affineconstraints.hh:820
auto istlVectorBackend(Vector &v)
T is_same_v
Definition dunefunctionsboundaryfunctionalassembler.hh:29
STL namespace.
T size(T... args)
T swap(T... args)