Reference documentation for deal.II version 9.0.0
tensor_accessors.h
1 // ---------------------------------------------------------------------
2 //
3 // Copyright (C) 1998 - 2017 by the deal.II authors
4 //
5 // This file is part of the deal.II library.
6 //
7 // The deal.II library is free software; you can use it, redistribute
8 // it, and/or modify it under the terms of the GNU Lesser General
9 // Public License as published by the Free Software Foundation; either
10 // version 2.1 of the License, or (at your option) any later version.
11 // The full text of the license can be found in the file LICENSE at
12 // the top level of the deal.II distribution.
13 //
14 // ---------------------------------------------------------------------
15 
16 #ifndef dealii_tensor_accessors_h
17 #define dealii_tensor_accessors_h
18 
19 #include <deal.II/base/config.h>
20 #include <deal.II/base/template_constraints.h>
21 #include <deal.II/base/table_indices.h>
22 
23 
24 DEAL_II_NAMESPACE_OPEN
25 
71 namespace TensorAccessors
72 {
73  // forward declarations
74  namespace internal
75  {
76  template <int index, int rank, typename T> class ReorderedIndexView;
77  template <int position, int rank> struct ExtractHelper;
78  template <int no_contr, int rank_1, int rank_2, int dim> class Contract;
79  template <int rank_1, int rank_2, int dim> class Contract3;
80  }
81 
82 
99  template <typename T>
100  struct ValueType
101  {
102  typedef typename T::value_type value_type;
103  };
104 
105  template <typename T>
106  struct ValueType<const T>
107  {
108  typedef const typename T::value_type value_type;
109  };
110 
111  template <typename T, std::size_t N>
112  struct ValueType<T[N]>
113  {
114  typedef T value_type;
115  };
116 
117  template <typename T, std::size_t N>
118  struct ValueType<const T[N]>
119  {
120  typedef const T value_type;
121  };
122 
123 
131  template <int deref_steps, typename T>
132  struct ReturnType
133  {
134  typedef typename ReturnType<deref_steps - 1, typename ValueType<T>::value_type>::value_type value_type;
135  };
136 
137  template <typename T>
138  struct ReturnType<0, T>
139  {
140  typedef T value_type;
141  };
142 
143 
183  template <int index, int rank, typename T>
184  inline DEAL_II_ALWAYS_INLINE
185  internal::ReorderedIndexView<index, rank, T>
187  {
188  static_assert(0 <= index && index < rank,
189  "The specified index must lie within the range [0,rank)");
190 
191  return internal::ReorderedIndexView<index, rank, T>(t);
192  }
193 
194 
217  template <int rank, typename T, typename ArrayType> typename
218  ReturnType<rank, T>::value_type &
219  extract(T &t, const ArrayType &indices)
220  {
221  return internal::ExtractHelper<0, rank>::template extract<T, ArrayType>(t, indices);
222  }
223 
224 
263  template <int no_contr, int rank_1, int rank_2, int dim, typename T1, typename T2, typename T3>
264  inline DEAL_II_ALWAYS_INLINE
265  void contract(T1 &result, const T2 &left, const T3 &right)
266  {
267  static_assert(rank_1 >= no_contr, "The rank of the left tensor must be "
268  "equal or greater than the number of "
269  "contractions");
270  static_assert(rank_2 >= no_contr, "The rank of the right tensor must be "
271  "equal or greater than the number of "
272  "contractions");
273 
274  internal::Contract<no_contr, rank_1, rank_2, dim>::template contract<T1, T2, T3>
275  (result, left, right);
276  }
277 
278 
307  template <int rank_1, int rank_2, int dim, typename T1, typename T2, typename T3, typename T4>
308  T1 contract3(const T2 &left, const T3 &middle, const T4 &right)
309  {
310  return internal::Contract3<rank_1, rank_2, dim>::template contract3<T1, T2, T3, T4>
311  (left, middle, right);
312  }
313 
314 
315  namespace internal
316  {
317  // -------------------------------------------------------------------------
318  // Forward declarations and type traits
319  // -------------------------------------------------------------------------
320 
321  template <int rank, typename S> class StoreIndex;
322  template <typename T> class Identity;
323  template <int no_contr, int dim> class Contract2;
324 
334  template <typename T>
336  {
337  typedef T &type;
338  };
339 
340  template <int rank, typename S>
341  struct ReferenceType<StoreIndex<rank, S> >
342  {
343  typedef StoreIndex<rank, S> type;
344  };
345 
346  template <int index, int rank, typename T>
347  struct ReferenceType<ReorderedIndexView<index, rank, T> >
348  {
349  typedef ReorderedIndexView<index, rank, T> type;
350  };
351 
352 
353  // TODO: Is there a possibility to just have the following block of
354  // explanation on an internal page in doxygen? If, yes. Doxygen
355  // wizards, your call!
356 
357  // -------------------------------------------------------------------------
358  // Implementation of helper classes for reordered_index_view
359  // -------------------------------------------------------------------------
360 
361  // OK. This is utterly brutal template magic. Therefore, we will not
362  // comment on the individual internal helper classes, because this is
363  // of not much value, but explain the general recursion procedure.
364  //
365  // (In order of appearance)
366  //
367  // Our task is to reorder access to a tensor object where a specified
368  // index is moved to the end. Thus we want to construct an object
369  // <code>reordered</code> out of a <code>tensor</code> where the
370  // following access patterns are equivalent:
371  // @code
372  // tensor [i_0]...[i_index-1][i_index][i_index+1]...[i_n]
373  // reordered [i_0]...[i_index_1][i_index+1]...[i_n][i_index]
374  // @endcode
375  //
376  // The first task is to get rid of the application of
377  // [i_0]...[i_index-1]. This is a classical recursion pattern - relay
378  // the task from <index, rank> to <index-1, rank-1> by accessing the
379  // subtensor object:
380 
381  template <int index, int rank, typename T>
382  class ReorderedIndexView
383  {
384  public:
385  ReorderedIndexView(typename ReferenceType<T>::type t) : t_(t) {}
386 
387  typedef ReorderedIndexView<index - 1, rank - 1, typename ValueType<T>::value_type>
388  value_type;
389 
390  // Recurse by applying index j directly:
391  inline DEAL_II_ALWAYS_INLINE
392  value_type operator[](unsigned int j) const
393  {
394  return value_type(t_[j]);
395  }
396 
397  private:
398  typename ReferenceType<T>::type t_;
399  };
400 
401  // At some point we hit the condition index == 0 and rank > 1, i.e.,
402  // the first index should be reordered to the end.
403  //
404  // At this point we cannot be lazy any more and have to start storing
405  // indices because we get them in the wrong order. The user supplies
406  // [i_0][i_1]...[i_{rank - 1}]
407  // but we have to call the subtensor object with
408  // [i_{rank - 1}[i_0][i_1]...[i_{rank-2}]
409  //
410  // So give up and relay the task to the StoreIndex class:
411 
412  template <int rank, typename T>
413  class ReorderedIndexView<0, rank, T>
414  {
415  public:
416  ReorderedIndexView(typename ReferenceType<T>::type t) : t_(t) {}
417 
418  typedef StoreIndex<rank - 1, internal::Identity<T> > value_type;
419 
420  inline DEAL_II_ALWAYS_INLINE
421  value_type operator[](unsigned int j) const
422  {
423  return value_type(Identity<T>(t_), j);
424  }
425 
426  private:
427  typename ReferenceType<T>::type t_;
428  };
429 
430  // Sometimes, we're lucky and don't have to do anything. In this case
431  // just return the original tensor.
432 
433  template <typename T>
434  class ReorderedIndexView<0, 1, T>
435  {
436  public:
437  ReorderedIndexView(typename ReferenceType<T>::type t) : t_(t) {}
438 
439  typedef typename ReferenceType<typename ValueType<T>::value_type>::type value_type;
440 
441  inline DEAL_II_ALWAYS_INLINE
442  value_type operator[](unsigned int j) const
443  {
444  return t_[j];
445  }
446 
447  private:
448  typename ReferenceType<T>::type t_;
449  };
450 
451  // Here, Identity is a helper class to ground the recursion in
452  // StoreIndex. Its implementation is easy - we haven't stored any
453  // indices yet. So, we just provide a function apply that returns the
454  // application of an index j to the stored tensor t_:
455 
456  template <typename T>
457  class Identity
458  {
459  public:
460  Identity(typename ReferenceType<T>::type t) : t_(t) {}
461 
462  typedef typename ValueType<T>::value_type return_type;
463 
464  inline DEAL_II_ALWAYS_INLINE
465  typename ReferenceType<return_type>::type apply(unsigned int j) const
466  {
467  return t_[j];
468  }
469 
470  private:
471  typename ReferenceType<T>::type t_;
472  };
473 
474  // StoreIndex is a class that stores an index recursively with every
475  // invocation of operator[](unsigned int j): We do this by recursively
476  // creating a new StoreIndex class of lower rank that stores the
477  // supplied index j and holds a copy of the current class (with all
478  // other stored indices). Again, we provide an apply member function
479  // that knows how to apply an index on the highest rank and all
480  // subsequently stored indices:
481 
482  template <int rank, typename S>
483  class StoreIndex
484  {
485  public:
486  StoreIndex(S s, int i) : s_(s), i_(i) {}
487 
488  typedef StoreIndex<rank - 1, StoreIndex<rank, S> > value_type;
489 
490  inline DEAL_II_ALWAYS_INLINE
491  value_type operator[](unsigned int j) const
492  {
493  return value_type(*this, j);
494  }
495 
496  typedef typename ValueType<typename S::return_type>::value_type return_type;
497 
498  inline
499  typename ReferenceType<return_type>::type apply(unsigned int j) const
500  {
501  return s_.apply(j)[i_];
502  }
503 
504  private:
505  const S s_;
506  const int i_;
507  };
508 
509  // We have to store indices until we hit rank == 1. Then, upon the next
510  // invocation of operator[](unsigned int j) we have all necessary
511  // information available to return the actual object.
512 
513  template <typename S>
514  class StoreIndex<1, S>
515  {
516  public:
517  StoreIndex(S s, int i) : s_(s), i_(i) {}
518 
519  typedef typename ValueType<typename S::return_type>::value_type return_type;
520  typedef return_type value_type;
521 
522  inline DEAL_II_ALWAYS_INLINE
523  return_type &operator[](unsigned int j) const
524  {
525  return s_.apply(j)[i_];
526  }
527 
528  private:
529  const S s_;
530  const int i_;
531  };
532 
533 
534  // -------------------------------------------------------------------------
535  // Implementation of helper classes for extract
536  // -------------------------------------------------------------------------
537 
538  // Straightforward recursion implemented by specializing ExtractHelper
539  // for position == rank. We use the type trait ReturnType<rank, T> to
540  // have an idea what the final type will be.
541  template <int position, int rank>
542  struct ExtractHelper
543  {
544  template <typename T, typename ArrayType>
545  inline
546  static
547  typename ReturnType<rank - position, T>::value_type &
548  extract(T &t,
549  const ArrayType &indices)
550  {
551  return ExtractHelper<position + 1, rank>::
552  template extract<typename ValueType<T>::value_type, ArrayType>
553  (t[indices[position]], indices);
554  }
555  };
556 
557  // For position == rank there is nothing to extract, just return the
558  // object.
559  template <int rank>
560  struct ExtractHelper<rank, rank>
561  {
562  template <typename T, typename ArrayType>
563  inline
564  static
565  T &extract(T &t,
566  const ArrayType &)
567  {
568  return t;
569  }
570  };
571 
572 
573  // -------------------------------------------------------------------------
574  // Implementation of helper classes for contract
575  // -------------------------------------------------------------------------
576 
577  // Straightforward recursive pattern:
578  //
579  // As long as rank_1 > no_contr, assign indices from the left tensor to
580  // result. This builds up the first part of the nested outer loops:
581  //
582  // for(unsigned int i_0; i_0 < dim; ++i_0)
583  // ...
584  // for(i_; i_ < dim; ++i_)
585  // [...]
586  // result[i_0]..[i_] ... left[i_0]..[i_] ...
587 
588  template <int no_contr, int rank_1, int rank_2, int dim>
589  class Contract
590  {
591  public:
592  template <typename T1, typename T2, typename T3>
593  inline DEAL_II_ALWAYS_INLINE static
594  void contract(T1 &result, const T2 &left, const T3 &right)
595  {
596  for (unsigned int i = 0; i < dim; ++i)
597  Contract<no_contr, rank_1 - 1, rank_2, dim>::
598  contract(result[i], left[i], right);
599  }
600  };
601 
602  // If rank_1 == no_contr leave out the remaining no_contr indices for
603  // the contraction and assign indices from the right tensor to the
604  // result. This builds up the second part of the nested loops:
605  //
606  // for(unsigned int i_0 = 0; i_0 < dim; ++i_0)
607  // ...
608  // for(unsigned int i_ = 0; i_ < dim; ++i_)
609  // for(unsigned int j_0 = 0; j_0 < dim; ++j_0)
610  // ...
611  // for(unsigned int j_ = 0; j_ < dim; ++j_)
612  // [...]
613  // result[i_0]..[i_][j_0]..[j_] ... left[i_0]..[i_] ... right[j_0]..[j_]
614  //
615 
616  template <int no_contr, int rank_2, int dim>
617  class Contract<no_contr, no_contr, rank_2, dim>
618  {
619  public:
620  template <typename T1, typename T2, typename T3>
621  inline DEAL_II_ALWAYS_INLINE static
622  void contract(T1 &result, const T2 &left, const T3 &right)
623  {
624  for (unsigned int i = 0; i < dim; ++i)
625  Contract<no_contr, no_contr, rank_2 - 1, dim>::
626  contract(result[i], left, right[i]);
627  }
628  };
629 
630  // If rank_1 == rank_2 == no_contr we have built up all of the outer
631  // loop. Now, it is time to do the actual contraction:
632  //
633  // [...]
634  // {
635  // result[i_0]..[i_][j_0]..[j_] = 0.;
636  // for(unsigned int k_0 = 0; k_0 < dim; ++k_0)
637  // ...
638  // for(unsigned int k_ = 0; k_ < dim; ++k_)
639  // result[i_0]..[i_][j_0]..[j_] += left[i_0]..[i_][k_0]..[k_] * right[j_0]..[j_][k_0]..[k_];
640  // }
641  //
642  // Relay this summation to another helper class.
643 
644  template <int no_contr, int dim>
645  class Contract<no_contr, no_contr, no_contr, dim>
646  {
647  public:
648  template <typename T1, typename T2, typename T3>
649  inline DEAL_II_ALWAYS_INLINE static
650  void contract(T1 &result, const T2 &left, const T3 &right)
651  {
652  result = Contract2<no_contr, dim>::template contract2<T1>(left, right);
653  }
654  };
655 
656  // Straightforward recursion:
657  //
658  // Contract leftmost index and recurse one down.
659 
660  template <int no_contr, int dim>
661  class Contract2
662  {
663  public:
664  template <typename T1, typename T2, typename T3>
665  inline DEAL_II_ALWAYS_INLINE static
666  T1 contract2(const T2 &left, const T3 &right)
667  {
668  // Some auto-differentiable numbers need explicit
669  // zero initialization.
670  T1 result = ::internal::NumberType<T1>::value(0.0);
671  for (unsigned int i = 0; i < dim; ++i)
672  result += Contract2<no_contr - 1, dim>::template contract2<T1>(left[i], right[i]);
673  return result;
674  }
675  };
676 
677  // A contraction of two objects of order 0 is just a scalar
678  // multiplication:
679 
680  template <int dim>
681  class Contract2<0, dim>
682  {
683  public:
684  template <typename T1, typename T2, typename T3>
685  inline DEAL_II_ALWAYS_INLINE static
686  T1 contract2(const T2 &left, const T3 &right)
687  {
688  return left * right;
689  }
690  };
691 
692 
693  // -------------------------------------------------------------------------
694  // Implementation of helper classes for contract3
695  // -------------------------------------------------------------------------
696 
697  // Fully contract three tensorial objects
698  //
699  // As long as rank_1 > 0, recurse over left and middle:
700  //
701  // for(unsigned int i_0; i_0 < dim; ++i_0)
702  // ...
703  // for(i_; i_ < dim; ++i_)
704  // [...]
705  // left[i_0]..[i_] ... middle[i_0]..[i_] ... right
706 
707  template <int rank_1, int rank_2, int dim>
708  class Contract3
709  {
710  public:
711  template <typename T1, typename T2, typename T3, typename T4>
712  static inline
713  T1 contract3(const T2 &left, const T3 &middle, const T4 &right)
714  {
715  // Some auto-differentiable numbers need explicit
716  // zero initialization.
717  T1 result = ::internal::NumberType<T1>::value(0.0);
718  for (unsigned int i = 0; i < dim; ++i)
719  result += Contract3<rank_1 - 1, rank_2, dim>::template contract3<T1>(left[i], middle[i], right);
720  return result;
721  }
722  };
723 
724  // If rank_1 ==0, continue to recurse over middle and right:
725  //
726  // for(unsigned int i_0; i_0 < dim; ++i_0)
727  // ...
728  // for(i_; i_ < dim; ++i_)
729  // for(unsigned int j_0; j_0 < dim; ++j_0)
730  // ...
731  // for(j_; j_ < dim; ++j_)
732  // [...]
733  // left[i_0]..[i_] ... middle[i_0]..[i_][j_0]..[j_] ... right[j_0]..[j_]
734 
735  template <int rank_2, int dim>
736  class Contract3<0, rank_2, dim>
737  {
738  public:
739  template <typename T1, typename T2, typename T3, typename T4>
740  static inline
741  T1 contract3(const T2 &left, const T3 &middle, const T4 &right)
742  {
743  // Some auto-differentiable numbers need explicit
744  // zero initialization.
745  T1 result = ::internal::NumberType<T1>::value(0.0);
746  for (unsigned int i = 0; i < dim; ++i)
747  result += Contract3<0, rank_2 - 1, dim>::template contract3<T1>(left, middle[i], right[i]);
748  return result;
749  }
750  };
751 
752  // Contraction of three tensorial objects of rank 0 is just a scalar
753  // multiplication.
754 
755  template <int dim>
756  class Contract3<0, 0, dim>
757  {
758  public:
759  template <typename T1, typename T2, typename T3, typename T4>
760  static inline
761  T1 contract3(const T2 &left, const T3 &middle, const T4 &right)
762  {
763  return left * middle * right;
764  }
765  };
766 
767  // -------------------------------------------------------------------------
768 
769  } /* namespace internal */
770 } /* namespace TensorAccessors */
771 
772 DEAL_II_NAMESPACE_CLOSE
773 
774 #endif /* dealii_tensor_accessors_h */
internal::ReorderedIndexView< index, rank, T > reordered_index_view(T &t)
ReturnType< rank, T >::value_type & extract(T &t, const ArrayType &indices)
T1 contract3(const T2 &left, const T3 &middle, const T4 &right)
void contract(T1 &result, const T2 &left, const T3 &right)