version 3.11-dev
assembly/assembler.hh
Go to the documentation of this file.
1// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2// vi: set et ts=4 sw=4 sts=4:
3//
4// SPDX-FileCopyrightText: Copyright © DuMux Project contributors, see AUTHORS.md in root folder
5// SPDX-License-Identifier: GPL-3.0-or-later
6//
12#ifndef DUMUX_ASSEMBLER_HH
13#define DUMUX_ASSEMBLER_HH
14
15#include <cassert>
16#include <iostream>
17#include <vector>
18#include <deque>
19#include <memory>
20#include <utility>
21
22#include <dune/common/std/type_traits.hh>
23#include <dune/grid/common/rangegenerators.hh>
24
31
34
38
41
42#include "cvfelocalassembler_.hh"
43
45
46template<class DiscretizationMethod>
48
49template<class DM>
50struct LocalAssemblerChooser<DiscretizationMethods::CVFE<DM>>
51{
52 template<class TypeTag, class Impl, DiffMethod diffMethod, bool isImplicit>
54};
55
56template<class TypeTag, class Impl, DiffMethod diffMethod, bool isImplicit>
59>::template type<TypeTag, Impl, diffMethod, isImplicit>;
60
62template<class P>
63using ProblemConstraintsDetector = decltype(std::declval<P>().constraints());
64
65template<class P>
66constexpr inline bool hasGlobalConstraints()
67{ return Dune::Std::is_detected<ProblemConstraintsDetector, P>::value; }
68
69} // end namespace Dumux::Experimental::Detail
70
71namespace Dumux::Experimental {
72
81template<class TypeTag, DiffMethod diffMethod, bool isImplicit = true, class LocalResidual = GetPropType<TypeTag, Properties::LocalResidual>>
83{
85 using GridView = typename GridGeo::GridView;
86 using Element = typename GridView::template Codim<0>::Entity;
87 using ElementSeed = typename GridView::Grid::template Codim<0>::EntitySeed;
89
92
93public:
98
100
101 using GridGeometry = GridGeo;
103
109 Assembler(std::shared_ptr<const Problem> problem,
110 std::shared_ptr<const GridGeometry> gridGeometry,
111 std::shared_ptr<GridVariables> gridVariables)
112 : problem_(problem)
113 , gridGeometry_(gridGeometry)
114 , gridVariables_(gridVariables)
115 , timeLoop_()
116 , isStationaryProblem_(true)
117 {
118 static_assert(isImplicit, "Explicit assembler for stationary problem doesn't make sense!");
120 && Grid::Capabilities::supportsMultithreading(gridGeometry_->gridView())
122 && getParam<bool>("Assembly.Multithreading", true);
123
124 maybeComputeColors_();
125 }
126
132 Assembler(std::shared_ptr<const Problem> problem,
133 std::shared_ptr<const GridGeometry> gridGeometry,
134 std::shared_ptr<GridVariables> gridVariables,
135 std::shared_ptr<const TimeLoop> timeLoop,
136 const SolutionVector& prevSol)
137 : problem_(problem)
138 , gridGeometry_(gridGeometry)
139 , gridVariables_(gridVariables)
140 , timeLoop_(timeLoop)
141 , prevSol_(&prevSol)
142 , isStationaryProblem_(!timeLoop)
143 {
145 && Grid::Capabilities::supportsMultithreading(gridGeometry_->gridView())
147 && getParam<bool>("Assembly.Multithreading", true);
148
149 maybeComputeColors_();
150 }
151
156 template<class PartialReassembler = DefaultPartialReassembler>
157 void assembleJacobianAndResidual(const SolutionVector& curSol, const PartialReassembler* partialReassembler = nullptr)
158 {
159 checkAssemblerState_();
160 resetJacobian_(partialReassembler);
161 resetResidual_();
162
163 assemble_([&](const Element& element)
164 {
165 LocalAssembler localAssembler(*this, element, curSol);
166 localAssembler.assembleJacobianAndResidual(*jacobian_, *residual_, *gridVariables_, partialReassembler);
167 });
168
169 enforcePeriodicConstraints_(*jacobian_, *residual_, curSol, *gridGeometry_);
170
171 auto applyDirichletConstraint = [&] (const auto& dofIdx,
172 const auto& values,
173 const auto eqIdx,
174 const auto pvIdx)
175 {
176 (*residual_)[dofIdx][eqIdx] = curSol[dofIdx][pvIdx] - values[pvIdx];
177
178 auto& row = (*jacobian_)[dofIdx];
179 for (auto col = row.begin(); col != row.end(); ++col)
180 row[col.index()][eqIdx] = 0.0;
181
182 (*jacobian_)[dofIdx][dofIdx][eqIdx][pvIdx] = 1.0;
183 };
184 enforceProblemConstraints_(*problem_, *gridGeometry_, applyDirichletConstraint);
185 }
186
191 {
192 checkAssemblerState_();
193 resetJacobian_();
194
195 assemble_([&](const Element& element)
196 {
197 LocalAssembler localAssembler(*this, element, curSol);
198 localAssembler.assembleJacobian(*jacobian_, *gridVariables_);
199 });
200
201 enforcePeriodicConstraints_(*jacobian_, curSol, *gridGeometry_);
202
203 auto applyDirichletConstraint = [&] (const auto& dofIdx,
204 const auto& values,
205 const auto eqIdx,
206 const auto pvIdx)
207 {
208 auto& row = (*jacobian_)[dofIdx];
209 for (auto col = row.begin(); col != row.end(); ++col)
210 row[col.index()][eqIdx] = 0.0;
211
212 (*jacobian_)[dofIdx][dofIdx][eqIdx][pvIdx] = 1.0;
213 };
214 enforceProblemConstraints_(*problem_, *gridGeometry_, applyDirichletConstraint);
215 }
216
219 {
220 resetResidual_();
221 assembleResidual(*residual_, curSol);
222 }
223
225 void assembleResidual(ResidualType& r, const SolutionVector& curSol) const
226 {
227 checkAssemblerState_();
228
229 assemble_([&](const Element& element)
230 {
231 LocalAssembler localAssembler(*this, element, curSol);
232 localAssembler.assembleResidual(r);
233 });
234
235 enforcePeriodicConstraints_(r, curSol, *gridGeometry_);
236
237 auto applyDirichletConstraint = [&] (const auto& dofIdx,
238 const auto& values,
239 const auto eqIdx,
240 const auto pvIdx)
241 {
242 r[dofIdx][eqIdx] = curSol[dofIdx][pvIdx] - values[pvIdx];
243 };
244 enforceProblemConstraints_(*problem_, *gridGeometry_, applyDirichletConstraint);
245 }
246
252 void setLinearSystem(std::shared_ptr<JacobianMatrix> A,
253 std::shared_ptr<ResidualType> r)
254 {
255 jacobian_ = A;
256 residual_ = r;
257
258 // check and/or set the BCRS matrix's build mode
259 if (jacobian_->buildMode() == JacobianMatrix::BuildMode::unknown)
260 jacobian_->setBuildMode(JacobianMatrix::random);
261 else if (jacobian_->buildMode() != JacobianMatrix::BuildMode::random)
262 DUNE_THROW(Dune::NotImplemented, "Only BCRS matrices with random build mode are supported at the moment");
263
264 setResidualSize_();
265 setJacobianPattern_();
266 }
267
273 {
274 jacobian_ = std::make_shared<JacobianMatrix>();
275 jacobian_->setBuildMode(JacobianMatrix::random);
276 residual_ = std::make_shared<ResidualType>();
277
278 setResidualSize_();
279 setJacobianPattern_();
280 }
281
286 {
287 setResidualSize_();
288 setJacobianPattern_();
289 maybeComputeColors_();
290 }
291
293 std::size_t numDofs() const
294 { return gridGeometry_->numDofs(); }
295
297 const Problem& problem() const
298 { return *problem_; }
299
302 { return *gridGeometry_; }
303
305 const GridView& gridView() const
306 { return gridGeometry().gridView(); }
307
310 { return *gridVariables_; }
311
314 { return *gridVariables_; }
315
318 { return *jacobian_; }
319
322 { return *residual_; }
323
325 const SolutionVector& prevSol() const
326 { return *prevSol_; }
327
332 void setTimeLoop(std::shared_ptr<const TimeLoop> timeLoop)
333 { timeLoop_ = timeLoop; isStationaryProblem_ = !static_cast<bool>(timeLoop); }
334
340 { prevSol_ = &u; }
341
346 { return isStationaryProblem_; }
347
352 { return LocalResidual(problem_.get(), timeLoop_.get()); }
353
358 {
359 gridVariables().update(curSol);
360 }
361
365 void resetTimeStep(const SolutionVector& curSol)
366 {
367 gridVariables().resetTimeStep(curSol);
368 }
369
370private:
374 void setJacobianPattern_()
375 {
376 // resize the jacobian and the residual
377 const auto numDofs = this->numDofs();
378 jacobian_->setSize(numDofs, numDofs);
379
380 // create occupation pattern of the jacobian
381 const auto occupationPattern = getJacobianPattern<isImplicit>(gridGeometry());
382
383 // export pattern to jacobian
384 occupationPattern.exportIdx(*jacobian_);
385 }
386
388 void setResidualSize_()
389 { residual_->resize(numDofs()); }
390
392 void maybeComputeColors_()
393 {
394 if (enableMultithreading_)
395 elementSets_ = computeColoring(gridGeometry()).sets;
396 }
397
398 // reset the residual vector to 0.0
399 void resetResidual_()
400 {
401 if(!residual_)
402 {
403 residual_ = std::make_shared<ResidualType>();
404 setResidualSize_();
405 }
406
407 (*residual_) = 0.0;
408 }
409
410 // reset the Jacobian matrix to 0.0
411 template <class PartialReassembler = DefaultPartialReassembler>
412 void resetJacobian_(const PartialReassembler *partialReassembler = nullptr)
413 {
414 if(!jacobian_)
415 {
416 jacobian_ = std::make_shared<JacobianMatrix>();
417 jacobian_->setBuildMode(JacobianMatrix::random);
418 setJacobianPattern_();
419 }
420
421 if (partialReassembler)
422 partialReassembler->resetJacobian(*this);
423 else
424 *jacobian_ = 0.0;
425 }
426
427 // check if the assembler is in a correct state for assembly
428 void checkAssemblerState_() const
429 {
430 if (!isStationaryProblem_ && !prevSol_)
431 DUNE_THROW(Dune::InvalidStateException, "Assembling instationary problem but previous solution was not set!");
432 }
433
439 template<typename AssembleElementFunc>
440 void assemble_(AssembleElementFunc&& assembleElement) const
441 {
442 // a state that will be checked on all processes
443 bool succeeded = false;
444
445 // try assembling using the local assembly function
446 try
447 {
448 if (enableMultithreading_)
449 {
450 assert(elementSets_.size() > 0);
451
452 // make this element loop run in parallel
453 // for this we have to color the elements so that we don't get
454 // race conditions when writing into the global matrix
455 // each color can be assembled using multiple threads
456 for (const auto& elements : elementSets_)
457 {
458 Dumux::parallelFor(elements.size(), [&](const std::size_t i)
459 {
460 const auto element = gridView().grid().entity(elements[i]);
461 assembleElement(element);
462 });
463 }
464 }
465 else
466 for (const auto& element : elements(gridView()))
467 assembleElement(element);
468
469 // if we get here, everything worked well on this process
470 succeeded = true;
471 }
472 // throw exception if a problem occurred
473 catch (const NumericalProblem& e)
474 {
475 std::cout << "rank " << gridView().comm().rank()
476 << " caught an exception while assembling:" << e.what()
477 << "\n";
478 succeeded = false;
479 }
480
481 // make sure everything worked well on all processes
482 if (gridView().comm().size() > 1)
483 succeeded = gridView().comm().min(succeeded);
484
485 // if not succeeded rethrow the error on all processes
486 if (!succeeded)
487 DUNE_THROW(NumericalProblem, "A process did not succeed in linearizing the system");
488 }
489
490 template<class GG>
491 void enforcePeriodicConstraints_(JacobianMatrix& jac, ResidualType& res, const SolutionVector& curSol, const GG& gridGeometry) const
492 {
493 if constexpr (Dumux::Detail::hasPeriodicDofMap<GG>())
494 {
495 for (const auto& m : gridGeometry.periodicDofMap())
496 {
497 if (m.first < m.second)
498 {
499 // add the second row to the first
500 res[m.first] += res[m.second];
501 const auto end = jac[m.second].end();
502 for (auto it = jac[m.second].begin(); it != end; ++it)
503 jac[m.first][it.index()] += (*it);
504
505 // enforce constraint in second row
506 res[m.second] = curSol[m.second] - curSol[m.first];
507
508 // set derivatives accordingly in jacobian, i.e. id for m.second and -id for m.first
509 auto setMatrixBlock = [] (auto& matrixBlock, double diagValue)
510 {
511 for (int eIdx = 0; eIdx < matrixBlock.N(); ++eIdx)
512 matrixBlock[eIdx][eIdx] = diagValue;
513 };
514
515 for (auto it = jac[m.second].begin(); it != end; ++it)
516 {
517 auto& matrixBlock = *it;
518 matrixBlock = 0.0;
519
520 assert(matrixBlock.N() == matrixBlock.M());
521 if(it.index() == m.second)
522 setMatrixBlock(matrixBlock, 1.0);
523
524 if(it.index() == m.first)
525 setMatrixBlock(matrixBlock, -1.0);
526
527 }
528 }
529 }
530 }
531 }
532
533 template<class GG>
534 void enforcePeriodicConstraints_(JacobianMatrix& jac, const SolutionVector&, const GG& gridGeometry) const
535 {
536 if constexpr (Dumux::Detail::hasPeriodicDofMap<GG>())
537 {
538 for (const auto& m : gridGeometry.periodicDofMap())
539 {
540 if (m.first < m.second)
541 {
542 // add the second row to the first
543 const auto end = jac[m.second].end();
544 for (auto it = jac[m.second].begin(); it != end; ++it)
545 jac[m.first][it.index()] += (*it);
546
547 // set derivatives accordingly in jacobian, i.e. id for m.second and -id for m.first
548 auto setMatrixBlock = [] (auto& matrixBlock, double diagValue)
549 {
550 for (int eIdx = 0; eIdx < matrixBlock.N(); ++eIdx)
551 matrixBlock[eIdx][eIdx] = diagValue;
552 };
553
554 for (auto it = jac[m.second].begin(); it != end; ++it)
555 {
556 auto& matrixBlock = *it;
557 matrixBlock = 0.0;
558
559 assert(matrixBlock.N() == matrixBlock.M());
560 if(it.index() == m.second)
561 setMatrixBlock(matrixBlock, 1.0);
562
563 if(it.index() == m.first)
564 setMatrixBlock(matrixBlock, -1.0);
565
566 }
567 }
568 }
569 }
570 }
571
572 template<class GG>
573 void enforcePeriodicConstraints_(ResidualType& res, const SolutionVector& curSol, const GG& gridGeometry) const
574 {
575 if constexpr (Dumux::Detail::hasPeriodicDofMap<GG>())
576 {
577 for (const auto& m : gridGeometry.periodicDofMap())
578 {
579 if (m.first < m.second)
580 {
581 // add the second row to the first
582 res[m.first] += res[m.second];
583
584 // enforce constraint in second row
585 res[m.second] = curSol[m.second] - curSol[m.first];
586 }
587 }
588 }
589 }
590
591 template<class Problem, class GG, typename ApplyFunction>
592 void enforceProblemConstraints_(const Problem& problem, const GG&, const ApplyFunction& applyDirichletConstraint) const
593 {
594 if constexpr (Detail::hasGlobalConstraints<Problem>())
595 {
596 for (const auto& constraintData : problem.constraints())
597 {
598 const auto& constraintInfo = constraintData.constraintInfo();
599 const auto& values = constraintData.values();
600 const auto dofIdx = constraintData.dofIndex();
601 // set the constraint in residual and jacobian
602 for (int eqIdx = 0; eqIdx < constraintInfo.size(); ++eqIdx)
603 {
604 if (constraintInfo.isConstraintEquation(eqIdx))
605 {
606 const auto pvIdx = constraintInfo.eqToPriVarIndex(eqIdx);
607 assert(0 <= pvIdx && pvIdx < constraintInfo.size());
608 applyDirichletConstraint(dofIdx, values, eqIdx, pvIdx);
609 }
610 }
611 }
612 }
613 }
614
616 std::shared_ptr<const Problem> problem_;
617
619 std::shared_ptr<const GridGeometry> gridGeometry_;
620
622 std::shared_ptr<GridVariables> gridVariables_;
623
625 std::shared_ptr<const TimeLoop> timeLoop_;
626
628 const SolutionVector* prevSol_ = nullptr;
629
631 bool isStationaryProblem_;
632
634 std::shared_ptr<JacobianMatrix> jacobian_;
635 std::shared_ptr<ResidualType> residual_;
636
638 bool enableMultithreading_ = false;
639 std::deque<std::vector<ElementSeed>> elementSets_;
640};
641
642} // namespace Dumux::Experimental
643
644#endif
A linear system assembler (residual and Jacobian) for general discretization schemes.
Definition: assembly/assembler.hh:83
Assembler(std::shared_ptr< const Problem > problem, std::shared_ptr< const GridGeometry > gridGeometry, std::shared_ptr< GridVariables > gridVariables, std::shared_ptr< const TimeLoop > timeLoop, const SolutionVector &prevSol)
The constructor for instationary problems.
Definition: assembly/assembler.hh:132
typename Dumux::Detail::NativeDuneVectorType< SolutionVector >::type ResidualType
Definition: assembly/assembler.hh:97
void updateGridVariables(const SolutionVector &curSol)
Update the grid variables.
Definition: assembly/assembler.hh:357
GetPropType< TypeTag, Properties::GridVariables > GridVariables
Definition: assembly/assembler.hh:99
void assembleResidual(const SolutionVector &curSol)
compute the residuals using the internal residual
Definition: assembly/assembler.hh:218
Assembler(std::shared_ptr< const Problem > problem, std::shared_ptr< const GridGeometry > gridGeometry, std::shared_ptr< GridVariables > gridVariables)
The constructor for stationary problems.
Definition: assembly/assembler.hh:109
LocalResidual localResidual() const
Create a local residual object (used by the local assembler)
Definition: assembly/assembler.hh:351
ResidualType & residual()
The residual vector (rhs)
Definition: assembly/assembler.hh:321
void assembleJacobianAndResidual(const SolutionVector &curSol, const PartialReassembler *partialReassembler=nullptr)
Assembles the global Jacobian of the residual and the residual for the current solution.
Definition: assembly/assembler.hh:157
const GridGeometry & gridGeometry() const
The global finite volume geometry.
Definition: assembly/assembler.hh:301
GetPropType< TypeTag, Properties::Scalar > Scalar
Definition: assembly/assembler.hh:94
void updateAfterGridAdaption()
Resizes jacobian and residual and recomputes colors.
Definition: assembly/assembler.hh:285
const SolutionVector & prevSol() const
The solution of the previous time step.
Definition: assembly/assembler.hh:325
std::size_t numDofs() const
Returns the number of degrees of freedom.
Definition: assembly/assembler.hh:293
void assembleResidual(ResidualType &r, const SolutionVector &curSol) const
assemble a residual r
Definition: assembly/assembler.hh:225
GridGeo GridGeometry
Definition: assembly/assembler.hh:101
GetPropType< TypeTag, Properties::SolutionVector > SolutionVector
Definition: assembly/assembler.hh:96
void setTimeLoop(std::shared_ptr< const TimeLoop > timeLoop)
Set time loop for instationary problems.
Definition: assembly/assembler.hh:332
void setLinearSystem(std::shared_ptr< JacobianMatrix > A, std::shared_ptr< ResidualType > r)
Tells the assembler which jacobian and residual to use. This also resizes the containers to the requi...
Definition: assembly/assembler.hh:252
void setPreviousSolution(const SolutionVector &u)
Sets the solution from which to start the time integration. Has to be called prior to assembly for ti...
Definition: assembly/assembler.hh:339
const GridView & gridView() const
The gridview.
Definition: assembly/assembler.hh:305
GetPropType< TypeTag, Properties::Problem > Problem
Definition: assembly/assembler.hh:102
void assembleJacobian(const SolutionVector &curSol)
Assembles only the global Jacobian of the residual.
Definition: assembly/assembler.hh:190
JacobianMatrix & jacobian()
The jacobian matrix.
Definition: assembly/assembler.hh:317
bool isStationaryProblem() const
Whether we are assembling a stationary or instationary problem.
Definition: assembly/assembler.hh:345
const GridVariables & gridVariables() const
The global grid variables.
Definition: assembly/assembler.hh:313
void setLinearSystem()
The version without arguments uses the default constructor to create the jacobian and residual object...
Definition: assembly/assembler.hh:272
GetPropType< TypeTag, Properties::JacobianMatrix > JacobianMatrix
Definition: assembly/assembler.hh:95
GridVariables & gridVariables()
The global grid variables.
Definition: assembly/assembler.hh:309
void resetTimeStep(const SolutionVector &curSol)
Reset the gridVariables.
Definition: assembly/assembler.hh:365
const Problem & problem() const
The problem.
Definition: assembly/assembler.hh:297
An assembler for Jacobian and residual contribution per element (CVFE methods)
Definition: experimental/assembly/cvfelocalassembler.hh:328
The element-wise residual for grid-based discretization schemes.
Definition: assembly/localresidual.hh:37
detects which entries in the Jacobian have to be recomputed
Definition: partialreassembler.hh:420
Manages the handling of time dependent problems.
Definition: common/timeloop.hh:84
Coloring schemes for shared-memory-parallel assembly.
Defines all properties used in Dumux.
Manages the handling of time dependent problems.
An enum class to define various differentiation methods available in order to compute the derivatives...
Helper to extract native Dune vector types from particular Dumux types.
Some exceptions thrown in DuMux
dune-grid capabilities compatibility layer
constexpr bool isSerial()
Checking whether the backend is serial.
Definition: multithreading.hh:45
void parallelFor(const std::size_t count, const FunctorType &functor)
A parallel for loop (multithreading)
Definition: parallel_for.hh:160
typename GetProp< TypeTag, Property >::type GetPropType
get the type alias defined in the property
Definition: propertysystem.hh:296
Helper function to generate Jacobian pattern for different discretization methods.
The available discretization methods in Dumux.
Multithreading in Dumux.
Definition: assembly/assembler.hh:44
constexpr bool hasGlobalConstraints()
Definition: assembly/assembler.hh:66
typename LocalAssemblerChooser< typename GetPropType< TypeTag, Properties::GridGeometry >::DiscretizationMethod >::template type< TypeTag, Impl, diffMethod, isImplicit > LocalAssemblerChooser_t
Definition: assembly/assembler.hh:59
decltype(std::declval< P >().constraints()) ProblemConstraintsDetector
helper struct detecting if problem has a constraints() function
Definition: assembly/assembler.hh:63
Definition: assembly/assembler.hh:44
bool supportsMultithreading(const GridView &gridView)
Definition: gridcapabilities.hh:34
auto computeColoring(const GridGeometry &gg, int verbosity=1)
Compute iterable lists of element seeds partitioned by color.
Definition: coloring.hh:241
Parallel for loop (multithreading)
The infrastructure to retrieve run-time parameters from Dune::ParameterTrees.
Type traits to detect periodicity support.
typename NativeDuneVectorTypeImpl< V, Dune::Std::is_detected< Detail::DuneVectors::StateDetector, V >{} >::type type
Definition: dunevectors.hh:59
Definition: assembly/assembler.hh:47
Traits specifying if a given discretization tag supports coloring.
Definition: coloring.hh:294