192 NonVoidOr<CCLocalAssembler<TypeTag, Assembler, DiffMethod::numeric, Implementation>, Implementation>>
199 using FVElementGeometry =
typename GridGeometry::LocalView;
200 using Element =
typename FVElementGeometry::Element;
203 using Problem =
typename GridVariables::GridVolumeVariables::Problem;
209 static constexpr int maxElementStencilSize = GridGeometry::maxElementStencilSize;
222 void assembleJacobian(JacobianMatrix& A, GridVariables& gridVariables,
const NumEqVector& origResidual)
224 if (this->isImplicit())
225 assembleJacobianImplicit_(A, gridVariables, origResidual);
227 assembleJacobianExplicit_(A, gridVariables, origResidual);
245 const auto& element = this->element();
246 const auto& fvGeometry = this->fvGeometry();
247 const auto& gridGeometry = this->fvGeometry().gridGeometry();
248 auto&& curElemVolVars = this->curElemVolVars();
249 auto&& elemFluxVarsCache = this->elemFluxVarsCache();
252 const auto globalI = gridGeometry.elementMapper().index(element);
253 const auto& connectivityMap = gridGeometry.connectivityMap();
254 const auto numNeighbors = connectivityMap[globalI].size();
257 Dune::ReservedVector<Element, maxElementStencilSize> neighborElements;
258 neighborElements.resize(numNeighbors);
262 Residuals origResiduals(numNeighbors + 1); origResiduals = 0.0;
263 origResiduals[0] = origResidual;
268 auto evalNeighborFlux = [&] (
const auto& neighbor,
const auto& scvf)
270 if (neighbor.partitionType() == Dune::GhostEntity)
273 return this->evalFlux(neighbor, scvf);
279 for (
const auto& dataJ : connectivityMap[globalI])
281 neighborElements[j-1] = gridGeometry.element(dataJ.globalJ);
282 for (
const auto scvfIdx : dataJ.scvfsJ)
283 origResiduals[j] += evalNeighborFlux(neighborElements[j-1], fvGeometry.scvf(scvfIdx));
289 const auto& scv = fvGeometry.scv(globalI);
290 auto& curVolVars = ParentType::getVolVarAccess(gridVariables.curGridVolVars(), curElemVolVars, scv);
294 const auto& curSol = this->asImp_().curSol();
295 const auto origPriVars = curSol[globalI];
296 const auto origVolVars = curVolVars;
299 auto elemSol = elementSolution<FVElementGeometry>(origPriVars);
303 Residuals partialDerivs(numNeighbors + 1);
305 for (
int pvIdx = 0; pvIdx < numEq; ++pvIdx)
309 auto evalResiduals = [&](Scalar priVar)
311 Residuals partialDerivsTmp(numNeighbors + 1);
312 partialDerivsTmp = 0.0;
314 elemSol[0][pvIdx] = priVar;
315 this->asImp_().maybeUpdateCouplingContext(scv, elemSol, pvIdx);
316 curVolVars.update(elemSol, this->asImp_().problem(), element, scv);
317 elemFluxVarsCache.update(element, fvGeometry, curElemVolVars);
318 if (enableGridFluxVarsCache)
319 gridVariables.gridFluxVarsCache().updateElement(element, fvGeometry, curElemVolVars);
322 partialDerivsTmp[0] = this->evalLocalResidual()[0];
325 for (std::size_t k = 0; k < numNeighbors; ++k)
326 for (
auto scvfIdx : connectivityMap[globalI][k].scvfsJ)
327 partialDerivsTmp[k+1] += evalNeighborFlux(neighborElements[k], fvGeometry.scvf(scvfIdx));
329 return partialDerivsTmp;
333 static const NumericEpsilon<Scalar, numEq> eps_{this->asImp_().problem().paramGroup()};
334 static const int numDiffMethod = getParamFromGroup<int>(this->asImp_().problem().paramGroup(),
"Assembly.NumericDifferenceMethod");
336 eps_(elemSol[0][pvIdx], pvIdx), numDiffMethod);
341 if (this->elementIsGhost())
343 partialDerivs[0] = 0.0;
344 partialDerivs[0][pvIdx] = 1.0;
348 curVolVars = origVolVars;
351 elemSol[0][pvIdx] = origPriVars[pvIdx];
354 this->asImp_().maybeUpdateCouplingContext(scv, elemSol, pvIdx);
358 if constexpr (Problem::enableInternalDirichletConstraints())
361 const auto internalDirichletConstraintsOwnElement = this->asImp_().problem().hasInternalDirichletConstraint(this->
element(), scv);
362 const auto dirichletValues = this->asImp_().problem().internalDirichlet(this->
element(), scv);
364 for (
int eqIdx = 0; eqIdx < numEq; ++eqIdx)
366 if (internalDirichletConstraintsOwnElement[eqIdx])
368 origResiduals[0][eqIdx] = origVolVars.priVars()[eqIdx] - dirichletValues[eqIdx];
369 A[globalI][globalI][eqIdx][pvIdx] = (eqIdx == pvIdx) ? 1.0 : 0.0;
372 A[globalI][globalI][eqIdx][pvIdx] += partialDerivs[0][eqIdx];
377 for (
const auto& dataJ : connectivityMap[globalI])
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);
383 for (
int eqIdx = 0; eqIdx < numEq; ++eqIdx)
385 if (internalDirichletConstraintsNeighbor[eqIdx])
386 A[dataJ.globalJ][globalI][eqIdx][pvIdx] = 0.0;
388 A[dataJ.globalJ][globalI][eqIdx][pvIdx] += partialDerivs[j][eqIdx];
396 for (
int eqIdx = 0; eqIdx < numEq; eqIdx++)
399 A[globalI][globalI][eqIdx][pvIdx] += partialDerivs[0][eqIdx];
403 for (
const auto& dataJ : connectivityMap[globalI])
404 A[dataJ.globalJ][globalI][eqIdx][pvIdx] += partialDerivs[j++][eqIdx];
415 if (enableGridFluxVarsCache)
416 gridVariables.gridFluxVarsCache().updateElement(element, fvGeometry, curElemVolVars);
419 ElementResidualVector orig{origResidual};
420 this->asImp_().maybeEvalAdditionalDomainDerivatives(orig, A, gridVariables);
427 void assembleJacobianExplicit_(JacobianMatrix& A, GridVariables& gridVariables,
const NumEqVector& origResidual)
429 if (this->assembler().isStationaryProblem())
430 DUNE_THROW(Dune::InvalidStateException,
"Using explicit jacobian assembler with stationary local residual");
433 auto storageResidual = origResidual;
443 const auto& fvGeometry = this->fvGeometry();
444 const auto& gridGeometry = this->fvGeometry().gridGeometry();
445 auto&& curElemVolVars = this->curElemVolVars();
448 const auto globalI = gridGeometry.elementMapper().index(element);
449 const auto& scv = fvGeometry.scv(globalI);
450 auto& curVolVars = ParentType::getVolVarAccess(gridVariables.curGridVolVars(), curElemVolVars, scv);
454 const auto& curSol = this->asImp_().curSol();
455 const auto origPriVars = curSol[globalI];
456 const auto origVolVars = curVolVars;
459 auto elemSol = elementSolution<FVElementGeometry>(origPriVars);
463 for (
int pvIdx = 0; pvIdx < numEq; ++pvIdx)
468 auto evalStorage = [&](Scalar priVar)
472 elemSol[0][pvIdx] = priVar;
473 curVolVars.update(elemSol, this->asImp_().problem(),
element, scv);
474 return this->evalStorage()[0];
478 if (!this->elementIsGhost())
480 static const NumericEpsilon<Scalar, numEq> eps_{this->asImp_().problem().paramGroup()};
481 static const int numDiffMethod = getParamFromGroup<int>(this->asImp_().problem().paramGroup(),
"Assembly.NumericDifferenceMethod");
483 eps_(elemSol[0][pvIdx], pvIdx), numDiffMethod);
489 else partialDeriv[pvIdx] = 1.0;
492 curVolVars = origVolVars;
495 elemSol[0][pvIdx] = origPriVars[pvIdx];
499 if constexpr (Problem::enableInternalDirichletConstraints())
502 const auto internalDirichletConstraints = this->asImp_().problem().hasInternalDirichletConstraint(this->
element(), scv);
503 const auto dirichletValues = this->asImp_().problem().internalDirichlet(this->
element(), scv);
505 for (
int eqIdx = 0; eqIdx < numEq; ++eqIdx)
509 if (internalDirichletConstraints[eqIdx])
511 storageResidual[eqIdx] = origVolVars.priVars()[eqIdx] - dirichletValues[eqIdx];
512 A[globalI][globalI][eqIdx][pvIdx] = (eqIdx == pvIdx) ? 1.0 : 0.0;
515 A[globalI][globalI][eqIdx][pvIdx] += partialDeriv[eqIdx];
520 for (
int eqIdx = 0; eqIdx < numEq; eqIdx++)
521 A[globalI][globalI][eqIdx][pvIdx] += partialDeriv[eqIdx];