GCC Code Coverage Report


Directory: ../../../builds/dumux-repositories/
File: /builds/dumux-repositories/dumux/dumux/experimental/assembly/cvfelocalassembler.hh
Date: 2024-05-04 19:09:25
Exec Total Coverage
Lines: 102 159 64.2%
Functions: 19 45 42.2%
Branches: 97 339 28.6%

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-FileCopyrightInfo: 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 * \brief An assembler for Jacobian and residual contribution per element (CVFE methods)
13 */
14 #ifndef DUMUX_EXPERIMENTAL_CVFE_LOCAL_ASSEMBLER_HH
15 #define DUMUX_EXPERIMENTAL_CVFE_LOCAL_ASSEMBLER_HH
16
17 #include <dune/common/exceptions.hh>
18 #include <dune/common/hybridutilities.hh>
19 #include <dune/common/reservedvector.hh>
20 #include <dune/grid/common/gridenums.hh>
21 #include <dune/istl/matrixindexset.hh>
22 #include <dune/istl/bvector.hh>
23
24 #include <dumux/common/typetraits/typetraits.hh>
25 #include <dumux/common/properties.hh>
26 #include <dumux/common/parameters.hh>
27 #include <dumux/common/numericdifferentiation.hh>
28
29 #include <dumux/assembly/numericepsilon.hh>
30 #include <dumux/assembly/diffmethod.hh>
31 #include <dumux/experimental/assembly/fvlocalassemblerbase.hh>
32 #include <dumux/assembly/partialreassembler.hh>
33 #include <dumux/assembly/entitycolor.hh>
34
35 #include <dumux/discretization/cvfe/elementsolution.hh>
36
37 #include <dumux/assembly/volvardeflectionhelper_.hh>
38
39 namespace Dumux::Experimental {
40
41 /*!
42 * \ingroup Experimental
43 * \ingroup Assembly
44 * \ingroup CVFEDiscretization
45 * \brief A base class for all local CVFE assemblers
46 * \tparam TypeTag The TypeTag
47 * \tparam Assembler The assembler type
48 * \tparam Implementation The actual implementation
49 */
50 template<class TypeTag, class Assembler, class Implementation>
51 51968 class CVFELocalAssemblerBase : public Experimental::FVLocalAssemblerBase<TypeTag, Assembler, Implementation>
52 {
53 using ParentType = Experimental::FVLocalAssemblerBase<TypeTag, Assembler, Implementation>;
54 using JacobianMatrix = GetPropType<TypeTag, Properties::JacobianMatrix>;
55 using GridVariables = GetPropType<TypeTag, Properties::GridVariables>;
56 using Problem = GetPropType<TypeTag, Properties::Problem>;
57 using ElementVolumeVariables = typename GridVariables::GridVolumeVariables::LocalView;
58 using SolutionVector = typename Assembler::SolutionVector;
59
60 static constexpr int numEq = GetPropType<TypeTag, Properties::ModelTraits>::numEq();
61 static constexpr int dim = GetPropType<TypeTag, Properties::GridGeometry>::GridView::dimension;
62
63 public:
64
65 38528 using ParentType::ParentType;
66
67 using LocalResidual = typename ParentType::LocalResidual;
68 using ElementResidualVector = typename LocalResidual::ElementResidualVector;
69
70
71 33792 void bindLocalViews()
72 {
73 33792 ParentType::bindLocalViews();
74 135168 this->elemBcTypes().update(this->asImp_().problem(), this->element(), this->fvGeometry());
75 33792 }
76
77 /*!
78 * \brief Computes the derivatives with respect to the given element and adds them
79 * to the global matrix. The element residual is written into the right hand side.
80 */
81 template <class ResidualVector, class StageParams, class PartialReassembler = DefaultPartialReassembler, class CouplingFunction = Noop>
82 3456 void assembleJacobianAndResidual(JacobianMatrix& jac, ResidualVector& res, GridVariables& gridVariables,
83 const StageParams& stageParams, ResidualVector& temporal, ResidualVector& spatial,
84 ResidualVector& constrainedDofs,
85 const PartialReassembler* partialReassembler = nullptr,
86 const CouplingFunction& maybeAssembleCouplingBlocks = noop)
87 {
88
0/2
✗ Branch 1 not taken.
✗ Branch 2 not taken.
3456 this->asImp_().bindLocalViews();
89 13824 const auto eIdxGlobal = this->asImp_().problem().gridGeometry().elementMapper().index(this->element());
90
91
0/4
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
6912 this->localResidual().spatialWeight(1.0);
92
0/4
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
6912 this->localResidual().temporalWeight(1.0);
93
94
0/2
✗ Branch 1 not taken.
✗ Branch 2 not taken.
3456 const auto sWeight = stageParams.spatialWeight(stageParams.size()-1);
95
0/2
✗ Branch 1 not taken.
✗ Branch 2 not taken.
3456 const auto tWeight = stageParams.temporalWeight(stageParams.size()-1);
96
97
0/4
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
6912 const auto flux = this->evalLocalFluxAndSourceResidual(this->curElemVolVars());
98 3456 const auto storage = this->localResidual().evalStorage(this->fvGeometry(), this->curElemVolVars());
99 3456 ElementResidualVector origResidual(flux.size());
100 3456 origResidual = 0.0;
101
4/4
✓ Branch 0 taken 27648 times.
✓ Branch 1 taken 3456 times.
✓ Branch 2 taken 27648 times.
✓ Branch 3 taken 3456 times.
65664 for (const auto& scv : scvs(this->fvGeometry()))
102 {
103 82944 spatial[scv.dofIndex()] += flux[scv.localDofIndex()];
104 82944 temporal[scv.dofIndex()] += storage[scv.localDofIndex()];
105 193536 origResidual[scv.localDofIndex()] += flux[scv.localDofIndex()]*sWeight + storage[scv.localDofIndex()]*tWeight;
106 110592 res[scv.dofIndex()] += origResidual[scv.localDofIndex()];
107 }
108
109
2/4
✓ Branch 0 taken 3456 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3456 times.
✗ Branch 3 not taken.
6912 this->localResidual().spatialWeight(sWeight);
110
2/4
✓ Branch 0 taken 3456 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3456 times.
✗ Branch 3 not taken.
6912 this->localResidual().temporalWeight(tWeight);
111
112 if (partialReassembler && partialReassembler->elementColor(eIdxGlobal) == EntityColor::green)
113 {
114 // assemble the coupling blocks for coupled models (does nothing if not coupled)
115 maybeAssembleCouplingBlocks(origResidual);
116 }
117
1/2
✓ Branch 0 taken 3456 times.
✗ Branch 1 not taken.
3456 else if (!this->elementIsGhost())
118 {
119
0/2
✗ Branch 1 not taken.
✗ Branch 2 not taken.
3456 this->asImp_().assembleJacobian(jac, gridVariables, origResidual, partialReassembler); // forward to the internal implementation
120
121 // assemble the coupling blocks for coupled models (does nothing if not coupled)
122 3456 maybeAssembleCouplingBlocks(origResidual);
123 }
124 else
125 {
126 // Treatment of ghost elements
127 assert(this->elementIsGhost());
128
129 // handle dofs per codimension
130 const auto& gridGeometry = this->asImp_().problem().gridGeometry();
131 Dune::Hybrid::forEach(std::make_integer_sequence<int, dim>{}, [&](auto d)
132 {
133 constexpr int codim = dim - d;
134 const auto& localCoeffs = gridGeometry.feCache().get(this->element().type()).localCoefficients();
135 for (int idx = 0; idx < localCoeffs.size(); ++idx)
136 {
137 const auto& localKey = localCoeffs.localKey(idx);
138
139 // skip if we are not handling this codim right now
140 if (localKey.codim() != codim)
141 continue;
142
143 // do not change the non-ghost entities
144 auto entity = this->element().template subEntity<codim>(localKey.subEntity());
145 if (entity.partitionType() == Dune::InteriorEntity || entity.partitionType() == Dune::BorderEntity)
146 continue;
147
148 // WARNING: this only works if the mapping from codim+subEntity to
149 // global dofIndex is unique (on dof per entity of this codim).
150 // For more general mappings, we should use a proper local-global mapping here.
151 // For example through dune-functions.
152 const auto dofIndex = gridGeometry.dofMapper().index(entity);
153
154 // this might be a vector-valued dof
155 using BlockType = typename JacobianMatrix::block_type;
156 BlockType &J = jac[dofIndex][dofIndex];
157 for (int j = 0; j < BlockType::rows; ++j)
158 J[j][j] = 1.0;
159
160 // set residual for the ghost dof
161 res[dofIndex] = 0;
162 constrainedDofs[dofIndex] = 1;
163 }
164 });
165 }
166
167 160368 auto applyDirichlet = [&] (const auto& scvI,
168 const auto& dirichletValues,
169 const auto eqIdx,
170 52304 const auto pvIdx)
171 {
172 361776 res[scvI.dofIndex()][eqIdx] = this->curElemVolVars()[scvI].priVars()[pvIdx] - dirichletValues[pvIdx];
173 104608 constrainedDofs[scvI.dofIndex()][eqIdx] = 1;
174
175 52304 auto& row = jac[scvI.dofIndex()];
176
4/6
✗ Branch 0 not taken.
✓ Branch 1 taken 858064 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 858064 times.
✓ Branch 4 taken 805760 times.
✓ Branch 5 taken 52304 times.
1663824 for (auto col = row.begin(); col != row.end(); ++col)
177 2417280 row[col.index()][eqIdx] = 0.0;
178
179 104608 jac[scvI.dofIndex()][scvI.dofIndex()][eqIdx][pvIdx] = 1.0;
180
181 // if a periodic dof has Dirichlet values also apply the same Dirichlet values to the other dof
182
1/2
✗ Branch 3 not taken.
✓ Branch 4 taken 52304 times.
156912 if (this->asImp_().problem().gridGeometry().dofOnPeriodicBoundary(scvI.dofIndex()))
183 {
184 const auto periodicDof = this->asImp_().problem().gridGeometry().periodicallyMappedDof(scvI.dofIndex());
185 res[periodicDof][eqIdx] = this->curElemVolVars()[scvI].priVars()[pvIdx] - dirichletValues[pvIdx];
186 constrainedDofs[periodicDof][eqIdx] = 1;
187 const auto end = jac[periodicDof].end();
188 for (auto it = jac[periodicDof].begin(); it != end; ++it)
189 (*it) = periodicDof != it.index() ? 0.0 : 1.0;
190 }
191 };
192
193 6912 this->asImp_().enforceDirichletConstraints(applyDirichlet);
194 3456 }
195
196 /*!
197 * \brief Computes the derivatives with respect to the given element and adds them
198 * to the global matrix.
199 */
200 void assembleJacobian(JacobianMatrix& jac, GridVariables& gridVariables)
201 {
202 this->asImp_().bindLocalViews();
203 this->asImp_().assembleJacobian(jac, gridVariables); // forward to the internal implementation
204
205 auto applyDirichlet = [&] (const auto& scvI,
206 const auto& dirichletValues,
207 const auto eqIdx,
208 const auto pvIdx)
209 {
210 auto& row = jac[scvI.dofIndex()];
211 for (auto col = row.begin(); col != row.end(); ++col)
212 row[col.index()][eqIdx] = 0.0;
213
214 jac[scvI.dofIndex()][scvI.dofIndex()][eqIdx][pvIdx] = 1.0;
215 };
216
217 this->asImp_().enforceDirichletConstraints(applyDirichlet);
218 }
219
220 /*!
221 * \brief Assemble the residual only
222 */
223 template <class ResidualVector>
224 void assembleResidual(ResidualVector& res)
225 {
226 this->asImp_().bindLocalViews();
227 const auto residual = this->evalLocalResidual();
228
229 for (const auto& scv : scvs(this->fvGeometry()))
230 res[scv.dofIndex()] += residual[scv.localDofIndex()];
231
232 auto applyDirichlet = [&] (const auto& scvI,
233 const auto& dirichletValues,
234 const auto eqIdx,
235 const auto pvIdx)
236 {
237 res[scvI.dofIndex()][eqIdx] = this->curElemVolVars()[scvI].priVars()[pvIdx] - dirichletValues[pvIdx];
238 };
239
240 this->asImp_().enforceDirichletConstraints(applyDirichlet);
241 }
242
243 /*!
244 * \brief Assemble the residual only
245 */
246 template<class ResidualVector>
247 8960 void assembleCurrentResidual(ResidualVector& spatialRes, ResidualVector& temporalRes)
248 {
249 8960 this->asImp_().bindLocalViews();
250 17920 const auto flux = this->evalLocalFluxAndSourceResidual(this->curElemVolVars());
251 8960 const auto storage = this->localResidual().evalStorage(this->fvGeometry(), this->curElemVolVars());
252
4/4
✓ Branch 0 taken 40960 times.
✓ Branch 1 taken 8960 times.
✓ Branch 2 taken 40960 times.
✓ Branch 3 taken 8960 times.
108800 for (const auto& scv : scvs(this->fvGeometry()))
253 {
254 122880 spatialRes[scv.dofIndex()] += flux[scv.localDofIndex()];
255 163840 temporalRes[scv.dofIndex()] += storage[scv.localDofIndex()];
256 }
257 8960 }
258
259 //! Enforce Dirichlet constraints
260 template<typename ApplyFunction>
261 void enforceDirichletConstraints(const ApplyFunction& applyDirichlet)
262 {
263 // enforce Dirichlet boundary conditions
264
0/2
✗ Branch 1 not taken.
✗ Branch 2 not taken.
3456 this->asImp_().evalDirichletBoundaries(applyDirichlet);
265 // take care of internal Dirichlet constraints (if enabled)
266 3456 this->asImp_().enforceInternalDirichletConstraints(applyDirichlet);
267 }
268
269 /*!
270 * \brief Evaluates Dirichlet boundaries
271 */
272 template< typename ApplyDirichletFunctionType >
273 29568 void evalDirichletBoundaries(ApplyDirichletFunctionType applyDirichlet)
274 {
275 // enforce Dirichlet boundaries by overwriting partial derivatives with 1 or 0
276 // and set the residual to (privar - dirichletvalue)
277
4/4
✓ Branch 0 taken 4112 times.
✓ Branch 1 taken 25456 times.
✓ Branch 2 taken 4112 times.
✓ Branch 3 taken 25456 times.
59136 if (this->elemBcTypes().hasDirichlet())
278 {
279
4/4
✓ Branch 0 taken 28544 times.
✓ Branch 1 taken 4112 times.
✓ Branch 2 taken 28544 times.
✓ Branch 3 taken 4112 times.
40880 for (const auto& scvI : scvs(this->fvGeometry()))
280 {
281 85632 const auto bcTypes = this->elemBcTypes().get(this->fvGeometry(), scvI);
282
4/4
✓ Branch 0 taken 18160 times.
✓ Branch 1 taken 10384 times.
✓ Branch 2 taken 18160 times.
✓ Branch 3 taken 10384 times.
57088 if (bcTypes.hasDirichlet())
283 {
284 36320 const auto dirichletValues = this->asImp_().problem().dirichlet(this->element(), scvI);
285
286 // set the Dirichlet conditions in residual and jacobian
287
2/2
✓ Branch 0 taken 52304 times.
✓ Branch 1 taken 18160 times.
70464 for (int eqIdx = 0; eqIdx < numEq; ++eqIdx)
288 {
289
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 52304 times.
52304 if (bcTypes.isDirichlet(eqIdx))
290 {
291
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 52304 times.
52304 const auto pvIdx = bcTypes.eqToDirichletIndex(eqIdx);
292
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 52304 times.
52304 assert(0 <= pvIdx && pvIdx < numEq);
293 52304 applyDirichlet(scvI, dirichletValues, eqIdx, pvIdx);
294 }
295 }
296 }
297 }
298 }
299 29568 }
300
301 /*!
302 * \brief Update the coupling context for coupled models.
303 * \note This does nothing per default (not a coupled model).
304 */
305 template<class... Args>
306 void maybeUpdateCouplingContext(Args&&...) {}
307
308 /*!
309 * \brief Update the additional domain derivatives for coupled models.
310 * \note This does nothing per default (not a coupled model).
311 */
312 template<class... Args>
313 void maybeEvalAdditionalDomainDerivatives(Args&&...) {}
314 };
315
316 /*!
317 * \ingroup Experimental
318 * \ingroup Assembly
319 * \ingroup CVFEDiscretization
320 * \brief An assembler for Jacobian and residual contribution per element (CVFE methods)
321 * \tparam TypeTag The TypeTag
322 * \tparam diffMethod The differentiation method to residual compute derivatives
323 * \tparam Implementation via CRTP
324 */
325 template<class TypeTag, class Assembler, DiffMethod diffMethod = DiffMethod::numeric, class Implementation = void>
326 class CVFELocalAssembler;
327
328 /*!
329 * \ingroup Experimental
330 * \ingroup Assembly
331 * \ingroup CVFEDiscretization
332 * \brief Control volume finite element local assembler using numeric differentiation
333 */
334 template<class TypeTag, class Assembler, class Implementation>
335
2/8
✓ Branch 0 taken 10368 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 3072 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
51968 class CVFELocalAssembler<TypeTag, Assembler, DiffMethod::numeric, Implementation>
336 : public CVFELocalAssemblerBase<TypeTag, Assembler,
337 NonVoidOr<CVFELocalAssembler<TypeTag, Assembler, DiffMethod::numeric, Implementation>, Implementation>>
338 {
339 using ThisType = CVFELocalAssembler<TypeTag, Assembler, DiffMethod::numeric, Implementation>;
340 using ParentType = CVFELocalAssemblerBase<TypeTag, Assembler, NonVoidOr<ThisType, Implementation>>;
341 using Scalar = GetPropType<TypeTag, Properties::Scalar>;
342 using GridVariables = GetPropType<TypeTag, Properties::GridVariables>;
343 using VolumeVariables = GetPropType<TypeTag, Properties::VolumeVariables>;
344 using JacobianMatrix = GetPropType<TypeTag, Properties::JacobianMatrix>;
345
346 static constexpr int numEq = GetPropType<TypeTag, Properties::ModelTraits>::numEq();
347 static constexpr int dim = GetPropType<TypeTag, Properties::GridGeometry>::GridView::dimension;
348
349 static constexpr bool enableGridFluxVarsCache
350 = GridVariables::GridFluxVariablesCache::cachingEnabled;
351 static constexpr bool solutionDependentFluxVarsCache
352 = GridVariables::GridFluxVariablesCache::FluxVariablesCache::isSolDependent;
353
354 public:
355
356 using LocalResidual = typename ParentType::LocalResidual;
357 using ElementResidualVector = typename LocalResidual::ElementResidualVector;
358
3/9
✓ Branch 1 taken 4736 times.
✓ Branch 2 taken 14976 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 18816 times.
✗ Branch 7 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
38528 using ParentType::ParentType;
359
360 template <class PartialReassembler = DefaultPartialReassembler>
361 29568 void assembleJacobian(JacobianMatrix& A, GridVariables& gridVariables,
362 const ElementResidualVector& origResiduals,
363 const PartialReassembler* partialReassembler = nullptr)
364 {
365
1/2
✓ Branch 0 taken 29568 times.
✗ Branch 1 not taken.
29568 if (this->isImplicit())
366 29568 assembleJacobianImplicit_(A, gridVariables, origResiduals, partialReassembler);
367 else
368 assembleJacobianExplicit_(A, gridVariables, origResiduals, partialReassembler);
369 29568 }
370
371 private:
372 /*!
373 * \brief Computes the derivatives with respect to the given element and adds them
374 * to the global matrix.
375 *
376 * \return The element residual at the current solution.
377 */
378 template <class PartialReassembler = DefaultPartialReassembler>
379 29568 void assembleJacobianImplicit_(JacobianMatrix& A, GridVariables& gridVariables,
380 const ElementResidualVector& origResiduals,
381 const PartialReassembler* partialReassembler = nullptr)
382 {
383 // get some aliases for convenience
384 29568 const auto& element = this->element();
385
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 29561 times.
29568 const auto& fvGeometry = this->fvGeometry();
386
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 3454 times.
29568 const auto& curSol = this->asImp_().curSol();
387
388
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 29561 times.
29568 auto&& curElemVolVars = this->curElemVolVars();
389
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 29561 times.
29568 auto&& elemFluxVarsCache = this->elemFluxVarsCache();
390
391 //////////////////////////////////////////////////////////////////////////////////////////////////
392 // //
393 // Calculate derivatives of all dofs in stencil with respect to the dofs in the element. In the //
394 // neighboring elements we do so by computing the derivatives of the fluxes which depend on the //
395 // actual element. In the actual element we evaluate the derivative of the entire residual. //
396 // //
397 //////////////////////////////////////////////////////////////////////////////////////////////////
398
399 // if all volvars in the stencil have to be updated or if it's enough to only update the
400 // volVars for the scv whose associated dof has been deflected
401
6/8
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 29561 times.
✓ Branch 3 taken 5 times.
✓ Branch 4 taken 2 times.
✓ Branch 6 taken 5 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 3 times.
✗ Branch 10 not taken.
29568 static const bool updateAllVolVars = getParamFromGroup<bool>(
402
2/4
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
12 this->asImp_().problem().paramGroup(), "Assembly.BoxVolVarsDependOnAllElementDofs", false
403 );
404
405 // create the element solution
406 59136 auto elemSol = elementSolution(element, curSol, fvGeometry.gridGeometry());
407
408 // create the vector storing the partial derivatives
409 59136 ElementResidualVector partialDerivs(fvGeometry.numScv());
410
411 29568 Dumux::Detail::VolVarsDeflectionHelper deflectionHelper(
412 [&] (const auto& scv) -> VolumeVariables& {
413 2147328 return this->getVolVarAccess(gridVariables.curGridVolVars(), curElemVolVars, scv);
414 },
415 fvGeometry,
416 updateAllVolVars
417 );
418
419 // calculation of the derivatives
420
4/4
✓ Branch 0 taken 132096 times.
✓ Branch 1 taken 29568 times.
✓ Branch 2 taken 132096 times.
✓ Branch 3 taken 29568 times.
191232 for (const auto& scv : scvs(fvGeometry))
421 {
422 // dof index and corresponding actual pri vars
423 132096 const auto dofIdx = scv.dofIndex();
424
1/2
✓ Branch 0 taken 90624 times.
✗ Branch 1 not taken.
132096 deflectionHelper.setCurrent(scv);
425
426 // calculate derivatives w.r.t to the privars at the dof at hand
427
2/2
✓ Branch 0 taken 291840 times.
✓ Branch 1 taken 132096 times.
423936 for (int pvIdx = 0; pvIdx < numEq; pvIdx++)
428 {
429 291840 partialDerivs = 0.0;
430
431 1833984 auto evalResiduals = [&](Scalar priVar)
432 {
433 // update the volume variables and compute element residual
434 583680 elemSol[scv.localDofIndex()][pvIdx] = priVar;
435 583680 deflectionHelper.deflect(elemSol, scv, this->asImp_().problem());
436 if constexpr (solutionDependentFluxVarsCache)
437 {
438 elemFluxVarsCache.update(element, fvGeometry, curElemVolVars);
439 if constexpr (enableGridFluxVarsCache)
440 gridVariables.gridFluxVarsCache().updateElement(element, fvGeometry, curElemVolVars);
441 }
442 500736 this->asImp_().maybeUpdateCouplingContext(scv, elemSol, pvIdx);
443 291840 return this->evalLocalResidual();
444 };
445
446 // derive the residuals numerically
447
7/10
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 291833 times.
✓ Branch 3 taken 5 times.
✓ Branch 4 taken 2 times.
✓ Branch 6 taken 5 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 5 times.
✗ Branch 10 not taken.
✓ Branch 12 taken 5 times.
✗ Branch 13 not taken.
291840 static const NumericEpsilon<Scalar, numEq> eps_{this->asImp_().problem().paramGroup()};
448
7/10
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 291832 times.
✓ Branch 3 taken 5 times.
✓ Branch 4 taken 3 times.
✓ Branch 6 taken 5 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 5 times.
✗ Branch 10 not taken.
✓ Branch 12 taken 5 times.
✗ Branch 13 not taken.
291840 static const int numDiffMethod = getParamFromGroup<int>(this->asImp_().problem().paramGroup(), "Assembly.NumericDifferenceMethod");
449
3/6
✓ Branch 1 taken 291840 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 291840 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 291840 times.
✗ Branch 8 not taken.
875520 NumericDifferentiation::partialDerivative(evalResiduals, elemSol[scv.localDofIndex()][pvIdx], partialDerivs, origResiduals,
450 875520 eps_(elemSol[scv.localDofIndex()][pvIdx], pvIdx), numDiffMethod);
451
452 // update the global stiffness matrix with the current partial derivatives
453
4/4
✓ Branch 0 taken 1499136 times.
✓ Branch 1 taken 291840 times.
✓ Branch 2 taken 1499136 times.
✓ Branch 3 taken 291840 times.
2082816 for (const auto& scvJ : scvs(fvGeometry))
454 {
455 // don't add derivatives for green dofs
456 if (!partialReassembler
457
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 835584 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
835584 || partialReassembler->dofColor(scvJ.dofIndex()) != EntityColor::green)
458 {
459
2/2
✓ Branch 0 taken 3661824 times.
✓ Branch 1 taken 1499136 times.
5160960 for (int eqIdx = 0; eqIdx < numEq; eqIdx++)
460 {
461 // A[i][col][eqIdx][pvIdx] is the rate of change of
462 // the residual of equation 'eqIdx' at dof 'i'
463 // depending on the primary variable 'pvIdx' at dof
464 // 'col'.
465
4/8
✓ Branch 1 taken 3661824 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3661824 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 3661824 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 3661824 times.
✗ Branch 11 not taken.
14647296 A[scvJ.dofIndex()][dofIdx][eqIdx][pvIdx] += partialDerivs[scvJ.localDofIndex()][eqIdx];
466 }
467 }
468 }
469
470 // restore the original state of the scv's volume variables
471 291840 deflectionHelper.restore(scv);
472
473 // restore the original element solution
474
4/8
✓ Branch 1 taken 82944 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 82944 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 82944 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 82944 times.
✗ Branch 11 not taken.
1167360 elemSol[scv.localDofIndex()][pvIdx] = curSol[scv.dofIndex()][pvIdx];
475
1/2
✓ Branch 1 taken 82944 times.
✗ Branch 2 not taken.
291840 this->asImp_().maybeUpdateCouplingContext(scv, elemSol, pvIdx);
476 }
477 }
478
479 // restore original state of the flux vars cache in case of global caching.
480 // In the case of local caching this is obsolete because the elemFluxVarsCache used here goes out of scope after this.
481 if constexpr (enableGridFluxVarsCache)
482 10368 gridVariables.gridFluxVarsCache().updateElement(element, fvGeometry, curElemVolVars);
483
484 // evaluate additional derivatives that might arise from the coupling (no-op if not coupled)
485 29568 this->asImp_().maybeEvalAdditionalDomainDerivatives(origResiduals, A, gridVariables);
486 29568 }
487
488 /*!
489 * \brief Computes the derivatives with respect to the given element and adds them
490 * to the global matrix.
491 * \return The element residual at the current solution.
492 */
493 template <class PartialReassembler = DefaultPartialReassembler>
494 void assembleJacobianExplicit_(JacobianMatrix& A, GridVariables& gridVariables,
495 const ElementResidualVector& origResiduals,
496 const PartialReassembler* partialReassembler = nullptr)
497 {
498 if (partialReassembler)
499 DUNE_THROW(Dune::NotImplemented, "partial reassembly for explicit time discretization");
500
501 // get some aliases for convenience
502 const auto& element = this->element();
503 const auto& fvGeometry = this->fvGeometry();
504 const auto& curSol = this->asImp_().curSol();
505 auto&& curElemVolVars = this->curElemVolVars();
506
507 // create the element solution
508 auto elemSol = elementSolution(element, curSol, fvGeometry.gridGeometry());
509
510 // create the vector storing the partial derivatives
511 ElementResidualVector partialDerivs(fvGeometry.numScv());
512
513 // calculation of the derivatives
514 for (const auto& scv : scvs(fvGeometry))
515 {
516 // dof index and corresponding actual pri vars
517 const auto dofIdx = scv.dofIndex();
518 auto& curVolVars = this->getVolVarAccess(gridVariables.curGridVolVars(), curElemVolVars, scv);
519 const VolumeVariables origVolVars(curVolVars);
520
521 // calculate derivatives w.r.t to the privars at the dof at hand
522 for (int pvIdx = 0; pvIdx < numEq; pvIdx++)
523 {
524 partialDerivs = 0.0;
525
526 auto evalStorage = [&](Scalar priVar)
527 {
528 elemSol[scv.localDofIndex()][pvIdx] = priVar;
529 curVolVars.update(elemSol, this->asImp_().problem(), element, scv);
530 return this->evalStorage();
531 };
532
533 // derive the residuals numerically
534 static const NumericEpsilon<Scalar, numEq> eps_{this->asImp_().problem().paramGroup()};
535 static const int numDiffMethod = getParamFromGroup<int>(this->asImp_().problem().paramGroup(), "Assembly.NumericDifferenceMethod");
536 NumericDifferentiation::partialDerivative(evalStorage, elemSol[scv.localDofIndex()][pvIdx], partialDerivs, origResiduals,
537 eps_(elemSol[scv.localDofIndex()][pvIdx], pvIdx), numDiffMethod);
538
539 // update the global stiffness matrix with the current partial derivatives
540 for (int eqIdx = 0; eqIdx < numEq; eqIdx++)
541 {
542 // A[i][col][eqIdx][pvIdx] is the rate of change of
543 // the residual of equation 'eqIdx' at dof 'i'
544 // depending on the primary variable 'pvIdx' at dof
545 // 'col'.
546 A[dofIdx][dofIdx][eqIdx][pvIdx] += partialDerivs[scv.localDofIndex()][eqIdx];
547 }
548
549 // restore the original state of the scv's volume variables
550 curVolVars = origVolVars;
551
552 // restore the original element solution
553 elemSol[scv.localDofIndex()][pvIdx] = curSol[scv.dofIndex()][pvIdx];
554 }
555 }
556 }
557 };
558
559 } // end namespace Dumux
560
561 #endif
562