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 |