DUNE-ACFEM (unstable)

ellipticoperator.hh
1#ifndef __ELLIPTIC_OPERATOR_HH__
2#define __ELLIPTIC_OPERATOR_HH__
3
4#include <optional>
5#include <dune/common/fmatrix.hh>
6
7#include <dune/fem/operator/common/differentiableoperator.hh>
8#include <dune/fem/operator/common/temporarylocalmatrix.hh>
9#include <dune/fem/operator/common/stencil.hh>
10
12#include "../models/modelfacade.hh"
13#include "../common/quadrature.hh"
14#include "../algorithms/operatorselector.hh"
15
16namespace Dune {
17
18 namespace ACFem {
19
78 template<class Model,
79 class DomainFunction,
80 class RangeFunction = DomainFunction,
81 class Constraints = EmptyBlockConstraints<typename RangeFunction::DiscreteFuncionSpaceType>,
82 template<class GridPart> class QuadratureTraits = DefaultQuadratureTraits>
84 : public virtual std::conditional<EffectiveModelTraits<Model>::isLinear,
85 Fem::LinearOperator<DomainFunction, RangeFunction>,
86 Fem::Operator<DomainFunction, RangeFunction> >::type
87 {
88 typedef typename std::conditional<
89 EffectiveModelTraits<Model>::isLinear,
90 Fem::LinearOperator<DomainFunction, RangeFunction>,
91 Fem::Operator<DomainFunction, RangeFunction> >::type
92 BaseType;
93 public:
95 using typename BaseType::DomainFunctionType;
96
100 using typename BaseType::RangeFunctionType;
101
103 using typename BaseType::DomainFieldType;
104
106 using typename BaseType::RangeFieldType;
107
113 using DiscreteFunctionType = RangeFunctionType;
114
115 static_assert(std::is_base_of<Fem::IsDiscreteFunction, RangeFunctionType>::value,
116 "The RangeFunctionType has to be a discrete function");
117
118 static_assert(std::is_base_of<Fem::HasLocalFunction, DomainFunctionType>::value,
119 "The DomainFunctionType has to be provide a local function");
120
121 using ModelTraits = EffectiveModelTraits<Model>;
122 using ModelType = EffectiveModel<Model>;
124 using ConstraintsOperatorType = std::decay_t<Constraints>;
125 protected:
126 typedef typename DomainFunctionType::FunctionSpaceType DomainFunctionSpaceType;
127 typedef typename RangeFunctionType::FunctionSpaceType RangeFunctionSpaceType;
128 enum {
129 dimRange = RangeFunctionSpaceType::dimRange,
130 dimDomain = RangeFunctionSpaceType::dimDomain
131 };
132 typedef typename RangeFunctionSpaceType::DomainType DomainType;
133 typedef typename RangeFunctionSpaceType::RangeType RangeType;
134 typedef typename RangeFunctionSpaceType::JacobianRangeType JacobianRangeType;
135
136 // how's about
137 static_assert(std::is_same<DomainType, typename DomainFunctionSpaceType::DomainType>::value,
138 "Domain and range functions need to live on the same domain");
139
140 // how's about divergence constraints?
141 static_assert(std::is_same<DomainFunctionSpaceType, RangeFunctionSpaceType>::value,
142 "Domain and range function spaces have to be the same");
143
144 typedef typename RangeFunctionType::DiscreteFunctionSpaceType DiscreteFunctionSpaceType;
145 typedef typename RangeFunctionType::LocalFunctionType LocalFunctionType;
146
147 typedef typename Fem::ConstLocalFunction<DomainFunctionType> LocalDomainFunctionType;
148
149 typedef typename DiscreteFunctionSpaceType::GridPartType GridPartType;
150 typedef typename GridPartType::IntersectionIteratorType IntersectionIteratorType;
151 typedef typename IntersectionIteratorType::Intersection IntersectionType;
152
165
167 template<bool conforming>
168 using IntersectionQuadrature = typename QuadratureTraitsType::template IntersectionQuadrature<conforming>;
169
178 typedef typename QuadratureTraitsType::template IntersectionMassQuadrature<true> BndryMassQuadratureType;
182 static constexpr bool hasFlux = ModelTraits::template Exists<PDEModel::flux>::value;
183 static constexpr bool hasSources = ModelTraits::template Exists<PDEModel::source>::value;
184 static constexpr bool hasRobin = ModelTraits::template Exists<PDEModel::robinFlux>::value;
185 static constexpr bool hasSingular = ModelTraits:: template Exists<PDEModel::singularFlux>::value;
186 static constexpr bool isAffineLinear = ModelTraits::isLinear;
187 static constexpr bool hasMassQuadrature = QuadratureTraitsType::hasMassQuadrature;
188 static constexpr bool hasFaceMassQuadrature = QuadratureTraitsType::hasMassQuadrature;
189
191 static constexpr bool useDG = DGSelectorType::useDG;
192 static constexpr DGFlavour dgFlavour = DGSelectorType::dgFlavour;
193 static constexpr int sFormDG = DGSelectorType::sFormDG;
194
195 public:
208 template<class ConstraintsArg,
209 std::enable_if_t<std::is_constructible<Constraints, ConstraintsArg>::value, int> = 0>
210 EllipticOperator(const ModelType& model, ConstraintsArg&& constraints)
211 : model_(model)
212 , constraintsOperator_(constraints)
213 , penalty_(DGSelectorType::dgPenalty())
214 {}
215
225 EllipticOperator(const ModelType &model)
226 : model_(model)
227 , penalty_(DGSelectorType::dgPenalty())
228 {}
229
231 : model_(other.model_)
232 , constraintsOperator_(std::move(other.constraintsOperator_))
233 , penalty_(other.penalty_)
234 {}
235
237 : model_(std::move(other.model_))
238 , constraintsOperator_(std::move(other.constraintsOperator_))
239 , penalty_(std::move(other.penalty_))
240 {}
241
243 virtual void operator() (const DomainFunctionType &u, RangeFunctionType &w) const;
244
246 virtual bool symmetric() const
247 {
249 }
250
251 virtual bool positiveDefinite() const
252 {
254 }
255
256 protected:
257 const ConstraintsOperatorType& constraints() const {
258 return constraintsOperator_;
259 }
260 const ModelType& model() const {
261 return model_;
262 }
263 ModelType& model() {
264 return model_;
265 }
266 const ModelClosure& closure() const {
267 return model_;
268 }
269 ModelClosure& closure() {
270 return model_;
271 }
272 const double penalty() const {
273 return penalty_;
274 }
275
276 private:
277 template<bool conforming>
278 void computeInteriorSkeletonContributions(
279 const GridPartType& gridPart,
280 const IntersectionType& intersection,
281 const RangeFieldType& penalty,
282 const LocalDomainFunctionType& uLocal,
283 const LocalDomainFunctionType& uLocalNb,
284 const ModelClosure& model,
285 const ModelClosure& modelNb,
286 LocalFunctionType& wLocal) const;
287
288 protected:
289 ModelClosure model_;
290 Constraints constraintsOperator_;
291 const double penalty_;
292 };
293
294 template<class JacobianOperator,
295 class Model,
296 class Constraints = EmptyBlockConstraints<typename JacobianOperator::RangeFunctionType::DiscreteFunctionSpaceType>,
297 template<class> class QuadratureTraits = DefaultQuadratureTraits>
298 class DifferentiableEllipticOperator
299 : public EllipticOperator<Model,
300 typename JacobianOperator::DomainFunctionType,
301 typename JacobianOperator::RangeFunctionType,
302 Constraints, QuadratureTraits>,
303 public virtual Fem::DifferentiableOperator<JacobianOperator>,
304 public virtual std::conditional<EffectiveModelTraits<Model>::isLinear,
305 Fem::AssembledOperator<typename JacobianOperator::DomainFunctionType,
306 typename JacobianOperator::RangeFunctionType>,
307 Fem::Operator<typename JacobianOperator::DomainFunctionType,
308 typename JacobianOperator::RangeFunctionType> >::type
309 {
310 typedef
311 EllipticOperator<Model,
312 typename JacobianOperator::DomainFunctionType,
313 typename JacobianOperator::RangeFunctionType,
314 Constraints, QuadratureTraits>
315 BaseType;
316 public:
317 using JacobianOperatorType = JacobianOperator;
318
319 using typename BaseType::ModelType;
320 using typename BaseType::ModelClosure;
321 using typename BaseType::ModelTraits;
322 using typename BaseType::ConstraintsOperatorType;
323
324 using typename BaseType::DomainFunctionType;
325 using typename BaseType::RangeFunctionType;
326
327 using typename BaseType::DomainFieldType;
328 using typename BaseType::RangeFieldType;
329
330 static_assert(std::is_base_of<Fem::IsDiscreteFunction, DomainFunctionType>::value,
331 "For matrix assembly the DomainFunctionType has to be a discrete function");
332
333 // TODO: allow for different discrete function space types.
334
335 protected:
336 using BaseType::dimDomain; // slighlty misleading name ...
337 using BaseType::dimRange; // slighlty misleading name ...
338
339 using typename BaseType::DomainFunctionSpaceType;
340 using typename BaseType::LocalDomainFunctionType;
341
342 using typename BaseType::DiscreteFunctionSpaceType;
343 using typename BaseType::LocalFunctionType;
344
345 using typename BaseType::GridPartType;
346 using typename BaseType::IntersectionType;
347
348 using typename BaseType::RangeType;
349 using typename BaseType::JacobianRangeType;
350
351 using typename BaseType::QuadratureType;
352 using typename BaseType::MassQuadratureType;
353 using typename BaseType::BndryQuadratureType;
355
356 template<bool conforming>
357 using IntersectionQuadrature = typename BaseType::template IntersectionQuadrature<conforming>;
358
359 using BaseType::hasFlux;
360 using BaseType::hasSources;
361 using BaseType::hasRobin;
362 using BaseType::hasSingular;
363 using BaseType::isAffineLinear;
364 using BaseType::hasMassQuadrature;
365 using BaseType::hasFaceMassQuadrature;
366 using BaseType::useDG;
367 using BaseType::dgFlavour;
368 using BaseType::sFormDG;
369
370 // new typedefs
371 using DiscreteDomainFunctionSpaceType = typename DomainFunctionType::DiscreteFunctionSpaceType;
372
373 static_assert(std::is_same<DiscreteDomainFunctionSpaceType, DiscreteFunctionSpaceType>::value,
374 "Unfortunately the discrete function space type of domain and range need to coincide. Sorry for that.");
375
376 using ElementMatrixType = Fem::TemporaryLocalMatrix<DiscreteDomainFunctionSpaceType, DiscreteFunctionSpaceType>;
377
378 public:
380// using BaseType::EllipticOperator;
381 // using EllipticOperator::EllipticOperator;
382 using EllipticOperator<
383 Model,
384 typename JacobianOperator::DomainFunctionType,
385 typename JacobianOperator::RangeFunctionType,
386 Constraints, QuadratureTraits>::EllipticOperator;
387
389 using BaseType::operator();
391 using BaseType::positiveDefinite;
392
394 void jacobian(const DomainFunctionType &u, JacobianOperatorType &jOp) const;
395
396 private:
397 // storage for basis functions, mutable in order to keep jacobian() const
398 mutable std::vector<RangeType> phi_;
399 mutable std::vector<JacobianRangeType> dphi_;
400
401 mutable std::vector<RangeType> phiNb_;
402 mutable std::vector<JacobianRangeType> dphiNb_;
403
404 void reserveAssemblyStorage(size_t maxNumBasisFunctions) const
405 {
406 phi_.resize(maxNumBasisFunctions);
407 dphi_.resize(maxNumBasisFunctions);
408
409 phiNb_.resize(maxNumBasisFunctions);
410 dphiNb_.resize(maxNumBasisFunctions);
411 }
412
413 void releaseAssemblyStorage() const
414 {
415 phi_.resize(0);
416 dphi_.resize(0);
417
418 phiNb_.resize(0);
419 dphiNb_.resize(0);
420 }
421
422 template<bool conforming>
423 void computeInteriorSkeletonContributions(
424 const GridPartType& gridPart,
425 const IntersectionType& intersection,
426 const RangeFieldType& penalty,
427 const LocalDomainFunctionType& uLocal,
428 const LocalDomainFunctionType& uLocalNb,
429 const ModelClosure& model,
430 const ModelClosure& modelNb,
431 ElementMatrixType& elementMatrix,
432 ElementMatrixType& elementMatrixNb) const;
433
434 protected:
435 using BaseType::model_;
436 using BaseType::model;
437 using BaseType::closure;
438 using BaseType::constraints;
439 using BaseType::penalty;
440 };
441
442 // Implementation of application operator.
443 template<class Model,
444 class DomainFunction, class RangeFunction,
445 class Constraints,
446 template<class> class QuadratureTraits>
448 ::operator()(const DomainFunctionType &u, RangeFunctionType &w) const
449 {
450 // clear destination
451 w.clear();
452
453 // get discrete function space
454 const auto& dfSpace = w.space();
455 const auto& gridPart = dfSpace.gridPart();
456
457 // possibly update
458 // dirichlet constraints are done weakly in DG
459 // as we are not (necessarily) in a lagrange space
460 // if that is the case, one could switch to setting them again hard
461 constraints().update();
462
463 // obtain local functions, note that unfortunately
464 // not all HasLocalFunction objects have a localFunction(void)
465 // method, hence not "auto"
466 LocalDomainFunctionType uLocal(u);
467 LocalDomainFunctionType uLocalNb(u);
468 LocalFunctionType wLocal(w);
469
470 // mutable copy of the model-closure
471 auto model = closure();
472
473 // For DG we need a copy of the model which lives on the neighbour
474 auto modelNb = closure();
475
476 // iterate over grid
477 const auto end = dfSpace.end();
478 for (auto it = dfSpace.begin(); it != end; ++it) {
479
480 // get entity (here element)
481 const auto& entity = *it;
482 // get elements geometry
483 const auto& geometry = entity.geometry();
484
485 // get local representation of the discrete functions
486 uLocal.bind(entity);
487
488 wLocal.init(entity);
489
490 // call per-element initializer for the integral contributions
491 model.bind(entity);
492
493 // element integrals, compute all bulk contributions
494 if constexpr((hasSources && !hasMassQuadrature) || hasFlux) {
495 // obtain quadrature order
496 const int quadOrder = uLocal.order() + wLocal.order();
497
498 // 2nd and 0 order in one quad-loop
499 QuadratureType quadrature(entity, quadOrder);
500 const size_t numQuadraturePoints = quadrature.nop();
501 for (size_t pt = 0; pt < numQuadraturePoints; ++pt) {
502
503 const typename QuadratureType::CoordinateType &x = quadrature.point(pt);
504 const double weight = quadrature.weight(pt) * geometry.integrationElement(x);
505
506 RangeType phiKern(0);
507 JacobianRangeType dPhiKern(0);
508
509 // compute the second order contribution
510 JacobianRangeType du;
511 uLocal.jacobian(quadrature[pt], du);
512
513 // compute the source contribution
514 RangeType vu;
515 uLocal.evaluate(quadrature[pt], vu);
516
517 if constexpr (hasSources && !hasMassQuadrature && hasFlux) {
518 RangeType phiKern = weight * model.source(quadrature[pt], vu, du);
519 JacobianRangeType dPhiKern = weight * model.flux(quadrature[pt], vu, du);
520 wLocal.axpy(quadrature[pt], phiKern, dPhiKern);
521 } else if constexpr (hasFlux) {
522 JacobianRangeType dPhiKern = weight * model.flux(quadrature[pt], vu, du);
523 wLocal.axpy(quadrature[pt], dPhiKern);
524 } else {
525 RangeType phiKern = weight * model.source(quadrature[pt], vu, du);
526 wLocal.axpy(quadrature[pt], phiKern);
527 }
528 } // end quad loop
529 } // end ordinary bulk contributions
530
531 // Special code-path to allow e.g. for mass-lumping
532 if constexpr (hasSources && hasMassQuadrature) {
533 // obtain quadrature order
534 const int quadOrder = uLocal.order() + wLocal.order();
535
536 MassQuadratureType quadrature(entity, quadOrder);
537
538 const size_t numQuadraturePoints = quadrature.nop();
539 for (size_t pt = 0; pt < numQuadraturePoints; ++pt) {
540
541 const typename MassQuadratureType::CoordinateType &x = quadrature.point(pt);
542 const double weight = quadrature.weight(pt) * geometry.integrationElement(x);
543
544 // compute the second order contribution
545 JacobianRangeType du;
546 uLocal.jacobian(quadrature[pt], du);
547
548 // compute the source contribution
549 RangeType vu;
550 uLocal.evaluate(quadrature[pt], vu);
551
552 RangeType phiKern = weight * model.source(quadrature[pt], vu, du);
553
554 // add to local function
555 wLocal.axpy(quadrature[pt], phiKern);
556 }
557 } // end of mass-lumping
558
559 // end bulk block
560
561 if constexpr (useDG || hasRobin || hasSingular) { // add skeleton integrals as necessary
562
563 const auto area = useDG ? entity.geometry().volume() : 0.0;
564
565 const auto end = gridPart.iend(entity);
566 for (auto it = gridPart.ibegin(entity); it != end; ++it ) { // looping over intersections
567
569 const auto& intersection = *it;
570 if (useDG && intersection.neighbor()) {
571 const auto neighbor = intersection.outside() ;
572 const auto& intersectionGeometry = intersection.geometry();
573
574 // compute penalty factor
575 const auto intersectionArea = intersectionGeometry.volume();
576 //beta is still missing the diffusion coefficient!!
577 const auto beta = penalty() * intersectionArea / std::min(area, neighbor.geometry().volume());
578
579 modelNb.bind(neighbor); // model in outside element
580 uLocalNb.bind(neighbor); // local u on outisde element
581
582 if (intersection.conforming()) {
583 computeInteriorSkeletonContributions<true>(
584 gridPart, intersection, beta, uLocal, uLocalNb, model, modelNb, wLocal);
585 } else {
586 computeInteriorSkeletonContributions<false>(
587 gridPart, intersection, beta, uLocal, uLocalNb, model, modelNb, wLocal);
588 }
589
590 // unbind neighbour things
591 uLocalNb.unbind();
592 modelNb.unbind();
593
594 } else if (intersection.boundary() && (hasRobin || hasSingular)) {
595
596 const auto bdCond = model.classifyBoundary(intersection);
597
598 if (bdCond.first && !bdCond.second.all()) {
599
600 int quadOrder = 3*dfSpace.order();
601 BndryMassQuadratureType intersectionQuad(gridPart, intersection, quadOrder);
602 const auto &bndryQuad = intersectionQuad.inside();
603 const int numQuadraturePoints = bndryQuad.nop();
604
605 for (int pt = 0; pt < numQuadraturePoints; ++pt) {
606 // quadrature weight
607 const double weight = bndryQuad.weight(pt);
608
609 // Get the un-normalized outer normal
610 DomainType integrationNormal
611 = intersection.integrationOuterNormal(bndryQuad.localPoint(pt));
612
613 const double integrationElement = integrationNormal.two_norm();
614
615 integrationNormal /= integrationElement;
616
617 RangeType vu;
618 uLocal.evaluate(bndryQuad[pt], vu);
619 JacobianRangeType du;
620 uLocal.jacobian(bndryQuad[pt], du);
621
622 if constexpr (hasRobin && hasSingular) {
623 RangeType phiKern = weight * integrationElement * model.robinFlux(bndryQuad[pt], integrationNormal, vu, du);
624 JacobianRangeType dphiKern = weight * integrationElement * model.singularFlux(bndryQuad[pt], integrationNormal, vu, du);
625 wLocal.axpy(bndryQuad[pt], phiKern, dphiKern);
626 } else if constexpr (hasRobin) {
627 RangeType phiKern = weight *integrationElement * model.robinFlux(bndryQuad[pt], integrationNormal, vu, du);
628 wLocal.axpy(bndryQuad[pt], phiKern);
629 } else if constexpr (hasSingular) {
630 JacobianRangeType dphiKern = weight * integrationElement * model.singularFlux(bndryQuad[pt], integrationNormal, vu, du);
631 wLocal.axpy(bndryQuad[pt], dphiKern);
632 }
633 } // quadrature loop
634 } // Robin branch
635 } // domain boundary
636 } // intersection loop
637 } // end of face integrals branch
638
639 uLocal.unbind();
640 model.unbind();
641
642 } // end mesh loop
643
644 // communicate data (in parallel runs)
645 w.communicate();
646
647 // Apply constraints
648 constraints()(u, w);
649 }
650
651 // helper method in order to distinguish between conforming and
652 // non-conforming intersections.
653 template<class Model,
654 class DomainFunction, class RangeFunction,
655 class Constraints,
656 template<class> class QuadratureTraits>
657 template<bool conforming>
660 const GridPartType& gridPart,
661 const IntersectionType& intersection,
662 const RangeFieldType& penalty,
663 const LocalDomainFunctionType& uLocal,
664 const LocalDomainFunctionType& uLocalNb,
665 const ModelClosure& model,
666 const ModelClosure& modelNb,
667 LocalFunctionType& wLocal) const
668 {
669 const int quadOrder = std::max(uLocal.order(),uLocalNb.order()) + wLocal.order();
670 IntersectionQuadrature<conforming> intersectionQuad(gridPart, intersection, quadOrder);
671 const auto& quadInside = intersectionQuad.inside();
672 const auto& quadOutside = intersectionQuad.outside();
673
674 const size_t numQuadraturePoints = quadInside.nop();
675
676 for (size_t pt = 0; pt < numQuadraturePoints; ++pt) {
678 // get coordinate of quadrature point on the reference element of the intersection
679 const auto& x = quadInside.localPoint(pt);
680 const auto integrationNormal = intersection.integrationOuterNormal(x);
681 const auto integrationElement = integrationNormal.two_norm();
682 const auto weight = quadInside.weight( pt );
683
684 RangeType value;
685 JacobianRangeType dvalue;
686
687 RangeType vuIn, vuOut, jump;
688 JacobianRangeType duIn, duOut;
689 uLocal.evaluate(quadInside[pt], vuIn);
690 uLocal.jacobian(quadInside[pt], duIn);
691 auto aduIn = model.flux(quadInside[pt], vuIn, duIn);
692
693 uLocalNb.evaluate(quadOutside[ pt ], vuOut);
694 uLocalNb.jacobian(quadOutside[ pt ], duOut);
695 auto aduOut = modelNb.flux(quadOutside[pt], vuOut, duOut);
696
698
700 jump = vuIn - vuOut;
701 // penalty term : beta [u] [phi] = beta (u+ - u-)(phi+ - phi-)=beta (u+ - u-)phi+
702 //value = jump;
703 value = penalty * integrationElement * jump;
704 // {A grad u}.[phi] = {A grad u}.phi+ n_+ = 0.5*(grad u+ + grad u-).n_+ phi+
705 //aduIn += aduOut;
706 //aduIn *= -0.5;
707 value += contractInner(-(aduIn + aduOut)/2_f, integrationNormal);
708 // [ u ] * { A grad phi_en } = -normal(u+ - u-) * 0.5 grad phi_en
709 // here we need a dyadic product of u x n
710#if 0
711 dvalue = -0.5 * sFormDG * outer(jump, integrationNormal);
712#else
713 for (int r = 0; r < dimRange; ++r) {
714 for (int d = 0; d < dimDomain; ++d) {
715 dvalue[r][d] = -0.5 * sFormDG * integrationNormal[d] * jump[r];
716 }
717 }
718#endif
719
720 auto adValue = model.flux(quadInside[pt], jump, dvalue);
721
722 wLocal.axpy(quadInside[pt], (RangeType)(weight * value), (JacobianRangeType)(weight * adValue));
724 } // end quadrature loop
725 }
726
727 // implementation of Jacobian
728 template<class JacobianOperator,
729 class Model, class Constraints,
730 template<class> class QuadratureTraits>
731 void DifferentiableEllipticOperator<JacobianOperator, Model, Constraints, QuadratureTraits>
732 ::jacobian (const DomainFunctionType &u, JacobianOperator &jOp) const
733 {
734 // If we use DG we need Diagonal and Neighbor stencil for entity-entity-pairing and entity-neighbor-pairing
735 //Otherwise the diagonal Stencil suffices
736 typedef typename std::conditional<!(useDG),
737 Dune::Fem::DiagonalStencil<DiscreteFunctionSpaceType,
738 DiscreteFunctionSpaceType>,
739 Dune::Fem::DiagonalAndNeighborStencil<DiscreteFunctionSpaceType,
740 DiscreteFunctionSpaceType>
741 >::type StencilType;
742 typedef Dune::Fem::TemporaryLocalMatrix<DiscreteFunctionSpaceType, DiscreteFunctionSpaceType> ElementMatrixType;
743
744 const DiscreteFunctionSpaceType & dfSpace = u.space();
745 const GridPartType& gridPart = dfSpace.gridPart();
746
747 // Reserve Stencil storage
748 jOp.reserve(StencilType(dfSpace, dfSpace));
749 jOp.clear();
750
751 // BIG FAT NOTE: we need maxNumDofs * blockSize many items
752 const size_t maxNumBasisFunctions =
753 DiscreteFunctionSpaceType::localBlockSize * dfSpace.blockMapper().maxNumDofs();
754 // allocate space for phi_, dphi_, phiNb_, dphiNb_ ...
755 reserveAssemblyStorage(maxNumBasisFunctions);
756
757 ElementMatrixType elementMatrix(dfSpace, dfSpace);
758 ElementMatrixType elementMatrixNb(dfSpace, dfSpace); // for DG
759
760 // storage for point of linearization, will never be initialized
761 // for affine-linear models
762 LocalDomainFunctionType uLocal(u);
763 RangeType u0(0);
764 JacobianRangeType Du0(0);
765
766 // For DG:
767 LocalDomainFunctionType uLocalNb(u);
768 RangeType u0Nb(0);
769 JacobianRangeType Du0Nb(0);
770
771 // mutable copy of the model-closure
772 auto model = closure();
773
774 // For DG we need a copy of the model which lives on the neighbour
775 auto modelNb = closure();
776
777 // For DG the dirichlet constraints are applied differently
778 //via the boundary terms.
779 constraints().update();
780 //bool totallyConstrained = false;
781
782 const auto end = dfSpace.end();
783 for (auto it = dfSpace.begin(); it != end; ++it) {
784 const auto &entity = *it;
785
786 // totallyConstrained = constraints-get-information-on-entity(entity) or so
787
788 //if (totallyConstrained) {
789 // if all DOFs are fixed then there is nothing to compute ...
790 // continue;
791 //}
792
793 const auto& geometry = entity.geometry();
794
795 model.bind(entity); // call per-element initializer for the operator
796
797 if constexpr (!isAffineLinear) { // get values for linearization
798 uLocal.bind(entity);
799 }
800
801 // use a temporary matrix
802 elementMatrix.init(entity, entity);
803 elementMatrix.clear();
804
805 const auto& baseSet = elementMatrix.domainBasisFunctionSet();
806 const auto numBaseFunctions = baseSet.size();
807
808 if constexpr ((hasSources && !hasMassQuadrature) || hasFlux) {
809 // use one qudrature for 0- and 2nd-order term
810
811 QuadratureType quadrature(entity, 2*dfSpace.order());
812 const size_t numQuadraturePoints = quadrature.nop();
813
814 for (size_t pt = 0; pt < numQuadraturePoints; ++pt) {
815
816 const auto& x = quadrature.point(pt);
817 const auto weight = quadrature.weight(pt) * geometry.integrationElement(x);
818
819 // evaluate all basis functions at given quadrature point
820 baseSet.evaluateAll(quadrature[pt], phi_);
821
822 // evaluate jacobians of all basis functions at given quadrature point
823 baseSet.jacobianAll(quadrature[pt], dphi_);
824
825 if constexpr (!isAffineLinear) { // get values for linearization
826 uLocal.evaluate(quadrature[pt], u0);
827 uLocal.jacobian(quadrature[pt], Du0);
828 }
829
830 for (size_t localCol = 0; localCol < numBaseFunctions; ++localCol) {
831 if constexpr (hasSources && !hasMassQuadrature && hasFlux) {
832 auto aphi = model.linearizedSource(u0, Du0, quadrature[pt], phi_[localCol], dphi_[localCol]);
833 auto adphi = model.linearizedFlux(u0, Du0, quadrature[pt], phi_[localCol], dphi_[localCol]);
834 elementMatrix.column(localCol).axpy(phi_, dphi_, aphi, adphi, weight);
835 } else if constexpr (hasFlux) {
836 auto adphi = model.linearizedFlux(u0, Du0, quadrature[pt], phi_[localCol], dphi_[localCol]);
837 elementMatrix.column(localCol).axpy(dphi_, adphi, weight);
838 } else {
839 auto aphi = model.linearizedSource(u0, Du0, quadrature[pt], phi_[localCol], dphi_[localCol]);
840 elementMatrix.column(localCol).axpy(phi_, aphi, weight);
841 }
842 }
843 }
844 }
845
846 // Special case for e.g. mass-lumping using a "lumping"-quadrature
847 if constexpr (hasSources && hasMassQuadrature) {
848
849 MassQuadratureType quadrature(entity, 2*dfSpace.order());
850 const size_t numQuadraturePoints = quadrature.nop();
851
852 for (size_t pt = 0; pt < numQuadraturePoints; ++pt) {
853
854 const typename MassQuadratureType::CoordinateType &x = quadrature.point(pt);
855 const double weight = quadrature.weight(pt) * geometry.integrationElement(x);
856
857 //const typename ElementGeometryType::Jacobian &gjit = geometry.jacobianInverseTransposed(x);
858
859 // evaluate all basis functions at given quadrature point
860 baseSet.evaluateAll(quadrature[pt], phi_);
861
862 // evaluate jacobians of all basis functions at given quadrature point
863 baseSet.jacobianAll(quadrature[pt], dphi_);
864
865 if constexpr (!isAffineLinear) { // get value for linearization
866 uLocal.evaluate(quadrature[pt], u0);
867 uLocal.jacobian(quadrature[pt], Du0);
868 }
869
870 for (size_t localCol = 0; localCol < numBaseFunctions; ++localCol) {
871
872 auto aphi = model.linearizedSource(u0, Du0, quadrature[pt], phi_[localCol], dphi_[localCol]);
873
874 // get column object and call axpy method
875 elementMatrix.column(localCol).axpy(phi_, aphi, weight);
876 }
877 }
878 } // mass lumping part
879
880 // end bulk integration
881
882 if constexpr (useDG || hasRobin) { // skeleton contributions
883
884 const auto area = useDG ? geometry.volume() : 0.0; // not needed for Robin
885
886 const auto end = gridPart.iend(entity);
887 for (auto it = gridPart.ibegin(entity); it != end; ++it) {
888 const auto& intersection = *it;
889
890 if (useDG && intersection.neighbor()) {
891 const auto neighbor = intersection.outside();
892 const auto& intersectionGeometry = intersection.geometry();
893
894 // compute penalty factor
895 const auto intersectionArea = intersectionGeometry.volume();
896 //beta is still missing the diffusion coefficient!!
897 const auto beta = penalty() * intersectionArea / std::min(area, neighbor.geometry().volume());
898
899 // bind neighbour per-entity objects
900 modelNb.bind(neighbor);
901 if constexpr (!isAffineLinear) { // get values for linearization
902 uLocalNb.bind(neighbor);
903 }
904
905 // use a temporary matrix for entity-neighbour contributions
906 elementMatrixNb.init(neighbor, entity);
907 elementMatrixNb.clear();
908
909 if (intersection.conforming()) {
910 computeInteriorSkeletonContributions<true>(
911 gridPart, intersection, beta, uLocal, uLocalNb, model, modelNb,
912 elementMatrix, elementMatrixNb);
913 } else {
914 computeInteriorSkeletonContributions<false>(
915 gridPart, intersection, beta, uLocal, uLocalNb, model, modelNb,
916 elementMatrix, elementMatrixNb);
917 }
918
919 // finished one DG-intersection, add element matrix to global operator.
920 jOp.addLocalMatrix(neighbor, entity, elementMatrixNb);
921
922 if constexpr (!isAffineLinear) {
923 uLocalNb.unbind();
924 }
925 // unbind neighbour model
926 modelNb.unbind();
927
928 } else if (intersection.boundary() && (hasRobin || hasSingular)) {
929
930 auto bdCond = model.classifyBoundary(intersection);
931
932 if (bdCond.first && !bdCond.second.all()) {
933
934 // TODO: make more realistic assumptions on the
935 // quad-order. HOWTO?
936 const auto quadOrder = 3*dfSpace.order();
937 BndryMassQuadratureType intersectionQuad(gridPart, intersection, quadOrder);
938 const auto& bndryQuad = intersectionQuad.inside();
939 const auto numQuadraturePoints = bndryQuad.nop();
940
941 for (size_t pt = 0; pt < numQuadraturePoints; ++pt) {
942 // One quad-loop for all contributions
943
944 // quadrature weight
945 const auto weight = bndryQuad.weight(pt);
946 (void)weight;
947
948 // Get the un-normalized outer normal
949 auto normal = intersection.integrationOuterNormal(bndryQuad.localPoint(pt));
950 const auto integrationElement = normal.two_norm();
951 normal /= integrationElement;
952
953 // Get values of all basis functions at the
954 // quad-points. Gradients are not needed.
955 baseSet.evaluateAll(bndryQuad[pt], phi_);
956 baseSet.jacobianAll(bndryQuad[pt], dphi_);
957
958 if constexpr (!isAffineLinear) {
959 // get value for linearization
960 uLocal.evaluate(bndryQuad[pt], u0);
961 uLocal.jacobian(bndryQuad[pt], Du0);
962 }
963
964 for (size_t localCol = 0; localCol < numBaseFunctions; ++localCol) {
965 if constexpr (hasRobin && hasSingular) {
966 auto alphaphi = integrationElement * model.linearizedRobinFlux(u0, Du0, bndryQuad[pt], normal, phi_[localCol], dphi_[localCol]);
967 auto alphadphi = integrationElement * model.linearizedSingularFlux(u0, Du0, bndryQuad[pt], normal, phi_[localCol], dphi_[localCol]);
968 elementMatrix.column(localCol).axpy(phi_, dphi_, alphaphi, alphadphi, weight);
969 } else if constexpr (hasRobin) {
970 auto alphaphi = integrationElement * model.linearizedRobinFlux(u0, Du0, bndryQuad[pt], normal, phi_[localCol], dphi_[localCol]);
971 elementMatrix.column(localCol).axpy(phi_, alphaphi, weight);
972 } else if constexpr (hasSingular) {
973 auto alphadphi = integrationElement * model.linearizedSingularFlux(u0, Du0, bndryQuad[pt], normal, phi_[localCol], dphi_[localCol]);
974 elementMatrix.column(localCol).axpy(dphi_, alphadphi, weight);
975 }
976 } // basis function loop
977 } // quad loop
978 } // Robin branch
979 } // boundary contributions
980 } // intersection loop
981 } // end of skeleton part
982
983 jOp.addLocalMatrix(entity, entity, elementMatrix);
984
985 if constexpr (!isAffineLinear) {
986 uLocal.unbind();
987 }
988 model.unbind(); // release the model
989
990 } // end grid traversal
991
992 releaseAssemblyStorage();
993
994 constraints().jacobian(u, jOp);
995
996 jOp.finalize();
997
998 //jOp.print(std::cerr);
999 }
1000
1001 // helper method in order to distinguish between conforming and
1002 // non-conforming intersections.
1003 template<class JacobianOperator,
1004 class Model, class Constraints,
1005 template<class> class QuadratureTraits>
1006 template<bool conforming>
1007 void DifferentiableEllipticOperator<JacobianOperator, Model, Constraints, QuadratureTraits>
1008 ::computeInteriorSkeletonContributions(
1009 const GridPartType& gridPart,
1010 const IntersectionType& intersection,
1011 const RangeFieldType& penalty,
1012 const LocalDomainFunctionType& uLocal,
1013 const LocalDomainFunctionType& uLocalNb,
1014 const ModelClosure& model,
1015 const ModelClosure& modelNb,
1016 ElementMatrixType& elementMatrix,
1017 ElementMatrixType& elementMatrixNb) const
1018 {
1019 // FIXME: is this reasonable?
1020 const auto quadOrder = (!isAffineLinear ? std::max(uLocal.order(), uLocalNb.order()) : 0) + elementMatrix.rangeSpace().order() + 1;
1021 IntersectionQuadrature<conforming> intersectionQuad(gridPart, intersection, quadOrder);
1022 const auto& quadInside = intersectionQuad.inside();
1023 const auto& quadOutside = intersectionQuad.outside();
1024
1025 // get neighbor's base function set
1026 const auto& baseSetNb = elementMatrixNb.domainBasisFunctionSet();
1027
1028 // refetch to avoid yet another argument to the helper function
1029 const auto& baseSet = elementMatrix.domainBasisFunctionSet();
1030 const auto numBaseFunctions = baseSet.size();
1031
1032 // storage for point of linearization
1033 RangeType u0, u0Nb;
1034 JacobianRangeType Du0, Du0Nb;
1035
1036 const auto numFaceQuadPoints = quadInside.nop();
1037 for (size_t pt = 0; pt < numFaceQuadPoints; ++pt) {
1038 const auto& x = quadInside.localPoint(pt);
1039 auto normal = intersection.integrationOuterNormal(x);
1040 const auto faceVol = normal.two_norm();
1041 normal /= faceVol; // make it into a unit normal
1042
1043 const auto quadWeight = quadInside.weight(pt);
1044 const auto weight = quadWeight * faceVol;
1045
1046 if constexpr (!isAffineLinear) { // get values for linearization
1047 uLocal.evaluate(quadInside[pt], u0);
1048 uLocal.jacobian(quadInside[pt], Du0);
1049 uLocalNb.evaluate(quadInside[pt], u0Nb);
1050 uLocalNb.jacobian(quadInside[pt], Du0Nb);
1051 }
1052
1054 // evaluate basis function of face inside E^- (entity)
1056
1057 baseSet.evaluateAll(quadInside[pt], phi_);
1058 baseSet.jacobianAll(quadInside[pt], dphi_);
1059
1061 // evaluate basis function of face inside E^+ (neighbor)
1063
1064 baseSetNb.evaluateAll(quadOutside[pt], phiNb_);
1065 baseSetNb.jacobianAll(quadOutside[pt], dphiNb_);
1066
1067 for (size_t i = 0; i < numBaseFunctions; ++i) {
1068 auto adphiEn = dphi_[i];
1069 auto adphiNb = dphiNb_[i];
1070 dphi_[i] = model.linearizedFlux(u0, Du0, quadInside[ pt ], phi_[i], adphiEn);
1071 dphiNb_[i] = modelNb.linearizedFlux(u0Nb, Du0Nb, quadOutside[ pt ], phiNb_[i], adphiNb);
1072 }
1073
1074 // At this point dphi and dphiNb contain the value of
1075 // the "diffusive" flux applied to the test functions.
1076
1077 // [Assemble skeleton terms: compute factors for axpy method]
1078 for (size_t localCol = 0; localCol < numBaseFunctions; ++localCol) {
1079 RangeType valueEn(0), valueNb(0); // NB: usmv() only adds!
1080 JacobianRangeType dvalueEn(0), dvalueNb(0); // NB: usmv() only adds!
1081
1082 // -{ A grad u } * [ phi_en ]
1083 dphi_[localCol].usmv(-0.5, normal, valueEn);
1084
1085 // -{ A grad u } * [ phi_en ]
1086 dphiNb_[localCol].usmv(-0.5, normal, valueNb);
1087
1088 // [ u ] * [ phi_en ] = u^- * phi_en^-
1089 valueEn.axpy(penalty, phi_[localCol]);
1090
1091 valueNb.axpy(-penalty, phiNb_[localCol] );
1092 // here we need a dyadic product of u x n
1093#if 0
1094 dvalueEn = -0.5 * sFormDG * outer(phi_[localCol], normal);
1095 dvalueNb = 0.5 * sFormDG * outer(phiNb_[localCol] , normal);
1096#else
1097 for (int r = 0; r< dimRange; ++r) {
1098 for (int d = 0; d< dimDomain; ++d) {
1099 // [ u ] * { grad phi_en }
1100 dvalueEn[r][d] = - 0.5 * sFormDG * phi_[localCol][r] * normal[d];
1101
1102 // [ u ] * { grad phi_en }
1103 dvalueNb[r][d] = 0.5 * sFormDG * phiNb_[localCol][r] * normal[d];
1104 }
1105 }
1106#endif
1107
1108 elementMatrix.column(localCol).axpy(phi_, dphi_, valueEn, dvalueEn, weight);
1109 elementMatrixNb.column(localCol).axpy(phi_, dphi_, valueNb, dvalueNb, weight);
1110
1111 } // end loop over DoFs
1112
1113 } // end face quadrature loop
1114
1115 }
1116
1118
1120
1121 } // namespace ACFem
1122
1123} // namespace Dune
1124
1125#endif // #ifndef __ELLIPTIC_OPERATOR_HH__
A class defining an elliptic operator.
Definition: ellipticoperator.hh:87
EllipticOperator(const ModelType &model, ConstraintsArg &&constraints)
Constructor including constraints.
Definition: ellipticoperator.hh:210
QuadratureTraitsType::FaceMassQuadratureType FaceMassQuadratureType
Use IntersectionQuadrature to create appropriate face quadratures.
Definition: ellipticoperator.hh:164
QuadratureTraitsType::template IntersectionMassQuadrature< true > BndryMassQuadratureType
Use IntersectionQuadrature to create appropriate face quadratures.
Definition: ellipticoperator.hh:178
QuadratureTraitsType::BulkMassQuadratureType MassQuadratureType
Use IntersectionQuadrature to create appropriate face quadratures.
Definition: ellipticoperator.hh:162
QuadratureTraitsType::FaceQuadratureType FaceQuadratureType
Use IntersectionQuadrature to create appropriate face quadratures.
Definition: ellipticoperator.hh:163
QuadratureTraitsType::BulkQuadratureType QuadratureType
Use IntersectionQuadrature to create appropriate face quadratures.
Definition: ellipticoperator.hh:161
QuadratureTraits< GridPartType > QuadratureTraitsType
Use IntersectionQuadrature to create appropriate face quadratures.
Definition: ellipticoperator.hh:160
EllipticOperator(const ModelType &model)
Constructor excluding constraints.
Definition: ellipticoperator.hh:225
typename QuadratureTraitsType::template IntersectionQuadrature< conforming > IntersectionQuadrature
Use IntersectionQuadrature to create appropriate face quadratures.
Definition: ellipticoperator.hh:168
IntersectionQuadrature< true > BndryQuadratureType
Definition: ellipticoperator.hh:177
virtual bool symmetric() const
linear operator interface
Definition: ellipticoperator.hh:246
RangeFunctionType DiscreteFunctionType
Alias for the range function type which is required to be a discrete function in order to provide sto...
Definition: ellipticoperator.hh:113
A class defining the "closure" type of all supported model-method and method-call-signatures.
Definition: modelfacade.hh:72
Empty do-nothing constraints.
DGFlavour
Definition: operatorselector.hh:24
virtual void operator()(const DomainFunctionType &u, RangeFunctionType &w) const
application operator, works "on the fly"
Definition: ellipticoperator.hh:448
auto outer(T1 &&t1, T2 &&t2)
Outer tensor product.
Definition: expressions.hh:138
constexpr auto contractInner(T1 &&t1, T2 &&t2, IndexConstant< N >=IndexConstant< N >{})
Contraction over the #N inner dimensions.
Definition: expressions.hh:118
DefaultQuadratureTraits< GridPart > QuadratureTraits
Traits class with ordinary quadratures in the bulk and on the skeleton.
Definition: quadrature.hh:132
STL namespace.
Helper traits-class, defining likely quadrature types.
Definition: quadrature.hh:23
static const bool hasMassQuadrature
Set to true if BulkMassQuadratureType differs from BulkQuadratureType.
Definition: quadrature.hh:55
FaceQuadratureType FaceMassQuadratureType
The quadrature to use for integrating mass contributions over faces.
Definition: quadrature.hh:44
Fem::CachingQuadrature< GridPartType, 0 > BulkQuadratureType
The quadrature to use for integrating over bulk elements.
Definition: quadrature.hh:27
Fem::CachingQuadrature< GridPartType, 1 > FaceQuadratureType
The quadrature to use for integrating over faces.
Definition: quadrature.hh:30
BulkQuadratureType BulkMassQuadratureType
The quadrature to use for integrating mass contributions.
Definition: quadrature.hh:37
Default expression traits definition is a recursion in order to ease disambiguation.
Definition: expressiontraits.hh:54
Definition: operatorselector.hh:31
Creative Commons License   |  Legal Statements / Impressum  |  Hosted by TU Dresden  |  generated with Hugo v0.111.3 (Dec 27, 23:30, 2024)