GCC Code Coverage Report


Directory: ../../../builds/dumux-repositories/
File: dumux/dumux/experimental/assembly/subdomaincvfelocalassembler.hh
Date: 2026-10-17 21:47:00
Exec Total Coverage
Lines: 83 86 96.5%
Functions: 12 12 100.0%
Branches: 42 61 68.9%

Line Branch Exec Source
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 //
7 /*!
8 * \file
9 * \ingroup Experimental
10 * \ingroup Assembly
11 * \ingroup CVFEDiscretization
12 * \ingroup MultiDomain
13 * \brief An assembler for Jacobian and residual contribution per element (CVFE methods) for multidomain problems
14 */
15 #ifndef DUMUX_EXPERIMENTAL_SUBDOMAIN_CVFE_LOCAL_ASSEMBLER_HH
16 #define DUMUX_EXPERIMENTAL_SUBDOMAIN_CVFE_LOCAL_ASSEMBLER_HH
17
18 #include <type_traits>
19
20 #include <dune/common/reservedvector.hh>
21 #include <dune/common/indices.hh>
22 #include <dune/common/hybridutilities.hh>
23 #include <dune/grid/common/gridenums.hh> // for GhostEntity
24 #include <dune/istl/matrixindexset.hh>
25
26 #include <dumux/common/typetraits/localdofs_.hh>
27 #include <dumux/common/reservedblockvector.hh>
28 #include <dumux/common/properties.hh>
29 #include <dumux/common/parameters.hh>
30 #include <dumux/common/numericdifferentiation.hh>
31 #include <dumux/common/numeqvector.hh>
32 #include <dumux/assembly/numericepsilon.hh>
33 #include <dumux/assembly/diffmethod.hh>
34 #include <dumux/discretization/extrusion.hh>
35
36 #include <dumux/experimental/assembly/cvfelocalassembler.hh>
37
38 namespace Dumux::Experimental {
39
40 /*!
41 * \ingroup Experimental
42 * \ingroup Assembly
43 * \ingroup CVFEDiscretization
44 * \ingroup MultiDomain
45 * \brief A base class for all CVFE subdomain local assemblers
46 * \tparam id the id of the sub domain
47 * \tparam TypeTag the TypeTag
48 * \tparam Assembler the assembler type
49 * \tparam Implementation the actual implementation type
50 */
51 template<std::size_t id, class TypeTag, class Assembler, class Implementation, DiffMethod dm>
52 1694032 class SubDomainCVFELocalAssemblerBase : public Experimental::CVFELocalAssembler<TypeTag, Assembler, dm, Implementation>
53 {
54 using ParentType = Experimental::CVFELocalAssembler<TypeTag, Assembler, dm, Implementation>;
55
56 using Problem = GetPropType<TypeTag, Properties::Problem>;
57 using LocalResidualValues = Dumux::NumEqVector<GetPropType<TypeTag, Properties::PrimaryVariables>>;
58 using JacobianMatrix = GetPropType<TypeTag, Properties::JacobianMatrix>;
59 using SolutionVector = typename Assembler::SolutionVector;
60 using ElementBoundaryTypes = GetPropType<TypeTag, Properties::ElementBoundaryTypes>;
61
62 using GridVariables = GetPropType<TypeTag, Properties::GridVariables>;
63 using GridVolumeVariables = typename GridVariables::GridVolumeVariables;
64 using ElementVolumeVariables = typename GridVolumeVariables::LocalView;
65 using ElementFluxVariablesCache = typename GridVariables::GridFluxVariablesCache::LocalView;
66 using Scalar = typename GridVariables::Scalar;
67
68 using GridGeometry = typename GridVariables::GridGeometry;
69 using FVElementGeometry = typename GridGeometry::LocalView;
70 using SubControlVolume = typename GridGeometry::SubControlVolume;
71 using SubControlVolumeFace = typename GridGeometry::SubControlVolumeFace;
72 using Extrusion = Extrusion_t<GridGeometry>;
73 using GridView = typename GridGeometry::GridView;
74 using Element = typename GridView::template Codim<0>::Entity;
75
76 using CouplingManager = typename Assembler::CouplingManager;
77
78 static constexpr auto numEq = GetPropType<TypeTag, Properties::ModelTraits>::numEq();
79
80 public:
81 //! export the domain id of this sub-domain
82 static constexpr auto domainId = typename Dune::index_constant<id>();
83 //! pull up constructor of parent class
84 using ParentType::ParentType;
85 //! export element residual vector type
86 using ElementResidualVector = typename ParentType::LocalResidual::ElementResidualVector;
87
88 // the constructor
89 847016 explicit SubDomainCVFELocalAssemblerBase(
90 const Assembler& assembler,
91 const Element& element,
92 const SolutionVector& curSol,
93 CouplingManager& couplingManager
94 )
95 : ParentType(assembler,
96 element,
97 curSol,
98 localView(assembler.gridGeometry(domainId)),
99 847016 localView(assembler.gridVariables(domainId).curGridVolVars()),
100 847016 localView(assembler.gridVariables(domainId).gridFluxVarsCache()),
101 847016 assembler.localResidual(domainId),
102 847016 (element.partitionType() == Dune::GhostEntity),
103 847016 assembler.isImplicit())
104
1/2
✓ Branch 4 taken 847016 times.
✗ Branch 5 not taken.
1694032 , couplingManager_(couplingManager)
105 847016 {}
106
107 /*!
108 * \brief Computes the derivatives with respect to the given element and adds them
109 * to the global matrix. The element residual is written into the right hand side.
110 */
111 template<class JacobianMatrixRow, class SubResidualVector, class GridVariablesTuple, class StageParams>
112 569096 void assembleJacobianAndResidual(JacobianMatrixRow& jacRow, SubResidualVector& res, GridVariablesTuple& gridVariables,
113 const StageParams& stageParams, SubResidualVector& temporal, SubResidualVector& spatial,
114 SubResidualVector& constrainedDofs)
115 {
116 569096 auto assembleCouplingBlocks = [&](const auto& residual)
117 {
118 // assemble the coupling blocks
119 using namespace Dune::Hybrid;
120 569096 forEach(integralRange(Dune::Hybrid::size(jacRow)), [&](auto&& i)
121 {
122 if constexpr (std::decay_t<decltype(i)>{} != id)
123 569096 this->assembleJacobianCoupling(i, jacRow, residual, gridVariables);
124 });
125 };
126
127 // the coupled model does not support partial reassembly yet
128 569096 const DefaultPartialReassembler* noReassembler = nullptr;
129 569096 ParentType::assembleJacobianAndResidual(
130 569096 jacRow[domainId], res,
131
1/2
✓ Branch 1 taken 569096 times.
✗ Branch 2 not taken.
569096 *std::get<domainId>(gridVariables),
132 stageParams, temporal, spatial,
133 constrainedDofs,
134 noReassembler,
135 assembleCouplingBlocks
136 );
137 }
138
139 /*!
140 * \brief Assemble the entries in a coupling block of the jacobian.
141 * There is no coupling block between a domain and itself.
142 */
143 template<std::size_t otherId, class JacRow, class GridVariables>
144 569096 void assembleJacobianCoupling(Dune::index_constant<otherId> domainJ, JacRow& jacRow,
145 const ElementResidualVector& res, GridVariables& gridVariables)
146 {
147 if constexpr (id != otherId)
148 569096 this->asImp_().assembleJacobianCoupling(domainJ, jacRow[domainJ], res, *std::get<domainId>(gridVariables));
149 }
150
151 /*!
152 * \brief Prepares all local views necessary for local assembly.
153 */
154 847016 void bindLocalViews()
155 {
156 // get some references for convenience
157 847016 const auto& element = this->element();
158 847016 const auto& curSol = this->curSol(domainId);
159 847016 auto&& fvGeometry = this->fvGeometry();
160 847016 auto&& curElemVolVars = this->curElemVolVars();
161 847016 auto&& elemFluxVarsCache = this->elemFluxVarsCache();
162
163 // bind the caches
164 847016 couplingManager_.bindCouplingContext(domainId, element, this->assembler());
165 847016 fvGeometry.bind(element);
166
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 847016 times.
847016 if (this->localResidual().skipSpatial())
167 ✗ curElemVolVars.bindElement(element, fvGeometry, curSol);
168 else
169 {
170 847016 curElemVolVars.bind(element, fvGeometry, curSol);
171 847016 elemFluxVarsCache.bind(element, fvGeometry, curElemVolVars);
172 }
173
174 847016 this->elemBcTypes().update(problem(), this->element(), this->fvGeometry());
175 847016 }
176
177 //! return reference to the underlying problem
178 template<std::size_t i = domainId>
179 6874897 const Problem& problem(Dune::index_constant<i> dId = domainId) const
180
6/15
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 10 taken 2 times.
✓ Branch 11 taken 5 times.
✓ Branch 14 taken 2 times.
✓ Branch 15 taken 5 times.
✓ Branch 17 taken 2 times.
✓ Branch 18 taken 5 times.
✗ Branch 5 not taken.
✗ Branch 8 not taken.
✗ Branch 12 not taken.
✗ Branch 16 not taken.
✗ Branch 19 not taken.
6890881 { return this->assembler().problem(domainId); }
181
182 //! return reference to the underlying problem
183 template<std::size_t i = domainId>
184 1985208 const auto& curSol(Dune::index_constant<i> dId = domainId) const
185
2/2
✓ Branch 1 taken 7 times.
✓ Branch 2 taken 569089 times.
1416112 { return ParentType::curSol()[dId]; }
186
187 //! return reference to the coupling manager
188 12071976 CouplingManager& couplingManager()
189 13213631 { return couplingManager_; }
190
191 private:
192 CouplingManager& couplingManager_; //!< the coupling manager
193 };
194
195 /*!
196 * \ingroup Experimental
197 * \ingroup Assembly
198 * \ingroup CVFEDiscretization
199 * \ingroup MultiDomain
200 * \brief The CVFE scheme multidomain local assembler
201 * \tparam id the id of the sub domain
202 * \tparam TypeTag the TypeTag
203 * \tparam Assembler the assembler type
204 * \tparam DM the numeric differentiation method
205 */
206 template<std::size_t id, class TypeTag, class Assembler, DiffMethod DM = DiffMethod::numeric>
207 class SubDomainCVFELocalAssembler;
208
209 /*!
210 * \ingroup Experimental
211 * \ingroup Assembly
212 * \ingroup CVFEDiscretization
213 * \ingroup MultiDomain
214 * \brief CVFE scheme multi domain local assembler using numeric differentiation
215 */
216 template<std::size_t id, class TypeTag, class Assembler>
217 847016 class SubDomainCVFELocalAssembler<id, TypeTag, Assembler, DiffMethod::numeric>
218 : public SubDomainCVFELocalAssemblerBase<id, TypeTag, Assembler,
219 SubDomainCVFELocalAssembler<id, TypeTag, Assembler, DiffMethod::numeric>, DiffMethod::numeric>
220 {
221 using ThisType = SubDomainCVFELocalAssembler<id, TypeTag, Assembler, DiffMethod::numeric>;
222 using ParentType = SubDomainCVFELocalAssemblerBase<id, TypeTag, Assembler, ThisType, DiffMethod::numeric>;
223 using VolumeVariables = GetPropType<TypeTag, Properties::VolumeVariables>;
224
225 using ElementVolumeVariables = typename GetPropType<TypeTag, Properties::GridVolumeVariables>::LocalView;
226 using GridGeometry = GetPropType<TypeTag, Properties::GridGeometry>;
227 using FVElementGeometry = typename GridGeometry::LocalView;
228 using SubControlVolume = typename GridGeometry::SubControlVolume;
229 using GridView = typename GridGeometry::GridView;
230 using Element = typename GridView::template Codim<0>::Entity;
231 using Problem = GetPropType<TypeTag, Properties::Problem>;
232
233 static constexpr int numEq = GetPropType<TypeTag, Properties::ModelTraits>::numEq();
234 static constexpr int dim = GridView::dimension;
235
236 static constexpr bool enableGridFluxVarsCache = GetPropType<TypeTag, Properties::GridVariables>::GridFluxVariablesCache::cachingEnabled;
237 static constexpr bool enableGridVolVarsCache = GetPropType<TypeTag, Properties::GridVariables>::GridVolumeVariables::cachingEnabled;
238 static constexpr auto domainI = Dune::index_constant<id>();
239
240 public:
241 847016 using ParentType::ParentType;
242 //! export element residual vector type
243 using ElementResidualVector = typename ParentType::LocalResidual::ElementResidualVector;
244
245 /*!
246 * \brief Update the coupling context for coupled models.
247 */
248 template<class ScvOrLocalDof, class ElemSol>
249 9216128 void maybeUpdateCouplingContext(const ScvOrLocalDof& scvOrLocalDof, ElemSol& elemSol, const int pvIdx)
250 {
251
1/2
✓ Branch 1 taken 9216128 times.
✗ Branch 2 not taken.
9216128 if (this->assembler().isImplicit())
252 9216128 this->couplingManager().updateCouplingContext(domainI, *this, domainI, scvOrLocalDof.dofIndex(), elemSol[Dumux::Detail::LocalDofs::index(scvOrLocalDof)], pvIdx);
253 9216128 }
254
255 /*!
256 * \brief Update the additional domain derivatives for coupled models.
257 */
258 template<class JacobianMatrixDiagBlock, class GridVariables>
259 569096 void maybeEvalAdditionalDomainDerivatives(const ElementResidualVector& origResiduals, JacobianMatrixDiagBlock& A, GridVariables& gridVariables)
260 {
261 569096 if (this->assembler().isImplicit())
262 this->couplingManager().evalAdditionalDomainDerivatives(domainI, *this, origResiduals, A, gridVariables);
263 }
264
265 /*!
266 * \brief Computes the derivatives with respect to the given element and adds them
267 * to the global matrix.
268 */
269 template<std::size_t otherId, class JacobianBlock, class GridVariables>
270 569096 void assembleJacobianCoupling(Dune::index_constant<otherId> domainJ, JacobianBlock& A,
271 const ElementResidualVector& res, GridVariables& gridVariables)
272 {
273
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 569096 times.
569096 if (!this->assembler().isImplicit())
274 ✗ return;
275
276 ////////////////////////////////////////////////////////////////////////////////////////////////////////
277 // Calculate derivatives of all dofs in the element with respect to all dofs in the coupling stencil. //
278 ////////////////////////////////////////////////////////////////////////////////////////////////////////
279
280 // get some aliases for convenience
281 569096 const auto& element = this->element();
282 569096 const auto& fvGeometry = this->fvGeometry();
283 569096 auto&& curElemVolVars = this->curElemVolVars();
284 569096 auto&& elemFluxVarsCache = this->elemFluxVarsCache();
285
286 // convenience lambda for call to update self
287 2852392 auto updateCoupledVariables = [&] ()
288 {
289 // Update ourself after the context has been modified. Depending on the
290 // type of caching, other objects might have to be updated. All ifs can be optimized away.
291 if constexpr (enableGridFluxVarsCache)
292 {
293 if constexpr (enableGridVolVarsCache)
294 this->couplingManager().updateCoupledVariables(domainI, *this, gridVariables.curGridVolVars(), gridVariables.gridFluxVarsCache());
295 else
296 this->couplingManager().updateCoupledVariables(domainI, *this, curElemVolVars, gridVariables.gridFluxVarsCache());
297 }
298 else
299 {
300 if constexpr (enableGridVolVarsCache)
301 this->couplingManager().updateCoupledVariables(domainI, *this, gridVariables.curGridVolVars(), elemFluxVarsCache);
302 else
303 1141648 this->couplingManager().updateCoupledVariables(domainI, *this, curElemVolVars, elemFluxVarsCache);
304 }
305 };
306
307 // get element stencil information
308 569096 const auto& stencil = this->couplingManager().couplingStencil(domainI, element, domainJ);
309 569096 const auto& curSolJ = this->curSol(domainJ);
310
2/2
✓ Branch 0 taken 569096 times.
✓ Branch 1 taken 569096 times.
1138192 for (const auto globalJ : stencil)
311 {
312 // undeflected privars and privars to be deflected
313 569096 const auto origPriVarsJ = curSolJ[globalJ];
314 569096 auto priVarsJ = origPriVarsJ;
315
316 // the undeflected coupling residual
317 569096 const auto origResidual = this->couplingManager().evalCouplingResidual(domainI, *this, domainJ, globalJ);
318
319
2/2
✓ Branch 0 taken 572552 times.
✓ Branch 1 taken 569096 times.
1141648 for (int pvIdx = 0; pvIdx < JacobianBlock::block_type::cols; ++pvIdx)
320 {
321
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 572545 times.
572552 auto evalCouplingResidual = [&](auto priVar)
322 {
323 572552 priVarsJ[pvIdx] = priVar;
324 572552 this->couplingManager().updateCouplingContext(domainI, *this, domainJ, globalJ, priVarsJ, pvIdx);
325 572552 updateCoupledVariables();
326 572552 return this->couplingManager().evalCouplingResidual(domainI, *this, domainJ, globalJ);
327 };
328
329 // derive the residuals numerically
330
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 572545 times.
572552 ElementResidualVector partialDerivs(fvGeometry.numScv());
331
332
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 572545 times.
572552 const auto& paramGroup = this->assembler().problem(domainJ).paramGroup();
333
4/6
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 572545 times.
✓ Branch 3 taken 7 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 7 times.
✗ Branch 7 not taken.
572552 static const int numDiffMethod = getParamFromGroup<int>(paramGroup, "Assembly.NumericDifferenceMethod");
334
4/6
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 572545 times.
✓ Branch 3 taken 7 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 7 times.
✗ Branch 7 not taken.
572552 static const auto epsCoupl = this->couplingManager().numericEpsilon(domainJ, paramGroup);
335
336 572552 NumericDifferentiation::partialDerivative(evalCouplingResidual, origPriVarsJ[pvIdx], partialDerivs, origResidual,
337 572552 epsCoupl(origPriVarsJ[pvIdx], pvIdx), numDiffMethod);
338
339 // update the global stiffness matrix with the current partial derivatives
340
2/2
✓ Branch 0 taken 2317856 times.
✓ Branch 1 taken 572552 times.
2890408 for (const auto& scv : scvs(fvGeometry))
341 {
342
2/2
✓ Branch 0 taken 4691008 times.
✓ Branch 1 taken 2317856 times.
7008864 for (int eqIdx = 0; eqIdx < numEq; eqIdx++)
343 {
344 // A[i][col][eqIdx][pvIdx] is the rate of change of
345 // the residual of equation 'eqIdx' at dof 'i'
346 // depending on the primary variable 'pvIdx' at dof
347 // 'col'.
348
2/2
✓ Branch 1 taken 1725712 times.
✓ Branch 2 taken 2965296 times.
4691008 A[scv.dofIndex()][globalJ][eqIdx][pvIdx] += partialDerivs[scv.localDofIndex()][eqIdx];
349
350 // If the dof is coupled by a Dirichlet condition,
351 // set the derived value only once (i.e. overwrite existing values).
352
2/2
✓ Branch 0 taken 1725712 times.
✓ Branch 1 taken 2965296 times.
4691008 if (this->elemBcTypes().hasDirichlet())
353 {
354 1725712 const auto bcTypes = this->elemBcTypes().get(fvGeometry, scv);
355
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1725712 times.
1725712 if (bcTypes.isCouplingDirichlet(eqIdx))
356 ✗ A[scv.dofIndex()][globalJ][eqIdx][pvIdx] = partialDerivs[scv.localDofIndex()][eqIdx];
357
2/2
✓ Branch 0 taken 499356 times.
✓ Branch 1 taken 1226356 times.
2225068 else if (bcTypes.isDirichlet(eqIdx))
358 499356 A[scv.dofIndex()][globalJ][eqIdx][pvIdx] = 0.0;
359 }
360
361 // enforce internal Dirichlet constraints
362 if constexpr (Problem::enableInternalDirichletConstraints())
363 {
364 const auto internalDirichletConstraints = this->problem().hasInternalDirichletConstraint(this->element(), scv);
365 if (internalDirichletConstraints[eqIdx])
366 A[scv.dofIndex()][globalJ][eqIdx][pvIdx] = 0.0;
367 }
368
369 }
370 }
371
372 // restore the current element solution
373 572552 priVarsJ[pvIdx] = origPriVarsJ[pvIdx];
374
375 // restore the undeflected state of the coupling context
376 572552 this->couplingManager().updateCouplingContext(domainI, *this, domainJ, globalJ, priVarsJ, pvIdx);
377 }
378
379 // Restore original state of the flux vars cache and/or vol vars.
380 // This has to be done in case they depend on variables of domainJ before
381 // we continue with the numeric derivative w.r.t the next globalJ. Otherwise,
382 // the next "origResidual" will be incorrect.
383 569096 updateCoupledVariables();
384 }
385 }
386 };
387
388 } // end namespace Dumux
389
390 #endif
391