Dune-Fufem 2.11-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
17
19
20#if DUNE_VERSION_GTE(DUNE_FUNCTIONS, 2, 11)
23#endif
24
25#include <dune/assembler/backends/istlmatrixbackend.hh>
26
28
29namespace Dune::Fufem {
30
31namespace Impl {
32
33 // This class stores a range of indices and wraps it in
34 // a LocalView-like interface. This allows to pass a
35 // generic set of indices to the scatter() methods
36 // of the MatrixBackend implementations in dune-assembler.
37 template<class Index>
38 class FakeLocalView
39 {
40 class FakeTree
41 {
42 std::size_t size_;
43
44 public:
45
46 FakeTree(std::size_t size)
47 : size_(size)
48 {}
49
50 constexpr std::size_t size() const
51 {
52 return size_;
53 }
54
55 constexpr std::size_t localIndex(std::size_t nodeLocalIndex) const
56 {
57 return nodeLocalIndex;
58 }
59 };
60
61 using Indices = std::vector<Index>;
62
63 Indices indices_;
64
65 public:
66
67 constexpr FakeTree tree() const
68 {
69 return FakeTree{size()};
70 }
71
72 constexpr std::size_t size() const
73 {
74 return indices_.size();
75 }
76
77 constexpr const Index& index(std::size_t localIndex) const
78 {
79 return indices_[localIndex];
80 }
81
82 constexpr const auto& indices() const
83 {
84 return indices_;
85 }
86
87 constexpr auto& indices()
88 {
89 return indices_;
90 }
91
92 template<class Range>
93 constexpr void fillFromRange(Range&& range)
94 {
95 indices_.resize(range.size());
96 std::copy(range.begin(), range.end(), indices_.begin());
97 }
98
99 constexpr void clear()
100 {
101 indices_.clear();
102 }
103
104 };
105
106} // end namespace Impl
107
108
109
199template<class BV, class V, class MI, class C = double>
201{
202
203 template<class OtherMultiIndex>
204 static decltype(auto) toMultiIndex(const OtherMultiIndex& omi)
205 {
207 return omi;
209 return MultiIndex(omi);
210 else if constexpr (requires { MultiIndex{{omi}}; })
211 return MultiIndex{{omi}};
212 else if constexpr (requires(MultiIndex mi){ mi.push_back(0); omi.size(); })
213 {
214 MultiIndex mi;
215 for(auto i : omi)
216 mi.push_back(i);
217 return mi;
218 }
219 else
220 static_assert(Dune::AlwaysFalse<OtherMultiIndex>::value, "Value cannot be converted to MultiIndex type");
221 }
222
223 // The following two helpers exist, because the vector backends support non-matching
224 // container types, where a `FieldVector<T,1>` is used instead of a plain `T`. Then
225 // traversing the container will produce a trailing multi-index digit 0 which does
226 // not show up in the basis. Hence, a search in the unordered_map fails if the
227 // multi-index has been inserted as key without the trailing 0. To avoid this pitfall
228 // the following helpers ignore a trailing multi-index digit 0 when computing hashes
229 // and during equality comparison. Notice that this does not produce false positives
230 // because a basis cannot contain the multi-indices `a=(a_0,...,a_n,0)` and `b=(a_0,...a_n)`
231 // at the same time, since the latter would correspond to an inner node in the index
232 // tree if the former exists.
233 //
234 // These custom functors can be removed once we require that the container nesting
235 // structure must exactly match the basis indices and check for this during index access.
236 // Until then, using these functors avoids that some constraints are silently
237 // overlooked, because they are not found by `std::unordered_map::find` due
238 // to the mismatch.
239 struct MultiIndexHash
240 {
241 std::size_t operator()(const MI& mi) const
242 {
243 std::size_t seed = 0;
244 for (auto i : Dune::range(mi.size()-1))
245 Dune::hash_combine(seed, mi[i]);
246 if (mi.back() != 0)
247 Dune::hash_combine(seed, mi.back());
248 return seed;
249 }
250 };
251
252 struct MultiIndexEquals
253 {
254 bool operator()(const MI& mi1, const MI& mi2) const
255 {
256 auto compareDigits = [&](auto size) {
257 for (auto i : Dune::range(size))
258 if (mi1[i] != mi2[i])
259 return false;
260 return true;
261 };
262 if (mi1.size() == mi2.size())
263 return compareDigits(mi1.size());
264 else if (mi1.size()+1 == mi2.size())
265 {
266 if (mi2.back() == 0)
267 return compareDigits(mi1.size());
268 }
269 else if (mi2.size()+1 == mi1.size())
270 {
271 if (mi1.back() == 0)
272 return compareDigits(mi2.size());
273 }
274 return false;
275 }
276 };
277
278public:
279
280 using BitVector = BV;
281 using Vector = V;
282 using MultiIndex = MI;
283 using Coefficient = C;
284
288
289
290
291 AffineConstraints() = default;
294
299 : isConstrained_(std::move(isConstrained))
300 , constantTerm_(std::move(constantTerm))
301 {}
302
307 : isConstrained_(isConstrained)
308 , constantTerm_(constantTerm)
309 {}
310
314 template<class MultiIndexType>
315 bool isConstrained(const MultiIndexType& i) const
316 {
317 return Dune::Functions::istlVectorBackend(isConstrained_)[toMultiIndex(i)];
318 }
319
324 {
325 return isConstrained_;
326 }
327
332 {
333 return isConstrained_;
334 }
335
344 const LinearTerm& linearTerm() const
345 {
346 return interpolation_;
347 }
348
358 {
359 return interpolation_;
360 }
361
373 template<class MultiIndexType>
374 const LinearTermRow& linearTerm(const MultiIndexType& i) const
375 {
376 auto it = interpolation_.find(toMultiIndex(i));
377 if (it != interpolation_.end())
378 return it->second;
379 else
380 return emptyInterpolationRow_;
381 }
382
390 {
391 return constantTerm_;
392 }
393
401 {
402 return constantTerm_;
403 }
404
411 template<class MultiIndexType>
412 const auto& constantTerm(const MultiIndexType& i) const
413 {
414 return Dune::Functions::istlVectorBackend(constantTerm_)[toMultiIndex(i)];
415 }
416
423 template<class MultiIndexType>
424 auto& constantTerm(const MultiIndexType& i)
425 {
426 return Dune::Functions::istlVectorBackend(constantTerm_)[i];
427 }
428
440 template<class Vector>
441 void interpolate(Vector& v) const
442 {
443 auto vectorBackend = Dune::Functions::istlVectorBackend(v);
444 Dune::Fufem::recursiveForEachVectorEntry(v, [&](auto&& vector_i, const auto& i)
445 {
446 if (isConstrained(i))
447 {
448 vector_i = constantTerm(i);
449 for(const auto& [j, L_ij] : linearTerm(i))
450 vector_i += L_ij * vectorBackend[j];
451 }
452 });
453 }
454
468 template<class Vector>
470 {
471 auto vectorBackend = Dune::Functions::istlVectorBackend(v);
472 Dune::Fufem::recursiveForEachVectorEntry(v, [&](auto&& vector_i, const auto& i)
473 {
474 if (isConstrained(i))
475 {
476 vector_i = 0;
477 for(const auto& [j, L_ij] : linearTerm(i))
478 vector_i += L_ij * vectorBackend[j];
479 }
480 });
481 }
482
490 template<class MatrixPatternBackend, class Basis>
491 void extendMatrixPattern(MatrixPatternBackend& patternBackend, const Basis& basis) const
492 {
493 // Check if the MatrixPatternBackend supports the dune-assembler interface.
494 if constexpr (requires() { typename MatrixPatternBackend::LocalPattern; })
495 {
496 // This functions extracts the global indices from a linear combination
497 auto globalIndices = [](const auto& linearTerm) {
500 [](const auto& coeff) -> decltype(auto) { return coeff.first; }
501 );
502 };
503
504 // We need to pass LocalViews and a LocalPattern to the scatter
505 // methods of dune-assembler MatrixBackends.
506 auto localPattern = typename MatrixPatternBackend::LocalPattern();
507 auto newRowIndices = Impl::FakeLocalView<typename Basis::MultiIndex>();
508 auto newColIndices = Impl::FakeLocalView<typename Basis::MultiIndex>();
509 auto accumulatedRow = typename Basis::MultiIndex();
510 auto accumulatedColIndices = std::vector<typename Basis::MultiIndex>();
511 patternBackend.forEachEntry([&](const auto& row, const auto& col)
512 {
513 if (isConstrained(row))
514 {
515 newRowIndices.fillFromRange(globalIndices(linearTerm(toMultiIndex(row))));
516 if (isConstrained(col))
517 newColIndices.fillFromRange(globalIndices(linearTerm(toMultiIndex(col))));
518 else
519 newColIndices.fillFromRange(std::array{toMultiIndex(col)});
520 localPattern.resize(newRowIndices.size(), newColIndices.size());
521 localPattern.addAll();
522 patternBackend.scatter(newRowIndices, newColIndices, localPattern);
523 }
524 else
525 {
526 if (isConstrained(col))
527 {
528 if (accumulatedRow != toMultiIndex(row))
529 {
530 if (accumulatedColIndices.size()>0)
531 {
532 newRowIndices.fillFromRange(std::array{accumulatedRow});
533 std::swap(newColIndices.indices(), accumulatedColIndices);
534 localPattern.resize(newRowIndices.size(), newColIndices.size());
535 localPattern.addAll();
536 patternBackend.scatter(newRowIndices, newColIndices, localPattern);
537 }
538 accumulatedColIndices.clear();
539 accumulatedRow = toMultiIndex(row);
540 }
541 for(const auto& newColIndex : globalIndices(linearTerm(toMultiIndex(col))))
542 accumulatedColIndices.push_back(newColIndex);
543 }
544 }
545 });
546 if (accumulatedColIndices.size()>0)
547 {
548 newRowIndices.fillFromRange(std::array{accumulatedRow});
549 std::swap(newColIndices.indices(), accumulatedColIndices);
550 localPattern.resize(newRowIndices.size(), newColIndices.size());
551 localPattern.addAll();
552 patternBackend.scatter(newRowIndices, newColIndices, localPattern);
553 }
554 }
555 else
556 {
557 auto localView = basis.localView();
558 for(const auto& element : elements(basis.gridView()))
559 {
560 localView.bind(element);
561 for(auto i_local : Dune::range(localView.tree().size()))
562 {
563 auto i = localView.index(localView.tree().localIndex(i_local));
564 for(auto j_local : Dune::range(localView.tree().size()))
565 {
566 auto j = localView.index(localView.tree().localIndex(j_local));
567 if (isConstrained(i))
568 {
569 if (isConstrained(j))
570 for(const auto& [k, L_ik] : linearTerm(i))
571 for(const auto& [l, L_jl] : linearTerm(j))
572 patternBackend.insertEntry(k, l);
573 else
574 for(const auto& [k, L_ik] : linearTerm(i))
575 patternBackend.insertEntry(k, j);
576 }
577 else if (isConstrained(j))
578 {
579 for(const auto& [l, L_jl] : linearTerm(j))
580 patternBackend.insertEntry(i, l);
581 }
582 }
583 }
584 }
585 }
586 }
587
596 template<class Matrix>
597 void constrainMatrix(Matrix& A) const
598 {
599 auto matrixBackend = Dune::Assembler::ISTLMatrixBackend(A);
600 Dune::Fufem::recursiveForEachMatrixEntry(A, [&](auto&& matrix_ij, const auto& i, const auto& j)
601 {
602 auto equals = [](const auto& i, const auto& j) {
603 if constexpr (std::is_same_v<decltype(i), decltype(j)>)
604 return i==j;
605 else
606 return false;
607 };
608 if (isConstrained(i))
609 {
610 if (isConstrained(j))
611 for(const auto& [k, L_ik] : linearTerm(i))
612 for(const auto& [l, L_jl] : linearTerm(j))
613 matrixBackend(k, l) += L_ik*L_jl * matrix_ij;
614 else
615 for(const auto& [k, L_ik] : linearTerm(i))
616 matrixBackend(k, j) += L_ik * matrix_ij;
617 matrix_ij = equals(i, j) ? 1 : 0;
618 }
619 else if (isConstrained(j))
620 {
621 for(const auto& [l, L_jl] : linearTerm(j))
622 matrixBackend(i, l) += L_jl * matrix_ij;
623 matrix_ij = equals(i, j) ? 1 : 0;
624 }
625 });
626 }
627
634 template<class Vector>
635 void constrainVector(Vector& b) const
636 {
637 auto vectorBackend = Dune::Functions::istlVectorBackend(b);
638 Dune::Fufem::recursiveForEachVectorEntry(b, [&](auto&& vector_i, const auto& i)
639 {
640 if (isConstrained(i))
641 {
642 for(const auto& [k, L_ik] : linearTerm(i))
643 vectorBackend[k] += L_ik * vector_i;
644 vector_i = constantTerm(i);
645 }
646 });
647 }
648
657 template<class Vector>
659 {
660 auto vectorBackend = Dune::Functions::istlVectorBackend(b);
661 Dune::Fufem::recursiveForEachVectorEntry(b, [&](auto&& vector_i, const auto& i)
662 {
663 if (isConstrained(i))
664 {
665 for(const auto& [k, L_ik] : linearTerm(i))
666 vectorBackend[k] += L_ik * vector_i;
667 vector_i = 0;
668 }
669 });
670 }
671
680 template<class Matrix, class Vector>
682 {
683 A.mmv(constantTerm_, b);
686 }
687
698 template<class Matrix, class Vector>
704
708 void report() const
709 {
710 Dune::Fufem::recursiveForEachVectorEntry(isConstrained_, [&](auto&& isConstrained_i, const auto& i)
711 {
712 if (isConstrained(i))
713 {
714 std::cout << "x_" << i << " = ";
715 for(const auto& [j, L_ij] : linearTerm(i))
716 std::cout << L_ij << " * x_" << j << " + " ;
718 }
719 });
720 std::cout << "interpolated dofs : " << interpolation_.size() << std::endl;
721 }
722
728 bool check() const
729 {
730 bool pass = true;
731 for(const auto& [i, L_i] : interpolation_)
732 {
733 for(const auto& [j, L_ij] : L_i)
734 {
735 if (isConstrained(j))
736 {
737 std::cout << "Error: interpolation for constrained DOF " << i << " depends on constrained DOF " << j << std::endl;
738 pass = false;
739 }
740 }
741 }
742 return pass;
743 }
744
757 {
758 auto dependenciesResolved = isConstrained_;
759 auto dependenciesResolvedBackend = Dune::Functions::istlVectorBackend(dependenciesResolved);
760 dependenciesResolvedBackend = false;
761 for(auto& [i, L_i] : interpolation_)
762 if (not(dependenciesResolvedBackend[i]))
763 resolveDOF(dependenciesResolved, i, L_i);
764 }
765
766private:
767
768 void resolveDOF(BitVector& dependenciesResolved, const MultiIndex& i, LinearTermRow& L_i)
769 {
770 auto dependenciesResolvedBackend = Dune::Functions::istlVectorBackend(dependenciesResolved);
771 auto c = Dune::Functions::istlVectorBackend(constantTerm_);
772 auto it = L_i.begin();
773 while (it != L_i.end())
774 {
775 auto& [j, L_ij] = *it;
776 // If interpolation weight is itself constraint,
777 // first resolve it recursively (if needed)
778 // and then insert all indirect dependencies directly
779 if (isConstrained(j))
780 {
781 c[i] += L_ij * c[j];
782 auto L_j_it = interpolation_.find(j);
783 if (L_j_it != interpolation_.end())
784 {
785 auto& L_j = L_j_it->second;
786 if (not(dependenciesResolvedBackend[j]))
787 resolveDOF(dependenciesResolved, j, L_j);
788 for (const auto& [k, L_jk] : L_j)
789 L_i[k] += L_ij * L_jk;
790 }
791 else
792 dependenciesResolvedBackend[j] = true;
793 it = L_i.erase(it);
794 }
795 else
796 ++it;
797 }
798 dependenciesResolvedBackend[i] = true;
799 }
800
801 BitVector isConstrained_;
802 LinearTerm interpolation_;
803 LinearTermRow emptyInterpolationRow_;
804 ConstantTerm constantTerm_;
805};
806
807
808
824template<class BitVector=std::monostate, class Vector=std::monostate, class Basis>
825auto makeAffineConstraints(const Basis& basis)
826{
827 using MultiIndex = typename Basis::MultiIndex;
828 using Coefficient = double;
829
830 auto generateBitVector = [&] {
832 {
833#if DUNE_VERSION_GTE(DUNE_FUNCTIONS, 2, 11)
834 return Dune::Functions::makeContainer<char>(basis.rootBasis().preBasis().containerDescriptor(), false);
835#else
837 return std::vector<char>(basis.size(), 0);
838 else
839 return std::monostate{};
840#endif
841 }
842 else
843 {
844 auto v = BitVector();
845 Dune::Functions::istlVectorBackend(v).resize(basis);
847 return v;
848 }
849 };
850 auto generateVector = [&] {
852 {
853#if DUNE_VERSION_GTE(DUNE_FUNCTIONS, 2, 11)
854 return Dune::Functions::makeISTLVector<Coefficient>(basis.rootBasis().preBasis().containerDescriptor());
855#else
857 return Dune::BlockVector<Coefficient>(basis.size(), 0);
858 else
859 return std::monostate{};
860#endif
861 }
862 else
863 {
864 auto v = Vector();
865 Dune::Functions::istlVectorBackend(v).resize(basis);
867 return v;
868 }
869 };
870 using GeneratedBitVector = decltype(generateBitVector());
871 using GeneratedVector = decltype(generateVector());
872 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.");
873 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.");
875}
876
877
878
879} // namespace Dune::Fufem
880
881
882
883#endif // DUNE_FUFEM_CONSTRAINTS_AFFINECONSTRAINTS_HH
Col col
void seed(const Vertex &vertex)
auto istlVectorBackend(Vector &v)
int size() const
void clear()
bool equals(const SLListConstIterator< T, A > &other) const
auto transformedRangeView(R &&range, F &&f)
static constexpr IntegralRange< std::decay_t< T > > range(T &&from, U &&to) noexcept
std::ptrdiff_t index() const
virtual void operator()()=0
void hash_combine(std::size_t &seed, const T &arg)
auto makeAffineConstraints(const Basis &basis)
Create and initialize an AffineConstraints object for a basis.
Definition affineconstraints.hh:825
STL namespace.
Definition dunefunctionsboundaryfunctionalassembler.hh:29
void mmv(const X &x, Y &y) const
Class to handle affine constraints on a subset of DOFs.
Definition affineconstraints.hh:201
void constrainVectorHomogeneously(Vector &b) const
Constrain vector to a constrained basis.
Definition affineconstraints.hh:658
AffineConstraints(const BitVector &isConstrained, const Vector &constantTerm)
Construct from bit vector and vector.
Definition affineconstraints.hh:306
AffineConstraints(const AffineConstraints &)=default
LinearTerm & linearTerm()
Mutable access to linear part of affine map.
Definition affineconstraints.hh:357
void resolveDependencies()
Resolve dependencies of constrained DOFs on other constrained DOFs.
Definition affineconstraints.hh:756
void constrainMatrix(Matrix &A) const
Constrain matrix to a constrained basis.
Definition affineconstraints.hh:597
const auto & constantTerm(const MultiIndexType &i) const
Const access to i-th row of constant part of affine map.
Definition affineconstraints.hh:412
void report() const
Report the constraints to std::cout.
Definition affineconstraints.hh:708
AffineConstraints(BitVector &&isConstrained, Vector &&constantTerm)
Construct from bit vector and vector.
Definition affineconstraints.hh:298
std::map< MI, Coefficient > LinearTermRow
Definition affineconstraints.hh:285
const LinearTermRow & linearTerm(const MultiIndexType &i) const
Const access to i-th row of linear part of affine map.
Definition affineconstraints.hh:374
const LinearTerm & linearTerm() const
Const access to linear part of affine map.
Definition affineconstraints.hh:344
void extendMatrixPattern(MatrixPatternBackend &patternBackend, const Basis &basis) const
Extend matrix pattern for a constrained basis.
Definition affineconstraints.hh:491
void constrainVector(Vector &b) const
Constrain vector to a constrained basis.
Definition affineconstraints.hh:635
V Vector
Definition affineconstraints.hh:281
bool check() const
Check constraints for consistency.
Definition affineconstraints.hh:728
bool isConstrained(const MultiIndexType &i) const
Check if i-th DOF is constrained.
Definition affineconstraints.hh:315
const BitVector & isConstrained() const
Const access to bit-vector marking constrained DOFs.
Definition affineconstraints.hh:323
const ConstantTerm & constantTerm() const
Const access to constant part of affine map.
Definition affineconstraints.hh:389
BitVector & isConstrained()
Mutable access to bit-vector marking constrained DOFs.
Definition affineconstraints.hh:331
void interpolate(Vector &v) const
Interpolate vector according to affine constraints.
Definition affineconstraints.hh:441
void constrainLinearSystem(Matrix &A, Vector &b) const
Constrain linear system affine subspace satisfying the constraints.
Definition affineconstraints.hh:681
void interpolateHomogeneously(Vector &v) const
Interpolate vector according to affine constraints.
Definition affineconstraints.hh:469
std::unordered_map< MultiIndex, LinearTermRow, MultiIndexHash, MultiIndexEquals > LinearTerm
Definition affineconstraints.hh:286
MI MultiIndex
Definition affineconstraints.hh:282
void constrainLinearSystemHomogeneously(Matrix &A, Vector &b) const
Constrain linear system affine subspace satisfying the constraints.
Definition affineconstraints.hh:699
BV BitVector
Definition affineconstraints.hh:280
ConstantTerm & constantTerm()
Mutable access to constant part of affine map.
Definition affineconstraints.hh:400
AffineConstraints(AffineConstraints &&)=default
C Coefficient
Definition affineconstraints.hh:283
Vector ConstantTerm
Definition affineconstraints.hh:287
auto & constantTerm(const MultiIndexType &i)
Mutable access to i-th row of constant part of affine map.
Definition affineconstraints.hh:424
T copy(T... args)
T end(T... args)
T endl(T... args)
T find(T... args)
T forward(T... args)
T is_same_v
T size(T... args)
T swap(T... args)