KASKADE 7 development version
Loading...
Searching...
No Matches
assemblerCore.hh
Go to the documentation of this file.
1/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2/* */
3/* This file is part of the library KASKADE 7 */
4/* https://www.zib.de/research/projects/kaskade7-finite-element-toolbox */
5/* */
6/* Copyright (C) 2002-2025 Zuse Institute Berlin */
7/* */
8/* KASKADE 7 is distributed under the terms of the ZIB Academic License. */
9/* see $KASKADE/academic.txt */
10/* */
11/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
12
13#ifndef ASSEMBLERCORE_HH
14#define ASSEMBLERCORE_HH
15
16#include <iostream>
17#include <numeric>
18#include <memory>
19#include <mutex>
20#include <tuple>
21#include <new>
22#include <type_traits>
23
24#include <boost/version.hpp>
25#include <boost/mpl/accumulate.hpp>
26#include <boost/mpl/bool.hpp>
27#include <boost/mpl/if.hpp>
28#include <boost/timer/timer.hpp>
29
30#include <boost/exception/diagnostic_information.hpp>
31#include <boost/fusion/algorithm.hpp>
32#include <boost/fusion/sequence.hpp>
33#include <boost/fusion/include/find_if.hpp>
34
35#include "dune/common/config.h"
36
37#include "dune/common/fvector.hh"
38#include "dune/common/fmatrix.hh"
39#include "dune/geometry/type.hh"
40#include "dune/grid/common/capabilities.hh"
41#include "dune/istl/bcrsmatrix.hh"
42#include "dune/istl/bdmatrix.hh"
43
44#include "fem/assemblerFlags.hh"
45#include "fem/firstless.hh"
46#include "fem/fixfusion.hh"
47#include "fem/functional_aux.hh"
48#include "fem/gridmanager.hh"
49#include "fem/quadrature.hh"
50#include "fem/variables.hh"
51
55
58#include "utilities/timing.hh"
60
61
62// this is omitted in boost/fusion/sequence/container/vector/detail/vector_n.hpp
63#undef N
64
65namespace Kaskade
66{
67
73
74 static int const timerStatistics = 0x1; // gather global execution time statistics
75 static int const scatterStatistics = 0x2;
76 static int const lockStatistics = 0x4;
77 static int const localTimerStatistics = 0x0; // gather local (per element) time statistics
88 static int const statistics = 0;
89
90 //---------------------------------------------------------------------
91
98 namespace AssemblyDetail {
99
100 using namespace boost::fusion;
101
102
109 class RowGroupManager
110 {
111 public:
115 RowGroupManager();
116
120 virtual ~RowGroupManager();
121
122 // Noncopyable since we contain mutexes.
123 RowGroupManager(RowGroupManager const& m) = delete;
124 RowGroupManager& operator=(RowGroupManager const& m) = delete;
125
131 void init(int nrg, size_t rows);
132
139 void init(int nrg, std::vector<std::vector<size_t>> const& colIndex);
140
148 void init(int nrg, std::vector<size_t> const& weight);
149
154 std::pair<size_t,size_t> range(int n) const;
155
159 std::mutex& mutex(int n) const;
160
164 int size() const;
165
166
167 private:
168 // start indices of row groups
169 std::vector<size_t> rowGroupStart;
170
171 // Support for simultaneous write access to different row groups.
172 std::mutex* mutexes;
173
174 // deletes the mutexes
175 void clear();
176 };
177
178 // ---------------------------------------------------------------------------------------------
179
190 template <class Policy, class RowVar, class ColVar, class SparseIdx>
191 struct MatrixBlock
192 {
193 static int const rowId = RowVar::id;
194 static int const colId = ColVar::id;
195 static int const rowSpaceIndex = RowVar::spaceIndex;
196 static int const colSpaceIndex = ColVar::spaceIndex;
197 static bool const symmetric = Policy::template BlockInfo<rowId,colId>::symmetric;
198 static bool const mirror = Policy::template BlockInfo<rowId,colId>::mirror;
199 static bool const lumped = Policy::template BlockInfo<rowId,colId>::lumped;
200 static bool const makePositive = Policy::template BlockInfo<rowId,colId>::makePositive;
201
202 using SparseIndex = SparseIdx;
203 typedef MatrixBlock<Policy,RowVar,ColVar,SparseIndex> Self;
204 typedef typename Policy::Scalar Scalar;
205 typedef typename Policy::Spaces Spaces;
206 typedef Dune::FieldMatrix<Scalar,RowVar::m,ColVar::m> BlockType;
207 typedef NumaBCRSMatrix<BlockType, SparseIndex> Matrix;
208
209 typedef typename SpaceType<Spaces,RowVar::spaceIndex>::type RowSpace;
210 typedef typename SpaceType<Spaces,ColVar::spaceIndex>::type ColSpace;
211 typedef typename RowSpace::GridView GridView;
212 typedef typename GridView::template Codim<0>::Iterator CellIterator;
213
214 static int const dim = ColSpace::dim;
215
219 MatrixBlock() {}
220
224 MatrixBlock(MatrixBlock const& mb)
225 : matrix(new Matrix(*mb.matrix))
226 , isDense(mb.isDense)
227 {}
228
232 MatrixBlock& operator=(MatrixBlock const& mb)
233 {
234 *matrix = *mb.matrix;
235 isDense = mb.isDense;
236 }
237
238 MatrixBlock& operator=(Scalar x)
239 {
240 assert(matrix.get());
241 *matrix = x;
242 return *this;
243 }
244
245 MatrixBlock& operator+=(MatrixBlock const& mb)
246 {
247 *matrix += *mb.matrix;
248 return *this;
249 }
250
251
255 Matrix& globalMatrix()
256 {
257 assert(matrix.get());
258 return *matrix;
259 }
260
264 Matrix const& globalMatrix() const
265 {
266 assert(matrix.get());
267 return *matrix;
268 }
269
277 std::shared_ptr<Matrix const> globalMatrixPointer() const { return matrix; }
278
279
293 template <class FaceOracle>
294 void init(Spaces const& spaces, CellIterator first, CellIterator last,
295 FaceOracle const& considerFace)
296 {
297 RowSpace const& rowSpace = *at_c<RowVar::spaceIndex>(spaces);
298 ColSpace const& colSpace = *at_c<ColVar::spaceIndex>(spaces);
299
300 // @todo: respect *dynamic* presence flag. static one is respected
301 // on construction of sequence of matrix blocks. Respect symmetric flag.
302 if (!Policy::template BlockInfo<rowId,colId>::present)
303 {
304 assert("Aieee! Nonpresent matrix block detected!\n"==0);
305 abort();
306 }
307
308 boost::timer::cpu_timer timer;
309
310 size_t nnz = 0;
311 size_t const rows = rowSpace.degreesOfFreedom();
312 size_t const cols = colSpace.degreesOfFreedom();
313
314 // If mass lumping is desired, we just create a diagonal matrix.
315 // Otherwise, we need to establish the connectivity pattern of
316 // the ansatz functions.
317 if (Policy::template BlockInfo<rowId,colId>::lumped)
318 {
319 assert(RowVar::spaceIndex==ColVar::spaceIndex && rows==cols);
320 NumaCRSPatternCreator<SparseIndex> creator(rows,cols,false,1);
321 for (size_t i=0; i<rows; ++i)
322 creator.addElements(&i,&i+1,&i,&i+1);
323 matrix.reset(new Matrix(creator)); // zero init
324 nnz = rows;
325 }
326 else
327 {
328 // In case of global support of shape functions of one of the involved spaces (usually ConstantSpace),
329 // the resulting matrix is dense. Use a shortcut to define the sparsity structure.
330 bool const dense = RowSpace::Mapper::globalSupport || ColSpace::Mapper::globalSupport;
331
332
333 // Allocate sparse matrix and define the sparsity structure. Estimate the required number of entries per row
334 // for efficient preallocation.
335 // Ws 2016-01-22: Zero nnz init is actually faster on P1 pattern, probably because otherwise the
336 // chunks put a high burden on the memory system by requesting memory at the same time. This appears
337 // to parallelize quite badly and hence is turned off in parallel mode.
338 int preallocateEntriesPerRow = 0;
339 if (NumaThreadPool::instance().nodes()==1) // sequential allocation in NumaCRSPatternCreator
340 {
341 int localSize = colSpace.mapper().globalIndices(0).size(); // # entries per row of local matrices (rough estimate)
342 preallocateEntriesPerRow = dense? 0: // for dense matrices we'll use a specialized version
343 dim<=1? 2*localSize: // make room for (roughly) all elemental matrices appended...
344 dim==2? 8*localSize: // ... this is overprovisioning, but faster due to less ...
345 20*localSize; // ... allocations and data movement.
346 }
347 NumaCRSPatternCreator<SparseIndex> creator(rows,cols,symmetric,preallocateEntriesPerRow);
348
349 if (statistics & timerStatistics)
350 std::cout << "initial creator construction (" << rowId << "," << colId << "): " << timer.format();
351 timer.start();
352
353
354 if (dense)
355 {
356 creator.addAllElements();
357 if (statistics & timerStatistics)
358 std::cout << "entering indices for (" << rowId << "," << colId << "): " << timer.format();
359 timer.start();
360 }
361 else
362 {
363 // Traditional local FE spaces - that's the hard case. We have to traverse the grid and pick up all
364 // interactions of (local) basis functions.
365
366 // Iteration over all cells. Note that the grid views of row and
367 // column spaces need not cover the whole grid (e.g., in case of
368 // local support). It would be best to iterate over the grid
369 // view with smaller support, but for simplicity we choose the
370 // row space.
371
372 size_t const nCells = rowSpace.indexSet().size(0);
373
374 std::vector<typename RowSpace::Mapper::GlobalIndexRange> rIndices(nCells,rowSpace.mapper().initGlobalIndexRange());
375 std::vector<typename ColSpace::Mapper::GlobalIndexRange> cIndices(nCells,colSpace.mapper().initGlobalIndexRange());
376 for (size_t i=0; i<nCells; ++i)
377 {
378 rIndices[i] = rowSpace.mapper().globalIndices(i);
379 cIndices[i] = colSpace.mapper().globalIndices(i);
380 }
381 if (statistics & timerStatistics)
382 std::cout << "gathering indices for (" << rowId << "," << colId << "): " << timer.format();
383 timer.start();
384
385 creator.addElements(rIndices,cIndices); // enter all entries in one go
386
387 // We may need to assemble integrals on inner faces - then there are matrix entries coupling degrees of freedom
388 // associated to different cells (e.g., in discontinuous Galerkin methods). Add these entries, too.
389 if(Policy::considerInnerFaces)
390 {
391 GridView const& gridView = rowSpace.gridView();
392
393 for (CellIterator ci=first; ci!=last; ++ci)
394 {
395 auto const& rIndices = rowSpace.mapper().globalIndices(*ci);
396
397 for (auto const& face: intersections(gridView,*ci))
398 {
399 // only true inner faces are considered, and only if requested
400 if (face.boundary() || !face.neighbor() || considerFace(face)==false)
401 continue;
402
403 auto const& cIndices = colSpace.mapper().globalIndices(face.outside());
404
405 creator.addElements(begin(rIndices),end(rIndices),
406 begin(cIndices),end(cIndices));
407 } // end iteration over faces
408 } // end iteration over cells
409 } // end Policy::considerInnerFaces
410
411 if (statistics & timerStatistics)
412 std::cout << "entering indices for (" << rowId << "," << colId << "): " << timer.format();
413 timer.start();
414
415 creator.balance();
416 if (statistics & timerStatistics)
417 std::cout << "balancing for (" << rowId << "," << colId << "): " << timer.format();
418 timer.start();
419 }
420
421 // Now that the pattern creator is filled, create pattern and matrix.
422
423 // Count number of nonzeros.
424 nnz = creator.nonzeroes();
425
426 // Create the matrix (zero initialization of entries).
427 matrix.reset(new Matrix(creator));
428 if (statistics & timerStatistics)
429 std::cout << "creating pattern & matrix for (" << rowId << "," << colId << "): " << timer.format();
430 timer.start();
431 }
432
433 // Check density (nonzeros>(rows*cols/2). Be careful not to get
434 // overflow for very large and sparse matrices!
435 if (std::min(rows,cols)>0)
436 isDense = nnz/std::min(rows,cols) > std::max(rows,cols)/2;
437 else
438 isDense = false; // well, empty matrices are as sparse as one can hope for ;)
439
440 if (statistics & timerStatistics)
441 std::cout << "init cleanup for (" << rowId << "," << colId << "): " << timer.format() << "\n";
442 }
443
444 // Returns true if the matrix is essentially dense (more than 50% nonzeros).
445 bool dense() const { return isDense; }
446
447 private:
448 std::shared_ptr<Matrix> matrix;
449 bool isDense;
450 };
451
452 // ---------------------------------------------------------------------------------------
453
454 // a boost::fusion predicate telling us whether the current matrix block has the given row and column indices.
455 template <int row, int col>
456 struct IsBlock
457 {
458 template <class MatrixBlock>
459 struct apply
460 {
461 using type = std::conditional_t<MatrixBlock::rowId==row && MatrixBlock::colId==col,boost::mpl::true_,boost::mpl::false_>;
462 };
463 };
464
465 // ---------------------------------------------------------------------------------------------
466
483 template <class Filter, class AnsatzVariables, class TestVariables, class SparseIndex>
484 class BlockArray
485 {
486 // Retain only those blocks which are present in the functional
487 struct PresenceFilter
488 {
489 template <class Block>
490 struct apply
491 {
492 typedef boost::mpl::bool_<Filter::template BlockInfo<Block::rowId,Block::colId>::present> type;
493 };
494 };
495
496
497 public:
502 using type = decltype(as_vector(filter_if<PresenceFilter>(
503 transform(AllSystemBlocks<AnsatzVariables,TestVariables>(),
504 [](auto varPair)
505 {
506 using RowVar = typename decltype(varPair)::first_type;
507 using ColVar = typename decltype(varPair)::second_type;
508 return MatrixBlock<Filter,RowVar,ColVar,SparseIndex>();
509 })
510 )));
511 };
512
513
514
515 //----------------------------------------------------------------------------------------------
516
520 template <class Policy, class RV>
521 struct RhsBlock
522 {
523 typedef typename std::remove_reference<RV>::type RowVar;
524
525 static int const rowId = RowVar::id;
526
527 typedef typename Policy::Scalar Scalar;
528 typedef typename Policy::Spaces Spaces;
529 typedef Dune::FieldVector<Scalar,RowVar::m> BlockType;
530 typedef typename Dune::BlockVector<BlockType> Rhs;
531 typedef RhsBlock<Policy,RowVar> Self;
532
533 static int const rowSpaceIndex = RowVar::spaceIndex;
534 typedef typename SpaceType<Spaces,RowVar::spaceIndex>::type RowSpace;
535 typedef typename RowSpace::GridView::template Codim<0>::Iterator CellIterator;
536
537
541 void init(Spaces const& spaces, CellIterator /*first*/, CellIterator /*last*/, int nrg)
542 {
543 RowSpace const& rowSpace = *at_c<RowVar::spaceIndex>(spaces);
544 size_t rows = rowSpace.degreesOfFreedom();
545
546 rowGroupManager = std::make_unique<RowGroupManager>();
547 rowGroupManager->init(nrg,rows);
548 }
549
550 RowGroupManager& rowGroup() { return *rowGroupManager; }
551
552 // A class storing a local element rhs vector and the corresponding indices
553 struct LocalVector
554 {
555 // just because the ranges are not default constructible
556 LocalVector(): ridx(RowSpace::Mapper::initSortedIndexRange()) {}
557
558 std::vector<BlockType> vector;
559
560 typedef typename RowSpace::Mapper::SortedIndexRange SortedRowIdx;
561
565 SortedRowIdx ridx;
566 };
567
568 // A structure for holding a sequence of several local vectors to be filled sequentially
569 // and to be scattered together.
570 struct LocalVectors
571 {
572 typedef Self RhsBlock;
573 typedef std::vector<BlockType> LocalVectorType;
574
575 LocalVectors(int n, RhsBlock& rb_): localVectors(n), currentLocalVector(0), rb(&rb_) {}
576
577 // Storage for local rhs vectors.
578 std::vector<LocalVector> localVectors;
579
580 // Which is the current local vector to be filled?
581 int currentLocalVector;
582
583 // Pointing back to its rhs block
584 RhsBlock* rb;
585 };
586
587 private:
588 std::unique_ptr<RowGroupManager> rowGroupManager;
589 };
590
594 template <class Policy, class TestVariables>
595 class RhsBlockArray
596 {
597 // Define a mapping from Variable to RhsBlock for Variable as row variable.
598 // Retain only those blocks which are present in the functional
599 struct PresenceFilter
600 {
601 template <class Block>
602 struct apply
603 {
604 typedef boost::mpl::bool_<Policy::template RhsBlockInfo<Block::rowId>::present> type;
605 };
606 };
607
608 static auto create(TestVariables t)
609 {
610 auto allBlocks = transform(t,[](auto tvar) { return RhsBlock<Policy,decltype(tvar)>(); });
611 return as_vector(filter_if<PresenceFilter>(allBlocks));
612 }
613
614 public:
615 typedef decltype(create(TestVariables())) type;
616 };
617
618 //---------------------------------------------------------------------
619
624 template <class Policy>
625 struct RhsLocalData
626 {
630 RhsLocalData(int n_): n(n_) {}
631
632 template <class RhsBlock>
633 typename RhsBlock::LocalVectors operator()(RhsBlock const& rb) const
634 {
635 // due to boost::fusion::transform providing only const references to the source sequence elements, we
636 // need a const cast here :(
637 return typename RhsBlock::LocalVectors(n,removeConst(rb));
638 }
639
640 private:
641 int n;
642 };
643
647 struct MatrixLocalData
648 {
653 MatrixLocalData(size_t s_)
654 : s(s_) {}
655
656 template <class MatrixBlock>
657 auto operator()(MatrixBlock const& mb) const
658 {
659 // When switching from boost 1.51 to 1.57, it seems that the filter_view only presents
660 // const references to the elements (why?), but we need non-const ones. Taking non-const
661 // reference as arguments leads to failure of overload resolution, omitting this
662 // functor, and ultimately to error. As a workaround, we take const reference and cast
663 // it away.
664 return LocalMatrices<typename MatrixBlock::BlockType,
665 MatrixBlock::lumped,
666 typename MatrixBlock::RowSpace::Mapper::SortedIndexRange,
667 typename MatrixBlock::ColSpace::Mapper::SortedIndexRange,
668 MatrixBlock>(removeConst(mb).globalMatrix(),s);
669 }
670
671 private:
672 size_t s;
673 };
674
675 //---------------------------------------------------------------------
676
677 template <class Spaces>
678 class GetMaxDerivativeBase
679 {
680 public:
681 GetMaxDerivativeBase(Spaces const& spaces_, std::map<void const*,int>& deriv_): spaces(spaces_), deriv(deriv_) {}
682
683 private:
684 Spaces const& spaces;
685 std::map<void const*,int>& deriv;
686
687 protected:
688 template <int spaceIndex>
689 void enterSpace(int d) const
690 {
691 void const* space = at_c<spaceIndex>(spaces);
692 auto i = deriv.find(space);
693 if (i==deriv.end())
694 deriv[space] = d;
695 else
696 i->second = std::max(i->second,d);
697 }
698 };
699
700 template <class Functional, class Spaces>
701 class GetMaxDerivativeMatrix: public GetMaxDerivativeBase<Spaces>
702 {
703 public:
704 GetMaxDerivativeMatrix(Spaces const& spaces, std::map<void const*,int>& deriv): GetMaxDerivativeBase<Spaces>(spaces,deriv) {}
705
706 template <class LocalMatrices>
707 void operator()(LocalMatrices const&) const
708 {
709 using MatrixBlock = typename LocalMatrices::InfoType;
710 int d = Functional::template D2<MatrixBlock::rowId,MatrixBlock::colId>::derivatives;
711 this->template enterSpace<MatrixBlock::rowSpaceIndex>(d);
712 this->template enterSpace<MatrixBlock::colSpaceIndex>(d);
713 }
714 };
715
716
717 template <class Functional, class Spaces>
718 class GetMaxDerivativeRhs: public GetMaxDerivativeBase<Spaces>
719 {
720 public:
721 GetMaxDerivativeRhs(Spaces const& spaces, std::map<void const*,int>& deriv): GetMaxDerivativeBase<Spaces>(spaces,deriv) {}
722
723 template <class RBlock>
724 void operator()(RBlock const&) const
725 {
726 int d = Functional::template D1<RBlock::RhsBlock::rowId>::derivatives;
727 this->template enterSpace<RBlock::RhsBlock::rowSpaceIndex>(d);
728 }
729 };
730
731
737 template <class Functional, class Spaces, class LocalMatrices, class RBlocks>
738 std::map<void const*,int> derivatives(Functional const& f, Spaces const& spaces, LocalMatrices const& localMatrices, RBlocks const& rblocks)
739 {
740 std::map<void const*,int> deriv; // map from space address to maximum occuring derivative
741
742 for_each(localMatrices,GetMaxDerivativeMatrix<Functional,Spaces>(spaces,deriv));
743 for_each(rblocks,GetMaxDerivativeRhs<Functional,Spaces>(spaces,deriv));
744 return deriv;
745 }
746
747 //---------------------------------------------------------------------
748
752 template <class Evaluators, class TestVariables>
753 struct ClearLocalRhs
754 {
755 ClearLocalRhs(Evaluators const& evaluators_):
756 eval(evaluators_)
757 {}
758
759 template <class LocalVectors>
760 void operator()(LocalVectors& lv) const
761 {
762 // Deletes all entries from vector and inserts the needed number
763 // of zero elements. Thus, finite elements with different number
764 // of shape functions are supported. Memory reallocations do
765 // rarely occur.
766 assert(lv.currentLocalVector < lv.localVectors.size());
767
768 int const rSpaceIdx = result_of::value_at_c<TestVariables,LocalVectors::RhsBlock::rowId>::type::spaceIndex;
769
770 auto& reval = at_c<rSpaceIdx>(eval); // evaluator
771
772 auto& rc = lv.localVectors[lv.currentLocalVector]; // current local vector structure
773
774 // set current local vector to correct size and value 0
775 rc.vector.resize(reval.size());
776 std::fill(rc.vector.begin(),rc.vector.end(),0);
777
778 // retrieve sorted indices
779 rc.ridx = reval.sortedIndices();
780 }
781
782 Evaluators const& eval;
783 };
784
788 template <class Evaluators, class AnsatzVariableSetDescription, class TestVariableSetDescription>
789 struct NewLocalMatrix
790 {
791 NewLocalMatrix(Evaluators const& reval_, Evaluators const& ceval_)
792 : reval(reval_), ceval(ceval_)
793 {}
794
795 template <class LocalMatrices>
796 void operator()(LocalMatrices& lm) const
797 {
798 using MatrixBlock = typename LocalMatrices::InfoType;
799
800 int const cSpaceIdx = spaceIndex<AnsatzVariableSetDescription,MatrixBlock::colId>;
801 int const rSpaceIdx = spaceIndex<TestVariableSetDescription ,MatrixBlock::rowId>;
802
803 lm.push_back(at_c<rSpaceIdx>(reval).sortedIndices(),at_c<cSpaceIdx>(ceval).sortedIndices());
804 }
805
806 Evaluators const& reval;
807 Evaluators const& ceval;
808 };
809
810
811 //---------------------------------------------------------------------
812
816 template <class TestVariableSetDescription, class Evaluators, class Real, class Cache>
817 struct UpdateLocalRhs
818 {
819 UpdateLocalRhs(Evaluators const& evaluators_, Real integrationFactor_, Cache const& cache_)
820 : evaluators(evaluators_)
821 , integrationFactor(integrationFactor_)
822 , cache(cache_)
823 {}
824
825 template <class LocalVectors>
826 void operator()(LocalVectors& lv) const
827 {
828 typedef typename LocalVectors::RhsBlock RhsBlock;
829
830 int const rSpaceIndex = spaceIndex<TestVariableSetDescription,RhsBlock::rowId>;
831 auto& rc = lv.localVectors[lv.currentLocalVector].vector; // current local vector
832 size_t const rows = rc.size();
833 for (size_t i=0; i<rows; ++i)
834 {
835 // Check that the cache returns the correct type. The typedefs just serve for
836 // creating readable error messages.
837 using rhsEntryType = std::remove_reference_t<decltype(rc[i])>;
838 using d1ResultType = decltype(cache.template d1<RhsBlock::rowId>(at_c<rSpaceIndex>(evaluators).evalData[i]));
839 static_assert(std::is_same<rhsEntryType,d1ResultType>::value,
840 "Types don't match. Check return type of d1() in problem formulation.");
841
842 rc[i] += integrationFactor * cache.template d1<RhsBlock::rowId>(at_c<rSpaceIndex>(evaluators).evalData[i]);
843 assert(!std::isnan(rc[i][0])); // check that first entry in rhs vector is valid
844 }
845 }
846
847 Evaluators const& evaluators;
848 Real integrationFactor;
849 Cache const& cache;
850 };
851
852
856 template <class AnsatzVariableSetDescription, class TestVariableSetDescription, class Evaluators, class Real, class Cache>
857 struct UpdateLocalMatrix
858 {
859 UpdateLocalMatrix(Evaluators const& evaluators_, Real integrationFactor_, Cache const& cache_):
860 evaluators(evaluators_), integrationFactor(integrationFactor_), cache(cache_)
861 {}
862
863 template <class LocalMatrices>
864 void operator()(LocalMatrices& lm) const
865 {
866 using MatrixBlock = typename LocalMatrices::InfoType;
867
868 // Get the evaluated ansatz (column) and test (row) functions
869 int const rowId = MatrixBlock::rowId;
870 int const colId = MatrixBlock::colId;
871 int const rSpaceIndex = spaceIndex<TestVariableSetDescription,rowId>;
872 int const cSpaceIndex = spaceIndex<AnsatzVariableSetDescription,colId>;
873 auto const& rEval = at_c<rSpaceIndex>(evaluators).evalData;
874 auto const& cEval = at_c<cSpaceIndex>(evaluators).evalData;
875
876 auto& A = lm.back(); // the current local Galerkin matrix to contribute to
877
878 // retrieve number of localized ansatz functions
879 int const rows = A.ridx().size();
880 int const cols = A.cidx().size();
881
882 // loop over all combinations of localized ansatz and test functions
883 for (int i=0; i<rows; ++i)
884 {
885 int begin = MatrixBlock::lumped? i: 0;
886 int end = (MatrixBlock::symmetric||MatrixBlock::lumped)? i+1: cols;
887 for (int j=begin; j<end; ++j)
888 {
889 static_assert(std::is_same<std::remove_reference_t<decltype(A(i,j))>,
890 decltype(cache.template d2<rowId,colId>(rEval[i],cEval[j]))
891 >::value, "Types don't match. Check return type of d2() in problem formulation.");
892 A(i,j) += integrationFactor * cache.template d2<rowId,colId>(rEval[i], cEval[j]);
893 assert(!std::isnan(A(i,j)[0][0])); // check that top left entry is valid
894 }
895 }
896 }
897
898 Evaluators const& evaluators;
899 Real integrationFactor;
900 Cache const& cache;
901 };
902
903
904 // TODO: factorize common functionality out of both UpdateLocalMatrix classes
905 template <class AnsatzVariableSetDescription, class TestVariableSetDescription,
906 class Evaluators, class Real, class Cache>
907 struct UpdateLocalMatrixFromInnerBoundaryCache
908 {
909 UpdateLocalMatrixFromInnerBoundaryCache(Evaluators const& insideEval, Evaluators const& outsideEval,
910 Real integrationFactor_, Cache const& cache_, bool sameCell_)
911 : insideEvaluator(insideEval)
912 , outsideEvaluator(outsideEval)
913 , integrationFactor(integrationFactor_)
914 , cache(cache_)
915 , sameCell(sameCell_)
916 {}
917
918 template <class LocalMatrices>
919 void operator()(LocalMatrices& lm) const
920 {
921 using MatrixBlock = typename LocalMatrices::InfoType;
922
923 int const rSpaceIndex = spaceIndex<TestVariableSetDescription, MatrixBlock::rowId>;
924 int const cSpaceIndex = spaceIndex<AnsatzVariableSetDescription,MatrixBlock::colId>;
925
926 auto& A = lm.localMatrices.back();
927
928 // retrieve number of localized ansatz functions
929 int const rows = A.ridx().size();
930 int const cols = A.cidx().size();
931
932 // loop over all combinations of localized ansatz and test functions
933 for (int i=0; i<rows; ++i)
934 {
935 int begin = MatrixBlock::lumped? i: 0;
936 int end = ((MatrixBlock::symmetric && sameCell)||MatrixBlock::lumped)? i+1: cols;
937 for (int j=begin; j<end; ++j)
938 {
939 auto tmp = cache.template d2<MatrixBlock::rowId,MatrixBlock::colId>(
940 at_c<rSpaceIndex>(insideEvaluator).evalData[i],
941 at_c<cSpaceIndex>(outsideEvaluator).evalData[j], sameCell);
942 A(i,j) += integrationFactor * tmp;
943 assert(!std::isnan(A(i,j)[0][0])); // check that top left entry is valid
944 }
945 }
946 }
947
948 Evaluators const& insideEvaluator, outsideEvaluator;
949 Real integrationFactor;
950 Cache const& cache;
951 bool sameCell;
952 };
953 //---------------------------------------------------------------------
954
958 template <class GlobalRhs>
959 struct ScatterLocalRhs
960 {
967 ScatterLocalRhs(GlobalRhs& globalRhs_, bool immediate_ = false):
968 globalRhs(globalRhs_), immediate(immediate_)
969 {}
970
971 template <class LocalVectors>
972 void operator()(LocalVectors& lv) const
973 {
974 auto& gRhs = at_c<LocalVectors::RhsBlock::rowId>(globalRhs.data);
975
976 auto const& rowGroups = lv.rb->rowGroup();
977
978 if (immediate || lv.currentLocalVector+1==lv.localVectors.size())
979 {
980 int const last = immediate ? lv.currentLocalVector : lv.localVectors.size();
981
982 // step through all row groups and scatter all the included local vectors' components
983 for (int rg=0; rg<rowGroups.size(); ++rg)
984 {
985 // obtain and lock the row group
986 auto const [firstRow,lastRow] = rowGroups.range(rg);
987 std::lock_guard lock(rowGroups.mutex(rg));
988
989 // step through all local vectors
990 for (int i=0; i<last; ++i)
991 {
992 auto& ri = lv.localVectors[i].vector;
993 auto& ridx = lv.localVectors[i].ridx;
994
995 assert(ri.size()==ridx.size());
996
997 for (int r=0; r<ridx.size(); ++r)
998 {
999 size_t const rGlobalIndex = ridx[r].first;
1000
1001 // check if this row is inside the row group
1002 if (firstRow<=rGlobalIndex && rGlobalIndex<lastRow)
1003 {
1004 size_t const rLocalIndex = ridx[r].second;
1005 gRhs[rGlobalIndex] += ri[rLocalIndex];
1006 }
1007 }
1008 }
1009 }
1010
1011 // everything's scattered - clean up
1012 lv.currentLocalVector = 0;
1013 }
1014 else
1015 ++lv.currentLocalVector; // prepare next local vector for updating
1016 }
1017
1018 GlobalRhs& globalRhs;
1019 bool immediate;
1020 };
1021
1022 //---------------------------------------------------------------------------------------------
1023
1034 template <class Functional>
1035 struct SymmetrizeLocalMatrix
1036 {
1037 template <class LocalMatrices>
1038 void operator()(LocalMatrices& lm) const
1039 {
1040 using MatrixBlock = typename LocalMatrices::InfoType;
1041
1042 // If a block is symmetric, only the lower part of the local
1043 // matrix is filled. Mirror to the upper part for easy access in
1044 // the following loop. For lumped (purely diagonal) blocks, of course,
1045 // nothing is to do.
1046 if constexpr (!MatrixBlock::lumped && MatrixBlock::symmetric)
1047 {
1048 auto& m = lm.back();
1049 int const N = m.ridx().size();
1050 assert(N==m.cidx().size());
1051
1052 // copy lower triangular part to upper triangular part, transposing
1053 // each entry
1054 for (int i=0; i<N-1; ++i)
1055 for (int j=i+1; j<N; ++j)
1056 m(i,j) = transpose(m(j,i));
1057
1058 // If we are asked to enforce positive semidefiniteness of these local
1059 // matrices (symmetric and non-lumped), we do this using an eigendecomposition,
1060 // and shifting all nonpositive eigenvalues to zero.
1061 if constexpr (MatrixBlock::makePositive)
1062 {
1063 assert(f);
1064 constexpr int row = MatrixBlock::rowId;
1065 constexpr int col = MatrixBlock::colId;
1066
1067 // First we need to extract the local matrix.
1068 using Entry = typename MatrixBlock::BlockType;
1069 DynamicMatrix<Entry> Aloc(N,N);
1070 for (int j=0; j<N; ++j)
1071 for (int i=0; i<N; ++i)
1072 Aloc[i][j] = m(i,j);
1073
1074 // now we set all negative eigenvalues to zero in the eigendecomposition
1075 bool changed = makePositiveSemiDefinite(Aloc,f->template makePositiveThreshold<row,col>());
1076
1077 // now we copy the matrix back
1078 if (changed)
1079 for (int j=0; j<N; ++j)
1080 for (int i=0; i<N; ++i)
1081 m(i,j) = Aloc[i][j];
1082 }
1083 }
1084 }
1085
1086 Functional const* f;
1087 };
1088
1089 //---------------------------------------------------------------------
1090 //---------------------------------------------------------------------
1091
1098 template <class Functional>
1099 struct VariationalFunctionalPolicy
1100 {
1101 typedef typename Functional::Scalar Scalar;
1102 typedef typename Functional::AnsatzVars::Spaces Spaces;
1103 static constexpr bool considerInnerFaces = hasInnerBoundaryCache<Functional>;
1104
1105 template <int rowId>
1106 struct RhsBlockInfo
1107 {
1108 static bool const present = Functional::template D1<rowId>::present;
1109 };
1110
1111
1112 template <int rowId, int colId>
1113 struct BlockInfo
1114 {
1115 static bool const present = Functional::template D2<rowId,colId>::present && rowId>=colId;
1116 static bool const symmetric = (rowId==colId || Functional::template D2<rowId,colId>::symmetric)
1117 && (result_of::value_at_c<typename Functional::TestVars::Variables,rowId>::type::spaceIndex
1118 == result_of::value_at_c<typename Functional::AnsatzVars::Variables,colId>::type::spaceIndex);
1119 static bool const mirror = rowId>colId;
1120 static bool const lumped = symmetric && Functional::template D2<rowId,colId>::lumped;
1121 static bool const makePositive = symmetric && Functional::template D2<rowId,colId>::makePositive;
1122 };
1123
1129 Scalar functional() const { return fValue; }
1130
1131 static constexpr bool hasValue = true;
1132
1133 Scalar fValue;
1134 };
1135
1143 template <class Functional>
1144 struct WeakFormulationPolicy
1145 {
1146 typedef typename Functional::Scalar Scalar;
1147 typedef typename Functional::AnsatzVars::Spaces Spaces;
1148 static constexpr bool considerInnerFaces = hasInnerBoundaryCache<Functional>;
1149
1150 template <int rowId>
1151 struct RhsBlockInfo
1152 {
1153 static bool const present = Functional::template D1<rowId>::present;
1154 };
1155
1156
1157 template <int rowId, int colId>
1158 struct BlockInfo
1159 {
1160 static bool const present = Functional::template D2<rowId,colId>::present;
1161 static bool const symmetric = Functional::template D2<rowId,colId>::symmetric
1162 && (result_of::value_at_c<typename Functional::TestVars::Variables,rowId>::type::spaceIndex
1163 == result_of::value_at_c<typename Functional::AnsatzVars::Variables,colId>::type::spaceIndex);
1164 static bool const mirror = false;
1165 static bool const lumped = symmetric && Functional::template D2<rowId,colId>::lumped;
1166 static bool const makePositive = symmetric && Functional::template D2<rowId,colId>::makePositive;
1167 };
1168
1169 public:
1170 static constexpr bool hasValue = false;
1171
1172 Scalar fValue = 0;
1173 };
1174
1175
1181 template <class Problem>
1182 using FormulationPolicy = std::conditional_t<Problem::type==VariationalFunctional,
1183 VariationalFunctionalPolicy<Problem>,
1184 WeakFormulationPolicy<Problem>>;
1185
1186 // ---------------------------------------------------------------------------------------------
1187
1192 struct TakeAllBlocks
1193 {
1194 template <int row> struct D1 { static bool const assemble = true; };
1195 template <int row, int col> struct D2 { static bool const assemble = true; };
1196 };
1197
1198 // Mapping a block filter to an MPL predicate
1199 template <class BlockFilter>
1200 struct MatrixBlockFilter
1201 {
1202 template <class X>
1203 struct apply
1204 {
1205 typedef boost::mpl::bool_<BlockFilter::template D2<X::rowId,X::colId>::assemble> type;
1206 };
1207 };
1208
1209 template <class> struct Fill;
1210
1211 template <class Block, bool, bool>
1212 struct CopyBlock
1213 {
1214 template <class OtherBlock>
1215 static void apply(OtherBlock const&, std::unique_ptr<Block>&, bool, size_t){}
1216 };
1217
1218 template <class Scalar, int n, class Allocator, bool symmetric>
1219 struct CopyBlock<Dune::BCRSMatrix<Dune::FieldMatrix<Scalar,n,n>,Allocator>,symmetric,true>
1220 {
1221 typedef Dune::BCRSMatrix<Dune::FieldMatrix<Scalar,n,n>,Allocator> Block;
1222
1223 static void apply(Block const& from, std::unique_ptr<Block>& to, bool extractOnlyLowerTriangle, size_t nnz)
1224 {
1225 // copy
1226 if(extractOnlyLowerTriangle == symmetric)
1227 {
1228 to.reset(new Block(from));
1229 return;
1230 }
1231
1232 // only lower triangle is stored in the assembler -> restore full matrix
1233 if( (!extractOnlyLowerTriangle && symmetric) || (extractOnlyLowerTriangle && !symmetric) )
1234 {
1235 std::vector<std::vector<size_t> > bcrsPattern(from.N());
1236 for(size_t i=0; i<from.N(); ++i)
1237 for(size_t j=0; j<=i; ++j)
1238 {
1239 if(from.exists(i,j))
1240 {
1241 bcrsPattern[i].push_back(j);
1242 if(i!=j && !extractOnlyLowerTriangle)
1243 bcrsPattern[j].push_back(i);
1244 }
1245 }
1246 for(std::vector<size_t>& row : bcrsPattern) std::sort(row.begin(),row.end());
1247
1248 to.reset(new Block(from.N(),from.M(),nnz,Block::row_wise));
1249
1250 // init sparsity pattern
1251 for (typename Block::CreateIterator row=to->createbegin(); row!=to->createend(); ++row)
1252 for(size_t col : bcrsPattern[row.index()]) row.insert(col);
1253
1254 // read data
1255 for(size_t row=0; row<bcrsPattern.size(); ++row)
1256 for(size_t col : bcrsPattern[row])
1257 {
1258 if(row >= col) (*to)[row][col] = from[row][col];
1259 else if(!extractOnlyLowerTriangle)
1260 {
1261 for(size_t i=0; i<n; ++i)
1262 for(size_t j=0; j<n; ++j)
1263 (*to)[row][col][i][j] = from[col][row][j][i];
1264 }
1265 }
1266 }
1267 }
1268 };
1269
1270
1271 template <class BCRSMat, int rowIndex, int columnIndex>
1272 struct BlockToBCRS
1273 {
1274 explicit BlockToBCRS(std::unique_ptr<BCRSMat>& m, bool extractOnlyLowerTriangle_, size_t nnz_)
1275 : mat(m), extractOnlyLowerTriangle(extractOnlyLowerTriangle_), nnz(nnz_)
1276 {}
1277
1278 template <class MatrixBlock>
1279 void operator()(MatrixBlock const& mb) const
1280 {
1281 CopyBlock<BCRSMat,MatrixBlock::symmetric,MatrixBlock::rowId==rowIndex && MatrixBlock::colId==columnIndex>::apply(mb.globalMatrix(),mat,extractOnlyLowerTriangle,nnz);
1282 }
1283
1284 private:
1285 std::unique_ptr<BCRSMat>& mat;
1286 bool extractOnlyLowerTriangle;
1287 size_t nnz;
1288 };
1289
1290
1291 } // End of namespace AssemblyDetail
1293 //---------------------------------------------------------------------
1294 //---------------------------------------------------------------------
1295
1296
1308 template <class GridView>
1310 {
1311 using Cell = Kaskade::Cell<GridView>;
1313
1314 public:
1315 CachingBoundaryDetector(GridSignals& signals, GridView const& gridView_) :
1316 gridView(gridView_), indexSet(gridView.indexSet())
1317 {
1318 refConnection = signals.informAboutRefinement.connect(int(GridSignals::allocResources),
1319 [=](GridSignals::Status when){this->update(when);});
1321 }
1322
1324 gridView(gridView_), indexSet(gridView.indexSet())
1325 {
1327 }
1328
1329 bool hasBoundaryIntersections(Cell const& cell) const
1330 {
1331 assert(indexSet.size(0)==boundaryFlag.size()); // check for grid adaptation...
1332 return boundaryFlag[indexSet.index(cell)];
1333 }
1334
1335 private:
1336 GridView const& gridView;
1337 typename GridView::IndexSet const& indexSet;
1338 std::vector<bool> boundaryFlag;
1339 boost::signals2::scoped_connection refConnection;
1340
1341 void update(int const status)
1342 {
1343 using namespace Dune;
1344
1345 if (status == GridSignals::AfterRefinement)
1346 {
1347 boundaryFlag.resize(indexSet.size(0));
1348 // TODO: run this loop in parallel (no so easy because of concurrent access to the bool vector...)
1349 for (auto const& cell: elements(gridView))
1350 boundaryFlag[indexSet.index(cell)] = cell.hasBoundaryIntersections();
1351 }
1352 }
1353 };
1354
1362 {
1363 public:
1364 template <class GridView>
1367
1368 template <class GridView>
1371
1372 template <class Entity>
1373 bool hasBoundaryIntersections(Entity const& cell) const
1374 {
1375 return cell.hasBoundaryIntersections();
1376 }
1377 };
1378
1386 {
1387 public:
1388 template <class GridView>
1391
1392 template <class GridView>
1395
1396 template <class Entity>
1397 bool hasBoundaryIntersections(Entity const& cell) const
1398 {
1399 return true;
1400 }
1401 };
1402
1403
1404 // ----------------------------------------------------------------------------------------------
1405 // ----------------------------------------------------------------------------------------------
1406
1426 template <class F,
1427 class SparseIndex = size_t,
1428 class BoundaryDetector = CachingBoundaryDetector<typename F::AnsatzVars::GridView>,
1429 class QuadRule = Dune::QuadratureRule<typename F::AnsatzVars::Grid::ctype,
1430 F::AnsatzVars::Grid::dimension>>
1431 class VariationalFunctionalAssembler
1432 : public AssemblyDetail::FormulationPolicy<F>
1433 {
1434 protected:
1435 typedef typename AssemblyDetail::FormulationPolicy<F> Policy;
1436
1437 public:
1438 typedef VariationalFunctionalAssembler<F,SparseIndex,BoundaryDetector,QuadRule> Self;
1439
1441 typedef F Functional;
1443 typedef typename Functional::AnsatzVars AnsatzVariableSetDescription;
1444
1446 typedef typename Functional::TestVars TestVariableSetDescription;
1447
1449 typedef typename AnsatzVariableSetDescription::Grid Grid;
1450
1454 typedef typename AnsatzVariableSetDescription::GridView GridView;
1455
1457 typedef typename AnsatzVariableSetDescription::Spaces Spaces;
1459 typedef typename AnsatzVariableSetDescription::IndexSet IndexSet;
1460
1464 typedef typename Functional::Scalar field_type;
1465
1467
1468 protected:
1469 typedef typename AnsatzVariableSetDescription::Variables AnsatzVariables;
1470 typedef typename TestVariableSetDescription::Variables TestVariables;
1471
1472 static_assert(std::is_same<typename AnsatzVariableSetDescription::Spaces, typename TestVariableSetDescription::Spaces>::value,
1473 "VariableSetDescriptions for ansatz spaces and test spaces must provide the same space list.");
1474 static_assert(Functional::type==WeakFormulation || std::is_same<AnsatzVariableSetDescription,TestVariableSetDescription>::value,
1475 "For problem type VariationalFunctional, ansatz and test variables must be the same.");
1476
1477 // Whether we need to assemble on inner faces.
1479
1480 public:
1481
1488 using MatrixBlockArray = typename AssemblyDetail::BlockArray<Policy,AnsatzVariables,TestVariables,SparseIndex>::type;
1489
1495 typedef typename AssemblyDetail::RhsBlockArray<Policy,TestVariables>::type RhsBlockArray;
1496
1500 typedef typename TestVariableSetDescription::template CoefficientVector<> RhsArray;
1501
1502 private:
1503 VariationalFunctionalAssembler(GridManagerBase<Grid>& gridManager_, Spaces const& spaces)
1504 : spaces_(spaces),
1505 gridManager(gridManager_),
1506 gv(boost::fusion::at_c<0>(spaces)->gridView()),
1507 indexSet(gv.indexSet()),
1508 boundaryDetector(gridManager_.signals,gv),
1510 rowBlockFactor(2.0),
1511 localStorageSize(128000) // roughly 128 kB
1512 {
1513 static_assert(symmetryCheck<Functional,std::min(AnsatzVariableSetDescription::noOfVariables,
1514 TestVariableSetDescription::noOfVariables)>,
1515 "If a problem is of type VariationalFunctional, the present diagonal blocks need to be marked as symmetric in D2.");
1517 [this](GridSignals::Status when){this->reactToRefinement(when);});
1518 }
1519
1520
1521 public:
1524 : VariationalFunctionalAssembler(boost::fusion::deref(boost::fusion::begin(spaces))->gridManager(),spaces)
1525 {
1526 }
1527
1528 // injected here for backward compatibility and convenience. TODO: Remove in the long run?
1530 static unsigned int const VALUE = Assembler::VALUE;
1531 static unsigned int const RHS = Assembler::RHS;
1532 static unsigned int const MATRIX = Assembler::MATRIX;
1533 static unsigned int const EVERYTHING = Assembler::EVERYTHING;
1534
1539
1548 {
1550 return *this;
1551 }
1552
1564 {
1565 rowBlockFactor = a;
1566 return *this;
1567 }
1568
1577 {
1578 localStorageSize = s;
1579 return *this;
1580 }
1581
1590 {
1591 gatherTimings = gt;
1592 return *this;
1593 }
1594
1598
1600 void assemble(F const& f, unsigned int flags=Assembler::EVERYTHING, int nThreads=0, bool verbose=false)
1601 {
1602 if (flags)
1603 assemble<AssemblyDetail::TakeAllBlocks>(f,[](auto const& cell) { return true; },
1604 flags,nThreads,verbose);
1605 }
1606
1607 protected:
1608 // Define iterator and entity type for stepping through all cells of the grid.
1609 typedef typename GridView::template Codim<0>::Iterator CellIterator;
1610 typedef typename CellIterator::Entity Entity;
1611
1612
1613 public:
1614
1653 template <class BlockFilter, class CellFilter>
1654 void assemble(F const& f, CellFilter const& cellFilter, unsigned int flags=Assembler::EVERYTHING,
1655 int nTasks=0, bool verbose=false)
1656 {
1657 using namespace Dune;
1658 using namespace boost::fusion;
1659 using namespace AssemblyDetail;
1660
1661 auto& timings = Timings::instance();
1662 ScopedTimingSection assemblyTiming("assembly",gatherTimings);
1663
1664 if (gatherTimings) timings.start("get cell ranges");
1665 auto const& cellRanges = gridManager.cellRanges(gv);
1666 if (gatherTimings) timings.stop("get cell ranges");
1667
1668 // Choose the number of tasks
1669 if (nTasks < 1)
1670 nTasks = NumaThreadPool::instance().cpus();
1671
1672 if (!gridManager.gridIsThreadSafe())
1673 {
1674 if (verbose)
1675 std::cout << "Grid is not thread safe. Reducing number of used threads to 1." << '\n';
1676 nTasks = 1;
1677 }
1678 else
1679 if(verbose) std::cout << "#tasks:" << nTasks << " " << '\n';
1680 assert(nTasks>=1);
1681
1682 nTasks = std::min(nTasks,cellRanges.maxRanges());
1683
1684 if (gatherTimings) timings.start("creating rhs/matrix data structures");
1685 // set entries to zero
1686 auto clearGlobalData = [](auto& x) { x = 0; };
1687
1688 if (flags & RHS)
1689 for_each(getRhs().first.data,clearGlobalData);
1690
1691 if (flags & MATRIX)
1692 {
1693 typedef filter_view<MatrixBlockArray,MatrixBlockFilter<BlockFilter>> MatrixBlockFilter;
1694 for_each(MatrixBlockFilter(getMatrix(f)),clearGlobalData);
1695 }
1696 if (gatherTimings) timings.stop("creating rhs/matrix data structures");
1697
1698 // If there is only one task, things are somewhat simpler and more efficient...
1699 if (nTasks == 1)
1700 {
1701 ScopedTimingSection timer("perform sequential assembly",gatherTimings);
1702 Scalar retval =
1703 assembleCellRange<BlockFilter>(f,cellFilter,flags,gv.template begin<0>(),gv.template end<0>(),0);
1704 if (flags & VALUE)
1705 this->fValue = retval;
1706 }
1707 else
1708 {
1709 ScopedTimingSection parAssTime("perform parallel assembly",gatherTimings);
1710 // Partition the cells in ranges of almost equal size and do the assembly in parallel.
1711 std::vector<Scalar> values(nTasks,0.0);
1712
1713 parallelFor([&cellRanges,this,&f,flags,&values,&cellFilter](int i, int n) {
1714 auto range = cellRanges.range(n,i);
1715 values[i] = this->assembleCellRange<BlockFilter>(f,cellFilter,flags,range.begin(),range.end(),i);
1716 },
1717 nTasks);
1718
1719 // At the end, add all the functional values up
1720 if (flags & VALUE)
1721 this->fValue = std::accumulate(values.begin(),values.end(),0.0);
1722 }
1723
1724 // announce validity of assembled data
1725 validparts |= flags;
1726 }
1727
1728 private:
1729
1730
1742 template <class BlockFilter, class CellFilter>
1743 Scalar assembleCellRange(F const& f, CellFilter const& cellFilter,
1744 unsigned int flags, CellIterator cell, CellIterator last, int threadNo)
1745 {
1746 using namespace Dune;
1747 using namespace boost::fusion;
1748 using namespace AssemblyDetail;
1749
1750 // timer for gathering performance statistics
1751 boost::timer::cpu_timer scatterTimer, evalTimer, localTimer, setupTimer, totalTimer;
1752 if (statistics & localTimerStatistics)
1753 {
1754 evalTimer.stop();
1755 scatterTimer.stop();
1756 localTimer.stop();
1757 }
1758 // Obtain global data structures. Note that here is no race condition even if the
1759 // data is constructed on demand, as the required data structures have been created
1760 // and initialized before.
1761 MatrixBlockArray* globalMatrix = flags & MATRIX? & getMatrix() : nullptr;
1762 RhsArray* globalRhs = flags & RHS ? & getRhs().first : nullptr;
1763 RhsBlockArray* globalRhsBlocks = flags & RHS ? & getRhs().second: nullptr;
1764
1765
1766 using MatrixBlockFilter = filter_view<MatrixBlockArray,AssemblyDetail::MatrixBlockFilter<BlockFilter>>;
1767
1768 // Local contribution to global value.
1769 Scalar fValue = 0;
1770
1771 int const dim = Grid::dimension;
1772 typedef typename Grid::ctype CoordType;
1773
1774 // Allocate local rhs vectors that can be reused on each
1775 // element. Since all variables may have different element types
1776 // (i.e. different number of components), this has to be one local
1777 // rhs per variable.
1778 auto localRhs = as_vector(transform(*globalRhsBlocks,RhsLocalData<Policy>(nSimultaneousBlocks))); // assemble several cells en bloc before scattering
1779
1780 // We assemble local matrices into a buffer from where they are scattered simultaneously. The buffers
1781 // scatter automatically when they are full, such that access to previous local matrices is not possible.
1782 // For DG methods, there are several local matrices that are assembled simultaneously in a particular
1783 // access pattern, such that we use two buffers. The total buffer size is localStorageSize.
1784 using LocalMatrices = decltype(as_vector(transform(MatrixBlockFilter(*globalMatrix),MatrixLocalData(0))));
1785 LocalMatrices localMatrices, localNeighborMatrices;
1786 if (flags & MATRIX)
1787 {
1788 localMatrices = as_vector(transform(MatrixBlockFilter(*globalMatrix),
1789 MatrixLocalData(innerBoundaries? localStorageSize/2: localStorageSize)));
1790 localNeighborMatrices = as_vector(transform(MatrixBlockFilter(*globalMatrix),
1791 MatrixLocalData(innerBoundaries? localStorageSize/2: 0)));
1792 }
1793
1794 // element-local functional value
1795 Scalar floc = 0;
1796
1797 // Shape function cache. Remember that every thread has to use its own cache,
1798 // thus we create our own here.
1799 using SfCache = ShapeFunctionCache<Grid,Scalar>;
1800 SfCache sfCache;
1801
1802 // Quadrature rule caches. Remember that every thread has to use its own quadrature caches, thus we create our own here.
1803 QuadratureTraits<QuadRule> quadratureCache;
1804 QuadratureTraits<QuadratureRule<CoordType,dim-1>> faceQuadratureCache;
1805
1806 // Evaluators for shape functions of all FE spaces. Remember that
1807 // every thread has to use its own collection of evaluators (for both current cell and neighbouring cells in case
1808 // we assemble on inner boundaries)!
1809 auto evaluators = getEvaluators(spaces(),&sfCache,AssemblyDetail::derivatives(f,spaces(),localMatrices,localRhs));
1810 using Evaluators = decltype(evaluators);
1811 auto neighbourEvaluators = evaluators;
1812 // Create the "caches" responsible for evaluating the variational functional / weak formulation.
1813 // The inner boundary cache need not exist, thus we evaluate it as 0 if not present. Lambda function
1814 // is a trick to allow using constexpr if.
1815 auto domainCache = f.createDomainCache(flags);
1816 auto boundaryCache = f.createBoundaryCache(flags);
1817 auto innerBoundaryCache = [&](){ if constexpr (innerBoundaries) return f.createInnerBoundaryCache(flags);
1818 else return 0; }();
1819 // The symmetrizer functor. We pass the heterogeneous array of global matrices
1820 // such that run-time meta information, e.g., on enforcing positive definiteness,
1821 // can be used.
1822 SymmetrizeLocalMatrix<Functional> symmetrizeLocalMatrix{&f};
1823
1824 // Dummy variable sets to be fed to boost::fusion algorithms.
1825 typename AnsatzVariableSetDescription::Variables ansatzVars;
1826 typename TestVariableSetDescription::Variables testVars;
1827
1828 if (statistics & localTimerStatistics)
1829 setupTimer.stop();
1830
1831 // Assemble representation: step through all elements, assemble
1832 // the local matrix and rhs, and scatter their entries into the
1833 // global data structures.
1834 if (statistics & localTimerStatistics)
1835 evalTimer.resume();
1836
1837 while(cell!=last)
1838 {
1839 // If the cell is not of interest, skip
1840 if (!cellFilter(*cell))
1841 {
1842 ++cell;
1843 continue;
1844 }
1845
1846 // tell the problem on which cell we are
1847 domainCache.moveTo(*cell);
1848
1849 // for all spaces involved, compute the shape functions and
1850 // their global indices, which are needed for evaluating the
1851 // functional's derivative.
1852 moveEvaluatorsToCell(evaluators,*cell,indexSet.index(*cell));
1853 int const shapeFunctionMaxOrder = maxOrder(evaluators);
1854
1855 if (statistics & localTimerStatistics) localTimer.resume();
1856 if (statistics & localTimerStatistics) evalTimer.stop();
1857
1858 // create new local matrices and right hand sides.
1859 floc = 0;
1860 if (flags & RHS)
1861 for_each(localRhs,ClearLocalRhs<Evaluators,TestVariables>(evaluators));
1862
1863 if (flags & MATRIX)
1864 for_each(localMatrices,NewLocalMatrix<Evaluators,AnsatzVariableSetDescription,
1865 TestVariableSetDescription>(evaluators,evaluators));
1866
1867 // loop over all quadrature points and integrate local stiffness matrix and rhs
1868 int const integrationOrderOnCell = f.integrationOrder(*cell,shapeFunctionMaxOrder,false);
1869 GeometryType gt = cell->type();
1870
1871 QuadRule const qr = quadratureCache.rule(gt,integrationOrderOnCell);
1872 useQuadratureRuleInEvaluators(evaluators,qr,0);
1873
1874 size_t nQuadPos = qr.size();
1875 for (size_t g=0; g<nQuadPos; ++g)
1876 {
1877 // pos of integration point
1878 FieldVector<CoordType,dim> const& quadPos = qr[g].position();
1879
1880 // for all spaces involved, update the evaluators associated to this quadrature point
1881 moveEvaluatorsToIntegrationPoint(evaluators,quadPos,qr,g,0);
1882
1883 // prepare evaluation of functional
1884 domainCache.evaluateAt(quadPos,evaluators);
1885
1886 CoordType weightTimesDetJac(cell->geometry().integrationElement(quadPos)); // determinant of jacobian
1887 assert(weightTimesDetJac > 0);
1888 // weight of quadrature point
1889 weightTimesDetJac *= qr[g].weight();
1890
1891 // step through all local matrix and rhs blocks and update their data
1892 if constexpr (Self::hasValue)
1893 if (flags & VALUE)
1894 floc += weightTimesDetJac*domainCache.d0();
1895 if (flags & RHS)
1896 for_each(localRhs,UpdateLocalRhs<TestVariableSetDescription,Evaluators,Scalar,
1897 typename F::DomainCache>(evaluators,weightTimesDetJac,domainCache));
1898 if (flags & MATRIX)
1899 for_each(localMatrices,UpdateLocalMatrix<AnsatzVariableSetDescription,TestVariableSetDescription,
1900 Evaluators,Scalar,typename F::DomainCache>(
1901 evaluators,weightTimesDetJac,domainCache));
1902 }
1903
1904
1905 // Handle Robin (Cauchy) type boundary conditions and interior face terms. Loop over all
1906 // faces (codim 1) that are exterior boundaries or are relevant due to interior face
1907 // terms and integrate the contribution. We assume that intersections on the domain
1908 // boundary always consist of the whole face (codim 1 subentity).
1909 if(innerBoundaries || boundaryDetector.hasBoundaryIntersections(*cell))
1910 {
1911 int const integrationOrderOnFace = f.integrationOrder(*cell,shapeFunctionMaxOrder,true);
1912
1913 // This would be nice, but since there is no way to obtain an IntersectionIterator from an
1914 // Intersection, we'd need to pass the Intersection directly to boundaryCache.moveTo()
1915 // - which would be nicer, but is a breaking change. Consider this for the next major revision.
1916 //
1917 // for (auto const& face: intersections(gv,*cell))
1918 //
1919 // Instead, we use the old style.
1920 auto faceEnd = gv.iend(*cell);
1921 for (auto face=gv.ibegin(*cell); face!=faceEnd; ++face)
1922 {
1923 // @warning: make SURE the following does always hold: (i)
1924 // On boundary faces, there is exactly one intersection, and
1925 // this covers the whole face. (ii) When an intersection
1926 // covers the whole codim 1 subentity, its geometry in the
1927 // cell is the same as specified in the reference
1928 // element. Currently this is not guaranteed by the Dune
1929 // interface. Maybe there is a relation to the ominous
1930 // TwistUtility from Freiburg?
1931
1932 bool const onBoundary = face->boundary();
1933
1934 // Assemble only if needed.
1935 if (innerBoundaries || onBoundary)
1936 {
1937 GeometryType gt = face->type();
1938 QuadratureRule<CoordType,dim-1> const qr = faceQuadratureCache.rule(gt,integrationOrderOnFace);
1939 size_t const nQuadPos = qr.size();
1940
1941 // Move the caches to the current face (and neighboring evaluators to the adjacent cell).
1942 if (onBoundary)
1943 boundaryCache.moveTo(face);
1944 else if constexpr (innerBoundaries) // the constexpr if just protects the otherwise ill-formed body
1945 {
1946 assert(face->neighbor());
1947 assert(gt == face->geometryInOutside().type());
1948
1949 // If the problem claims the face is not relevant, skip assembly
1950 if (! f.considerFace(*face))
1951 continue;
1952
1953 innerBoundaryCache.moveTo(face);
1954 moveEvaluatorsToCell(neighbourEvaluators,face->outside());
1955
1956 // for each neighbor (i.e. face), there is one local matrix that is computed by quadrature. Let's
1957 // create a new local matrix if needed. The row (test variable) is given by the current cell, and the
1958 // column (ansatz variable) determined by the neighbor.
1959 if (flags & MATRIX)
1960 for_each(localNeighborMatrices,
1962 TestVariableSetDescription>(evaluators,neighbourEvaluators));
1963 }
1964
1965
1966 for (size_t g=0; g<nQuadPos; ++g)
1967 {
1968 // Evaluation by quadrature point index cannot be used on faces due to inconsistent numbering
1969 // of quadrature points relative to the cell (see below). Hence we don't need to announce the quadrature
1970 // rule to the evaluators here.
1971 // useQuadratureRuleInEvaluators(evaluators,qr,face->indexInInside());
1972
1973 // position of integration point
1974 Dune::FieldVector<CoordType,dim-1> quadPos = qr[g].position();
1975 CoordType weightTimesDetJac = qr[g].weight() * face->geometry().integrationElement(quadPos);
1976
1977 // evaluate values at integration points for all spaces
1978 // involved, update the evaluators associated to this
1979 // quadrature point
1980 //
1981 // TODO: check whether this call is
1982 // thread-safe. (currently as of 2010-02-11 this is not
1983 // guaranteed by the Dune interface for UG). Copying the
1984 // quadrature position instead of obtaining a const
1985 // reference minimizes the time window for concurrent
1986 // access.
1987 // Note that global() here returns the *cell-local* position.
1988 FieldVector<CoordType,dim> const quadPosInCell = face->geometryInInside().global(quadPos);
1989
1990 // Move evaluators to integration point
1991 // quadrature points on faces are not consistently indexed (if even symmetric...) - hence evaluating
1992 // shape functions based on quadrature point index is impossible
1993 // moveEvaluatorsToIntegrationPoint(evaluators,quadPosInCell,qr,g,face->indexInInside());
1994 moveEvaluatorsToIntegrationPoint(evaluators,quadPosInCell);
1995
1996 if (onBoundary) // assemble on domain boundary
1997 {
1998 boundaryCache.evaluateAt(quadPos,evaluators);
1999
2000 // step through all rhs blocks and update their local rhs
2001 if constexpr (Self::hasValue)
2002 if (flags & VALUE)
2003 floc += weightTimesDetJac*boundaryCache.d0();
2004 if (flags & RHS)
2005 for_each(localRhs,UpdateLocalRhs<TestVariableSetDescription,Evaluators,Scalar,
2006 typename F::BoundaryCache>(evaluators,weightTimesDetJac,boundaryCache));
2007 if (flags & MATRIX)
2008 for_each(localMatrices,UpdateLocalMatrix<AnsatzVariableSetDescription,TestVariableSetDescription,
2009 Evaluators,Scalar,typename F::BoundaryCache>(evaluators,weightTimesDetJac,boundaryCache));
2010
2011 } // done boundary faces
2012 else if constexpr (innerBoundaries) // assemble on inner boundary ("constexpr if"
2013 { // only because the body may be ill-formed)
2014 // Move in addition the neighboring evaluators to the correct evaluation point
2015 moveEvaluatorsToIntegrationPoint(neighbourEvaluators,face->geometryInOutside().global(quadPos));
2016
2017 innerBoundaryCache.evaluateAt(quadPos,evaluators,neighbourEvaluators);
2018
2019 // Update value. Remember that each face is visited twice, so we add here only our half.
2020 if constexpr (Self::hasValue)
2021 if (flags & VALUE)
2022 floc += 0.5 * weightTimesDetJac*innerBoundaryCache.d0();
2023
2024 using InnerBoundaryCache = typename Functional::InnerBoundaryCache;
2025
2026 // Step through all rhs blocks and update their local rhs. Remember that even if the face
2027 // is visited twice, these visits correspond to different rows (the row being associated
2028 // to the current center cell).
2029 // Hence we do not sum up the samve values twice and have to omit the factor 0.5.
2030 if (flags & RHS)
2031 boost::fusion::for_each(localRhs,UpdateLocalRhs<TestVariableSetDescription,Evaluators,
2032 Scalar,InnerBoundaryCache>
2033 (evaluators,weightTimesDetJac,innerBoundaryCache));
2034
2035 // Update local matrices. As with the rhs, we have to omit the factor 0.5.
2036 if (flags & MATRIX)
2037 {
2038 // update diagonal block, contribution of coupling terms on cell (boolean true)
2039 boost::fusion::for_each(localMatrices,UpdateLocalMatrixFromInnerBoundaryCache<AnsatzVariableSetDescription,
2040 TestVariableSetDescription,Evaluators,Scalar,InnerBoundaryCache>
2041 (evaluators,evaluators,weightTimesDetJac,innerBoundaryCache,true));
2042 // update off-diagonal block, contribution of coupling terms with neighbouring cell (boolean false)
2043 boost::fusion::for_each(localNeighborMatrices,UpdateLocalMatrixFromInnerBoundaryCache<AnsatzVariableSetDescription,
2044 TestVariableSetDescription,Evaluators,Scalar,InnerBoundaryCache>
2045 (evaluators,neighbourEvaluators,weightTimesDetJac,innerBoundaryCache,false));
2046 }
2047 }
2048 }
2049 }
2050 } // done face loop
2051 } // done boundary conditions
2052
2053 // symmetrize local matrix (only the center cell matrices, not the off-diagonal neighbor matrices which are
2054 // not symmetric even for symmetric problems).
2055 if (flags & MATRIX)
2056 for_each(localMatrices,symmetrizeLocalMatrix);
2057
2058 if (statistics & localTimerStatistics) scatterTimer.resume();
2059 if (statistics & localTimerStatistics) localTimer.stop();
2060
2061 // Move to next cell.
2062 ++cell;
2063 // Scatter local data into global data (note that this occurs blockwise, i.e.
2064 // the actual scatter may be delayed). Local matrix buffers scatter automatically.
2065 fValue += floc;
2066 if (flags & RHS)
2067 for_each(localRhs,ScatterLocalRhs<RhsArray>(*globalRhs));
2068
2069 if (statistics & localTimerStatistics) evalTimer.resume(); // count cell iterator increment as well
2070 if (statistics & localTimerStatistics) scatterTimer.stop();
2071 } // end iteration over cells
2072
2073 if (statistics & localTimerStatistics) scatterTimer.resume();
2074 if (statistics & localTimerStatistics) evalTimer.stop();
2075
2076 // scattering of local vectors may not be complete (there may be local data left over in the buffers). Again, local matrices
2077 // scatter automatically on destruction.
2078 if (flags & RHS)
2079 for_each(localRhs,ScatterLocalRhs<RhsArray>(*globalRhs,true));
2080
2081 if (statistics & localTimerStatistics) scatterTimer.stop();
2082
2083 // report statistics
2084 if (statistics & localTimerStatistics)
2085 {
2086 std::cerr << "--------------\nThread " << threadNo << ":\n";
2087 double totalTime = setupTimer.elapsed().wall + evalTimer.elapsed().wall + scatterTimer.elapsed().wall + localTimer.elapsed().wall;
2088 std::cerr.precision(1);
2089 std::cerr.setf(std::ios_base::fixed,std::ios_base::floatfield);
2090 std::cerr << "Total time: " << 100* totalTimer.elapsed().wall / totalTime << "% -- " << totalTimer.format()
2091 << "Setup time: " << 100* setupTimer.elapsed().wall / totalTime << "% -- " << setupTimer.format()
2092 << "Eval times: " << 100* evalTimer.elapsed().wall / totalTime << "% -- " << evalTimer.format()
2093 << "Scatter times: " << 100* scatterTimer.elapsed().wall / totalTime << "% -- " << scatterTimer.format()
2094 << "Local times: " << 100 * localTimer.elapsed().wall / totalTime << "% -- " << localTimer.format();
2095 std::cerr << "--------------\n";
2096 }
2097
2098 return fValue;
2099 }
2100
2101 public:
2106 int valid() { return validparts; }
2107
2113 void flush(int flags=(VALUE|RHS|MATRIX))
2114 {
2115 validparts &= !flags;
2116 if (flags & VALUE) this->fValue = 0;
2117 if (flags & RHS)
2118 {
2119 rhss.reset();
2120 rhsBlocks.reset();
2121 }
2122 if (flags & MATRIX) matrixBlocks.reset();
2123 }
2124
2129 std::pair<size_t,size_t> size(int row, int col) const
2130 {
2131 return std::make_pair(TestVariableSetDescription::degreesOfFreedom(spaces(),row,row+1),
2132 AnsatzVariableSetDescription::degreesOfFreedom(spaces(),col,col+1));
2133 }
2134
2139
2163 template <class Matrix>
2164 Matrix get(bool extractOnlyLowerTriangle=false,
2165 int rbegin=0, int rend=TestVariableSetDescription::noOfVariables,
2166 int cbegin=0, int cend=AnsatzVariableSetDescription::noOfVariables) const
2167 {
2168 // block sizes
2169 auto cSizes = AnsatzVariableSetDescription::variableDimensions(spaces());
2170 auto rSizes = TestVariableSetDescription::variableDimensions(spaces());
2171
2172 // row and column offsets
2173 std::vector<size_t> rowOff(rend-rbegin,0), colOff(cend-cbegin,0);
2174 std::partial_sum(rSizes.begin()+rbegin,rSizes.begin()+rend-1,rowOff.begin()+1);
2175 std::partial_sum(cSizes.begin()+cbegin,cSizes.begin()+cend-1,colOff.begin()+1);
2176
2177 return AssemblyDetail::Fill<Matrix>::apply(getMatrix(), rbegin, cbegin, rowOff, colOff,
2178 extractOnlyLowerTriangle,
2179 nnz(rbegin, rend, cbegin, cend,
2180 extractOnlyLowerTriangle),
2181 nrows(rbegin,rend), ncols(cbegin,cend));
2182 }
2183
2191 template <int row, int col>
2192 auto const& get() const
2193 {
2194 return (*boost::fusion::find_if<AssemblyDetail::IsBlock<row,col>>(getMatrix())).globalMatrix();
2195 }
2196
2202 template <class MatrixType, class BlockInformation>
2203 MatrixType get(bool extractOnlyLowerTriangle) const
2204 {
2205 using BI = BlockInformation;
2207 }
2208
2212
2216 template <class DataOutIter>
2217 void toSequence(int rbegin, int rend, DataOutIter xi) const
2218 {
2219 // WARNING: this assumes that for_each processes the rhsArray in
2220 // correct order from front to back!
2221 for_each(getRhs().first.data,BlockToSequence<DataOutIter>(rbegin,rend,xi));
2222 }
2223
2224
2229
2238 size_t nnz(size_t rbegin=0, size_t rend=TestVariableSetDescription::noOfVariables,
2239 size_t cbegin=0, size_t cend=AnsatzVariableSetDescription::noOfVariables, bool extractOnlyLowerTriangle=false) const
2240 {
2241 size_t n = 0;
2242 boost::fusion::for_each(getMatrix(),CountNonzeros(n,rbegin,rend,cbegin,cend,extractOnlyLowerTriangle));
2243 return n;
2244 }
2245
2249 size_t nrows(int firstBlock=0, int lastBlock=TestVariableSetDescription::noOfVariables) const
2250 {
2251 return TestVariableSetDescription::degreesOfFreedom(spaces(),firstBlock,lastBlock);
2252 }
2253
2257 size_t ncols(int firstBlock=0, int lastBlock=AnsatzVariableSetDescription::noOfVariables) const
2258 {
2259 return AnsatzVariableSetDescription::degreesOfFreedom(spaces(),firstBlock,lastBlock);
2260 }
2261
2265 Spaces const& spaces() const
2266 {
2267 return spaces_;
2268 }
2269
2273 GridView const& gridView() const
2274 {
2275 return gv;
2276 }
2277
2281
2290 {
2291 if (!matrixBlocks)
2292 createMatrix([](auto const& face) { return true; });
2293 return *matrixBlocks;
2294 }
2295
2296 private:
2297 // Returns the matrix, creating it on the fly if necessary.
2298 MatrixBlockArray& getMatrix(F const& f)
2299 {
2300 if (!matrixBlocks)
2301 createMatrix([&](auto const& face)
2302 {
2303 if constexpr (innerBoundaries)
2304 return f.considerFace(face);
2305 else
2306 return true;
2307 });
2308
2309 return *matrixBlocks;
2310 }
2311
2312
2313 template <class FaceOracle>
2314 void createMatrix(FaceOracle const& considerFace) const
2315 {
2316 matrixBlocks.reset(new MatrixBlockArray());
2317 boost::fusion::for_each(*matrixBlocks,[&](auto& block)
2318 {
2319 block.init(this->spaces(),gv.template begin<0>(),gv.template end<0>(),
2320 considerFace);
2321 });
2322 }
2323
2324 public:
2330 template <int first=0, int last=TestVariableSetDescription::noOfVariables>
2331 typename TestVariableSetDescription::template CoefficientVector<first,last> rhs() const
2332 {
2333 using Rhs = typename TestVariableSetDescription::template CoefficientVector<first,last>;
2334 return Rhs(make_range<first,last>(getRhs().first.data));
2335 }
2336
2337 protected:
2338 std::pair<RhsArray&,RhsBlockArray&> getRhs() const
2339 {
2340 if(rhss.get()==0)
2341 {
2342 // construct global vectors
2343// old version, should be equivalent to the following line.
2344// rhss.reset(new RhsArray(Variables_Detail::VariableRangeCreator<TestVariables,ConstructCoefficientVector<Spaces>>
2345// ::apply(ConstructCoefficientVector<Spaces>(spaces()))));
2346 rhss.reset(new RhsArray(TestVariableSetDescription::template zeroCoefficientVector<>(spaces())));
2347 // construct block infos
2348 rhsBlocks.reset(new RhsBlockArray());
2349 // Choose the number of row groups in the matrix blocks. The obvious choice would be 1 in sequential mode, but it
2350 // appears that a somewhat larger number is beneficial, probably due to better memory access locality. For multithreaded
2351 // mode, we choose the number of hardware threads as the default, since then all threads can (in principle) be active
2352 // simultaneously.
2353 int nrg = std::max((int)(rowBlockFactor*boost::thread::hardware_concurrency()),4);
2354 boost::fusion::for_each(*rhsBlocks,[&](auto& block)
2355 {
2356 block.init(this->spaces(),gv.template begin<0>(),gv.template end<0>(),nrg);
2357 });
2358 }
2359
2360 return std::pair<RhsArray&,RhsBlockArray&>(*rhss,*rhsBlocks);
2361 }
2362
2363
2364 // Resource management: delete matrix and rhs data structures as well as cell ranges on
2365 // refinement since they are no longer meaningful on the new grid.
2367 {
2368 if (status == GridSignals::BeforeRefinement)
2369 flush();
2370 }
2371
2372
2373 // resource management
2374 boost::signals2::scoped_connection refConnection;
2375
2378 GridView const& gv;
2380
2381 mutable std::shared_ptr<MatrixBlockArray> matrixBlocks; // matrix block info including global matrices
2382 mutable std::shared_ptr<RhsArray> rhss; // global rhs vectors
2383 mutable std::shared_ptr<RhsBlockArray> rhsBlocks; // rhs block info
2384 BoundaryDetector boundaryDetector;
2386
2387
2391 bool gatherTimings = false; // if true, use default Kaskade timings
2392
2393
2394 template <class IdxOutIter, class DataOutIter>
2396 {
2397 BlockToTriplet(size_t rbegin_, size_t cbegin_, std::vector<size_t> const& rowOff_, std::vector<size_t> const& colOff_,
2398 IdxOutIter& ri_, IdxOutIter& ci_, DataOutIter& xi_, bool extractOnlyLowerTriangle_):
2399 rbegin(rbegin_), cbegin(cbegin_), rowOff(rowOff_), colOff(colOff_),
2400 ri(ri_), ci(ci_), xi(xi_), extractOnlyLowerTriangle(extractOnlyLowerTriangle_)
2401 {}
2402
2403 template <class MatrixBlock>
2404 void operator()(MatrixBlock const& mb) const
2405 {
2406 // Check if block is in requested range
2407 if (inRange(mb.rowId,mb.colId))
2409 rowOff[MatrixBlock::rowId-rbegin],colOff[MatrixBlock::colId-cbegin],
2410 mb.rowId==mb.colId && extractOnlyLowerTriangle,
2411 mb.symmetric);
2412 // For mirror blocks, the transposed block needs to be written
2413 // if the transposed block is in the requested
2414 // range. Transposition is implicitly achieved by swapping
2415 // column and row index output iterators.
2416 if (MatrixBlock::mirror && inRange(mb.colId,mb.rowId) && extractOnlyLowerTriangle==false)
2418 colOff[MatrixBlock::rowId-cbegin],rowOff[MatrixBlock::colId-rbegin],
2419 false,
2420 mb.symmetric);
2421 }
2422
2423 bool inRange(size_t r, size_t c) const
2424 {
2425 return r>=rbegin && r<rbegin+rowOff.size() && c>=cbegin && c<cbegin+colOff.size();
2426 }
2427
2428
2430 std::vector<size_t> const& rowOff;
2431 std::vector<size_t> const& colOff;
2432 IdxOutIter& ri;
2433 IdxOutIter& ci;
2434 DataOutIter& xi;
2436 };
2437
2439 {
2440 CountNonzeros(size_t& n_, size_t rbegin_, size_t rend_, size_t cbegin_, size_t cend_, bool onlyLowerTriangle_):
2441 n(n_), rbegin(rbegin_), rend(rend_), cbegin(cbegin_), cend(cend_), onlyLowerTriangle(onlyLowerTriangle_)
2442 {}
2443
2444 template <class MatrixBlock>
2445 void operator()(MatrixBlock const& mb) const
2446 {
2447 size_t myN = Matrix_to_Triplet<typename MatrixBlock::Matrix>::nnz(mb.globalMatrix(),
2448 (mb.rowId==mb.colId) && onlyLowerTriangle,
2449 mb.symmetric);
2450 // Check if block is in requested range
2451 if (inRange(MatrixBlock::rowId,MatrixBlock::colId))
2452 n += myN;
2453
2454 // For subdiagonal blocks, the transposed block needs to be
2455 // counted if onlyLowerTriangle is false and the transposed
2456 // block is in the requested range and we need to mirror this
2457 // block.
2458 if (MatrixBlock::mirror && inRange(mb.colId,mb.rowId) && onlyLowerTriangle==false)
2459 n += myN;
2460 }
2461
2462 bool inRange(size_t r, size_t c) const
2463 {
2464 return r>=rbegin && r<rend && c>=cbegin && c<cend;
2465 }
2466
2467
2468
2469 size_t& n;
2472 };
2473
2474 template <class DataOutIter>
2476 {
2477 BlockToSequence(int& rbegin_, int& rend_, DataOutIter& out_): rbegin(rbegin_), rend(rend_), out(out_) {}
2478 template <class VectorBlock> void operator()(VectorBlock const& v) const
2479 {
2480 if (rbegin<=0 && rend>0)
2481 out = vectorToSequence(v,out);
2482 --rbegin;
2483 --rend;
2484 }
2485 private:
2486 int& rbegin;
2487 int& rend;
2488 DataOutIter& out;
2489 };
2490
2491 };
2492
2493
2494 // End of VariationalFunctionalAssembler
2495 //---------------------------------------------------------------------
2496 //---------------------------------------------------------------------
2497} /* end of namespace Kaskade */
2498
2499#endif
CachingBoundaryDetector(GridView const &gridView_)
bool hasBoundaryIntersections(Cell const &cell) const
CachingBoundaryDetector(GridSignals &signals, GridView const &gridView_)
ForwardingBoundaryDetector(GridSignals const &, GridView const &)
bool hasBoundaryIntersections(Entity const &cell) const
Basic functionality for managing grids and their refinement.
static NumaThreadPool & instance(int maxThreads=std::numeric_limits< int >::max())
Returns a globally unique thread pool instance.
int cpus() const
Reports the total number of CPUs (usually a multiple of nodes).
Definition threading.hh:338
A scope guard object that automatically closes a timing section on destruction.
Definition timing.hh:181
static Timings & instance()
Returns a reference to a single default instance.
TrivialBoundaryDetector(GridView const &)
TrivialBoundaryDetector(GridSignals const &, GridView const &)
bool hasBoundaryIntersections(Entity const &cell) const
Self & setNSimultaneousBlocks(int n)
Defines how many cells are assembled locally before scattering them together into the global data str...
int valid()
Returns a bitfield specifyign which of the parts have been assembled since construction or flush() ac...
void flush(int flags=(VALUE|RHS|MATRIX))
Destructs parts of the assembled quatities according to the format (VALUE|RHS|MATRX)
Self & setLocalStorageSize(size_t s)
Defines how many memory the locally assembled matrices may occupy before they are scattered.
Matrix get(bool extractOnlyLowerTriangle=false, int rbegin=0, int rend=TestVariableSetDescription::noOfVariables, int cbegin=0, int cend=AnsatzVariableSetDescription::noOfVariables) const
Extracts the submatrix defined by the half-open block ranges [rbegin,rend), [cbegin,...
Self & setGatherTimings(bool gt)
Whether to gather timing statistics using Kaskade::Timings.
AssemblyDetail::FormulationPolicy< F > Policy
void assemble(F const &f, CellFilter const &cellFilter, unsigned int flags=Assembler::EVERYTHING, int nTasks=0, bool verbose=false)
Create data in assembler.
typename AssemblyDetail::BlockArray< Policy, AnsatzVariables, TestVariables, SparseIndex >::type MatrixBlockArray
void toSequence(int rbegin, int rend, DataOutIter xi) const
Writes the subvector defined by the half-open block range [rbegin,rend) to the output iterator xi.
auto const & get() const
Extracts a raw single submatrix block indexed by row and column.
void reactToRefinement(GridSignals::Status const status)
size_t ncols(int firstBlock=0, int lastBlock=AnsatzVariableSetDescription::noOfVariables) const
Returns the number of scalar cols in the column block range [firstBlock,lastBlock[.
AssemblyDetail::RhsBlockArray< Policy, TestVariables >::type RhsBlockArray
TestVariableSetDescription::template CoefficientVector< first, last > rhs() const
Returns a contiguous subrange of the rhs coefficient vectors.
size_t nrows(int firstBlock=0, int lastBlock=TestVariableSetDescription::noOfVariables) const
Returns the number of scalar rows in the row block range [firstBlock,lastBlock[.
VariationalFunctionalAssembler< F, SparseIndex, BoundaryDetector, QuadRule > Self
VariationalFunctionalAssembler(Spaces const &spaces)
Construct an empty assembler, gridManager is implicitly obtained from the first space.
std::pair< size_t, size_t > size(int row, int col) const
the size of a matrix block (in terms of scalar rows/columns)
size_t nnz(size_t rbegin=0, size_t rend=TestVariableSetDescription::noOfVariables, size_t cbegin=0, size_t cend=AnsatzVariableSetDescription::noOfVariables, bool extractOnlyLowerTriangle=false) const
Returns the number of structurally nonzero entries in the submatrix defined by the half-open block ra...
MatrixType get(bool extractOnlyLowerTriangle) const
Extracts the submatrix defined by the half-open block ranges given as template parameter.
std::pair< RhsArray &, RhsBlockArray & > getRhs() const
void assemble(F const &f, unsigned int flags=Assembler::EVERYTHING, int nThreads=0, bool verbose=false)
Assembly without block filter or cell filter.
MatrixBlockArray & getMatrix() const
Returns a mutable reference to the sequence of matrix blocks.
Self & setRowBlockFactor(double a)
Defines how many more row blocks in each matrix are used compared to the number of threads.
boost::fusion::result_of::make_range< First, Last, Sequence >::type make_range(Sequence &s)
Constructs a boost::fusion view that picks a certain range from a forward sequence.
Definition fixfusion.hh:77
Utility classes for the definition and use of variational functionals.
std::tuple< std::vector< typename EntryTraits< Entry >::field_type >, DynamicMatrix< typename EntryTraits< Entry >::field_type >, std::vector< int > > qr(DynamicMatrix< Entry > const &A)
Computes a rank-revealing QR decomposition of using column pivoting.
void useQuadratureRuleInEvaluators(Evaluator &evals, QuadratureRule const &qr, int subId)
Tells all evaluators to use the given quadrature rule on the given subentity.
void moveEvaluatorsToIntegrationPoint(Evaluators &evals, Dune::FieldVector< CoordType, dim > const &x, Dune::QuadratureRule< CoordType, subDim > const &qr, int ip, int subId)
Moves all provided evaluators to the given integration point, evaluating the shape functions there.
int maxOrder(Evaluators const &evals)
Computes the maximum ansatz order used in a collection of evaluators.
typename boost::fusion::result_of::as_vector< typename boost::fusion::result_of::transform< Spaces, GetEvaluatorTypes >::type >::type Evaluators
the heterogeneous sequence type of Evaluators for the given spaces
auto getEvaluators(Spaces const &spaces, ShapeFunctionCache *cache, std::map< void const *, int > const &deriv=std::map< void const *, int >())
returns a heterogeneous sequence of evaluators for the given spaces
constexpr bool hasInnerBoundaryCache
Checks whether InnerBoundaryCache is declared in Functional.
@ VariationalFunctional
boost::signals2::signal< void(Status)> informAboutRefinement
A signal that is emitted thrice on grid modifications, once before adaptation takes place and twice a...
typename GridView::template Codim< 0 >::Entity Cell
The type of cells (entities of codimension 0) in the grid view.
Definition gridBasics.hh:35
Status
The argument type of the signal that is emitted before and after grid adaptation.
bool makePositiveSemiDefinite(Dune::FieldMatrix< Scalar, d, d > &A)
Computes a positive semi-definite approximation to by cutting off negative eigenvalues.
void parallelFor(Func const &f, int maxTasks=std::numeric_limits< int >::max())
A parallel for loop that executes the given functor in parallel on different CPUs.
Definition threading.hh:502
T & removeConst(T const &t)
A convenience template for removing the const qualifier from references and pointers.
Definition typeTraits.hh:30
void vector(char const *phase, char const *direction, int id, int peer, char const *stage, std::vector< T > const &data)
bool considerFace(Face const &face, std::vector< int > const &boundaryIds, std::vector< int > const &usedIds)
OutIter vectorToSequence(double x, OutIter iter)
Definition crsutil.hh:30
void moveEvaluatorsToCell(Evaluators &evals, Cell const &cell)
Moves all provided evaluators to the given cell.
constexpr bool symmetryCheck
Specialize this template for your matrix type in order to use it with VariationalFunctionalAssembler:...
A class that provides access to signals that are emitted from the grid manager on various occasions.
A class template that supports converting certain Dune matrices into the coordinate (triplet) format.
Definition crsutil.hh:177
BlockToSequence(int &rbegin_, int &rend_, DataOutIter &out_)
BlockToTriplet(size_t rbegin_, size_t cbegin_, std::vector< size_t > const &rowOff_, std::vector< size_t > const &colOff_, IdxOutIter &ri_, IdxOutIter &ci_, DataOutIter &xi_, bool extractOnlyLowerTriangle_)
CountNonzeros(size_t &n_, size_t rbegin_, size_t rend_, size_t cbegin_, size_t cend_, bool onlyLowerTriangle_)
Variables and their descriptions.