GCC Code Coverage Report


Directory: ../../../builds/dumux-repositories/
File: /builds/dumux-repositories/dumux/dumux/assembly/cclocalassembler.hh
Date: 2024-09-21 20:52:54
Exec Total Coverage
Lines: 128 137 93.4%
Functions: 362 453 79.9%
Branches: 133 224 59.4%

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 Assembly
10 * \ingroup CCDiscretization
11 * \brief An assembler for Jacobian and residual contribution per element (cell-centered methods)
12 */
13 #ifndef DUMUX_CC_LOCAL_ASSEMBLER_HH
14 #define DUMUX_CC_LOCAL_ASSEMBLER_HH
15
16 #include <dune/common/reservedvector.hh>
17 #include <dune/grid/common/gridenums.hh> // for GhostEntity
18 #include <dune/istl/matrixindexset.hh>
19
20 #include <dumux/common/reservedblockvector.hh>
21 #include <dumux/common/properties.hh>
22 #include <dumux/common/parameters.hh>
23 #include <dumux/common/numericdifferentiation.hh>
24 #include <dumux/common/numeqvector.hh>
25 #include <dumux/assembly/numericepsilon.hh>
26 #include <dumux/assembly/diffmethod.hh>
27 #include <dumux/assembly/fvlocalassemblerbase.hh>
28 #include <dumux/assembly/entitycolor.hh>
29 #include <dumux/assembly/partialreassembler.hh>
30 #include <dumux/discretization/fluxstencil.hh>
31 #include <dumux/discretization/cellcentered/elementsolution.hh>
32
33 namespace Dumux {
34
35 /*!
36 * \ingroup Assembly
37 * \ingroup CCDiscretization
38 * \brief A base class for all local cell-centered assemblers
39 * \tparam TypeTag The TypeTag
40 * \tparam Assembler The assembler type
41 * \tparam Implementation The actual implementation
42 * \tparam implicit Specifies whether the time discretization is implicit or not (i.e. explicit)
43 */
44 template<class TypeTag, class Assembler, class Implementation, bool implicit>
45 31529200 class CCLocalAssemblerBase : public FVLocalAssemblerBase<TypeTag, Assembler, Implementation, implicit>
46 {
47 using ParentType = FVLocalAssemblerBase<TypeTag, Assembler, Implementation, implicit>;
48 using GridView = typename GetPropType<TypeTag, Properties::GridGeometry>::GridView;
49 using JacobianMatrix = GetPropType<TypeTag, Properties::JacobianMatrix>;
50 using GridVariables = GetPropType<TypeTag, Properties::GridVariables>;
51 using ElementVolumeVariables = typename GetPropType<TypeTag, Properties::GridVolumeVariables>::LocalView;
52 using NumEqVector = Dumux::NumEqVector<GetPropType<TypeTag, Properties::PrimaryVariables>>;
53
54 public:
55
56 27569378 using ParentType::ParentType;
57
58 /*!
59 * \brief Computes the derivatives with respect to the given element and adds them
60 * to the global matrix. The element residual is written into the right hand side.
61 */
62 template <class ResidualVector, class PartialReassembler = DefaultPartialReassembler>
63 27907714 void assembleJacobianAndResidual(JacobianMatrix& jac, ResidualVector& res, GridVariables& gridVariables,
64 const PartialReassembler* partialReassembler)
65 {
66 27907714 this->asImp_().bindLocalViews();
67
3/6
✗ Branch 0 not taken.
✓ Branch 1 taken 9518 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 9518 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 9518 times.
83723142 const auto globalI = this->assembler().gridGeometry().elementMapper().index(this->element());
68 if (partialReassembler
69
6/6
✓ Branch 0 taken 5382330 times.
✓ Branch 1 taken 19569352 times.
✓ Branch 2 taken 1919668 times.
✓ Branch 3 taken 3462662 times.
✓ Branch 4 taken 1919668 times.
✓ Branch 5 taken 3462662 times.
25185154 && partialReassembler->elementColor(globalI) == EntityColor::green)
70 {
71
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 166912 times.
2687320 res[globalI] = this->asImp_().evalLocalResidual()[0]; // forward to the internal implementation
72 }
73 else
74 {
75 25921317 res[globalI] = this->asImp_().assembleJacobianAndResidualImpl(jac, gridVariables); // forward to the internal implementation
76 }
77 27907714 }
78
79 /*!
80 * \brief Computes the derivatives with respect to the given element and adds them
81 * to the global matrix.
82 */
83 void assembleJacobian(JacobianMatrix& jac, GridVariables& gridVariables)
84 {
85
1/4
✓ Branch 1 taken 180764 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
180764 this->asImp_().bindLocalViews();
86
1/4
✓ Branch 1 taken 180764 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
180764 this->asImp_().assembleJacobianAndResidualImpl(jac, gridVariables); // forward to the internal implementation
87 }
88
89 /*!
90 * \brief Assemble the residual only
91 */
92 template <class ResidualVector>
93 1043672 void assembleResidual(ResidualVector& res)
94 {
95 1043672 this->asImp_().bindLocalViews();
96 3131016 const auto globalI = this->assembler().gridGeometry().elementMapper().index(this->element());
97
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 501640 times.
1043672 res[globalI] = this->asImp_().evalLocalResidual()[0]; // forward to the internal implementation
98
99 using Problem = GetPropType<TypeTag, Properties::Problem>;
100 if constexpr (Problem::enableInternalDirichletConstraints())
101 {
102 204 const auto applyDirichlet = [&] (const auto& scvI,
103 const auto& dirichletValues,
104 const auto eqIdx,
105 4 const auto pvIdx)
106 {
107 4 res[scvI.dofIndex()][eqIdx] = this->curElemVolVars()[scvI].priVars()[pvIdx] - dirichletValues[pvIdx];
108 };
109
110 200 this->asImp_().enforceInternalDirichletConstraints(applyDirichlet);
111 }
112 1043672 }
113 };
114
115 /*!
116 * \ingroup Assembly
117 * \ingroup CCDiscretization
118 * \brief An assembler for Jacobian and residual contribution per element (cell-centered methods)
119 * \tparam TypeTag The TypeTag
120 * \tparam diffMethod The differentiation method to residual compute derivatives
121 * \tparam implicit Specifies whether the time discretization is implicit or not not (i.e. explicit)
122 */
123 template<class TypeTag, class Assembler, DiffMethod diffMethod = DiffMethod::numeric, bool implicit = true>
124 class CCLocalAssembler;
125
126 /*!
127 * \ingroup Assembly
128 * \ingroup CCDiscretization
129 * \brief Cell-centered scheme local assembler using numeric differentiation and implicit time discretization
130 */
131 template<class TypeTag, class Assembler>
132 18614152 class CCLocalAssembler<TypeTag, Assembler, DiffMethod::numeric, /*implicit=*/true>
133 : public CCLocalAssemblerBase<TypeTag, Assembler,
134 CCLocalAssembler<TypeTag, Assembler, DiffMethod::numeric, true>, true >
135 {
136 using ThisType = CCLocalAssembler<TypeTag, Assembler, DiffMethod::numeric, true>;
137 using ParentType = CCLocalAssemblerBase<TypeTag, Assembler, ThisType, true>;
138 using Scalar = GetPropType<TypeTag, Properties::Scalar>;
139 using NumEqVector = Dumux::NumEqVector<GetPropType<TypeTag, Properties::PrimaryVariables>>;
140 using Element = typename GetPropType<TypeTag, Properties::GridGeometry>::GridView::template Codim<0>::Entity;
141 using GridGeometry = GetPropType<TypeTag, Properties::GridGeometry>;
142 using FVElementGeometry = typename GridGeometry::LocalView;
143 using GridVariables = GetPropType<TypeTag, Properties::GridVariables>;
144 using JacobianMatrix = GetPropType<TypeTag, Properties::JacobianMatrix>;
145 using Problem = typename GridVariables::GridVolumeVariables::Problem;
146
147 enum { numEq = GetPropType<TypeTag, Properties::ModelTraits>::numEq() };
148 enum { dim = GetPropType<TypeTag, Properties::GridGeometry>::GridView::dimension };
149
150 using FluxStencil = Dumux::FluxStencil<FVElementGeometry>;
151 static constexpr int maxElementStencilSize = GridGeometry::maxElementStencilSize;
152 static constexpr bool enableGridFluxVarsCache = GetPropType<TypeTag, Properties::GridVariables>::GridFluxVariablesCache::cachingEnabled;
153
154 public:
155
156
2/8
✓ Branch 2 taken 16876696 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 43604 times.
✗ Branch 7 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
16920300 using ParentType::ParentType;
157
158 /*!
159 * \brief Computes the derivatives with respect to the given element and adds them
160 * to the global matrix.
161 *
162 * \return The element residual at the current solution.
163 */
164 14958288 NumEqVector assembleJacobianAndResidualImpl(JacobianMatrix& A, GridVariables& gridVariables)
165 {
166 //////////////////////////////////////////////////////////////////////////////////////////////////
167 // Calculate derivatives of all dofs in stencil with respect to the dofs in the element. In the //
168 // neighboring elements we do so by computing the derivatives of the fluxes which depend on the //
169 // actual element. In the actual element we evaluate the derivative of the entire residual. //
170 //////////////////////////////////////////////////////////////////////////////////////////////////
171
172 // get some aliases for convenience
173 14958288 const auto& element = this->element();
174 14958288 const auto& fvGeometry = this->fvGeometry();
175 14958288 const auto& gridGeometry = this->assembler().gridGeometry();
176 14958288 auto&& curElemVolVars = this->curElemVolVars();
177 14958288 auto&& elemFluxVarsCache = this->elemFluxVarsCache();
178
179 // get stencil information
180 29916576 const auto globalI = gridGeometry.elementMapper().index(element);
181 14958288 const auto& connectivityMap = gridGeometry.connectivityMap();
182 29916576 const auto numNeighbors = connectivityMap[globalI].size();
183
184 // container to store the neighboring elements
185 14958288 Dune::ReservedVector<Element, maxElementStencilSize> neighborElements;
186 14958288 neighborElements.resize(numNeighbors);
187
188 // assemble the undeflected residual
189 using Residuals = ReservedBlockVector<NumEqVector, maxElementStencilSize>;
190 29916576 Residuals origResiduals(numNeighbors + 1); origResiduals = 0.0;
191
2/3
✓ Branch 0 taken 31680 times.
✓ Branch 1 taken 7246812 times.
✗ Branch 2 not taken.
26931942 origResiduals[0] = this->evalLocalResidual()[0];
192
193 // lambda for convenient evaluation of the fluxes across scvfs in the neighbors
194 // if the neighbor is a ghost we don't want to add anything to their residual
195 // so we return 0 and omit computing the flux
196 485772822 auto evalNeighborFlux = [&] (const auto& neighbor, const auto& scvf)
197 {
198
3/5
✓ Branch 0 taken 5803176 times.
✓ Branch 1 taken 251156 times.
✓ Branch 2 taken 229368993 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
235423325 if (neighbor.partitionType() == Dune::GhostEntity)
199 346196 return NumEqVector(0.0);
200 else
201 359636085 return this->localResidual().evalFlux(this->problem(),
202 neighbor,
203 this->fvGeometry(),
204 this->curElemVolVars(),
205 1798180425 this->elemFluxVarsCache(), scvf);
206 };
207
208 // get the elements in which we need to evaluate the fluxes
209 // and calculate these in the undeflected state
210 14958288 unsigned int j = 1;
211
4/4
✓ Branch 0 taken 69110794 times.
✓ Branch 1 taken 14958288 times.
✓ Branch 2 taken 69110794 times.
✓ Branch 3 taken 14958288 times.
198054740 for (const auto& dataJ : connectivityMap[globalI])
212 {
213
1/2
✓ Branch 1 taken 20811222 times.
✗ Branch 2 not taken.
106658350 neighborElements[j-1] = gridGeometry.element(dataJ.globalJ);
214
4/4
✓ Branch 0 taken 113373052 times.
✓ Branch 1 taken 69110794 times.
✓ Branch 2 taken 70211802 times.
✓ Branch 3 taken 25949544 times.
390917236 for (const auto scvfIdx : dataJ.scvfsJ)
215
5/8
✓ Branch 1 taken 32105200 times.
✓ Branch 2 taken 8030062 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 32105200 times.
✓ Branch 5 taken 8030062 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 32105200 times.
✗ Branch 8 not taken.
410484016 origResiduals[j] += evalNeighborFlux(neighborElements[j-1], fvGeometry.scvf(scvfIdx));
216
217 69110794 ++j;
218 }
219
220 // reference to the element's scv (needed later) and corresponding vol vars
221
2/3
✓ Branch 0 taken 4666896 times.
✓ Branch 1 taken 2363892 times.
✗ Branch 2 not taken.
14958288 const auto& scv = fvGeometry.scv(globalI);
222
2/4
✓ Branch 1 taken 2363892 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2363892 times.
✗ Branch 5 not taken.
29916576 auto& curVolVars = ParentType::getVolVarAccess(gridVariables.curGridVolVars(), curElemVolVars, scv);
223
224 // save a copy of the original privars and vol vars in order
225 // to restore the original solution after deflection
226 14958288 const auto& curSol = this->curSol();
227
1/2
✓ Branch 1 taken 4100418 times.
✗ Branch 2 not taken.
14958288 const auto origPriVars = curSol[globalI];
228 14958288 const auto origVolVars = curVolVars;
229
230 // element solution container to be deflected
231
1/2
✓ Branch 1 taken 4100418 times.
✗ Branch 2 not taken.
14958288 auto elemSol = elementSolution(element, curSol, gridGeometry);
232
233 // derivatives in the neighbors with respect to the current elements
234 // in index 0 we save the derivative of the element residual with respect to it's own dofs
235 14958288 Residuals partialDerivs(numNeighbors + 1);
236
237
2/2
✓ Branch 0 taken 34723649 times.
✓ Branch 1 taken 14958288 times.
49681937 for (int pvIdx = 0; pvIdx < numEq; ++pvIdx)
238 {
239 34723649 partialDerivs = 0.0;
240
241 161881663 auto evalResiduals = [&](Scalar priVar)
242 {
243 34916609 Residuals partialDerivsTmp(numNeighbors + 1);
244 34916609 partialDerivsTmp = 0.0;
245 // update the volume variables and the flux var cache
246 57327067 elemSol[0][pvIdx] = priVar;
247 69833218 curVolVars.update(elemSol, this->problem(), element, scv);
248 108026506 elemFluxVarsCache.update(element, fvGeometry, curElemVolVars);
249 if (enableGridFluxVarsCache)
250
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2341340 times.
12021388 gridVariables.gridFluxVarsCache().updateElement(element, fvGeometry, curElemVolVars);
251
252 // calculate the residual with the deflected primary variables
253
2/2
✓ Branch 0 taken 63360 times.
✓ Branch 1 taken 11910294 times.
58863917 partialDerivsTmp[0] = this->evalLocalResidual()[0];
254
255 // calculate the fluxes in the neighbors with the deflected primary variables
256
2/2
✓ Branch 0 taken 158877979 times.
✓ Branch 1 taken 34916609 times.
193794588 for (std::size_t k = 0; k < numNeighbors; ++k)
257
4/4
✓ Branch 0 taken 246514189 times.
✓ Branch 1 taken 158877979 times.
✓ Branch 2 taken 139011648 times.
✓ Branch 3 taken 51375438 times.
1179915732 for (auto scvfIdx : connectivityMap[globalI][k].scvfsJ)
258 903425422 partialDerivsTmp[k+1] += evalNeighborFlux(neighborElements[k], fvGeometry.scvf(scvfIdx));
259
260 34916609 return partialDerivsTmp;
261 };
262
263 // derive the residuals numerically
264
7/10
✓ Branch 0 taken 310 times.
✓ Branch 1 taken 34723339 times.
✓ Branch 3 taken 138 times.
✓ Branch 4 taken 172 times.
✓ Branch 6 taken 138 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 138 times.
✗ Branch 10 not taken.
✓ Branch 12 taken 138 times.
✗ Branch 13 not taken.
34723649 static const NumericEpsilon<Scalar, numEq> eps_{this->problem().paramGroup()};
265
7/10
✓ Branch 0 taken 248 times.
✓ Branch 1 taken 34723401 times.
✓ Branch 3 taken 138 times.
✓ Branch 4 taken 110 times.
✓ Branch 6 taken 138 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 138 times.
✗ Branch 10 not taken.
✓ Branch 12 taken 138 times.
✗ Branch 13 not taken.
34723649 static const int numDiffMethod = getParamFromGroup<int>(this->problem().paramGroup(), "Assembly.NumericDifferenceMethod");
266
2/4
✓ Branch 1 taken 34723649 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 32541477 times.
✗ Branch 5 not taken.
67265126 NumericDifferentiation::partialDerivative(evalResiduals, elemSol[0][pvIdx], partialDerivs, origResiduals,
267 67265126 eps_(elemSol[0][pvIdx], pvIdx), numDiffMethod);
268
269 // Correct derivative for ghost elements, i.e. set a 1 for the derivative w.r.t. the
270 // current primary variable and a 0 elsewhere. As we always solve for a delta of the
271 // solution with respect to the initial one, this results in a delta of 0 for ghosts.
272
2/2
✓ Branch 0 taken 171215 times.
✓ Branch 1 taken 34552434 times.
34723649 if (this->elementIsGhost())
273 {
274 171215 partialDerivs[0] = 0.0;
275 510685 partialDerivs[0][pvIdx] = 1.0;
276 }
277
278 // restore the original state of the scv's volume variables
279 34723649 curVolVars = origVolVars;
280
281 // restore the current element solution
282 67265126 elemSol[0][pvIdx] = origPriVars[pvIdx];
283
284 // add the current partial derivatives to the global jacobian matrix
285 // no special treatment is needed if globalJ is a ghost because then derivatives have been assembled to 0 above
286 if constexpr (Problem::enableInternalDirichletConstraints())
287 {
288 // check if own element has internal Dirichlet constraint
289 const auto internalDirichletConstraintsOwnElement = this->problem().hasInternalDirichletConstraint(this->element(), scv);
290 const auto dirichletValues = this->problem().internalDirichlet(this->element(), scv);
291
292 for (int eqIdx = 0; eqIdx < numEq; ++eqIdx)
293 {
294 if (internalDirichletConstraintsOwnElement[eqIdx])
295 {
296 origResiduals[0][eqIdx] = origVolVars.priVars()[eqIdx] - dirichletValues[eqIdx];
297 A[globalI][globalI][eqIdx][pvIdx] = (eqIdx == pvIdx) ? 1.0 : 0.0;
298 }
299 else
300 A[globalI][globalI][eqIdx][pvIdx] += partialDerivs[0][eqIdx];
301 }
302
303 // off-diagonal entries
304 j = 1;
305 for (const auto& dataJ : connectivityMap[globalI])
306 {
307 const auto& neighborElement = neighborElements[j-1];
308 const auto& neighborScv = fvGeometry.scv(dataJ.globalJ);
309 const auto internalDirichletConstraintsNeighbor = this->problem().hasInternalDirichletConstraint(neighborElement, neighborScv);
310
311 for (int eqIdx = 0; eqIdx < numEq; ++eqIdx)
312 {
313 if (internalDirichletConstraintsNeighbor[eqIdx])
314 A[dataJ.globalJ][globalI][eqIdx][pvIdx] = 0.0;
315 else
316 A[dataJ.globalJ][globalI][eqIdx][pvIdx] += partialDerivs[j][eqIdx];
317 }
318
319 ++j;
320 }
321 }
322 else // no internal Dirichlet constraints specified
323 {
324
2/2
✓ Branch 0 taken 88649833 times.
✓ Branch 1 taken 34723649 times.
123373482 for (int eqIdx = 0; eqIdx < numEq; eqIdx++)
325 {
326 // the diagonal entries
327
4/8
✓ Branch 1 taken 88649833 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 88649833 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 88649833 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 86467661 times.
✗ Branch 11 not taken.
352417160 A[globalI][globalI][eqIdx][pvIdx] += partialDerivs[0][eqIdx];
328
329 // off-diagonal entries
330 88649833 j = 1;
331
4/4
✓ Branch 0 taken 386663457 times.
✓ Branch 1 taken 92574979 times.
✓ Branch 2 taken 386663457 times.
✓ Branch 3 taken 92574979 times.
1135776538 for (const auto& dataJ : connectivityMap[globalI])
332
4/8
✓ Branch 1 taken 390588603 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 390588603 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 390588603 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 384481285 times.
✗ Branch 11 not taken.
1556247094 A[dataJ.globalJ][globalI][eqIdx][pvIdx] += partialDerivs[j++][eqIdx];
333 }
334 }
335 }
336
337 // restore original state of the flux vars cache in case of global caching.
338 // This has to be done in order to guarantee that everything is in an undeflected
339 // state before the assembly of another element is called. In the case of local caching
340 // this is obsolete because the elemFluxVarsCache used here goes out of scope after this.
341 // We only have to do this for the last primary variable, for all others the flux var cache
342 // is updated with the correct element volume variables before residual evaluations
343 if (enableGridFluxVarsCache)
344
2/4
✓ Branch 1 taken 579386 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 579386 times.
✗ Branch 5 not taken.
5870834 gridVariables.gridFluxVarsCache().updateElement(element, fvGeometry, curElemVolVars);
345
346 // return the original residual
347 34016994 return origResiduals[0];
348 }
349 };
350
351
352 /*!
353 * \ingroup Assembly
354 * \ingroup CCDiscretization
355 * \brief Cell-centered scheme local assembler using numeric differentiation and explicit time discretization
356 */
357 template<class TypeTag, class Assembler>
358 class CCLocalAssembler<TypeTag, Assembler, DiffMethod::numeric, /*implicit=*/false>
359 : public CCLocalAssemblerBase<TypeTag, Assembler,
360 CCLocalAssembler<TypeTag, Assembler, DiffMethod::numeric, false>, false>
361 {
362 using ThisType = CCLocalAssembler<TypeTag, Assembler, DiffMethod::numeric, false>;
363 using ParentType = CCLocalAssemblerBase<TypeTag, Assembler, ThisType, false>;
364 using Scalar = GetPropType<TypeTag, Properties::Scalar>;
365 using NumEqVector = Dumux::NumEqVector<GetPropType<TypeTag, Properties::PrimaryVariables>>;
366 using Element = typename GetPropType<TypeTag, Properties::GridGeometry>::GridView::template Codim<0>::Entity;
367 using GridVariables = GetPropType<TypeTag, Properties::GridVariables>;
368 using JacobianMatrix = GetPropType<TypeTag, Properties::JacobianMatrix>;
369 using Problem = typename GridVariables::GridVolumeVariables::Problem;
370
371 enum { numEq = GetPropType<TypeTag, Properties::ModelTraits>::numEq() };
372
373 public:
374 using ParentType::ParentType;
375
376 /*!
377 * \brief Computes the derivatives with respect to the given element and adds them
378 * to the global matrix.
379 *
380 * \return The element residual at the current solution.
381 */
382 NumEqVector assembleJacobianAndResidualImpl(JacobianMatrix& A, GridVariables& gridVariables)
383 {
384 if (this->assembler().isStationaryProblem())
385 DUNE_THROW(Dune::InvalidStateException, "Using explicit jacobian assembler with stationary local residual");
386
387 // assemble the undeflected residual
388 auto residual = this->evalLocalResidual()[0];
389 const auto storageResidual = this->evalLocalStorageResidual()[0];
390
391 //////////////////////////////////////////////////////////////////////////////////////////////////
392 // Calculate derivatives of all dofs in stencil with respect to the dofs in the element. In the //
393 // neighboring elements all derivatives are zero. For the assembled element only the storage //
394 // derivatives are non-zero. //
395 //////////////////////////////////////////////////////////////////////////////////////////////////
396
397 // get some aliases for convenience
398 const auto& element = this->element();
399 const auto& fvGeometry = this->fvGeometry();
400 const auto& gridGeometry = this->assembler().gridGeometry();
401 auto&& curElemVolVars = this->curElemVolVars();
402
403 // reference to the element's scv (needed later) and corresponding vol vars
404 const auto globalI = gridGeometry.elementMapper().index(element);
405 const auto& scv = fvGeometry.scv(globalI);
406 auto& curVolVars = ParentType::getVolVarAccess(gridVariables.curGridVolVars(), curElemVolVars, scv);
407
408 // save a copy of the original privars and vol vars in order
409 // to restore the original solution after deflection
410 const auto& curSol = this->curSol();
411 const auto origPriVars = curSol[globalI];
412 const auto origVolVars = curVolVars;
413
414 // element solution container to be deflected
415 auto elemSol = elementSolution(element, curSol, gridGeometry);
416
417 NumEqVector partialDeriv;
418
419 // derivatives in the neighbors with respect to the current elements
420 for (int pvIdx = 0; pvIdx < numEq; ++pvIdx)
421 {
422 // reset derivatives of element dof with respect to itself
423 partialDeriv = 0.0;
424
425 auto evalStorage = [&](Scalar priVar)
426 {
427 // update the volume variables and calculate
428 // the residual with the deflected primary variables
429 elemSol[0][pvIdx] = priVar;
430 curVolVars.update(elemSol, this->problem(), element, scv);
431 return this->evalLocalStorageResidual()[0];
432 };
433
434 // for non-ghosts compute the derivative numerically
435 if (!this->elementIsGhost())
436 {
437 static const NumericEpsilon<Scalar, numEq> eps_{this->problem().paramGroup()};
438 static const int numDiffMethod = getParamFromGroup<int>(this->problem().paramGroup(), "Assembly.NumericDifferenceMethod");
439 NumericDifferentiation::partialDerivative(evalStorage, elemSol[0][pvIdx], partialDeriv, storageResidual,
440 eps_(elemSol[0][pvIdx], pvIdx), numDiffMethod);
441 }
442
443 // for ghost elements we assemble a 1.0 where the primary variable and zero everywhere else
444 // as we always solve for a delta of the solution with respect to the initial solution this
445 // results in a delta of zero for ghosts
446 else partialDeriv[pvIdx] = 1.0;
447
448 // restore the original state of the scv's volume variables
449 curVolVars = origVolVars;
450
451 // restore the current element solution
452 elemSol[0][pvIdx] = origPriVars[pvIdx];
453
454 // add the current partial derivatives to the global jacobian matrix
455 // only diagonal entries for explicit jacobians
456 if constexpr (Problem::enableInternalDirichletConstraints())
457 {
458 // check if own element has internal Dirichlet constraint
459 const auto internalDirichletConstraints = this->problem().hasInternalDirichletConstraint(this->element(), scv);
460 const auto dirichletValues = this->problem().internalDirichlet(this->element(), scv);
461
462 for (int eqIdx = 0; eqIdx < numEq; ++eqIdx)
463 {
464 if (internalDirichletConstraints[eqIdx])
465 {
466 residual[eqIdx] = origVolVars.priVars()[eqIdx] - dirichletValues[eqIdx];
467 A[globalI][globalI][eqIdx][pvIdx] = (eqIdx == pvIdx) ? 1.0 : 0.0;
468 }
469 else
470 A[globalI][globalI][eqIdx][pvIdx] += partialDeriv[eqIdx];
471 }
472 }
473 else
474 {
475 for (int eqIdx = 0; eqIdx < numEq; eqIdx++)
476 A[globalI][globalI][eqIdx][pvIdx] += partialDeriv[eqIdx];
477 }
478 }
479
480 // return the original residual
481 return residual;
482 }
483 };
484
485 /*!
486 * \ingroup Assembly
487 * \ingroup CCDiscretization
488 * \brief Cell-centered scheme local assembler using analytic (hand-coded) differentiation and implicit time discretization
489 */
490 template<class TypeTag, class Assembler>
491 9328248 class CCLocalAssembler<TypeTag, Assembler, DiffMethod::analytic, /*implicit=*/true>
492 : public CCLocalAssemblerBase<TypeTag, Assembler,
493 CCLocalAssembler<TypeTag, Assembler, DiffMethod::analytic, true>, true>
494 {
495 using ThisType = CCLocalAssembler<TypeTag, Assembler, DiffMethod::analytic, true>;
496 using ParentType = CCLocalAssemblerBase<TypeTag, Assembler, ThisType, true>;
497 using NumEqVector = Dumux::NumEqVector<GetPropType<TypeTag, Properties::PrimaryVariables>>;
498 using JacobianMatrix = GetPropType<TypeTag, Properties::JacobianMatrix>;
499 using GridVariables = GetPropType<TypeTag, Properties::GridVariables>;
500 using Problem = typename GridVariables::GridVolumeVariables::Problem;
501
502 enum { numEq = GetPropType<TypeTag, Properties::ModelTraits>::numEq() };
503
504 public:
505
2/8
✓ Branch 2 taken 8793950 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 23328 times.
✗ Branch 7 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
8817278 using ParentType::ParentType;
506
507 /*!
508 * \brief Computes the derivatives with respect to the given element and adds them
509 * to the global matrix.
510 *
511 * \return The element residual at the current solution.
512 */
513 8315950 NumEqVector assembleJacobianAndResidualImpl(JacobianMatrix& A, const GridVariables& gridVariables)
514 {
515 // treat ghost separately, we always want zero update for ghosts
516
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8315950 times.
8315950 if (this->elementIsGhost())
517 {
518 const auto globalI = this->assembler().gridGeometry().elementMapper().index(this->element());
519 for (int pvIdx = 0; pvIdx < numEq; ++pvIdx)
520 A[globalI][globalI][pvIdx][pvIdx] = 1.0;
521
522 // return zero residual
523 return NumEqVector(0.0);
524 }
525
526 // assemble the undeflected residual
527 8315950 auto residual = this->evalLocalResidual()[0];
528
529 // get some aliases for convenience
530 8315950 const auto& problem = this->problem();
531 8315950 const auto& element = this->element();
532 8315950 const auto& fvGeometry = this->fvGeometry();
533 8315950 const auto& curElemVolVars = this->curElemVolVars();
534 8315950 const auto& elemFluxVarsCache = this->elemFluxVarsCache();
535
536 // get reference to the element's current vol vars
537 24947850 const auto globalI = this->assembler().gridGeometry().elementMapper().index(element);
538
1/2
✓ Branch 0 taken 8304980 times.
✗ Branch 1 not taken.
8315950 const auto& scv = fvGeometry.scv(globalI);
539 8315950 const auto& volVars = curElemVolVars[scv];
540
541 // if the problem is instationary, add derivative of storage term
542
2/2
✓ Branch 0 taken 8261916 times.
✓ Branch 1 taken 54034 times.
8315950 if (!this->assembler().isStationaryProblem())
543 24785748 this->localResidual().addStorageDerivatives(A[globalI][globalI], problem, element, fvGeometry, volVars, scv);
544
545 // add source term derivatives
546 24947850 this->localResidual().addSourceDerivatives(A[globalI][globalI], problem, element, fvGeometry, volVars, scv);
547
548 // add flux derivatives for each scvf
549
6/6
✓ Branch 0 taken 16934916 times.
✓ Branch 1 taken 8315950 times.
✓ Branch 2 taken 16934916 times.
✓ Branch 3 taken 8315950 times.
✓ Branch 4 taken 36088 times.
✓ Branch 5 taken 852 times.
33566816 for (const auto& scvf : scvfs(fvGeometry))
550 {
551 // inner faces
552
2/2
✓ Branch 0 taken 16882338 times.
✓ Branch 1 taken 52578 times.
16934916 if (!scvf.boundary())
553 33764676 this->localResidual().addFluxDerivatives(A[globalI], problem, element, fvGeometry, curElemVolVars, elemFluxVarsCache, scvf);
554
555 // boundary faces
556 else
557 {
558
2/2
✓ Branch 0 taken 4806 times.
✓ Branch 1 taken 758 times.
52578 const auto& bcTypes = problem.boundaryTypes(element, scvf);
559
560 // add Dirichlet boundary flux derivatives
561
6/8
✓ Branch 0 taken 6908 times.
✓ Branch 1 taken 45670 times.
✓ Branch 2 taken 6908 times.
✓ Branch 3 taken 45670 times.
✓ Branch 4 taken 6908 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 6908 times.
✗ Branch 7 not taken.
105156 if (bcTypes.hasDirichlet() && !bcTypes.hasNeumann())
562 13816 this->localResidual().addCCDirichletFluxDerivatives(A[globalI], problem, element, fvGeometry, curElemVolVars, elemFluxVarsCache, scvf);
563
564 // add Robin ("solution dependent Neumann") boundary flux derivatives
565
4/8
✓ Branch 0 taken 45670 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 45670 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 45670 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 45670 times.
91340 else if (bcTypes.hasNeumann() && !bcTypes.hasDirichlet())
566 76188 this->localResidual().addRobinFluxDerivatives(A[globalI], problem, element, fvGeometry, curElemVolVars, elemFluxVarsCache, scvf);
567
568 else
569 DUNE_THROW(Dune::NotImplemented, "Mixed boundary conditions. Use pure boundary conditions by converting Dirichlet BCs to Robin BCs");
570 }
571 }
572
573 if constexpr (Problem::enableInternalDirichletConstraints())
574 {
575 // check if own element has internal Dirichlet constraint
576 200 const auto internalDirichletConstraints = this->problem().hasInternalDirichletConstraint(this->element(), scv);
577 200 const auto dirichletValues = this->problem().internalDirichlet(this->element(), scv);
578
579
2/2
✓ Branch 0 taken 100 times.
✓ Branch 1 taken 100 times.
200 for (int pvIdx = 0; pvIdx < numEq; ++pvIdx)
580 {
581
2/2
✓ Branch 0 taken 100 times.
✓ Branch 1 taken 100 times.
200 for (int eqIdx = 0; eqIdx < numEq; ++eqIdx)
582 {
583
4/4
✓ Branch 0 taken 99 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 99 times.
✓ Branch 3 taken 1 times.
200 if (internalDirichletConstraints[eqIdx])
584 {
585 1 residual[eqIdx] = volVars.priVars()[eqIdx] - dirichletValues[eqIdx];
586 2 A[globalI][globalI][eqIdx][pvIdx] = (eqIdx == pvIdx) ? 1.0 : 0.0;
587
588 // inner faces
589
4/4
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 4 times.
6 for (const auto& scvf : scvfs(fvGeometry))
590
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1 times.
4 if (!scvf.boundary())
591
3/6
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 3 times.
15 A[globalI][fvGeometry.scv(scvf.outsideScvIdx()).dofIndex()][eqIdx][pvIdx] = 0.0;
592 }
593 }
594 }
595 }
596
597 // return element residual
598 8315950 return residual;
599 }
600 };
601
602 /*!
603 * \ingroup Assembly
604 * \ingroup CCDiscretization
605 * \brief Cell-centered scheme local assembler using analytic (hand-coded) differentiation and explicit time discretization
606 */
607 template<class TypeTag, class Assembler>
608 3586800 class CCLocalAssembler<TypeTag, Assembler, DiffMethod::analytic, /*implicit=*/false>
609 : public CCLocalAssemblerBase<TypeTag, Assembler,
610 CCLocalAssembler<TypeTag, Assembler, DiffMethod::analytic, false>, false>
611 {
612 using ThisType = CCLocalAssembler<TypeTag, Assembler, DiffMethod::analytic, false>;
613 using ParentType = CCLocalAssemblerBase<TypeTag, Assembler, ThisType, false>;
614 using NumEqVector = Dumux::NumEqVector<GetPropType<TypeTag, Properties::PrimaryVariables>>;
615 using JacobianMatrix = GetPropType<TypeTag, Properties::JacobianMatrix>;
616 using GridVariables = GetPropType<TypeTag, Properties::GridVariables>;
617 using Problem = typename GridVariables::GridVolumeVariables::Problem;
618
619 enum { numEq = GetPropType<TypeTag, Properties::ModelTraits>::numEq() };
620
621 public:
622
2/4
✓ Branch 2 taken 1826800 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 5000 times.
✗ Branch 7 not taken.
1831800 using ParentType::ParentType;
623
624 /*!
625 * \brief Computes the derivatives with respect to the given element and adds them
626 * to the global matrix.
627 *
628 * \return The element residual at the current solution.
629 */
630 1331800 NumEqVector assembleJacobianAndResidualImpl(JacobianMatrix& A, const GridVariables& gridVariables)
631 {
632 // treat ghost separately, we always want zero update for ghosts
633
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1331800 times.
1331800 if (this->elementIsGhost())
634 {
635 const auto globalI = this->assembler().gridGeometry().elementMapper().index(this->element());
636 for (int pvIdx = 0; pvIdx < numEq; ++pvIdx)
637 A[globalI][globalI][pvIdx][pvIdx] = 1.0;
638
639 // return zero residual
640 return NumEqVector(0.0);
641 }
642
643 // assemble the undeflected residual
644 1331800 const auto residual = this->evalLocalResidual()[0];
645
646 // get reference to the element's current vol vars
647 3995400 const auto globalI = this->assembler().gridGeometry().elementMapper().index(this->element());
648
2/4
✓ Branch 0 taken 76800 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 76800 times.
✗ Branch 3 not taken.
2663600 const auto& scv = this->fvGeometry().scv(globalI);
649 2663600 const auto& volVars = this->curElemVolVars()[scv];
650
651 // add hand-code derivative of storage term
652 6659000 this->localResidual().addStorageDerivatives(A[globalI][globalI], this->problem(), this->element(), this->fvGeometry(), volVars, scv);
653
654 if constexpr (Problem::enableInternalDirichletConstraints())
655 {
656 // check if own element has internal Dirichlet constraint
657 const auto internalDirichletConstraints = this->problem().hasInternalDirichletConstraint(this->element(), scv);
658 const auto dirichletValues = this->problem().internalDirichlet(this->element(), scv);
659
660 for (int pvIdx = 0; pvIdx < numEq; ++pvIdx)
661 {
662 for (int eqIdx = 0; eqIdx < numEq; ++eqIdx)
663 {
664 if (internalDirichletConstraints[eqIdx])
665 {
666 residual[eqIdx] = volVars.priVars()[eqIdx] - dirichletValues[eqIdx];
667 A[globalI][globalI][eqIdx][pvIdx] = (eqIdx == pvIdx) ? 1.0 : 0.0;
668 }
669 }
670 }
671 }
672
673 // return the original residual
674 1331800 return residual;
675 }
676 };
677
678 } // end namespace Dumux
679
680 #endif
681