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