197 NonVoidOr<CCLocalAssembler<TypeTag, Assembler, DiffMethod::numeric, Implementation>, Implementation>>
204 using FVElementGeometry =
typename GridGeometry::LocalView;
205 using Element =
typename FVElementGeometry::Element;
208 using Problem =
typename GridVariables::GridVolumeVariables::Problem;
214 static constexpr int maxElementStencilSize = GridGeometry::maxElementStencilSize;
218 using ParentType::ParentType;
227 void assembleJacobian(JacobianMatrix& A, GridVariables& gridVariables,
const NumEqVector& origResidual)
230 assembleJacobianImplicit_(A, gridVariables, origResidual);
232 assembleJacobianExplicit_(A, gridVariables, origResidual);
250 const auto& element = this->element();
251 const auto& fvGeometry = this->fvGeometry();
252 const auto& gridGeometry = this->fvGeometry().gridGeometry();
253 auto&& curElemVolVars = this->curElemVolVars();
254 auto&& elemFluxVarsCache = this->elemFluxVarsCache();
257 const auto globalI = gridGeometry.elementMapper().index(element);
258 const auto& connectivityMap = gridGeometry.connectivityMap();
259 const auto numNeighbors = connectivityMap[globalI].size();
262 Dune::ReservedVector<Element, maxElementStencilSize> neighborElements;
263 neighborElements.resize(numNeighbors);
267 Residuals origResiduals(numNeighbors + 1); origResiduals = 0.0;
268 origResiduals[0] = origResidual;
273 auto evalNeighborFlux = [&] (
const auto& neighbor,
const auto& scvf)
275 if (neighbor.partitionType() == Dune::GhostEntity)
278 return this->evalFlux(neighbor, scvf);
284 for (
const auto& dataJ : connectivityMap[globalI])
286 neighborElements[j-1] = gridGeometry.element(dataJ.globalJ);
287 for (
const auto scvfIdx : dataJ.scvfsJ)
288 origResiduals[j] += evalNeighborFlux(neighborElements[j-1], fvGeometry.scvf(scvfIdx));
294 const auto& scv = fvGeometry.scv(globalI);
295 auto& curVolVars = ParentType::getVolVarAccess(gridVariables.curGridVolVars(), curElemVolVars, scv);
299 const auto& curSol = this->asImp_().curSol();
300 const auto origPriVars = curSol[globalI];
301 const auto origVolVars = curVolVars;
308 Residuals partialDerivs(numNeighbors + 1);
310 for (
int pvIdx = 0; pvIdx < numEq; ++pvIdx)
314 auto evalResiduals = [&](PrimaryVariable priVar)
316 Residuals partialDerivsTmp(numNeighbors + 1);
317 partialDerivsTmp = 0.0;
319 elemSol[0][pvIdx] = priVar;
320 this->asImp_().maybeUpdateCouplingContext(scv, elemSol, pvIdx);
321 curVolVars.update(elemSol, this->asImp_().problem(), element, scv);
322 elemFluxVarsCache.update(element, fvGeometry, curElemVolVars);
323 if (enableGridFluxVarsCache)
324 gridVariables.gridFluxVarsCache().updateElement(element, fvGeometry, curElemVolVars);
327 partialDerivsTmp[0] = this->evalLocalResidual()[0];
330 for (std::size_t k = 0; k < numNeighbors; ++k)
331 for (
auto scvfIdx : connectivityMap[globalI][k].scvfsJ)
332 partialDerivsTmp[k+1] += evalNeighborFlux(neighborElements[k], fvGeometry.scvf(scvfIdx));
334 return partialDerivsTmp;
338 static const NumericEpsilon<PrimaryVariable, numEq> eps_{this->asImp_().problem().paramGroup()};
339 static const int numDiffMethod =
getParamFromGroup<int>(this->asImp_().problem().paramGroup(),
"Assembly.NumericDifferenceMethod");
341 eps_(elemSol[0][pvIdx], pvIdx), numDiffMethod);
346 if (this->elementIsGhost())
348 partialDerivs[0] = 0.0;
349 partialDerivs[0][pvIdx] = 1.0;
353 curVolVars = origVolVars;
356 elemSol[0][pvIdx] = origPriVars[pvIdx];
359 this->asImp_().maybeUpdateCouplingContext(scv, elemSol, pvIdx);
363 if constexpr (Problem::enableInternalDirichletConstraints())
366 const auto internalDirichletConstraintsOwnElement = this->asImp_().problem().hasInternalDirichletConstraint(this->
element(), scv);
367 const auto dirichletValues = this->asImp_().problem().internalDirichlet(this->
element(), scv);
369 for (
int eqIdx = 0; eqIdx < numEq; ++eqIdx)
371 if (internalDirichletConstraintsOwnElement[eqIdx])
373 origResiduals[0][eqIdx] = origVolVars.priVars()[eqIdx] - dirichletValues[eqIdx];
374 A[globalI][globalI][eqIdx][pvIdx] = (eqIdx == pvIdx) ? 1.0 : 0.0;
377 A[globalI][globalI][eqIdx][pvIdx] += partialDerivs[0][eqIdx];
382 for (
const auto& dataJ : connectivityMap[globalI])
384 const auto& neighborElement = neighborElements[j-1];
385 const auto& neighborScv = fvGeometry.scv(dataJ.globalJ);
386 const auto internalDirichletConstraintsNeighbor = this->asImp_().problem().hasInternalDirichletConstraint(neighborElement, neighborScv);
388 for (
int eqIdx = 0; eqIdx < numEq; ++eqIdx)
390 if (internalDirichletConstraintsNeighbor[eqIdx])
391 A[dataJ.globalJ][globalI][eqIdx][pvIdx] = 0.0;
393 A[dataJ.globalJ][globalI][eqIdx][pvIdx] += partialDerivs[j][eqIdx];
401 for (
int eqIdx = 0; eqIdx < numEq; eqIdx++)
404 A[globalI][globalI][eqIdx][pvIdx] += partialDerivs[0][eqIdx];
408 for (
const auto& dataJ : connectivityMap[globalI])
409 A[dataJ.globalJ][globalI][eqIdx][pvIdx] += partialDerivs[j++][eqIdx];
420 if (enableGridFluxVarsCache)
421 gridVariables.gridFluxVarsCache().updateElement(element, fvGeometry, curElemVolVars);
424 ElementResidualVector orig{origResidual};
425 this->asImp_().maybeEvalAdditionalDomainDerivatives(orig, A, gridVariables);
432 void assembleJacobianExplicit_(JacobianMatrix& A, GridVariables& gridVariables,
const NumEqVector& origResidual)
434 if (this->assembler().isStationaryProblem())
435 DUNE_THROW(Dune::InvalidStateException,
"Using explicit jacobian assembler with stationary local residual");
438 auto storageResidual = origResidual;
448 const auto& fvGeometry = this->fvGeometry();
449 const auto& gridGeometry = this->fvGeometry().gridGeometry();
450 auto&& curElemVolVars = this->curElemVolVars();
453 const auto globalI = gridGeometry.elementMapper().index(element);
454 const auto& scv = fvGeometry.scv(globalI);
455 auto& curVolVars = ParentType::getVolVarAccess(gridVariables.curGridVolVars(), curElemVolVars, scv);
459 const auto& curSol = this->asImp_().curSol();
460 const auto origPriVars = curSol[globalI];
461 const auto origVolVars = curVolVars;
468 for (
int pvIdx = 0; pvIdx < numEq; ++pvIdx)
473 auto evalStorage = [&](PrimaryVariable priVar)
477 elemSol[0][pvIdx] = priVar;
478 curVolVars.update(elemSol, this->asImp_().problem(), element, scv);
479 return this->evalStorage()[0];
483 if (!this->elementIsGhost())
485 static const NumericEpsilon<PrimaryVariable, numEq> eps_{this->asImp_().problem().paramGroup()};
486 static const int numDiffMethod =
getParamFromGroup<int>(this->asImp_().problem().paramGroup(),
"Assembly.NumericDifferenceMethod");
488 eps_(elemSol[0][pvIdx], pvIdx), numDiffMethod);
494 else partialDeriv[pvIdx] = 1.0;
497 curVolVars = origVolVars;
500 elemSol[0][pvIdx] = origPriVars[pvIdx];
504 if constexpr (Problem::enableInternalDirichletConstraints())
507 const auto internalDirichletConstraints = this->asImp_().problem().hasInternalDirichletConstraint(this->
element(), scv);
508 const auto dirichletValues = this->asImp_().problem().internalDirichlet(this->
element(), scv);
510 for (
int eqIdx = 0; eqIdx < numEq; ++eqIdx)
514 if (internalDirichletConstraints[eqIdx])
516 storageResidual[eqIdx] = origVolVars.priVars()[eqIdx] - dirichletValues[eqIdx];
517 A[globalI][globalI][eqIdx][pvIdx] = (eqIdx == pvIdx) ? 1.0 : 0.0;
520 A[globalI][globalI][eqIdx][pvIdx] += partialDeriv[eqIdx];
525 for (
int eqIdx = 0; eqIdx < numEq; eqIdx++)
526 A[globalI][globalI][eqIdx][pvIdx] += partialDeriv[eqIdx];