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 
16 namespace 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 
230  EllipticOperator(const EllipticOperator& other)
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;
354  using typename BaseType::BndryMassQuadratureType;
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();
390  using BaseType::symmetric;
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
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.80.0 (May 2, 22:35, 2024)