Reference documentation for deal.II version 8.5.1
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 #ifdef DEAL_II_WITH_CXX11
189  static_assert(0 <= index && index < rank,
190  "The specified index must lie within the range [0,rank)");
191 #endif
192 
193  return internal::ReorderedIndexView<index, rank, T>(t);
194  }
195 
196 
219  template<int rank, typename T, typename ArrayType> typename
220  ReturnType<rank, T>::value_type &
221  extract(T &t, const ArrayType &indices)
222  {
223  return internal::ExtractHelper<0, rank>::template extract<T, ArrayType>(t, indices);
224  }
225 
226 
265  template <int no_contr, int rank_1, int rank_2, int dim, typename T1, typename T2, typename T3>
266  inline DEAL_II_ALWAYS_INLINE
267  void contract(T1 &result, const T2 &left, const T3 &right)
268  {
269 #ifdef DEAL_II_WITH_CXX11
270  static_assert(rank_1 >= no_contr, "The rank of the left tensor must be "
271  "equal or greater than the number of "
272  "contractions");
273  static_assert(rank_2 >= no_contr, "The rank of the right tensor must be "
274  "equal or greater than the number of "
275  "contractions");
276 #endif
277 
278  internal::Contract<no_contr, rank_1, rank_2, dim>::template contract<T1, T2, T3>
279  (result, left, right);
280  }
281 
282 
311  template <int rank_1, int rank_2, int dim, typename T1, typename T2, typename T3, typename T4>
312  T1 contract3(const T2 &left, const T3 &middle, const T4 &right)
313  {
314  return internal::Contract3<rank_1, rank_2, dim>::template contract3<T1, T2, T3, T4>
315  (left, middle, right);
316  }
317 
318 
319  namespace internal
320  {
321  // -------------------------------------------------------------------------
322  // Forward declarations and type traits
323  // -------------------------------------------------------------------------
324 
325  template <int rank, typename S> class StoreIndex;
326  template <typename T> class Identity;
327  template <int no_contr, int dim> class Contract2;
328 
338  template<typename T>
340  {
341  typedef T &type;
342  };
343 
344  template <int rank, typename S>
345  struct ReferenceType<StoreIndex<rank, S> >
346  {
347  typedef StoreIndex<rank, S> type;
348  };
349 
350  template <int index, int rank, typename T>
351  struct ReferenceType<ReorderedIndexView<index, rank, T> >
352  {
353  typedef ReorderedIndexView<index, rank, T> type;
354  };
355 
356 
357  // TODO: Is there a possibility to just have the following block of
358  // explanation on an internal page in doxygen? If, yes. Doxygen
359  // wizards, your call!
360 
361  // -------------------------------------------------------------------------
362  // Implementation of helper classes for reordered_index_view
363  // -------------------------------------------------------------------------
364 
365  // OK. This is utterly brutal template magic. Therefore, we will not
366  // comment on the individual internal helper classes, because this is
367  // of not much value, but explain the general recursion procedure.
368  //
369  // (In order of appearance)
370  //
371  // Our task is to reorder access to a tensor object where a specified
372  // index is moved to the end. Thus we want to construct an object
373  // <code>reordered</code> out of a <code>tensor</code> where the
374  // following access patterns are equivalent:
375  // @code
376  // tensor [i_0]...[i_index-1][i_index][i_index+1]...[i_n]
377  // reordered [i_0]...[i_index_1][i_index+1]...[i_n][i_index]
378  // @endcode
379  //
380  // The first task is to get rid of the application of
381  // [i_0]...[i_index-1]. This is a classical recursion pattern - relay
382  // the task from <index, rank> to <index-1, rank-1> by accessing the
383  // subtensor object:
384 
385  template <int index, int rank, typename T>
386  class ReorderedIndexView
387  {
388  public:
389  ReorderedIndexView(typename ReferenceType<T>::type t) : t_(t) {}
390 
391  typedef ReorderedIndexView<index - 1, rank - 1, typename ValueType<T>::value_type>
392  value_type;
393 
394  // Recurse by applying index j directly:
395  inline DEAL_II_ALWAYS_INLINE
396  value_type operator[](unsigned int j) const
397  {
398  return value_type(t_[j]);
399  }
400 
401  private:
402  typename ReferenceType<T>::type t_;
403  };
404 
405  // At some point we hit the condition index == 0 and rank > 1, i.e.,
406  // the first index should be reordered to the end.
407  //
408  // At this point we cannot be lazy any more and have to start storing
409  // indices because we get them in the wrong order. The user supplies
410  // [i_0][i_1]...[i_{rank - 1}]
411  // but we have to call the subtensor object with
412  // [i_{rank - 1}[i_0][i_1]...[i_{rank-2}]
413  //
414  // So give up and relay the task to the StoreIndex class:
415 
416  template <int rank, typename T>
417  class ReorderedIndexView<0, rank, T>
418  {
419  public:
420  ReorderedIndexView(typename ReferenceType<T>::type t) : t_(t) {}
421 
422  typedef StoreIndex<rank - 1, internal::Identity<T> > value_type;
423 
424  inline DEAL_II_ALWAYS_INLINE
425  value_type operator[](unsigned int j) const
426  {
427  return value_type(Identity<T>(t_), j);
428  }
429 
430  private:
431  typename ReferenceType<T>::type t_;
432  };
433 
434  // Sometimes, we're lucky and don't have to do anything. In this case
435  // just return the original tensor.
436 
437  template <typename T>
438  class ReorderedIndexView<0, 1, T>
439  {
440  public:
441  ReorderedIndexView(typename ReferenceType<T>::type t) : t_(t) {}
442 
443  typedef typename ReferenceType<typename ValueType<T>::value_type>::type value_type;
444 
445  inline DEAL_II_ALWAYS_INLINE
446  value_type operator[](unsigned int j) const
447  {
448  return t_[j];
449  }
450 
451  private:
452  typename ReferenceType<T>::type t_;
453  };
454 
455  // Here, Identity is a helper class to ground the recursion in
456  // StoreIndex. Its implementation is easy - we haven't stored any
457  // indices yet. So, we just provide a function apply that returns the
458  // application of an index j to the stored tensor t_:
459 
460  template <typename T>
461  class Identity
462  {
463  public:
464  Identity(typename ReferenceType<T>::type t) : t_(t) {}
465 
466  typedef typename ValueType<T>::value_type return_type;
467 
468  inline DEAL_II_ALWAYS_INLINE
469  typename ReferenceType<return_type>::type apply(unsigned int j) const
470  {
471  return t_[j];
472  }
473 
474  private:
475  typename ReferenceType<T>::type t_;
476  };
477 
478  // StoreIndex is a class that stores an index recursively with every
479  // invocation of operator[](unsigned int j): We do this by recursively
480  // creating a new StoreIndex class of lower rank that stores the
481  // supplied index j and holds a copy of the current class (with all
482  // other stored indices). Again, we provide an apply member function
483  // that knows how to apply an index on the highest rank and all
484  // subsequently stored indices:
485 
486  template <int rank, typename S>
487  class StoreIndex
488  {
489  public:
490  StoreIndex(S s, int i) : s_(s), i_(i) {}
491 
492  typedef StoreIndex<rank - 1, StoreIndex<rank, S> > value_type;
493 
494  inline DEAL_II_ALWAYS_INLINE
495  value_type operator[](unsigned int j) const
496  {
497  return value_type(*this, j);
498  }
499 
500  typedef typename ValueType<typename S::return_type>::value_type return_type;
501 
502  inline
503  typename ReferenceType<return_type>::type apply(unsigned int j) const
504  {
505  return s_.apply(j)[i_];
506  }
507 
508  private:
509  const S s_;
510  const int i_;
511  };
512 
513  // We have to store indices until we hit rank == 1. Then, upon the next
514  // invocation of operator[](unsigned int j) we have all necessary
515  // information available to return the actual object.
516 
517  template <typename S>
518  class StoreIndex<1, S>
519  {
520  public:
521  StoreIndex(S s, int i) : s_(s), i_(i) {}
522 
523  typedef typename ValueType<typename S::return_type>::value_type return_type;
524  typedef return_type value_type;
525 
526  inline DEAL_II_ALWAYS_INLINE
527  return_type &operator[](unsigned int j) const
528  {
529  return s_.apply(j)[i_];
530  }
531 
532  private:
533  const S s_;
534  const int i_;
535  };
536 
537 
538  // -------------------------------------------------------------------------
539  // Implementation of helper classes for extract
540  // -------------------------------------------------------------------------
541 
542  // Straightforward recursion implemented by specializing ExtractHelper
543  // for position == rank. We use the type trait ReturnType<rank, T> to
544  // have an idea what the final type will be.
545  template<int position, int rank>
546  struct ExtractHelper
547  {
548  template<typename T, typename ArrayType>
549  inline
550  static
551  typename ReturnType<rank - position, T>::value_type &
552  extract(T &t,
553  const ArrayType &indices)
554  {
555  return ExtractHelper<position + 1, rank>::
556  template extract<typename ValueType<T>::value_type, ArrayType>
557  (t[indices[position]], indices);
558  }
559  };
560 
561  // For position == rank there is nothing to extract, just return the
562  // object.
563  template<int rank>
564  struct ExtractHelper<rank, rank>
565  {
566  template<typename T, typename ArrayType>
567  inline
568  static
569  T &extract(T &t,
570  const ArrayType &)
571  {
572  return t;
573  }
574  };
575 
576 
577  // -------------------------------------------------------------------------
578  // Implementation of helper classes for contract
579  // -------------------------------------------------------------------------
580 
581  // Straightforward recursive pattern:
582  //
583  // As long as rank_1 > no_contr, assign indices from the left tensor to
584  // result. This builds up the first part of the nested outer loops:
585  //
586  // for(unsigned int i_0; i_0 < dim; ++i_0)
587  // ...
588  // for(i_; i_ < dim; ++i_)
589  // [...]
590  // result[i_0]..[i_] ... left[i_0]..[i_] ...
591 
592  template <int no_contr, int rank_1, int rank_2, int dim>
593  class Contract
594  {
595  public:
596  template<typename T1, typename T2, typename T3>
597  inline DEAL_II_ALWAYS_INLINE static
598  void contract(T1 &result, const T2 &left, const T3 &right)
599  {
600  for (unsigned int i = 0; i < dim; ++i)
601  Contract<no_contr, rank_1 - 1, rank_2, dim>::
602  contract(result[i], left[i], right);
603  }
604  };
605 
606  // If rank_1 == no_contr leave out the remaining no_contr indices for
607  // the contraction and assign indices from the right tensor to the
608  // result. This builds up the second part of the nested loops:
609  //
610  // for(unsigned int i_0 = 0; i_0 < dim; ++i_0)
611  // ...
612  // for(unsigned int i_ = 0; i_ < dim; ++i_)
613  // for(unsigned int j_0 = 0; j_0 < dim; ++j_0)
614  // ...
615  // for(unsigned int j_ = 0; j_ < dim; ++j_)
616  // [...]
617  // result[i_0]..[i_][j_0]..[j_] ... left[i_0]..[i_] ... right[j_0]..[j_]
618  //
619 
620  template <int no_contr, int rank_2, int dim>
621  class Contract<no_contr, no_contr, rank_2, dim>
622  {
623  public:
624  template<typename T1, typename T2, typename T3>
625  inline DEAL_II_ALWAYS_INLINE static
626  void contract(T1 &result, const T2 &left, const T3 &right)
627  {
628  for (unsigned int i = 0; i < dim; ++i)
629  Contract<no_contr, no_contr, rank_2 - 1, dim>::
630  contract(result[i], left, right[i]);
631  }
632  };
633 
634  // If rank_1 == rank_2 == no_contr we have built up all of the outer
635  // loop. Now, it is time to do the actual contraction:
636  //
637  // [...]
638  // {
639  // result[i_0]..[i_][j_0]..[j_] = 0.;
640  // for(unsigned int k_0 = 0; k_0 < dim; ++k_0)
641  // ...
642  // for(unsigned int k_ = 0; k_ < dim; ++k_)
643  // result[i_0]..[i_][j_0]..[j_] += left[i_0]..[i_][k_0]..[k_] * right[j_0]..[j_][k_0]..[k_];
644  // }
645  //
646  // Relay this summation to another helper class.
647 
648  template <int no_contr, int dim>
649  class Contract<no_contr, no_contr, no_contr, dim>
650  {
651  public:
652  template<typename T1, typename T2, typename T3>
653  inline DEAL_II_ALWAYS_INLINE static
654  void contract(T1 &result, const T2 &left, const T3 &right)
655  {
656  result = Contract2<no_contr, dim>::template contract2<T1>(left, right);
657  }
658  };
659 
660  // Straightforward recursion:
661  //
662  // Contract leftmost index and recurse one down.
663 
664  template <int no_contr, int dim>
665  class Contract2
666  {
667  public:
668  template<typename T1, typename T2, typename T3>
669  inline DEAL_II_ALWAYS_INLINE static
670  T1 contract2(const T2 &left, const T3 &right)
671  {
672  T1 result = T1();
673  for (unsigned int i = 0; i < dim; ++i)
674  result += Contract2<no_contr - 1, dim>::template contract2<T1>(left[i], right[i]);
675  return result;
676  }
677  };
678 
679  // A contraction of two objects of order 0 is just a scalar
680  // multiplication:
681 
682  template <int dim>
683  class Contract2<0, dim>
684  {
685  public:
686  template<typename T1, typename T2, typename T3>
687  inline DEAL_II_ALWAYS_INLINE static
688  T1 contract2(const T2 &left, const T3 &right)
689  {
690  return left * right;
691  }
692  };
693 
694 
695  // -------------------------------------------------------------------------
696  // Implementation of helper classes for contract3
697  // -------------------------------------------------------------------------
698 
699  // Fully contract three tensorial objects
700  //
701  // As long as rank_1 > 0, recurse over left and middle:
702  //
703  // for(unsigned int i_0; i_0 < dim; ++i_0)
704  // ...
705  // for(i_; i_ < dim; ++i_)
706  // [...]
707  // left[i_0]..[i_] ... middle[i_0]..[i_] ... right
708 
709  template <int rank_1, int rank_2, int dim>
710  class Contract3
711  {
712  public:
713  template<typename T1, typename T2, typename T3, typename T4>
714  static inline
715  T1 contract3(const T2 &left, const T3 &middle, const T4 &right)
716  {
717  T1 result = T1();
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  T1 result = T1();
744  for (unsigned int i = 0; i < dim; ++i)
745  result += Contract3<0, rank_2 - 1, dim>::template contract3<T1>(left, middle[i], right[i]);
746  return result;
747  }
748  };
749 
750  // Contraction of three tensorial objects of rank 0 is just a scalar
751  // multiplication.
752 
753  template <int dim>
754  class Contract3<0, 0, dim>
755  {
756  public:
757  template<typename T1, typename T2, typename T3, typename T4>
758  static inline
759  T1 contract3(const T2 &left, const T3 &middle, const T4 &right)
760  {
761  return left * middle * right;
762  }
763  };
764 
765  // -------------------------------------------------------------------------
766 
767  } /* namespace internal */
768 } /* namespace TensorAccessors */
769 
770 DEAL_II_NAMESPACE_CLOSE
771 
772 #endif /* dealii__tensor_accessors_h */
DEAL_II_ALWAYS_INLINE internal::ReorderedIndexView< index, rank, T > reordered_index_view(T &t)
ReturnType< rank, T >::value_type & extract(T &t, const ArrayType &indices)
DEAL_II_ALWAYS_INLINE void contract(T1 &result, const T2 &left, const T3 &right)
T1 contract3(const T2 &left, const T3 &middle, const T4 &right)