3.3.0
DUNE for Multi-{Phase, Component, Scale, Physics, ...} flow and transport in porous media
subdomaincclocalassembler.hh
Go to the documentation of this file.
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 * See the file COPYING for full copying permissions. *
5 * *
6 * This program is free software: you can redistribute it and/or modify *
7 * it under the terms of the GNU General Public License as published by *
8 * the Free Software Foundation, either version 3 of the License, or *
9 * (at your option) any later version. *
10 * *
11 * This program is distributed in the hope that it will be useful, *
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14 * GNU General Public License for more details. *
15 * *
16 * You should have received a copy of the GNU General Public License *
17 * along with this program. If not, see <http://www.gnu.org/licenses/>. *
18 *****************************************************************************/
26#ifndef DUMUX_MULTIDOMAIN_CC_LOCAL_ASSEMBLER_HH
27#define DUMUX_MULTIDOMAIN_CC_LOCAL_ASSEMBLER_HH
28
29#include <dune/common/indices.hh>
30#include <dune/common/reservedvector.hh>
31#include <dune/common/hybridutilities.hh>
32#include <dune/grid/common/gridenums.hh> // for GhostEntity
33#include <dune/istl/matrixindexset.hh>
34
44
45namespace Dumux {
46
58template<std::size_t id, class TypeTag, class Assembler, class Implementation, bool implicit>
59class SubDomainCCLocalAssemblerBase : public FVLocalAssemblerBase<TypeTag, Assembler,Implementation, implicit>
60{
62
64 using LocalResidualValues = GetPropType<TypeTag, Properties::NumEqVector>;
66 using SolutionVector = typename Assembler::SolutionVector;
69
71 using GridVolumeVariables = typename GridVariables::GridVolumeVariables;
72 using ElementVolumeVariables = typename GridVolumeVariables::LocalView;
73 using ElementFluxVariablesCache = typename GridVariables::GridFluxVariablesCache::LocalView;
74 using Scalar = typename GridVariables::Scalar;
75
76 using GridGeometry = typename GridVariables::GridGeometry;
77 using FVElementGeometry = typename GridGeometry::LocalView;
78 using SubControlVolume = typename GridGeometry::SubControlVolume;
79 using SubControlVolumeFace = typename GridGeometry::SubControlVolumeFace;
80 using Extrusion = Extrusion_t<GridGeometry>;
81 using GridView = typename GridGeometry::GridView;
82 using Element = typename GridView::template Codim<0>::Entity;
83
84 using CouplingManager = typename Assembler::CouplingManager;
85 using ElementResidualVector = typename ParentType::LocalResidual::ElementResidualVector;
86
87public:
89 static constexpr auto domainId = typename Dune::index_constant<id>();
93 using ParentType::ParentType;
94
96 explicit SubDomainCCLocalAssemblerBase(const Assembler& assembler,
97 const Element& element,
98 const SolutionVector& curSol,
99 CouplingManager& couplingManager)
101 element,
102 curSol,
103 localView(assembler.gridGeometry(domainId)),
104 localView(assembler.gridVariables(domainId).curGridVolVars()),
105 localView(assembler.gridVariables(domainId).prevGridVolVars()),
106 localView(assembler.gridVariables(domainId).gridFluxVarsCache()),
108 (element.partitionType() == Dune::GhostEntity))
109 , couplingManager_(couplingManager)
110 {}
111
116 template<class JacobianMatrixRow, class GridVariablesTuple>
117 void assembleJacobianAndResidual(JacobianMatrixRow& jacRow, SubSolutionVector& res, GridVariablesTuple& gridVariables)
118 {
119 this->asImp_().bindLocalViews();
120
121 // for the diagonal jacobian block
122 const auto globalI = this->fvGeometry().gridGeometry().elementMapper().index(this->element());
123 res[globalI] = this->asImp_().assembleJacobianAndResidualImplInverse(jacRow[domainId], *std::get<domainId>(gridVariables));
124
125 // for the coupling blocks
126 using namespace Dune::Hybrid;
127 forEach(integralRange(Dune::Hybrid::size(jacRow)), [&](auto&& i)
128 {
129 if (i != id)
130 this->assembleJacobianCoupling(i, jacRow, res[globalI], gridVariables);
131 });
132 }
133
138 template<std::size_t otherId, class JacRow, class GridVariables,
139 typename std::enable_if_t<(otherId == id), int> = 0>
140 void assembleJacobianCoupling(Dune::index_constant<otherId> domainJ, JacRow& jacRow,
141 const LocalResidualValues& res, GridVariables& gridVariables)
142 {}
143
147 template<std::size_t otherId, class JacRow, class GridVariables,
148 typename std::enable_if_t<(otherId != id), int> = 0>
149 void assembleJacobianCoupling(Dune::index_constant<otherId> domainJ, JacRow& jacRow,
150 const LocalResidualValues& res, GridVariables& gridVariables)
151 {
152 this->asImp_().assembleJacobianCoupling(domainJ, jacRow[domainJ], res, *std::get<domainId>(gridVariables));
153 }
154
158 void assembleResidual(SubSolutionVector& res)
159 {
160 this->asImp_().bindLocalViews();
161 const auto globalI = this->fvGeometry().gridGeometry().elementMapper().index(this->element());
162 res[globalI] = this->evalLocalResidual()[0]; // forward to the internal implementation
163 }
164
168 ElementResidualVector evalLocalSourceResidual(const Element& element, const ElementVolumeVariables& elemVolVars) const
169 {
170 // initialize the residual vector for all scvs in this element
171 ElementResidualVector residual(this->fvGeometry().numScv());
172
173 // evaluate the volume terms (storage + source terms)
174 // forward to the local residual specialized for the discretization methods
175 for (auto&& scv : scvs(this->fvGeometry()))
176 {
177 const auto& curVolVars = elemVolVars[scv];
178 auto source = this->localResidual().computeSource(problem(), element, this->fvGeometry(), elemVolVars, scv);
179 source *= -Extrusion::volume(scv)*curVolVars.extrusionFactor();
180 residual[scv.indexInElement()] = std::move(source);
181 }
182
183 return residual;
184 }
185
189 ElementResidualVector evalLocalSourceResidual(const Element& neighbor) const
190 { return this->evalLocalSourceResidual(neighbor, implicit ? this->curElemVolVars() : this->prevElemVolVars()); }
191
195 LocalResidualValues evalLocalStorageResidual() const
196 {
197 return this->localResidual().evalStorage(this->element(), this->fvGeometry(), this->prevElemVolVars(), this->curElemVolVars())[0];
198 }
199
203 LocalResidualValues evalFluxResidual(const Element& neighbor,
204 const SubControlVolumeFace& scvf) const
205 {
206 const auto& elemVolVars = implicit ? this->curElemVolVars() : this->prevElemVolVars();
207 return this->localResidual().evalFlux(problem(), neighbor, this->fvGeometry(), elemVolVars, this->elemFluxVarsCache(), scvf);
208 }
209
214 {
215 // get some references for convenience
216 const auto& element = this->element();
217 const auto& curSol = this->curSol()[domainId];
218 auto&& fvGeometry = this->fvGeometry();
219 auto&& curElemVolVars = this->curElemVolVars();
220 auto&& elemFluxVarsCache = this->elemFluxVarsCache();
221
222 // bind the caches
223 couplingManager_.bindCouplingContext(domainId, element, this->assembler());
224 fvGeometry.bind(element);
225
226 if (implicit)
227 {
230 if (!this->assembler().isStationaryProblem())
231 this->prevElemVolVars().bindElement(element, fvGeometry, this->assembler().prevSol()[domainId]);
232 }
233 else
234 {
235 auto& prevElemVolVars = this->prevElemVolVars();
236 const auto& prevSol = this->assembler().prevSol()[domainId];
237
239 prevElemVolVars.bind(element, fvGeometry, prevSol);
241 }
242 }
243
245 const Problem& problem() const
246 { return this->assembler().problem(domainId); }
247
249 CouplingManager& couplingManager()
250 { return couplingManager_; }
251
252private:
253 CouplingManager& couplingManager_;
254};
255
267template<std::size_t id, class TypeTag, class Assembler, DiffMethod DM = DiffMethod::numeric, bool implicit = true>
269
276template<std::size_t id, class TypeTag, class Assembler>
277class SubDomainCCLocalAssembler<id, TypeTag, Assembler, DiffMethod::numeric, /*implicit=*/true>
278: public SubDomainCCLocalAssemblerBase<id, TypeTag, Assembler,
280{
281 using ThisType = SubDomainCCLocalAssembler<id, TypeTag, Assembler, DiffMethod::numeric, /*implicit=*/true>;
282 using ParentType = SubDomainCCLocalAssemblerBase<id, TypeTag, Assembler, ThisType, /*implicit=*/true>;
284
286 using LocalResidualValues = GetPropType<TypeTag, Properties::NumEqVector>;
287
289 using FVElementGeometry = typename GridGeometry::LocalView;
290 using GridView = typename GridGeometry::GridView;
291 using Element = typename GridView::template Codim<0>::Entity;
292
294 enum { dim = GridView::dimension };
295
296 static constexpr bool enableGridFluxVarsCache = getPropValue<TypeTag, Properties::EnableGridFluxVariablesCache>();
297 static constexpr bool enableGridVolVarsCache = getPropValue<TypeTag, Properties::EnableGridVolumeVariablesCache>();
298 static constexpr int maxElementStencilSize = GridGeometry::maxElementStencilSize;
299 static constexpr auto domainI = Dune::index_constant<id>();
300
301public:
303
310 template<class JacobianMatrixDiagBlock, class GridVariables>
311 LocalResidualValues assembleJacobianAndResidualImplInverse(JacobianMatrixDiagBlock& A, GridVariables& gridVariables)
312 {
314 // Calculate derivatives of all dofs in stencil with respect to the dofs in the element. In the //
315 // neighboring elements we do so by computing the derivatives of the fluxes which depend on the //
316 // actual element. In the actual element we evaluate the derivative of the entire residual. //
318
319 // get some aliases for convenience
320 const auto& element = this->element();
321 const auto& fvGeometry = this->fvGeometry();
322 const auto& gridGeometry = fvGeometry.gridGeometry();
323 auto&& curElemVolVars = this->curElemVolVars();
324 auto&& elemFluxVarsCache = this->elemFluxVarsCache();
325
326 // get stencil informations
327 const auto globalI = gridGeometry.elementMapper().index(element);
328 const auto& connectivityMap = gridGeometry.connectivityMap();
329 const auto numNeighbors = connectivityMap[globalI].size();
330
331 // container to store the neighboring elements
332 Dune::ReservedVector<Element, maxElementStencilSize> neighborElements;
333 neighborElements.resize(numNeighbors);
334
335 // assemble the undeflected residual
337 Residuals origResiduals(numNeighbors + 1); origResiduals = 0.0;
338 origResiduals[0] = this->evalLocalResidual()[0];
339
340 // lambda for convenient evaluation of the fluxes across scvfs in the neighbors
341 // if the neighbor is a ghost we don't want to add anything to their residual
342 // so we return 0 and omit computing the flux
343 const auto evalNeighborFlux = [&] (const auto& neighbor, const auto& scvf)
344 {
345 if (neighbor.partitionType() == Dune::GhostEntity)
346 return LocalResidualValues(0.0);
347 else
348 return this->evalFluxResidual(neighbor, scvf);
349 };
350
351 // get the elements in which we need to evaluate the fluxes
352 // and calculate these in the undeflected state
353 unsigned int j = 1;
354 for (const auto& dataJ : connectivityMap[globalI])
355 {
356 neighborElements[j-1] = gridGeometry.element(dataJ.globalJ);
357 for (const auto scvfIdx : dataJ.scvfsJ)
358 origResiduals[j] += evalNeighborFlux(neighborElements[j-1], fvGeometry.scvf(scvfIdx));
359
360 ++j;
361 }
362
363 // reference to the element's scv (needed later) and corresponding vol vars
364 const auto& scv = fvGeometry.scv(globalI);
365 auto& curVolVars = ParentType::getVolVarAccess(gridVariables.curGridVolVars(), curElemVolVars, scv);
366
367 // save a copy of the original privars and vol vars in order
368 // to restore the original solution after deflection
369 const auto& curSol = this->curSol()[domainI];
370 const auto origPriVars = curSol[globalI];
371 const auto origVolVars = curVolVars;
372
373 // element solution container to be deflected
374 auto elemSol = elementSolution<FVElementGeometry>(origPriVars);
375
376 // derivatives in the neighbors with repect to the current elements
377 // in index 0 we save the derivative of the element residual with respect to it's own dofs
378 Residuals partialDerivs(numNeighbors + 1);
379
380 for (int pvIdx = 0; pvIdx < numEq; ++pvIdx)
381 {
382 partialDerivs = 0.0;
383
384 auto evalResiduals = [&](Scalar priVar)
385 {
386 Residuals partialDerivsTmp(numNeighbors + 1);
387 partialDerivsTmp = 0.0;
388 // update the volume variables and the flux var cache
389 elemSol[0][pvIdx] = priVar;
390 this->couplingManager().updateCouplingContext(domainI, *this, domainI, globalI, elemSol[0], pvIdx);
391 curVolVars.update(elemSol, this->problem(), element, scv);
392 elemFluxVarsCache.update(element, fvGeometry, curElemVolVars);
393 if (enableGridFluxVarsCache)
394 gridVariables.gridFluxVarsCache().updateElement(element, fvGeometry, curElemVolVars);
395
396 // calculate the residual with the deflected primary variables
397 partialDerivsTmp[0] = this->evalLocalResidual()[0];
398
399 // calculate the fluxes in the neighbors with the deflected primary variables
400 for (std::size_t k = 0; k < connectivityMap[globalI].size(); ++k)
401 for (auto scvfIdx : connectivityMap[globalI][k].scvfsJ)
402 partialDerivsTmp[k+1] += evalNeighborFlux(neighborElements[k], fvGeometry.scvf(scvfIdx));
403
404 return partialDerivsTmp;
405 };
406
407 // derive the residuals numerically
408 static const int numDiffMethod = getParamFromGroup<int>(this->problem().paramGroup(), "Assembly.NumericDifferenceMethod");
409 static const NumericEpsilon<Scalar, numEq> eps_{this->problem().paramGroup()};
410 NumericDifferentiation::partialDerivative(evalResiduals, elemSol[0][pvIdx], partialDerivs, origResiduals,
411 eps_(elemSol[0][pvIdx], pvIdx), numDiffMethod);
412
413 // Correct derivative for ghost elements, i.e. set a 1 for the derivative w.r.t. the
414 // current primary variable and a 0 elsewhere. As we always solve for a delta of the
415 // solution with repect to the initial one, this results in a delta of 0 for ghosts.
416 if (this->elementIsGhost())
417 {
418 partialDerivs[0] = 0.0;
419 partialDerivs[0][pvIdx] = 1.0;
420 }
421
422 // add the current partial derivatives to the global jacobian matrix
423 if constexpr (Problem::enableInternalDirichletConstraints())
424 {
425 // check if own element has internal Dirichlet constraint
426 const auto internalDirichletConstraintsOwnElement = this->problem().hasInternalDirichletConstraint(this->element(), scv);
427 const auto dirichletValues = this->problem().internalDirichlet(this->element(), scv);
428
429 for (int eqIdx = 0; eqIdx < numEq; ++eqIdx)
430 {
431 if (internalDirichletConstraintsOwnElement[eqIdx])
432 {
433 origResiduals[0][eqIdx] = origVolVars.priVars()[eqIdx] - dirichletValues[eqIdx];
434 A[globalI][globalI][eqIdx][pvIdx] = (eqIdx == pvIdx) ? 1.0 : 0.0;
435 }
436 else
437 A[globalI][globalI][eqIdx][pvIdx] += partialDerivs[0][eqIdx];
438 }
439
440 // off-diagonal entries
441 j = 1;
442 for (const auto& dataJ : connectivityMap[globalI])
443 {
444 const auto& neighborElement = neighborElements[j-1];
445 const auto& neighborScv = fvGeometry.scv(dataJ.globalJ);
446 const auto internalDirichletConstraintsNeighbor = this->problem().hasInternalDirichletConstraint(neighborElement, neighborScv);
447
448 for (int eqIdx = 0; eqIdx < numEq; ++eqIdx)
449 {
450 if (internalDirichletConstraintsNeighbor[eqIdx])
451 A[dataJ.globalJ][globalI][eqIdx][pvIdx] = 0.0;
452 else
453 A[dataJ.globalJ][globalI][eqIdx][pvIdx] += partialDerivs[j][eqIdx];
454 }
455
456 ++j;
457 }
458 }
459 else // no internal Dirichlet constraints specified
460 {
461 for (int eqIdx = 0; eqIdx < numEq; eqIdx++)
462 {
463 // the diagonal entries
464 A[globalI][globalI][eqIdx][pvIdx] += partialDerivs[0][eqIdx];
465
466 // off-diagonal entries
467 j = 1;
468 for (const auto& dataJ : connectivityMap[globalI])
469 A[dataJ.globalJ][globalI][eqIdx][pvIdx] += partialDerivs[j++][eqIdx];
470 }
471 }
472
473 // restore the original state of the scv's volume variables
474 curVolVars = origVolVars;
475
476 // restore the current element solution
477 elemSol[0][pvIdx] = origPriVars[pvIdx];
478
479 // restore the undeflected state of the coupling context
480 this->couplingManager().updateCouplingContext(domainI, *this, domainI, globalI, elemSol[0], pvIdx);
481 }
482
483 // restore original state of the flux vars cache in case of global caching.
484 // This has to be done in order to guarantee that everything is in an undeflected
485 // state before the assembly of another element is called. In the case of local caching
486 // this is obsolete because the elemFluxVarsCache used here goes out of scope after this.
487 // We only have to do this for the last primary variable, for all others the flux var cache
488 // is updated with the correct element volume variables before residual evaluations
489 if (enableGridFluxVarsCache)
490 gridVariables.gridFluxVarsCache().updateElement(element, fvGeometry, curElemVolVars);
491
492 // compute potential additional derivatives causes by the coupling
493 typename ParentType::LocalResidual::ElementResidualVector origElementResidual({origResiduals[0]});
494 this->couplingManager().evalAdditionalDomainDerivatives(domainI, *this, origElementResidual, A, gridVariables);
495
496 // return the original residual
497 return origResiduals[0];
498 }
499
504 template<std::size_t otherId, class JacobianBlock, class GridVariables>
505 void assembleJacobianCoupling(Dune::index_constant<otherId> domainJ, JacobianBlock& A,
506 const LocalResidualValues& res, GridVariables& gridVariables)
507 {
509 // Calculate derivatives of all dofs in the element with respect to all dofs in the coupling stencil. //
511
512 // get some aliases for convenience
513 const auto& element = this->element();
514 const auto& fvGeometry = this->fvGeometry();
515 const auto& gridGeometry = fvGeometry.gridGeometry();
516 auto&& curElemVolVars = this->curElemVolVars();
517 auto&& elemFluxVarsCache = this->elemFluxVarsCache();
518
519 // get stencil informations
520 const auto globalI = gridGeometry.elementMapper().index(element);
521 const auto& stencil = this->couplingManager().couplingStencil(domainI, element, domainJ);
522 const auto& curSolJ = this->curSol()[domainJ];
523
524 // make sure the flux variables cache does not contain any artifacts from prior differentiation
525 elemFluxVarsCache.update(element, fvGeometry, curElemVolVars);
526
527 // convenience lambda for call to update self
528 auto updateCoupledVariables = [&] ()
529 {
530 // Update ourself after the context has been modified. Depending on the
531 // type of caching, other objects might have to be updated. All ifs can be optimized away.
532 if (enableGridFluxVarsCache)
533 {
534 if (enableGridVolVarsCache)
535 {
536 this->couplingManager().updateCoupledVariables(domainI, *this, gridVariables.curGridVolVars(), gridVariables.gridFluxVarsCache());
537 this->couplingManager().updateCoupledVariables(domainI, *this, gridVariables.curGridVolVars(), elemFluxVarsCache);
538 }
539 else
540 {
541 this->couplingManager().updateCoupledVariables(domainI, *this, curElemVolVars, gridVariables.gridFluxVarsCache());
542 this->couplingManager().updateCoupledVariables(domainI, *this, curElemVolVars, elemFluxVarsCache);
543 }
544 }
545 else
546 {
547 if (enableGridVolVarsCache)
548 this->couplingManager().updateCoupledVariables(domainI, *this, gridVariables.curGridVolVars(), elemFluxVarsCache);
549 else
550 this->couplingManager().updateCoupledVariables(domainI, *this, curElemVolVars, elemFluxVarsCache);
551 }
552 };
553
554 for (const auto& globalJ : stencil)
555 {
556 // undeflected privars and privars to be deflected
557 const auto origPriVarsJ = curSolJ[globalJ];
558 auto priVarsJ = origPriVarsJ;
559
560 const auto origResidual = this->couplingManager().evalCouplingResidual(domainI, *this, domainJ, globalJ)[0];
561
562 for (int pvIdx = 0; pvIdx < JacobianBlock::block_type::cols; ++pvIdx)
563 {
564 auto evalCouplingResidual = [&](Scalar priVar)
565 {
566 // update the volume variables and the flux var cache
567 priVarsJ[pvIdx] = priVar;
568 this->couplingManager().updateCouplingContext(domainI, *this, domainJ, globalJ, priVarsJ, pvIdx);
569 updateCoupledVariables();
570 return this->couplingManager().evalCouplingResidual(domainI, *this, domainJ, globalJ)[0];
571 };
572
573 // derive the residuals numerically
574 LocalResidualValues partialDeriv(0.0);
575 const auto& paramGroup = this->assembler().problem(domainJ).paramGroup();
576 static const int numDiffMethod = getParamFromGroup<int>(paramGroup, "Assembly.NumericDifferenceMethod");
577 static const auto epsCoupl = this->couplingManager().numericEpsilon(domainJ, paramGroup);
578 NumericDifferentiation::partialDerivative(evalCouplingResidual, priVarsJ[pvIdx], partialDeriv, origResidual,
579 epsCoupl(priVarsJ[pvIdx], pvIdx), numDiffMethod);
580
581 // add the current partial derivatives to the global jacobian matrix
582 if constexpr (Problem::enableInternalDirichletConstraints())
583 {
584 const auto& scv = fvGeometry.scv(globalI);
585 const auto internalDirichletConstraints = this->problem().hasInternalDirichletConstraint(this->element(), scv);
586 if (internalDirichletConstraints.none())
587 {
588 for (int eqIdx = 0; eqIdx < numEq; eqIdx++)
589 A[globalI][globalJ][eqIdx][pvIdx] += partialDeriv[eqIdx];
590 }
591 else
592 {
593 for (int eqIdx = 0; eqIdx < numEq; ++eqIdx)
594 {
595 if (internalDirichletConstraints[eqIdx])
596 A[globalI][globalJ][eqIdx][pvIdx] = 0.0;
597 else
598 A[globalI][globalJ][eqIdx][pvIdx] += partialDeriv[eqIdx];
599 }
600 }
601 }
602 else
603 {
604 for (int eqIdx = 0; eqIdx < numEq; eqIdx++)
605 A[globalI][globalJ][eqIdx][pvIdx] += partialDeriv[eqIdx];
606 }
607
608 // restore the current element solution
609 priVarsJ[pvIdx] = origPriVarsJ[pvIdx];
610
611 // restore the undeflected state of the coupling context
612 this->couplingManager().updateCouplingContext(domainI, *this, domainJ, globalJ, priVarsJ, pvIdx);
613 }
614
615 // restore original state of the flux vars cache and/or vol vars in case of global caching.
616 // This has to be done in order to guarantee that the undeflected residual computation done
617 // above is correct when jumping to the next coupled dof of domainJ.
618 updateCoupledVariables();
619 }
620 }
621};
622
629template<std::size_t id, class TypeTag, class Assembler>
630class SubDomainCCLocalAssembler<id, TypeTag, Assembler, DiffMethod::numeric, /*implicit=*/false>
631: public SubDomainCCLocalAssemblerBase<id, TypeTag, Assembler,
632 SubDomainCCLocalAssembler<id, TypeTag, Assembler, DiffMethod::numeric, false>, false>
633{
634 using ThisType = SubDomainCCLocalAssembler<id, TypeTag, Assembler, DiffMethod::numeric, /*implicit=*/false>;
635 using ParentType = SubDomainCCLocalAssemblerBase<id, TypeTag, Assembler, ThisType, /*implicit=*/false>;
636
638 using LocalResidualValues = GetPropType<TypeTag, Properties::NumEqVector>;
640
641 static constexpr int numEq = GetPropType<TypeTag, Properties::ModelTraits>::numEq();
642 static constexpr auto domainI = Dune::index_constant<id>();
643
644public:
646
657 template<class JacobianMatrixDiagBlock, class GridVariables>
658 LocalResidualValues assembleJacobianAndResidualImplInverse(JacobianMatrixDiagBlock& A, GridVariables& gridVariables)
659 {
660 if (this->assembler().isStationaryProblem())
661 DUNE_THROW(Dune::InvalidStateException, "Using explicit jacobian assembler with stationary local residual");
662
663 // assemble the undeflected residual
664 const auto residual = this->evalLocalResidual()[0];
665 const auto storageResidual = this->evalLocalStorageResidual();
666
668 // Calculate derivatives of all dofs in stencil with respect to the dofs in the element. In the //
669 // neighboring elements all derivatives are zero. For the assembled element only the storage //
670 // derivatives are non-zero. //
672
673 // get some aliases for convenience
674 const auto& element = this->element();
675 const auto& fvGeometry = this->fvGeometry();
676 const auto& gridGeometry = fvGeometry.gridGeometry();
677 auto&& curElemVolVars = this->curElemVolVars();
678
679 // reference to the element's scv (needed later) and corresponding vol vars
680 const auto globalI = gridGeometry.elementMapper().index(element);
681 const auto& scv = fvGeometry.scv(globalI);
682 auto& curVolVars = ParentType::getVolVarAccess(gridVariables.curGridVolVars(), curElemVolVars, scv);
683
684 // save a copy of the original privars and vol vars in order
685 // to restore the original solution after deflection
686 const auto& curSol = this->curSol()[domainI];
687 const auto origPriVars = curSol[globalI];
688 const auto origVolVars = curVolVars;
689
690 // element solution container to be deflected
691 auto elemSol = elementSolution(element, curSol, gridGeometry);
692
693 // derivatives in the neighbors with repect to the current elements
694 LocalResidualValues partialDeriv;
695 for (int pvIdx = 0; pvIdx < numEq; ++pvIdx)
696 {
697 // reset derivatives of element dof with respect to itself
698 partialDeriv = 0.0;
699
700 auto evalStorage = [&](Scalar priVar)
701 {
702 // update the volume variables and calculate
703 // the residual with the deflected primary variables
704 elemSol[0][pvIdx] = priVar;
705 curVolVars.update(elemSol, this->problem(), element, scv);
706 return this->evalLocalStorageResidual();
707 };
708
709 // for non-ghosts compute the derivative numerically
710 if (!this->elementIsGhost())
711 {
712 static const NumericEpsilon<Scalar, numEq> eps_{this->problem().paramGroup()};
713 static const int numDiffMethod = getParamFromGroup<int>(this->problem().paramGroup(), "Assembly.NumericDifferenceMethod");
714 NumericDifferentiation::partialDerivative(evalStorage, elemSol[0][pvIdx], partialDeriv, storageResidual,
715 eps_(elemSol[0][pvIdx], pvIdx), numDiffMethod);
716 }
717
718 // for ghost elements we assemble a 1.0 where the primary variable and zero everywhere else
719 // as we always solve for a delta of the solution with repect to the initial solution this
720 // results in a delta of zero for ghosts
721 else partialDeriv[pvIdx] = 1.0;
722
723 // add the current partial derivatives to the global jacobian matrix
724 // only diagonal entries for explicit jacobians
725 if constexpr (Problem::enableInternalDirichletConstraints())
726 {
727 // check if own element has internal Dirichlet constraint
728 const auto internalDirichletConstraints = this->problem().hasInternalDirichletConstraint(this->element(), scv);
729 const auto dirichletValues = this->problem().internalDirichlet(this->element(), scv);
730
731 for (int eqIdx = 0; eqIdx < numEq; ++eqIdx)
732 {
733 if (internalDirichletConstraints[eqIdx])
734 {
735 residual[eqIdx] = origVolVars.priVars()[eqIdx] - dirichletValues[eqIdx];
736 A[globalI][globalI][eqIdx][pvIdx] = (eqIdx == pvIdx) ? 1.0 : 0.0;
737 }
738 else
739 A[globalI][globalI][eqIdx][pvIdx] += partialDeriv[eqIdx];
740 }
741 }
742 else
743 {
744 for (int eqIdx = 0; eqIdx < numEq; eqIdx++)
745 A[globalI][globalI][eqIdx][pvIdx] += partialDeriv[eqIdx];
746 }
747
748 // restore the original state of the scv's volume variables
749 curVolVars = origVolVars;
750
751 // restore the current element solution
752 elemSol[0][pvIdx] = origPriVars[pvIdx];
753 }
754
755 // return the original residual
756 return residual;
757 }
758
765 template<std::size_t otherId, class JacobianBlock, class GridVariables>
766 void assembleJacobianCoupling(Dune::index_constant<otherId> domainJ, JacobianBlock& A,
767 const LocalResidualValues& res, GridVariables& gridVariables)
768 {}
769};
770
776template<std::size_t id, class TypeTag, class Assembler>
777class SubDomainCCLocalAssembler<id, TypeTag, Assembler, DiffMethod::analytic, /*implicit=*/true>
778: public SubDomainCCLocalAssemblerBase<id, TypeTag, Assembler,
779 SubDomainCCLocalAssembler<id, TypeTag, Assembler, DiffMethod::analytic, true>, true>
780{
781 using ThisType = SubDomainCCLocalAssembler<id, TypeTag, Assembler, DiffMethod::analytic, /*implicit=*/true>;
782 using ParentType = SubDomainCCLocalAssemblerBase<id, TypeTag, Assembler, ThisType, /*implicit=*/true>;
784 using LocalResidualValues = GetPropType<TypeTag, Properties::NumEqVector>;
786 using Element = typename GridView::template Codim<0>::Entity;
788
790 enum { dim = GridView::dimension };
791
792 static constexpr auto domainI = Dune::index_constant<id>();
793
794public:
796
803 template<class JacobianMatrixDiagBlock, class GridVariables>
804 LocalResidualValues assembleJacobianAndResidualImpl(JacobianMatrixDiagBlock& A, GridVariables& gridVariables)
805 {
806 // treat ghost separately, we always want zero update for ghosts
807 if (this->elementIsGhost())
808 {
809 const auto globalI = this->assembler().gridGeometry(domainI).elementMapper().index(this->element());
810 for (int pvIdx = 0; pvIdx < numEq; ++pvIdx)
811 A[globalI][globalI][pvIdx][pvIdx] = 1.0;
812
813 // return zero residual
814 return LocalResidualValues(0.0);
815 }
816
817 // get some aliases for convenience
818 const auto& problem = this->problem();
819 const auto& element = this->element();
820 const auto& fvGeometry = this->fvGeometry();
821 const auto& curElemVolVars = this->curElemVolVars();
822 const auto& elemFluxVarsCache = this->elemFluxVarsCache();
823
824 // get reference to the element's current vol vars
825 const auto globalI = this->assembler().gridGeometry(domainI).elementMapper().index(element);
826 const auto& scv = fvGeometry.scv(globalI);
827 const auto& volVars = curElemVolVars[scv];
828
829 // if the problem is instationary, add derivative of storage term
830 if (!this->assembler().isStationaryProblem())
831 this->localResidual().addStorageDerivatives(A[globalI][globalI], problem, element, fvGeometry, volVars, scv);
832
833 // add source term derivatives
834 this->localResidual().addSourceDerivatives(A[globalI][globalI], problem, element, fvGeometry, volVars, scv);
835
836 // add flux derivatives for each scvf
837 for (const auto& scvf : scvfs(fvGeometry))
838 {
839 // inner faces
840 if (!scvf.boundary())
841 this->localResidual().addFluxDerivatives(A[globalI], problem, element, fvGeometry, curElemVolVars, elemFluxVarsCache, scvf);
842
843 // boundary faces
844 else
845 {
846 const auto& bcTypes = problem.boundaryTypes(element, scvf);
847
848 // add Dirichlet boundary flux derivatives
849 if (bcTypes.hasDirichlet() && !bcTypes.hasNeumann())
850 this->localResidual().addCCDirichletFluxDerivatives(A[globalI], problem, element, fvGeometry, curElemVolVars, elemFluxVarsCache, scvf);
851
852 // add Robin ("solution dependent Neumann") boundary flux derivatives
853 else if (bcTypes.hasNeumann() && !bcTypes.hasDirichlet())
854 this->localResidual().addRobinFluxDerivatives(A[globalI], problem, element, fvGeometry, curElemVolVars, elemFluxVarsCache, scvf);
855
856 else
857 DUNE_THROW(Dune::NotImplemented, "Mixed boundary conditions. Use pure boundary conditions by converting Dirichlet BCs to Robin BCs");
858 }
859 }
860
861 if constexpr (Problem::enableInternalDirichletConstraints())
862 {
863 // check if own element has internal Dirichlet constraint
864 const auto internalDirichletConstraints = this->problem().hasInternalDirichletConstraint(this->element(), scv);
865 const auto dirichletValues = this->problem().internalDirichlet(this->element(), scv);
866
867 auto residual = this->evalLocalResidual()[0];
868
869 for (int pvIdx = 0; pvIdx < numEq; ++pvIdx)
870 {
871 for (int eqIdx = 0; eqIdx < numEq; ++eqIdx)
872 {
873 if (internalDirichletConstraints[eqIdx])
874 {
875 residual[eqIdx] = volVars.priVars()[eqIdx] - dirichletValues[eqIdx];
876 A[globalI][globalI][eqIdx][pvIdx] = (eqIdx == pvIdx) ? 1.0 : 0.0;
877
878 // inner faces
879 for (const auto& scvf : scvfs(fvGeometry))
880 if (!scvf.boundary())
881 A[globalI][fvGeometry.scv(scvf.outsideScvIdx()).dofIndex()][eqIdx][pvIdx] = 0.0;
882 }
883 }
884 }
885 return residual;
886 }
887 else
888 // return element residual
889 return this->evalLocalResidual()[0];
890 }
891
898 template<std::size_t otherId, class JacobianBlock, class GridVariables>
899 void assembleJacobianCoupling(Dune::index_constant<otherId> domainJ, JacobianBlock& A,
900 const LocalResidualValues& res, GridVariables& gridVariables)
901 {
903 // Calculate derivatives of all dofs in the element with respect to all dofs in the coupling stencil. //
905
906 // get some aliases for convenience
907 const auto& element = this->element();
908 const auto& fvGeometry = this->fvGeometry();
909 const auto& gridGeometry = fvGeometry.gridGeometry();
910 auto&& curElemVolVars = this->curElemVolVars();
911 // auto&& elemFluxVarsCache = this->elemFluxVarsCache();
912
913 // get stencil informations
914 const auto globalI = gridGeometry.elementMapper().index(element);
915 const auto& stencil = this->couplingManager().couplingStencil(domainI, element, domainJ);
916
917 for (const auto globalJ : stencil)
918 {
919 const auto& elementJ = this->assembler().gridGeometry(domainJ).element(globalJ);
920 this->couplingManager().addCouplingDerivatives(A[globalI][globalJ], domainI, element, fvGeometry, curElemVolVars, domainJ, elementJ);
921
922 // add the current partial derivatives to the global jacobian matrix
923 if constexpr (Problem::enableInternalDirichletConstraints())
924 {
925 const auto& scv = fvGeometry.scv(globalI);
926 const auto internalDirichletConstraints = this->problem().hasInternalDirichletConstraint(this->element(), scv);
927 if (internalDirichletConstraints.any())
928 {
929 for (int pvIdx = 0; pvIdx < numEq; ++pvIdx)
930 for (int eqIdx = 0; eqIdx < numEq; ++eqIdx)
931 if (internalDirichletConstraints[eqIdx])
932 A[globalI][globalJ][eqIdx][pvIdx] = 0.0;
933 }
934 }
935 }
936 }
937};
938
939} // end namespace Dumux
940
941#endif
An enum class to define various differentiation methods available in order to compute the derivatives...
An adapter class for local assemblers using numeric differentiation.
A class for numeric differentiation.
A arithmetic block vector type based on DUNE's reserved vector.
The infrastructure to retrieve run-time parameters from Dune::ParameterTrees.
Element solution classes and factory functions.
Helper classes to compute the integration elements.
GridCache::LocalView localView(const GridCache &gridCache)
Free function to get the local view of a grid cache object.
Definition: localview.hh:38
auto elementSolution(const Element &element, const SolutionVector &sol, const GridGeometry &gg) -> std::enable_if_t< GridGeometry::discMethod==DiscretizationMethod::box, BoxElementSolution< typename GridGeometry::LocalView, std::decay_t< decltype(std::declval< SolutionVector >()[0])> > >
Make an element solution for box schemes.
Definition: box/elementsolution.hh:115
DiffMethod
Differentiation methods in order to compute the derivatives of the residual i.e. the entries in the j...
Definition: diffmethod.hh:37
Definition: adapt.hh:29
typename Extrusion< T >::type Extrusion_t
Convenience alias for obtaining the extrusion type.
Definition: extrusion.hh:177
typename Properties::Detail::GetPropImpl< TypeTag, Property >::type::type GetPropType
get the type alias defined in the property (equivalent to old macro GET_PROP_TYPE(....
Definition: propertysystem.hh:149
Definition: common/pdesolver.hh:35
A base class for all local assemblers.
Definition: fvlocalassemblerbase.hh:48
ElementVolumeVariables & curElemVolVars()
The current element volume variables.
Definition: fvlocalassemblerbase.hh:265
typename LocalResidual::ElementResidualVector ElementResidualVector
Definition: fvlocalassemblerbase.hh:68
Implementation & asImp_()
Definition: fvlocalassemblerbase.hh:309
ElementVolumeVariables & prevElemVolVars()
The element volume variables of the provious time step.
Definition: fvlocalassemblerbase.hh:269
ElementResidualVector evalLocalResidual() const
Convenience function to evaluate the complete local residual for the current element....
Definition: fvlocalassemblerbase.hh:120
FVElementGeometry & fvGeometry()
The global finite volume geometry.
Definition: fvlocalassemblerbase.hh:261
const Assembler & assembler() const
The assembler.
Definition: fvlocalassemblerbase.hh:245
ElementFluxVariablesCache & elemFluxVarsCache()
The element flux variables cache.
Definition: fvlocalassemblerbase.hh:273
LocalResidual & localResidual()
The local residual for the current element.
Definition: fvlocalassemblerbase.hh:277
const Element & element() const
The current element.
Definition: fvlocalassemblerbase.hh:249
const SolutionVector & curSol() const
The current solution.
Definition: fvlocalassemblerbase.hh:257
A helper class for local assemblers using numeric differentiation to determine the epsilon.
Definition: numericepsilon.hh:41
static void partialDerivative(const Function &function, Scalar x0, FunctionEvalType &derivative, const FunctionEvalType &fx0, const int numericDifferenceMethod=1)
Computes the derivative of a function with repect to a function parameter.
Definition: numericdifferentiation.hh:61
A arithmetic block vector type based on DUNE's reserved vector.
Definition: reservedblockvector.hh:38
Definition: multidomain/couplingmanager.hh:46
A base class for all multidomain local assemblers.
Definition: subdomaincclocalassembler.hh:60
void bindLocalViews()
Prepares all local views necessary for local assembly.
Definition: subdomaincclocalassembler.hh:213
SubDomainCCLocalAssemblerBase(const Assembler &assembler, const Element &element, const SolutionVector &curSol, CouplingManager &couplingManager)
the constructor
Definition: subdomaincclocalassembler.hh:96
ElementResidualVector evalLocalSourceResidual(const Element &neighbor) const
Evaluates the local source term depending on time discretization scheme.
Definition: subdomaincclocalassembler.hh:189
LocalResidualValues evalFluxResidual(const Element &neighbor, const SubControlVolumeFace &scvf) const
Evaluates the fluxes depending on the chose time discretization scheme.
Definition: subdomaincclocalassembler.hh:203
void assembleJacobianAndResidual(JacobianMatrixRow &jacRow, SubSolutionVector &res, GridVariablesTuple &gridVariables)
Computes the derivatives with respect to the given element and adds them to the global matrix....
Definition: subdomaincclocalassembler.hh:117
void assembleJacobianCoupling(Dune::index_constant< otherId > domainJ, JacRow &jacRow, const LocalResidualValues &res, GridVariables &gridVariables)
Assemble the entries in a coupling block of the jacobian. There is no coupling block between a domain...
Definition: subdomaincclocalassembler.hh:140
const Problem & problem() const
return reference to the underlying problem
Definition: subdomaincclocalassembler.hh:245
CouplingManager & couplingManager()
return reference to the coupling manager
Definition: subdomaincclocalassembler.hh:249
static constexpr auto domainId
export the domain id of this sub-domain
Definition: subdomaincclocalassembler.hh:89
GetPropType< TypeTag, Properties::LocalResidual > LocalResidual
the local residual type of this domain
Definition: subdomaincclocalassembler.hh:91
ElementResidualVector evalLocalSourceResidual(const Element &element, const ElementVolumeVariables &elemVolVars) const
Evaluates the local source term for an element and given element volume variables.
Definition: subdomaincclocalassembler.hh:168
LocalResidualValues evalLocalStorageResidual() const
Evaluates the storage terms within the element.
Definition: subdomaincclocalassembler.hh:195
void assembleResidual(SubSolutionVector &res)
Assemble the residual only.
Definition: subdomaincclocalassembler.hh:158
The cell-centered scheme multidomain local assembler.
Definition: subdomaincclocalassembler.hh:268
Cell-centered scheme multidomain local assembler using numeric differentiation and implicit time disc...
Definition: subdomaincclocalassembler.hh:280
void assembleJacobianCoupling(Dune::index_constant< otherId > domainJ, JacobianBlock &A, const LocalResidualValues &res, GridVariables &gridVariables)
Computes the derivatives with respect to the given element and adds them to the global matrix.
Definition: subdomaincclocalassembler.hh:505
LocalResidualValues assembleJacobianAndResidualImplInverse(JacobianMatrixDiagBlock &A, GridVariables &gridVariables)
Computes the derivatives with respect to the given element and adds them to the global matrix.
Definition: subdomaincclocalassembler.hh:311
Cell-centered scheme multidomain local assembler using numeric differentiation and explicit time disc...
Definition: subdomaincclocalassembler.hh:633
LocalResidualValues assembleJacobianAndResidualImplInverse(JacobianMatrixDiagBlock &A, GridVariables &gridVariables)
Computes the derivatives with respect to the given element and adds them to the global matrix.
Definition: subdomaincclocalassembler.hh:658
void assembleJacobianCoupling(Dune::index_constant< otherId > domainJ, JacobianBlock &A, const LocalResidualValues &res, GridVariables &gridVariables)
Computes the coupling derivatives with respect to the given element and adds them to the global matri...
Definition: subdomaincclocalassembler.hh:766
Cell-centered scheme local assembler using analytic differentiation and implicit time discretization.
Definition: subdomaincclocalassembler.hh:780
void assembleJacobianCoupling(Dune::index_constant< otherId > domainJ, JacobianBlock &A, const LocalResidualValues &res, GridVariables &gridVariables)
Computes the derivatives with respect to the given element and adds them to the global matrix.
Definition: subdomaincclocalassembler.hh:899
LocalResidualValues assembleJacobianAndResidualImpl(JacobianMatrixDiagBlock &A, GridVariables &gridVariables)
Computes the derivatives with respect to the given element and adds them to the global matrix.
Definition: subdomaincclocalassembler.hh:804
Declares all properties used in Dumux.