GCC Code Coverage Report


Directory: ../../../builds/dumux-repositories/
File: /builds/dumux-repositories/dumux/dumux/experimental/assembly/cclocalassembler.hh
Date: 2024-09-21 20:52:54
Exec Total Coverage
Lines: 124 131 94.7%
Functions: 32 73 43.8%
Branches: 81 147 55.1%

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 CCDiscretization
12 * \brief An assembler for Jacobian and residual contribution per element (cell-centered methods)
13 */
14 #ifndef DUMUX_EXPERIMENTAL_CC_LOCAL_ASSEMBLER_HH
15 #define DUMUX_EXPERIMENTAL_CC_LOCAL_ASSEMBLER_HH
16
17 #include <dune/common/reservedvector.hh>
18 #include <dune/grid/common/gridenums.hh> // for GhostEntity
19 #include <dune/istl/matrixindexset.hh>
20
21 #include <dumux/common/typetraits/typetraits.hh>
22 #include <dumux/common/reservedblockvector.hh>
23 #include <dumux/common/properties.hh>
24 #include <dumux/common/parameters.hh>
25 #include <dumux/common/numericdifferentiation.hh>
26 #include <dumux/common/numeqvector.hh>
27 #include <dumux/assembly/numericepsilon.hh>
28 #include <dumux/assembly/diffmethod.hh>
29 #include <dumux/assembly/entitycolor.hh>
30 #include <dumux/assembly/partialreassembler.hh>
31 #include <dumux/discretization/fluxstencil.hh>
32 #include <dumux/discretization/cellcentered/elementsolution.hh>
33
34 #include <dumux/experimental/assembly/fvlocalassemblerbase.hh>
35
36 namespace Dumux::Experimental {
37
38 /*!
39 * \ingroup Experimental
40 * \ingroup Assembly
41 * \ingroup CCDiscretization
42 * \brief A base class for all local cell-centered assemblers
43 * \tparam TypeTag The TypeTag
44 * \tparam Assembler The assembler type
45 * \tparam Implementation The actual implementation
46 */
47 template<class TypeTag, class Assembler, class Implementation>
48 20083072 class CCLocalAssemblerBase : public Experimental::FVLocalAssemblerBase<TypeTag, Assembler, Implementation>
49 {
50 using ParentType = Experimental::FVLocalAssemblerBase<TypeTag, Assembler, Implementation>;
51 using GridGeometry = GetPropType<TypeTag, Properties::GridGeometry>;
52 using FVElementGeometry = typename GridGeometry::LocalView;
53 using Element = typename FVElementGeometry::Element;
54 using SubControlVolumeFace = typename GridGeometry::SubControlVolumeFace;
55 using GridView = typename GridGeometry::GridView;
56 using JacobianMatrix = GetPropType<TypeTag, Properties::JacobianMatrix>;
57 using GridVariables = GetPropType<TypeTag, Properties::GridVariables>;
58 using Problem = GetPropType<TypeTag, Properties::Problem>;
59 using ElementVolumeVariables = typename GetPropType<TypeTag, Properties::GridVolumeVariables>::LocalView;
60 using NumEqVector = Dumux::NumEqVector<GetPropType<TypeTag, Properties::PrimaryVariables>>;
61
62 public:
63
64 10056960 using ParentType::ParentType;
65
66 template <class ResidualVector, class StageParams, class PartialReassembler = DefaultPartialReassembler, class CouplingFunction = Noop>
67 3456 void assembleJacobianAndResidual(JacobianMatrix& jac, ResidualVector& res, GridVariables& gridVariables,
68 const StageParams& stageParams, ResidualVector& temporal, ResidualVector& spatial,
69 ResidualVector& constrainedDofs,
70 const PartialReassembler* partialReassembler = nullptr,
71 const CouplingFunction& maybeAssembleCouplingBlocks = noop)
72 {
73 3456 this->asImp_().bindLocalViews();
74 10368 const auto globalI = this->fvGeometry().gridGeometry().elementMapper().index(this->element());
75
76 6912 this->localResidual().spatialWeight(1.0);
77 6912 this->localResidual().temporalWeight(1.0);
78
79 6912 spatial[globalI] = this->evalLocalFluxAndSourceResidual(this->curElemVolVars())[0];
80 3456 temporal[globalI] = this->localResidual().evalStorage(this->fvGeometry(), this->curElemVolVars())[0];
81 6912 res[globalI] = spatial[globalI]*stageParams.spatialWeight(stageParams.size()-1)
82
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
17280 + temporal[globalI]*stageParams.temporalWeight(stageParams.size()-1);
83
84
0/6
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
10368 this->localResidual().spatialWeight(stageParams.spatialWeight(stageParams.size()-1));
85
0/6
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
10368 this->localResidual().temporalWeight(stageParams.temporalWeight(stageParams.size()-1));
86
87
88 if (partialReassembler
89 && partialReassembler->elementColor(globalI) == EntityColor::green)
90 {
91 // assemble the coupling blocks for coupled models (does nothing if not coupled)
92 maybeAssembleCouplingBlocks(res[globalI]);
93 }
94 else
95 {
96 3456 this->asImp_().assembleJacobian(jac, gridVariables, res[globalI]); // forward to the internal implementation
97
98 // assemble the coupling blocks for coupled models (does nothing if not coupled)
99 6912 maybeAssembleCouplingBlocks(res[globalI]);
100 }
101 3456 }
102
103 /*!
104 * \brief Assemble the residual only
105 */
106 template <class ResidualVector>
107 void assembleResidual(ResidualVector& res)
108 {
109 this->asImp_().bindLocalViews();
110 const auto globalI = this->assembler().gridGeometry().elementMapper().index(this->element());
111 res[globalI] = this->asImp_().evalLocalResidual()[0]; // forward to the internal implementation
112
113 using Problem = GetPropType<TypeTag, Properties::Problem>;
114 if constexpr (Problem::enableInternalDirichletConstraints())
115 {
116 const auto applyDirichlet = [&] (const auto& scvI,
117 const auto& dirichletValues,
118 const auto eqIdx,
119 const auto pvIdx)
120 {
121 res[scvI.dofIndex()][eqIdx] = this->curElemVolVars()[scvI].priVars()[pvIdx] - dirichletValues[pvIdx];
122 };
123
124 this->asImp_().enforceInternalDirichletConstraints(applyDirichlet);
125 }
126 }
127
128 /*!
129 * \brief Evaluates the fluxes (element can potentially be a neighbor)
130 */
131 NumEqVector evalFlux(const Element& neighbor,
132 const SubControlVolumeFace& scvf) const
133 {
134 3225792 return this->localResidual().evalFlux(
135 1612896 this->asImp_().problem(), neighbor,
136 this->fvGeometry(), this->curElemVolVars(),
137 this->elemFluxVarsCache(), scvf
138 8064480 );
139 }
140
141 /*!
142 * \brief Assemble the residual only
143 */
144 template<class SubResidualVector>
145 5013568 void assembleCurrentResidual(SubResidualVector& spatialRes, SubResidualVector& temporalRes)
146 {
147 5013568 this->asImp_().bindLocalViews();
148 15040704 const auto globalI = this->fvGeometry().gridGeometry().elementMapper().index(this->element());
149 10027136 spatialRes[globalI] = this->evalLocalFluxAndSourceResidual(this->curElemVolVars())[0];
150 5013568 temporalRes[globalI] = this->localResidual().evalStorage(this->fvGeometry(), this->curElemVolVars())[0];
151
152 if constexpr (Problem::enableInternalDirichletConstraints())
153 DUNE_THROW(Dune::NotImplemented, "Not implemented");
154 5013568 }
155
156 /*!
157 * \brief Update the coupling context for coupled models.
158 * \note This does nothing per default (not a coupled model).
159 */
160 template<class... Args>
161 void maybeUpdateCouplingContext(Args&&...) {}
162
163 /*!
164 * \brief Update the additional domain derivatives for coupled models.
165 * \note This does nothing per default (not a coupled model).
166 */
167 template<class... Args>
168 void maybeEvalAdditionalDomainDerivatives(Args&&...) {}
169 };
170
171 /*!
172 * \ingroup Experimental
173 * \ingroup Assembly
174 * \ingroup CCDiscretization
175 * \brief An assembler for Jacobian and residual contribution per element (cell-centered methods)
176 * \tparam TypeTag The TypeTag
177 * \tparam diffMethod The differentiation method to residual compute derivatives
178 * \tparam Implementation The actual implementation via CRTP
179 */
180 template<class TypeTag, class Assembler, DiffMethod diffMethod = DiffMethod::numeric, class Implementation = void>
181 class CCLocalAssembler;
182
183 /*!
184 * \ingroup Experimental
185 * \ingroup Assembly
186 * \ingroup CCDiscretization
187 * \brief Cell-centered scheme local assembler using numeric differentiation
188 */
189 template<class TypeTag, class Assembler, class Implementation>
190 15070016 class CCLocalAssembler<TypeTag, Assembler, DiffMethod::numeric, Implementation>
191 : public CCLocalAssemblerBase<TypeTag, Assembler,
192 NonVoidOr<CCLocalAssembler<TypeTag, Assembler, DiffMethod::numeric, Implementation>, Implementation>>
193 {
194 using ThisType = CCLocalAssembler<TypeTag, Assembler, DiffMethod::numeric, Implementation>;
195 using ParentType = CCLocalAssemblerBase<TypeTag, Assembler, NonVoidOr<ThisType, Implementation>>;
196 using Scalar = GetPropType<TypeTag, Properties::Scalar>;
197 using NumEqVector = Dumux::NumEqVector<GetPropType<TypeTag, Properties::PrimaryVariables>>;
198 using GridGeometry = GetPropType<TypeTag, Properties::GridGeometry>;
199 using FVElementGeometry = typename GridGeometry::LocalView;
200 using Element = typename FVElementGeometry::Element;
201 using GridVariables = GetPropType<TypeTag, Properties::GridVariables>;
202 using JacobianMatrix = GetPropType<TypeTag, Properties::JacobianMatrix>;
203 using Problem = typename GridVariables::GridVolumeVariables::Problem;
204
205 static constexpr int numEq = GetPropType<TypeTag, Properties::ModelTraits>::numEq();
206 static constexpr int dim = GetPropType<TypeTag, Properties::GridGeometry>::GridView::dimension;
207
208 using FluxStencil = Dumux::FluxStencil<FVElementGeometry>;
209 static constexpr int maxElementStencilSize = GridGeometry::maxElementStencilSize;
210 static constexpr bool enableGridFluxVarsCache = GetPropType<TypeTag, Properties::GridVariables>::GridFluxVariablesCache::cachingEnabled;
211
212 public:
213
3/9
✓ Branch 1 taken 4736 times.
✓ Branch 2 taken 5019200 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 5033024 times.
✗ Branch 7 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
10056960 using ParentType::ParentType;
214
215 using LocalResidual = typename ParentType::LocalResidual;
216 using ElementResidualVector = typename LocalResidual::ElementResidualVector;
217
218 /*!
219 * \brief Computes the derivatives with respect to the given element and adds them
220 * to the global matrix. Calculates the element residual at the current solution.
221 */
222 5043392 void assembleJacobian(JacobianMatrix& A, GridVariables& gridVariables, const NumEqVector& origResidual)
223 {
224
2/2
✓ Branch 0 taken 43392 times.
✓ Branch 1 taken 5000000 times.
5043392 if (this->isImplicit())
225 43392 assembleJacobianImplicit_(A, gridVariables, origResidual);
226 else
227 5000000 assembleJacobianExplicit_(A, gridVariables, origResidual);
228 5043392 }
229
230 private:
231
232 /*!
233 * \brief Computes the derivatives with respect to the given element and adds them to the global matrix.
234 * Calculates the element residual at the current solution.
235 */
236 43392 void assembleJacobianImplicit_(JacobianMatrix& A, GridVariables& gridVariables, const NumEqVector& origResidual)
237 {
238 //////////////////////////////////////////////////////////////////////////////////////////////////
239 // Calculate derivatives of all dofs in stencil with respect to the dofs in the element. In the //
240 // neighboring elements we do so by computing the derivatives of the fluxes which depend on the //
241 // actual element. In the actual element we evaluate the derivative of the entire residual. //
242 //////////////////////////////////////////////////////////////////////////////////////////////////
243
244 // get some aliases for convenience
245 43392 const auto& element = this->element();
246 43392 const auto& fvGeometry = this->fvGeometry();
247 43392 const auto& gridGeometry = this->fvGeometry().gridGeometry();
248 43392 auto&& curElemVolVars = this->curElemVolVars();
249 43392 auto&& elemFluxVarsCache = this->elemFluxVarsCache();
250
251 // get stencil information
252 86784 const auto globalI = gridGeometry.elementMapper().index(element);
253 43392 const auto& connectivityMap = gridGeometry.connectivityMap();
254 86784 const auto numNeighbors = connectivityMap[globalI].size();
255
256 // container to store the neighboring elements
257 43392 Dune::ReservedVector<Element, maxElementStencilSize> neighborElements;
258 43392 neighborElements.resize(numNeighbors);
259
260 // assemble the undeflected residual
261 using Residuals = ReservedBlockVector<NumEqVector, maxElementStencilSize>;
262 86784 Residuals origResiduals(numNeighbors + 1); origResiduals = 0.0;
263 43392 origResiduals[0] = origResidual;
264
265 // lambda for convenient evaluation of the fluxes across scvfs in the neighbors
266 // if the neighbor is a ghost we don't want to add anything to their residual
267 // so we return 0 and omit computing the flux
268 3269184 auto evalNeighborFlux = [&] (const auto& neighbor, const auto& scvf)
269 {
270
1/4
✗ Branch 1 not taken.
✓ Branch 2 taken 1612896 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
1612896 if (neighbor.partitionType() == Dune::GhostEntity)
271 return NumEqVector(0.0);
272 else
273 1612896 return this->evalFlux(neighbor, scvf);
274 };
275
276 // get the elements in which we need to evaluate the fluxes
277 // and calculate these in the undeflected state
278 43392 unsigned int j = 1;
279
4/4
✓ Branch 0 taken 238736 times.
✓ Branch 1 taken 43392 times.
✓ Branch 2 taken 238736 times.
✓ Branch 3 taken 43392 times.
651040 for (const auto& dataJ : connectivityMap[globalI])
280 {
281 238736 neighborElements[j-1] = gridGeometry.element(dataJ.globalJ);
282
4/4
✓ Branch 0 taken 537632 times.
✓ Branch 1 taken 238736 times.
✓ Branch 2 taken 446368 times.
✓ Branch 3 taken 147472 times.
1700208 for (const auto scvfIdx : dataJ.scvfsJ)
283 1597344 origResiduals[j] += evalNeighborFlux(neighborElements[j-1], fvGeometry.scvf(scvfIdx));
284
285 238736 ++j;
286 }
287
288 // reference to the element's scv (needed later) and corresponding vol vars
289
1/2
✓ Branch 0 taken 23424 times.
✗ Branch 1 not taken.
43392 const auto& scv = fvGeometry.scv(globalI);
290 86784 auto& curVolVars = ParentType::getVolVarAccess(gridVariables.curGridVolVars(), curElemVolVars, scv);
291
292 // save a copy of the original privars and vol vars in order
293 // to restore the original solution after deflection
294 43392 const auto& curSol = this->asImp_().curSol();
295 43392 const auto origPriVars = curSol[globalI];
296 43392 const auto origVolVars = curVolVars;
297
298 // element solution container to be deflected
299 43392 auto elemSol = elementSolution<FVElementGeometry>(origPriVars);
300
301 // derivatives in the neighbors with respect to the current elements
302 // in index 0 we save the derivative of the element residual with respect to it's own dofs
303 43392 Residuals partialDerivs(numNeighbors + 1);
304
305
2/2
✓ Branch 0 taken 86784 times.
✓ Branch 1 taken 43392 times.
130176 for (int pvIdx = 0; pvIdx < numEq; ++pvIdx)
306 {
307 86784 partialDerivs = 0.0;
308
309 440832 auto evalResiduals = [&](Scalar priVar)
310 {
311 86784 Residuals partialDerivsTmp(numNeighbors + 1);
312 86784 partialDerivsTmp = 0.0;
313 // update the volume variables and the flux var cache
314 93696 elemSol[0][pvIdx] = priVar;
315 86784 this->asImp_().maybeUpdateCouplingContext(scv, elemSol, pvIdx);
316 173568 curVolVars.update(elemSol, this->asImp_().problem(), element, scv);
317 182464 elemFluxVarsCache.update(element, fvGeometry, curElemVolVars);
318 if (enableGridFluxVarsCache)
319 79872 gridVariables.gridFluxVarsCache().updateElement(element, fvGeometry, curElemVolVars);
320
321 // calculate the residual with the deflected primary variables
322
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 86784 times.
260352 partialDerivsTmp[0] = this->evalLocalResidual()[0];
323
324 // calculate the fluxes in the neighbors with the deflected primary variables
325
2/2
✓ Branch 0 taken 477472 times.
✓ Branch 1 taken 86784 times.
564256 for (std::size_t k = 0; k < numNeighbors; ++k)
326
4/4
✓ Branch 0 taken 1075264 times.
✓ Branch 1 taken 477472 times.
✓ Branch 2 taken 892736 times.
✓ Branch 3 taken 294944 times.
4355360 for (auto scvfIdx : connectivityMap[globalI][k].scvfsJ)
327 3194688 partialDerivsTmp[k+1] += evalNeighborFlux(neighborElements[k], fvGeometry.scvf(scvfIdx));
328
329 86784 return partialDerivsTmp;
330 };
331
332 // derive the residuals numerically
333
6/10
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 86778 times.
✓ Branch 3 taken 6 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 6 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 6 times.
✗ Branch 10 not taken.
✓ Branch 12 taken 6 times.
✗ Branch 13 not taken.
86784 static const NumericEpsilon<Scalar, numEq> eps_{this->asImp_().problem().paramGroup()};
334
7/10
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 86775 times.
✓ Branch 3 taken 6 times.
✓ Branch 4 taken 3 times.
✓ Branch 6 taken 6 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 6 times.
✗ Branch 10 not taken.
✓ Branch 12 taken 6 times.
✗ Branch 13 not taken.
86784 static const int numDiffMethod = getParamFromGroup<int>(this->asImp_().problem().paramGroup(), "Assembly.NumericDifferenceMethod");
335
2/4
✓ Branch 1 taken 86784 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 86784 times.
✗ Branch 5 not taken.
173568 NumericDifferentiation::partialDerivative(evalResiduals, elemSol[0][pvIdx], partialDerivs, origResiduals,
336 173568 eps_(elemSol[0][pvIdx], pvIdx), numDiffMethod);
337
338 // Correct derivative for ghost elements, i.e. set a 1 for the derivative w.r.t. the
339 // current primary variable and a 0 elsewhere. As we always solve for a delta of the
340 // solution with respect to the initial one, this results in a delta of 0 for ghosts.
341
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 86784 times.
86784 if (this->elementIsGhost())
342 {
343 partialDerivs[0] = 0.0;
344 partialDerivs[0][pvIdx] = 1.0;
345 }
346
347 // restore the original state of the scv's volume variables
348 86784 curVolVars = origVolVars;
349
350 // restore the current element solution
351
2/4
✓ Branch 1 taken 6912 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6912 times.
✗ Branch 5 not taken.
173568 elemSol[0][pvIdx] = origPriVars[pvIdx];
352
353 // restore the undeflected state of the coupling context
354
1/2
✓ Branch 1 taken 6912 times.
✗ Branch 2 not taken.
86784 this->asImp_().maybeUpdateCouplingContext(scv, elemSol, pvIdx);
355
356 // add the current partial derivatives to the global jacobian matrix
357 // no special treatment is needed if globalJ is a ghost because then derivatives have been assembled to 0 above
358 if constexpr (Problem::enableInternalDirichletConstraints())
359 {
360 // check if own element has internal Dirichlet constraint
361 const auto internalDirichletConstraintsOwnElement = this->asImp_().problem().hasInternalDirichletConstraint(this->element(), scv);
362 const auto dirichletValues = this->asImp_().problem().internalDirichlet(this->element(), scv);
363
364 for (int eqIdx = 0; eqIdx < numEq; ++eqIdx)
365 {
366 if (internalDirichletConstraintsOwnElement[eqIdx])
367 {
368 origResiduals[0][eqIdx] = origVolVars.priVars()[eqIdx] - dirichletValues[eqIdx];
369 A[globalI][globalI][eqIdx][pvIdx] = (eqIdx == pvIdx) ? 1.0 : 0.0;
370 }
371 else
372 A[globalI][globalI][eqIdx][pvIdx] += partialDerivs[0][eqIdx];
373 }
374
375 // off-diagonal entries
376 j = 1;
377 for (const auto& dataJ : connectivityMap[globalI])
378 {
379 const auto& neighborElement = neighborElements[j-1];
380 const auto& neighborScv = fvGeometry.scv(dataJ.globalJ);
381 const auto internalDirichletConstraintsNeighbor = this->asImp_().problem().hasInternalDirichletConstraint(neighborElement, neighborScv);
382
383 for (int eqIdx = 0; eqIdx < numEq; ++eqIdx)
384 {
385 if (internalDirichletConstraintsNeighbor[eqIdx])
386 A[dataJ.globalJ][globalI][eqIdx][pvIdx] = 0.0;
387 else
388 A[dataJ.globalJ][globalI][eqIdx][pvIdx] += partialDerivs[j][eqIdx];
389 }
390
391 ++j;
392 }
393 }
394 else // no internal Dirichlet constraints specified
395 {
396
2/2
✓ Branch 0 taken 173568 times.
✓ Branch 1 taken 86784 times.
260352 for (int eqIdx = 0; eqIdx < numEq; eqIdx++)
397 {
398 // the diagonal entries
399
4/8
✓ Branch 1 taken 173568 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 173568 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 173568 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 173568 times.
✗ Branch 11 not taken.
694272 A[globalI][globalI][eqIdx][pvIdx] += partialDerivs[0][eqIdx];
400
401 // off-diagonal entries
402 173568 j = 1;
403
4/4
✓ Branch 0 taken 954944 times.
✓ Branch 1 taken 173568 times.
✓ Branch 2 taken 954944 times.
✓ Branch 3 taken 173568 times.
2604160 for (const auto& dataJ : connectivityMap[globalI])
404
4/8
✓ Branch 1 taken 954944 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 954944 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 954944 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 954944 times.
✗ Branch 11 not taken.
3819776 A[dataJ.globalJ][globalI][eqIdx][pvIdx] += partialDerivs[j++][eqIdx];
405 }
406 }
407 }
408
409 // restore original state of the flux vars cache in case of global caching.
410 // This has to be done in order to guarantee that everything is in an undeflected
411 // state before the assembly of another element is called. In the case of local caching
412 // this is obsolete because the elemFluxVarsCache used here goes out of scope after this.
413 // We only have to do this for the last primary variable, for all others the flux var cache
414 // is updated with the correct element volume variables before residual evaluations
415 if (enableGridFluxVarsCache)
416 39936 gridVariables.gridFluxVarsCache().updateElement(element, fvGeometry, curElemVolVars);
417
418 // evaluate additional derivatives that might arise from the coupling (no-op if not coupled)
419 43392 ElementResidualVector orig{origResidual};
420 43392 this->asImp_().maybeEvalAdditionalDomainDerivatives(orig, A, gridVariables);
421 43392 }
422
423 /*!
424 * \brief Computes the derivatives with respect to the given element and adds them
425 * to the global matrix. Calculates the element residual at the current solution.
426 */
427 5000000 void assembleJacobianExplicit_(JacobianMatrix& A, GridVariables& gridVariables, const NumEqVector& origResidual)
428 {
429 5000000 if (this->assembler().isStationaryProblem())
430 DUNE_THROW(Dune::InvalidStateException, "Using explicit jacobian assembler with stationary local residual");
431
432 // assemble the undeflected residual
433 5000000 auto storageResidual = origResidual;
434
435 //////////////////////////////////////////////////////////////////////////////////////////////////
436 // Calculate derivatives of all dofs in stencil with respect to the dofs in the element. In the //
437 // neighboring elements all derivatives are zero. For the assembled element only the storage //
438 // derivatives are non-zero. //
439 //////////////////////////////////////////////////////////////////////////////////////////////////
440
441 // get some aliases for convenience
442 5000000 const auto& element = this->element();
443 5000000 const auto& fvGeometry = this->fvGeometry();
444 5000000 const auto& gridGeometry = this->fvGeometry().gridGeometry();
445 5000000 auto&& curElemVolVars = this->curElemVolVars();
446
447 // reference to the element's scv (needed later) and corresponding vol vars
448 10000000 const auto globalI = gridGeometry.elementMapper().index(element);
449
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
5000000 const auto& scv = fvGeometry.scv(globalI);
450 10000000 auto& curVolVars = ParentType::getVolVarAccess(gridVariables.curGridVolVars(), curElemVolVars, scv);
451
452 // save a copy of the original privars and vol vars in order
453 // to restore the original solution after deflection
454 5000000 const auto& curSol = this->asImp_().curSol();
455 5000000 const auto origPriVars = curSol[globalI];
456 5000000 const auto origVolVars = curVolVars;
457
458 // element solution container to be deflected
459 5000000 auto elemSol = elementSolution<FVElementGeometry>(origPriVars);
460
461 // derivatives in the neighbors with respect to the current elements
462 5000000 NumEqVector partialDeriv;
463
2/2
✓ Branch 0 taken 10000000 times.
✓ Branch 1 taken 5000000 times.
15000000 for (int pvIdx = 0; pvIdx < numEq; ++pvIdx)
464 {
465 // reset derivatives of element dof with respect to itself
466 10000000 partialDeriv = 0.0;
467
468 50000000 auto evalStorage = [&](Scalar priVar)
469 {
470 // update the volume variables and calculate
471 // the residual with the deflected primary variables
472 20000000 elemSol[0][pvIdx] = priVar;
473 20000000 curVolVars.update(elemSol, this->asImp_().problem(), element, scv);
474 10000000 return this->evalStorage()[0];
475 };
476
477 // for non-ghosts compute the derivative numerically
478
1/2
✓ Branch 0 taken 10000000 times.
✗ Branch 1 not taken.
10000000 if (!this->elementIsGhost())
479 {
480
7/10
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 9999987 times.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 11 times.
✓ Branch 6 taken 2 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 2 times.
✗ Branch 10 not taken.
✓ Branch 12 taken 2 times.
✗ Branch 13 not taken.
10000000 static const NumericEpsilon<Scalar, numEq> eps_{this->asImp_().problem().paramGroup()};
481
7/10
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 9999988 times.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 10 times.
✓ Branch 6 taken 2 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 2 times.
✗ Branch 10 not taken.
✓ Branch 12 taken 2 times.
✗ Branch 13 not taken.
10000000 static const int numDiffMethod = getParamFromGroup<int>(this->asImp_().problem().paramGroup(), "Assembly.NumericDifferenceMethod");
482
2/4
✓ Branch 1 taken 10000000 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 10000000 times.
✗ Branch 5 not taken.
20000000 NumericDifferentiation::partialDerivative(evalStorage, elemSol[0][pvIdx], partialDeriv, storageResidual,
483 20000000 eps_(elemSol[0][pvIdx], pvIdx), numDiffMethod);
484 }
485
486 // for ghost elements we assemble a 1.0 where the primary variable and zero everywhere else
487 // as we always solve for a delta of the solution with respect to the initial solution this
488 // results in a delta of zero for ghosts
489 else partialDeriv[pvIdx] = 1.0;
490
491 // restore the original state of the scv's volume variables
492 10000000 curVolVars = origVolVars;
493
494 // restore the current element solution
495 20000000 elemSol[0][pvIdx] = origPriVars[pvIdx];
496
497 // add the current partial derivatives to the global jacobian matrix
498 // only diagonal entries for explicit jacobians
499 if constexpr (Problem::enableInternalDirichletConstraints())
500 {
501 // check if own element has internal Dirichlet constraint
502 const auto internalDirichletConstraints = this->asImp_().problem().hasInternalDirichletConstraint(this->element(), scv);
503 const auto dirichletValues = this->asImp_().problem().internalDirichlet(this->element(), scv);
504
505 for (int eqIdx = 0; eqIdx < numEq; ++eqIdx)
506 {
507 // TODO: we need to set constrained dof flags for these dofs and not modify the residual here
508 // this function only takes care of the Jacobian
509 if (internalDirichletConstraints[eqIdx])
510 {
511 storageResidual[eqIdx] = origVolVars.priVars()[eqIdx] - dirichletValues[eqIdx];
512 A[globalI][globalI][eqIdx][pvIdx] = (eqIdx == pvIdx) ? 1.0 : 0.0;
513 }
514 else
515 A[globalI][globalI][eqIdx][pvIdx] += partialDeriv[eqIdx];
516 }
517 }
518 else
519 {
520
2/2
✓ Branch 0 taken 20000000 times.
✓ Branch 1 taken 10000000 times.
30000000 for (int eqIdx = 0; eqIdx < numEq; eqIdx++)
521
3/6
✓ Branch 1 taken 20000000 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 20000000 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 20000000 times.
✗ Branch 8 not taken.
60000000 A[globalI][globalI][eqIdx][pvIdx] += partialDeriv[eqIdx];
522 }
523 }
524 5000000 }
525 };
526
527 } // end namespace Dumux
528
529 #endif
530