Reference documentation for deal.II version 8.5.1
tensor_product_kernels.h
1 // ---------------------------------------------------------------------
2 //
3 // Copyright (C) 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 
17 #ifndef dealii__matrix_free_tensor_product_kernels_h
18 #define dealii__matrix_free_tensor_product_kernels_h
19 
20 #include <deal.II/base/config.h>
21 #include <deal.II/base/aligned_vector.h>
22 #include <deal.II/base/utilities.h>
23 
24 
25 DEAL_II_NAMESPACE_OPEN
26 
27 
28 
29 namespace internal
30 {
36  {
54  };
55 
59  template <EvaluatorVariant variant, int dim, int fe_degree, int n_q_points_1d,
60  typename Number>
62  {};
63 
68  template <int dim, int fe_degree, int n_q_points_1d, typename Number>
69  struct EvaluatorTensorProduct<evaluate_general,dim,fe_degree,n_q_points_1d,Number>
70  {
71  static const unsigned int dofs_per_cell = Utilities::fixed_int_power<fe_degree+1,dim>::value;
72  static const unsigned int n_q_points = Utilities::fixed_int_power<n_q_points_1d,dim>::value;
73 
79  :
80  shape_values (0),
81  shape_gradients (0),
82  shape_hessians (0)
83  {}
84 
89  const AlignedVector<Number> &shape_gradients,
90  const AlignedVector<Number> &shape_hessians,
91  const unsigned int dummy1 = 0,
92  const unsigned int dummy2 = 0)
93  :
94  shape_values (shape_values.begin()),
95  shape_gradients (shape_gradients.begin()),
96  shape_hessians (shape_hessians.begin())
97  {
98  (void)dummy1;
99  (void)dummy2;
100  }
101 
102  template <int direction, bool dof_to_quad, bool add>
103  void
104  values (const Number in [],
105  Number out[]) const
106  {
107  apply<direction,dof_to_quad,add>(shape_values, in, out);
108  }
109 
110  template <int direction, bool dof_to_quad, bool add>
111  void
112  gradients (const Number in [],
113  Number out[]) const
114  {
115  apply<direction,dof_to_quad,add>(shape_gradients, in, out);
116  }
117 
118  template <int direction, bool dof_to_quad, bool add>
119  void
120  hessians (const Number in [],
121  Number out[]) const
122  {
123  apply<direction,dof_to_quad,add>(shape_hessians, in, out);
124  }
125 
126  template <int direction, bool dof_to_quad, bool add>
127  static void apply (const Number *shape_data,
128  const Number in [],
129  Number out []);
130 
131  const Number *shape_values;
132  const Number *shape_gradients;
133  const Number *shape_hessians;
134  };
135 
136  // evaluates the given shape data in 1d-3d using the tensor product
137  // form. does not use a particular layout of entries in the matrices
138  // like the functions below and corresponds to a usual matrix-matrix
139  // product
140  template <int dim, int fe_degree, int n_q_points_1d, typename Number>
141  template <int direction, bool dof_to_quad, bool add>
142  inline
143  void
144  EvaluatorTensorProduct<evaluate_general,dim,fe_degree,n_q_points_1d,Number>
145  ::apply (const Number *shape_data,
146  const Number in [],
147  Number out [])
148  {
149  AssertIndexRange (direction, dim);
150  const int mm = dof_to_quad ? (fe_degree+1) : n_q_points_1d,
151  nn = dof_to_quad ? n_q_points_1d : (fe_degree+1);
152 
153  const int n_blocks1 = (dim > 1 ? (direction > 0 ? nn : mm) : 1);
154  const int n_blocks2 = (dim > 2 ? (direction > 1 ? nn : mm) : 1);
156 
157  for (int i2=0; i2<n_blocks2; ++i2)
158  {
159  for (int i1=0; i1<n_blocks1; ++i1)
160  {
161  for (int col=0; col<nn; ++col)
162  {
163  Number val0;
164  if (dof_to_quad == true)
165  val0 = shape_data[col];
166  else
167  val0 = shape_data[col*n_q_points_1d];
168  Number res0 = val0 * in[0];
169  for (int ind=1; ind<mm; ++ind)
170  {
171  if (dof_to_quad == true)
172  val0 = shape_data[ind*n_q_points_1d+col];
173  else
174  val0 = shape_data[col*n_q_points_1d+ind];
175  res0 += val0 * in[stride*ind];
176  }
177  if (add == false)
178  out[stride*col] = res0;
179  else
180  out[stride*col] += res0;
181  }
182 
183  // increment: in regular case, just go to the next point in
184  // x-direction. If we are at the end of one chunk in x-dir, need
185  // to jump over to the next layer in z-direction
186  switch (direction)
187  {
188  case 0:
189  in += mm;
190  out += nn;
191  break;
192  case 1:
193  case 2:
194  ++in;
195  ++out;
196  break;
197  default:
198  Assert (false, ExcNotImplemented());
199  }
200  }
201  if (direction == 1)
202  {
203  in += nn*(mm-1);
204  out += nn*(nn-1);
205  }
206  }
207  }
208 
209 
210 
211  // This method applies the tensor product operation to produce face values
212  // out from cell values. As opposed to the apply_tensor_product method, this
213  // method assumes that the directions orthogonal to the face have
214  // fe_degree+1 degrees of freedom per direction and not n_q_points_1d for
215  // those directions lower than the one currently applied
216  template <int dim, int fe_degree, typename Number, int face_direction,
217  bool dof_to_quad, bool add>
218  inline
219  void
220  apply_tensor_product_face (const Number *shape_data,
221  const Number in [],
222  Number out [])
223  {
224  const int n_blocks1 = dim > 1 ? (fe_degree+1) : 1;
225  const int n_blocks2 = dim > 2 ? (fe_degree+1) : 1;
226 
227  AssertIndexRange (face_direction, dim);
228  const int mm = dof_to_quad ? (fe_degree+1) : 1,
229  nn = dof_to_quad ? 1 : (fe_degree+1);
230 
232 
233  for (int i2=0; i2<n_blocks2; ++i2)
234  {
235  for (int i1=0; i1<n_blocks1; ++i1)
236  {
237  if (dof_to_quad == true)
238  {
239  Number res0 = shape_data[0] * in[0];
240  for (int ind=1; ind<mm; ++ind)
241  res0 += shape_data[ind] * in[stride*ind];
242  if (add == false)
243  out[0] = res0;
244  else
245  out[0] += res0;
246  }
247  else
248  {
249  for (int col=0; col<nn; ++col)
250  if (add == false)
251  out[col*stride] = shape_data[col] * in[0];
252  else
253  out[col*stride] += shape_data[col] * in[0];
254  }
255 
256  // increment: in regular case, just go to the next point in
257  // x-direction. If we are at the end of one chunk in x-dir, need
258  // to jump over to the next layer in z-direction
259  switch (face_direction)
260  {
261  case 0:
262  in += mm;
263  out += nn;
264  break;
265  case 1:
266  ++in;
267  ++out;
268  // faces 2 and 3 in 3D use local coordinate system zx, which
269  // is the other way around compared to the tensor
270  // product. Need to take that into account.
271  if (dim == 3)
272  {
273  if (dof_to_quad)
274  out += fe_degree;
275  else
276  in += fe_degree;
277  }
278  break;
279  case 2:
280  ++in;
281  ++out;
282  break;
283  default:
284  Assert (false, ExcNotImplemented());
285  }
286  }
287  if (face_direction == 1 && dim == 3)
288  {
289  in += mm*(mm-1);
290  out += nn*(nn-1);
291  // adjust for local coordinate system zx
292  if (dof_to_quad)
293  out -= (fe_degree+1)*(fe_degree+1)-1;
294  else
295  in -= (fe_degree+1)*(fe_degree+1)-1;
296  }
297  }
298  }
299 
300 
301 
307  template <int dim, typename Number>
308  struct EvaluatorTensorProduct<evaluate_general,dim,-1,0,Number>
309  {
310  static const unsigned int dofs_per_cell = numbers::invalid_unsigned_int;
311  static const unsigned int n_q_points = numbers::invalid_unsigned_int;
312 
318  :
319  shape_values (0),
320  shape_gradients (0),
321  shape_hessians (0),
322  fe_degree (numbers::invalid_unsigned_int),
323  n_q_points_1d (numbers::invalid_unsigned_int)
324  {}
325 
330  const AlignedVector<Number> &shape_gradients,
331  const AlignedVector<Number> &shape_hessians,
332  const unsigned int fe_degree,
333  const unsigned int n_q_points_1d)
334  :
335  shape_values (shape_values.begin()),
336  shape_gradients (shape_gradients.begin()),
337  shape_hessians (shape_hessians.begin()),
338  fe_degree (fe_degree),
339  n_q_points_1d (n_q_points_1d)
340  {}
341 
342  template <int direction, bool dof_to_quad, bool add>
343  void
344  values (const Number *in,
345  Number *out) const
346  {
347  apply<direction,dof_to_quad,add>(shape_values, in, out);
348  }
349 
350  template <int direction, bool dof_to_quad, bool add>
351  void
352  gradients (const Number *in,
353  Number *out) const
354  {
355  apply<direction,dof_to_quad,add>(shape_gradients, in, out);
356  }
357 
358  template <int direction, bool dof_to_quad, bool add>
359  void
360  hessians (const Number *in,
361  Number *out) const
362  {
363  apply<direction,dof_to_quad,add>(shape_hessians, in, out);
364  }
365 
366  template <int direction, bool dof_to_quad, bool add>
367  void apply (const Number *shape_data,
368  const Number *in,
369  Number *out) const;
370 
371  const Number *shape_values;
372  const Number *shape_gradients;
373  const Number *shape_hessians;
374  const unsigned int fe_degree;
375  const unsigned int n_q_points_1d;
376  };
377 
378  // evaluates the given shape data in 1d-3d using the tensor product
379  // form. does not use a particular layout of entries in the matrices
380  // like the functions below and corresponds to a usual matrix-matrix
381  // product
382  template <int dim, typename Number>
383  template <int direction, bool dof_to_quad, bool add>
384  inline
385  void
386  EvaluatorTensorProduct<evaluate_general,dim,-1,0,Number>
387  ::apply (const Number *shape_data,
388  const Number *in,
389  Number *out) const
390  {
391  AssertIndexRange (direction, dim);
392  const int mm = dof_to_quad ? (fe_degree+1) : n_q_points_1d,
393  nn = dof_to_quad ? n_q_points_1d : (fe_degree+1);
394 
395  const int n_blocks1 = (dim > 1 ? (direction > 0 ? nn : mm) : 1);
396  const int n_blocks2 = (dim > 2 ? (direction > 1 ? nn : mm) : 1);
397  const int stride = direction==0 ? 1 : Utilities::fixed_power<direction>(nn);
398 
399  for (int i2=0; i2<n_blocks2; ++i2)
400  {
401  for (int i1=0; i1<n_blocks1; ++i1)
402  {
403  for (int col=0; col<nn; ++col)
404  {
405  Number val0;
406  if (dof_to_quad == true)
407  val0 = shape_data[col];
408  else
409  val0 = shape_data[col*n_q_points_1d];
410  Number res0 = val0 * in[0];
411  for (int ind=1; ind<mm; ++ind)
412  {
413  if (dof_to_quad == true)
414  val0 = shape_data[ind*n_q_points_1d+col];
415  else
416  val0 = shape_data[col*n_q_points_1d+ind];
417  res0 += val0 * in[stride*ind];
418  }
419  if (add == false)
420  out[stride*col] = res0;
421  else
422  out[stride*col] += res0;
423  }
424 
425  // increment: in regular case, just go to the next point in
426  // x-direction. If we are at the end of one chunk in x-dir, need
427  // to jump over to the next layer in z-direction
428  switch (direction)
429  {
430  case 0:
431  in += mm;
432  out += nn;
433  break;
434  case 1:
435  case 2:
436  ++in;
437  ++out;
438  break;
439  default:
440  Assert (false, ExcNotImplemented());
441  }
442  }
443  if (direction == 1)
444  {
445  in += nn*(mm-1);
446  out += nn*(nn-1);
447  }
448  }
449  }
450 
451 
452 
460  template <int dim, int fe_degree, int n_q_points_1d, typename Number>
461  struct EvaluatorTensorProduct<evaluate_symmetric,dim,fe_degree,n_q_points_1d,Number>
462  {
463  static const unsigned int dofs_per_cell = Utilities::fixed_int_power<fe_degree+1,dim>::value;
464  static const unsigned int n_q_points = Utilities::fixed_int_power<n_q_points_1d,dim>::value;
465 
470  const AlignedVector<Number> &shape_gradients,
471  const AlignedVector<Number> &shape_hessians,
472  const unsigned int dummy1 = 0,
473  const unsigned int dummy2 = 0)
474  :
475  shape_values (shape_values.begin()),
476  shape_gradients (shape_gradients.begin()),
477  shape_hessians (shape_hessians.begin())
478  {
479  (void)dummy1;
480  (void)dummy2;
481  }
482 
483  template <int direction, bool dof_to_quad, bool add>
484  void
485  values (const Number in [],
486  Number out[]) const;
487 
488  template <int direction, bool dof_to_quad, bool add>
489  void
490  gradients (const Number in [],
491  Number out[]) const;
492 
493  template <int direction, bool dof_to_quad, bool add>
494  void
495  hessians (const Number in [],
496  Number out[]) const;
497 
498  const Number *shape_values;
499  const Number *shape_gradients;
500  const Number *shape_hessians;
501  };
502 
503 
504 
505  // In this case, the 1D shape values read (sorted lexicographically, rows
506  // run over 1D dofs, columns over quadrature points):
507  // Q2 --> [ 0.687 0 -0.087 ]
508  // [ 0.4 1 0.4 ]
509  // [-0.087 0 0.687 ]
510  // Q3 --> [ 0.66 0.003 0.002 0.049 ]
511  // [ 0.521 1.005 -0.01 -0.230 ]
512  // [-0.230 -0.01 1.005 0.521 ]
513  // [ 0.049 0.002 0.003 0.66 ]
514  // Q4 --> [ 0.658 0.022 0 -0.007 -0.032 ]
515  // [ 0.608 1.059 0 0.039 0.176 ]
516  // [-0.409 -0.113 1 -0.113 -0.409 ]
517  // [ 0.176 0.039 0 1.059 0.608 ]
518  // [-0.032 -0.007 0 0.022 0.658 ]
519  //
520  // In these matrices, we want to use avoid computations involving zeros and
521  // ones and in addition use the symmetry in entries to reduce the number of
522  // read operations.
523  template <int dim, int fe_degree, int n_q_points_1d, typename Number>
524  template <int direction, bool dof_to_quad, bool add>
525  inline
526  void
527  EvaluatorTensorProduct<evaluate_symmetric,dim,fe_degree,n_q_points_1d,Number>
528  ::values (const Number in [],
529  Number out []) const
530  {
531  AssertIndexRange (direction, dim);
532  const int mm = dof_to_quad ? (fe_degree+1) : n_q_points_1d,
533  nn = dof_to_quad ? n_q_points_1d : (fe_degree+1);
534  const int n_cols = nn / 2;
535  const int mid = mm / 2;
536 
537  const int n_blocks1 = (dim > 1 ? (direction > 0 ? nn : mm) : 1);
538  const int n_blocks2 = (dim > 2 ? (direction > 1 ? nn : mm) : 1);
540 
541  for (int i2=0; i2<n_blocks2; ++i2)
542  {
543  for (int i1=0; i1<n_blocks1; ++i1)
544  {
545  for (int col=0; col<n_cols; ++col)
546  {
547  Number val0, val1, in0, in1, res0, res1;
548  if (dof_to_quad == true)
549  {
550  val0 = shape_values[col];
551  val1 = shape_values[nn-1-col];
552  }
553  else
554  {
555  val0 = shape_values[col*n_q_points_1d];
556  val1 = shape_values[(col+1)*n_q_points_1d-1];
557  }
558  if (mid > 0)
559  {
560  in0 = in[0];
561  in1 = in[stride*(mm-1)];
562  res0 = val0 * in0;
563  res1 = val1 * in0;
564  res0 += val1 * in1;
565  res1 += val0 * in1;
566  for (int ind=1; ind<mid; ++ind)
567  {
568  if (dof_to_quad == true)
569  {
570  val0 = shape_values[ind*n_q_points_1d+col];
571  val1 = shape_values[ind*n_q_points_1d+nn-1-col];
572  }
573  else
574  {
575  val0 = shape_values[col*n_q_points_1d+ind];
576  val1 = shape_values[(col+1)*n_q_points_1d-1-ind];
577  }
578  in0 = in[stride*ind];
579  in1 = in[stride*(mm-1-ind)];
580  res0 += val0 * in0;
581  res1 += val1 * in0;
582  res0 += val1 * in1;
583  res1 += val0 * in1;
584  }
585  }
586  else
587  res0 = res1 = Number();
588  if (dof_to_quad == true)
589  {
590  if (mm % 2 == 1)
591  {
592  val0 = shape_values[mid*n_q_points_1d+col];
593  val1 = val0 * in[stride*mid];
594  res0 += val1;
595  res1 += val1;
596  }
597  }
598  else
599  {
600  if (mm % 2 == 1 && nn % 2 == 0)
601  {
602  val0 = shape_values[col*n_q_points_1d+mid];
603  val1 = val0 * in[stride*mid];
604  res0 += val1;
605  res1 += val1;
606  }
607  }
608  if (add == false)
609  {
610  out[stride*col] = res0;
611  out[stride*(nn-1-col)] = res1;
612  }
613  else
614  {
615  out[stride*col] += res0;
616  out[stride*(nn-1-col)] += res1;
617  }
618  }
619  if ( dof_to_quad == true && nn%2==1 && mm%2==1 )
620  {
621  if (add==false)
622  out[stride*n_cols] = in[stride*mid];
623  else
624  out[stride*n_cols] += in[stride*mid];
625  }
626  else if (dof_to_quad == true && nn%2==1)
627  {
628  Number res0;
629  Number val0 = shape_values[n_cols];
630  if (mid > 0)
631  {
632  res0 = in[0] + in[stride*(mm-1)];
633  res0 *= val0;
634  for (int ind=1; ind<mid; ++ind)
635  {
636  val0 = shape_values[ind*n_q_points_1d+n_cols];
637  Number val1 = in[stride*ind] + in[stride*(mm-1-ind)];
638  val1 *= val0;
639  res0 += val1;
640  }
641  }
642  else
643  res0 = Number();
644  if (add == false)
645  out[stride*n_cols] = res0;
646  else
647  out[stride*n_cols] += res0;
648  }
649  else if (dof_to_quad == false && nn%2 == 1)
650  {
651  Number res0;
652  if (mid > 0)
653  {
654  Number val0 = shape_values[n_cols*n_q_points_1d];
655  res0 = in[0] + in[stride*(mm-1)];
656  res0 *= val0;
657  for (int ind=1; ind<mid; ++ind)
658  {
659  val0 = shape_values[n_cols*n_q_points_1d+ind];
660  Number val1 = in[stride*ind] + in[stride*(mm-1-ind)];
661  val1 *= val0;
662  res0 += val1;
663  }
664  if (mm % 2)
665  res0 += in[stride*mid];
666  }
667  else
668  res0 = in[0];
669  if (add == false)
670  out[stride*n_cols] = res0;
671  else
672  out[stride*n_cols] += res0;
673  }
674 
675  // increment: in regular case, just go to the next point in
676  // x-direction. If we are at the end of one chunk in x-dir, need to
677  // jump over to the next layer in z-direction
678  switch (direction)
679  {
680  case 0:
681  in += mm;
682  out += nn;
683  break;
684  case 1:
685  case 2:
686  ++in;
687  ++out;
688  break;
689  default:
690  Assert (false, ExcNotImplemented());
691  }
692  }
693  if (direction == 1)
694  {
695  in += nn*(mm-1);
696  out += nn*(nn-1);
697  }
698  }
699  }
700 
701 
702 
703  // For the specialized loop used for the gradient computation in
704  // here, the 1D shape values read (sorted lexicographically, rows
705  // run over 1D dofs, columns over quadrature points):
706  // Q2 --> [-2.549 -1 0.549 ]
707  // [ 3.098 0 -3.098 ]
708  // [-0.549 1 2.549 ]
709  // Q3 --> [-4.315 -1.03 0.5 -0.44 ]
710  // [ 6.07 -1.44 -2.97 2.196 ]
711  // [-2.196 2.97 1.44 -6.07 ]
712  // [ 0.44 -0.5 1.03 4.315 ]
713  // Q4 --> [-6.316 -1.3 0.333 -0.353 0.413 ]
714  // [10.111 -2.76 -2.667 2.066 -2.306 ]
715  // [-5.688 5.773 0 -5.773 5.688 ]
716  // [ 2.306 -2.066 2.667 2.76 -10.111 ]
717  // [-0.413 0.353 -0.333 -0.353 0.413 ]
718  //
719  // In these matrices, we want to use avoid computations involving
720  // zeros and ones and in addition use the symmetry in entries to
721  // reduce the number of read operations.
722  template <int dim, int fe_degree, int n_q_points_1d, typename Number>
723  template <int direction, bool dof_to_quad, bool add>
724  inline
725  void
726  EvaluatorTensorProduct<evaluate_symmetric,dim,fe_degree,n_q_points_1d,Number>
727  ::gradients (const Number in [],
728  Number out []) const
729  {
730  AssertIndexRange (direction, dim);
731  const int mm = dof_to_quad ? (fe_degree+1) : n_q_points_1d,
732  nn = dof_to_quad ? n_q_points_1d : (fe_degree+1);
733  const int n_cols = nn / 2;
734  const int mid = mm / 2;
735 
736  const int n_blocks1 = (dim > 1 ? (direction > 0 ? nn : mm) : 1);
737  const int n_blocks2 = (dim > 2 ? (direction > 1 ? nn : mm) : 1);
739 
740  for (int i2=0; i2<n_blocks2; ++i2)
741  {
742  for (int i1=0; i1<n_blocks1; ++i1)
743  {
744  for (int col=0; col<n_cols; ++col)
745  {
746  Number val0, val1, in0, in1, res0, res1;
747  if (dof_to_quad == true)
748  {
749  val0 = shape_gradients[col];
750  val1 = shape_gradients[nn-1-col];
751  }
752  else
753  {
754  val0 = shape_gradients[col*n_q_points_1d];
755  val1 = shape_gradients[(nn-col-1)*n_q_points_1d];
756  }
757  if (mid > 0)
758  {
759  in0 = in[0];
760  in1 = in[stride*(mm-1)];
761  res0 = val0 * in0;
762  res1 = val1 * in0;
763  res0 -= val1 * in1;
764  res1 -= val0 * in1;
765  for (int ind=1; ind<mid; ++ind)
766  {
767  if (dof_to_quad == true)
768  {
769  val0 = shape_gradients[ind*n_q_points_1d+col];
770  val1 = shape_gradients[ind*n_q_points_1d+nn-1-col];
771  }
772  else
773  {
774  val0 = shape_gradients[col*n_q_points_1d+ind];
775  val1 = shape_gradients[(nn-col-1)*n_q_points_1d+ind];
776  }
777  in0 = in[stride*ind];
778  in1 = in[stride*(mm-1-ind)];
779  res0 += val0 * in0;
780  res1 += val1 * in0;
781  res0 -= val1 * in1;
782  res1 -= val0 * in1;
783  }
784  }
785  else
786  res0 = res1 = Number();
787  if (mm % 2 == 1)
788  {
789  if (dof_to_quad == true)
790  val0 = shape_gradients[mid*n_q_points_1d+col];
791  else
792  val0 = shape_gradients[col*n_q_points_1d+mid];
793  val1 = val0 * in[stride*mid];
794  res0 += val1;
795  res1 -= val1;
796  }
797  if (add == false)
798  {
799  out[stride*col] = res0;
800  out[stride*(nn-1-col)] = res1;
801  }
802  else
803  {
804  out[stride*col] += res0;
805  out[stride*(nn-1-col)] += res1;
806  }
807  }
808  if ( nn%2 == 1 )
809  {
810  Number val0, res0;
811  if (dof_to_quad == true)
812  val0 = shape_gradients[n_cols];
813  else
814  val0 = shape_gradients[n_cols*n_q_points_1d];
815  res0 = in[0] - in[stride*(mm-1)];
816  res0 *= val0;
817  for (int ind=1; ind<mid; ++ind)
818  {
819  if (dof_to_quad == true)
820  val0 = shape_gradients[ind*n_q_points_1d+n_cols];
821  else
822  val0 = shape_gradients[n_cols*n_q_points_1d+ind];
823  Number val1 = in[stride*ind] - in[stride*(mm-1-ind)];
824  val1 *= val0;
825  res0 += val1;
826  }
827  if (add == false)
828  out[stride*n_cols] = res0;
829  else
830  out[stride*n_cols] += res0;
831  }
832 
833  // increment: in regular case, just go to the next point in
834  // x-direction. for y-part in 3D and if we are at the end of one
835  // chunk in x-dir, need to jump over to the next layer in
836  // z-direction
837  switch (direction)
838  {
839  case 0:
840  in += mm;
841  out += nn;
842  break;
843  case 1:
844  case 2:
845  ++in;
846  ++out;
847  break;
848  default:
849  Assert (false, ExcNotImplemented());
850  }
851  }
852 
853  if (direction == 1)
854  {
855  in += nn * (mm-1);
856  out += nn * (nn-1);
857  }
858  }
859  }
860 
861 
862 
863  // evaluates the given shape data in 1d-3d using the tensor product
864  // form assuming the symmetries of unit cell shape hessians for
865  // finite elements in FEEvaluation
866  template <int dim, int fe_degree, int n_q_points_1d, typename Number>
867  template <int direction, bool dof_to_quad, bool add>
868  inline
869  void
870  EvaluatorTensorProduct<evaluate_symmetric,dim,fe_degree,n_q_points_1d,Number>
871  ::hessians (const Number in [],
872  Number out []) const
873  {
874  AssertIndexRange (direction, dim);
875  const int mm = dof_to_quad ? (fe_degree+1) : n_q_points_1d,
876  nn = dof_to_quad ? n_q_points_1d : (fe_degree+1);
877  const int n_cols = nn / 2;
878  const int mid = mm / 2;
879 
880  const int n_blocks1 = (dim > 1 ? (direction > 0 ? nn : mm) : 1);
881  const int n_blocks2 = (dim > 2 ? (direction > 1 ? nn : mm) : 1);
883 
884  for (int i2=0; i2<n_blocks2; ++i2)
885  {
886  for (int i1=0; i1<n_blocks1; ++i1)
887  {
888  for (int col=0; col<n_cols; ++col)
889  {
890  Number val0, val1, in0, in1, res0, res1;
891  if (dof_to_quad == true)
892  {
893  val0 = shape_hessians[col];
894  val1 = shape_hessians[nn-1-col];
895  }
896  else
897  {
898  val0 = shape_hessians[col*n_q_points_1d];
899  val1 = shape_hessians[(col+1)*n_q_points_1d-1];
900  }
901  if (mid > 0)
902  {
903  in0 = in[0];
904  in1 = in[stride*(mm-1)];
905  res0 = val0 * in0;
906  res1 = val1 * in0;
907  res0 += val1 * in1;
908  res1 += val0 * in1;
909  for (int ind=1; ind<mid; ++ind)
910  {
911  if (dof_to_quad == true)
912  {
913  val0 = shape_hessians[ind*n_q_points_1d+col];
914  val1 = shape_hessians[ind*n_q_points_1d+nn-1-col];
915  }
916  else
917  {
918  val0 = shape_hessians[col*n_q_points_1d+ind];
919  val1 = shape_hessians[(col+1)*n_q_points_1d-1-ind];
920  }
921  in0 = in[stride*ind];
922  in1 = in[stride*(mm-1-ind)];
923  res0 += val0 * in0;
924  res1 += val1 * in0;
925  res0 += val1 * in1;
926  res1 += val0 * in1;
927  }
928  }
929  else
930  res0 = res1 = Number();
931  if (mm % 2 == 1)
932  {
933  if (dof_to_quad == true)
934  val0 = shape_hessians[mid*n_q_points_1d+col];
935  else
936  val0 = shape_hessians[col*n_q_points_1d+mid];
937  val1 = val0 * in[stride*mid];
938  res0 += val1;
939  res1 += val1;
940  }
941  if (add == false)
942  {
943  out[stride*col] = res0;
944  out[stride*(nn-1-col)] = res1;
945  }
946  else
947  {
948  out[stride*col] += res0;
949  out[stride*(nn-1-col)] += res1;
950  }
951  }
952  if ( nn%2 == 1 )
953  {
954  Number val0, res0;
955  if (dof_to_quad == true)
956  val0 = shape_hessians[n_cols];
957  else
958  val0 = shape_hessians[n_cols*n_q_points_1d];
959  if (mid > 0)
960  {
961  res0 = in[0] + in[stride*(mm-1)];
962  res0 *= val0;
963  for (int ind=1; ind<mid; ++ind)
964  {
965  if (dof_to_quad == true)
966  val0 = shape_hessians[ind*n_q_points_1d+n_cols];
967  else
968  val0 = shape_hessians[n_cols*n_q_points_1d+ind];
969  Number val1 = in[stride*ind] + in[stride*(mm-1-ind)];
970  val1 *= val0;
971  res0 += val1;
972  }
973  }
974  else
975  res0 = Number();
976  if (mm % 2 == 1)
977  {
978  if (dof_to_quad == true)
979  val0 = shape_hessians[mid*n_q_points_1d+n_cols];
980  else
981  val0 = shape_hessians[n_cols*n_q_points_1d+mid];
982  res0 += val0 * in[stride*mid];
983  }
984  if (add == false)
985  out[stride*n_cols] = res0;
986  else
987  out[stride*n_cols] += res0;
988  }
989 
990  // increment: in regular case, just go to the next point in
991  // x-direction. If we are at the end of one chunk in x-dir, need to
992  // jump over to the next layer in z-direction
993  switch (direction)
994  {
995  case 0:
996  in += mm;
997  out += nn;
998  break;
999  case 1:
1000  case 2:
1001  ++in;
1002  ++out;
1003  break;
1004  default:
1005  Assert (false, ExcNotImplemented());
1006  }
1007  }
1008  if (direction == 1)
1009  {
1010  in += nn*(mm-1);
1011  out += nn*(nn-1);
1012  }
1013  }
1014  }
1015 
1016 
1017 
1036  template <int dim, int fe_degree, int n_q_points_1d, typename Number>
1037  struct EvaluatorTensorProduct<evaluate_evenodd,dim,fe_degree,n_q_points_1d,Number>
1038  {
1039  static const unsigned int dofs_per_cell = Utilities::fixed_int_power<fe_degree+1,dim>::value;
1040  static const unsigned int n_q_points = Utilities::fixed_int_power<n_q_points_1d,dim>::value;
1041 
1047  :
1048  shape_values (0),
1049  shape_gradients (0),
1050  shape_hessians (0)
1051  {}
1052 
1058  const AlignedVector<Number> &shape_gradients,
1059  const AlignedVector<Number> &shape_hessians,
1060  const unsigned int dummy1 = 0,
1061  const unsigned int dummy2 = 0)
1062  :
1063  shape_values (shape_values.begin()),
1064  shape_gradients (shape_gradients.begin()),
1065  shape_hessians (shape_hessians.begin())
1066  {
1067  (void)dummy1;
1068  (void)dummy2;
1069  }
1070 
1071  template <int direction, bool dof_to_quad, bool add>
1072  void
1073  values (const Number in [],
1074  Number out[]) const
1075  {
1076  apply<direction,dof_to_quad,add,0>(shape_values, in, out);
1077  }
1078 
1079  template <int direction, bool dof_to_quad, bool add>
1080  void
1081  gradients (const Number in [],
1082  Number out[]) const
1083  {
1084  apply<direction,dof_to_quad,add,1>(shape_gradients, in, out);
1085  }
1086 
1087  template <int direction, bool dof_to_quad, bool add>
1088  void
1089  hessians (const Number in [],
1090  Number out[]) const
1091  {
1092  apply<direction,dof_to_quad,add,2>(shape_hessians, in, out);
1093  }
1094 
1095  template <int direction, bool dof_to_quad, bool add, int type>
1096  static void apply (const Number *shape_data,
1097  const Number in [],
1098  Number out []);
1099 
1100  const Number *shape_values;
1101  const Number *shape_gradients;
1102  const Number *shape_hessians;
1103  };
1104 
1105 
1106 
1107  template <int dim, int fe_degree, int n_q_points_1d, typename Number>
1108  template <int direction, bool dof_to_quad, bool add, int type>
1109  inline
1110  void
1111  EvaluatorTensorProduct<evaluate_evenodd,dim,fe_degree,n_q_points_1d,Number>
1112  ::apply (const Number *shapes,
1113  const Number in [],
1114  Number out [])
1115  {
1116  AssertIndexRange (type, 3);
1117  AssertIndexRange (direction, dim);
1118  const int mm = dof_to_quad ? (fe_degree+1) : n_q_points_1d,
1119  nn = dof_to_quad ? n_q_points_1d : (fe_degree+1);
1120  const int n_cols = nn / 2;
1121  const int mid = mm / 2;
1122 
1123  const int n_blocks1 = (dim > 1 ? (direction > 0 ? nn : mm) : 1);
1124  const int n_blocks2 = (dim > 2 ? (direction > 1 ? nn : mm) : 1);
1126 
1127  const int offset = (n_q_points_1d+1)/2;
1128 
1129  // this code may look very inefficient at first sight due to the many
1130  // different cases with if's at the innermost loop part, but all of the
1131  // conditionals can be evaluated at compile time because they are
1132  // templates, so the compiler should optimize everything away
1133  for (int i2=0; i2<n_blocks2; ++i2)
1134  {
1135  for (int i1=0; i1<n_blocks1; ++i1)
1136  {
1137  Number xp[mid>0?mid:1], xm[mid>0?mid:1];
1138  for (int i=0; i<mid; ++i)
1139  {
1140  if (dof_to_quad == true && type == 1)
1141  {
1142  xp[i] = in[stride*i] - in[stride*(mm-1-i)];
1143  xm[i] = in[stride*i] + in[stride*(mm-1-i)];
1144  }
1145  else
1146  {
1147  xp[i] = in[stride*i] + in[stride*(mm-1-i)];
1148  xm[i] = in[stride*i] - in[stride*(mm-1-i)];
1149  }
1150  }
1151  for (int col=0; col<n_cols; ++col)
1152  {
1153  Number r0, r1;
1154  if (mid > 0)
1155  {
1156  if (dof_to_quad == true)
1157  {
1158  r0 = shapes[col] * xp[0];
1159  r1 = shapes[fe_degree*offset + col] * xm[0];
1160  }
1161  else
1162  {
1163  r0 = shapes[col*offset] * xp[0];
1164  r1 = shapes[(fe_degree-col)*offset] * xm[0];
1165  }
1166  for (int ind=1; ind<mid; ++ind)
1167  {
1168  if (dof_to_quad == true)
1169  {
1170  r0 += shapes[ind*offset+col] * xp[ind];
1171  r1 += shapes[(fe_degree-ind)*offset+col] * xm[ind];
1172  }
1173  else
1174  {
1175  r0 += shapes[col*offset+ind] * xp[ind];
1176  r1 += shapes[(fe_degree-col)*offset+ind] * xm[ind];
1177  }
1178  }
1179  }
1180  else
1181  r0 = r1 = Number();
1182  if (mm % 2 == 1 && dof_to_quad == true)
1183  {
1184  if (type == 1)
1185  r1 += shapes[mid*offset+col] * in[stride*mid];
1186  else
1187  r0 += shapes[mid*offset+col] * in[stride*mid];
1188  }
1189  else if (mm % 2 == 1 && (nn % 2 == 0 || type > 0))
1190  r0 += shapes[col*offset+mid] * in[stride*mid];
1191 
1192  if (add == false)
1193  {
1194  out[stride*col] = r0 + r1;
1195  if (type == 1 && dof_to_quad == false)
1196  out[stride*(nn-1-col)] = r1 - r0;
1197  else
1198  out[stride*(nn-1-col)] = r0 - r1;
1199  }
1200  else
1201  {
1202  out[stride*col] += r0 + r1;
1203  if (type == 1 && dof_to_quad == false)
1204  out[stride*(nn-1-col)] += r1 - r0;
1205  else
1206  out[stride*(nn-1-col)] += r0 - r1;
1207  }
1208  }
1209  if ( type == 0 && dof_to_quad == true && nn%2==1 && mm%2==1 )
1210  {
1211  if (add==false)
1212  out[stride*n_cols] = in[stride*mid];
1213  else
1214  out[stride*n_cols] += in[stride*mid];
1215  }
1216  else if (dof_to_quad == true && nn%2==1)
1217  {
1218  Number r0;
1219  if (mid > 0)
1220  {
1221  r0 = shapes[n_cols] * xp[0];
1222  for (int ind=1; ind<mid; ++ind)
1223  r0 += shapes[ind*offset+n_cols] * xp[ind];
1224  }
1225  else
1226  r0 = Number();
1227  if (type != 1 && mm % 2 == 1)
1228  r0 += shapes[mid*offset+n_cols] * in[stride*mid];
1229 
1230  if (add == false)
1231  out[stride*n_cols] = r0;
1232  else
1233  out[stride*n_cols] += r0;
1234  }
1235  else if (dof_to_quad == false && nn%2 == 1)
1236  {
1237  Number r0;
1238  if (mid > 0)
1239  {
1240  if (type == 1)
1241  {
1242  r0 = shapes[n_cols*offset] * xm[0];
1243  for (int ind=1; ind<mid; ++ind)
1244  r0 += shapes[n_cols*offset+ind] * xm[ind];
1245  }
1246  else
1247  {
1248  r0 = shapes[n_cols*offset] * xp[0];
1249  for (int ind=1; ind<mid; ++ind)
1250  r0 += shapes[n_cols*offset+ind] * xp[ind];
1251  }
1252  }
1253  else
1254  r0 = Number();
1255 
1256  if (type == 0 && mm % 2 == 1)
1257  r0 += in[stride*mid];
1258  else if (type == 2 && mm % 2 == 1)
1259  r0 += shapes[n_cols*offset+mid] * in[stride*mid];
1260 
1261  if (add == false)
1262  out[stride*n_cols] = r0;
1263  else
1264  out[stride*n_cols] += r0;
1265  }
1266 
1267  // increment: in regular case, just go to the next point in
1268  // x-direction. If we are at the end of one chunk in x-dir, need to
1269  // jump over to the next layer in z-direction
1270  switch (direction)
1271  {
1272  case 0:
1273  in += mm;
1274  out += nn;
1275  break;
1276  case 1:
1277  case 2:
1278  ++in;
1279  ++out;
1280  break;
1281  default:
1282  Assert (false, ExcNotImplemented());
1283  }
1284  }
1285  if (direction == 1)
1286  {
1287  in += nn*(mm-1);
1288  out += nn*(nn-1);
1289  }
1290  }
1291  }
1292 
1293 } // end of namespace internal
1294 
1295 
1296 DEAL_II_NAMESPACE_CLOSE
1297 
1298 #endif
static const unsigned int invalid_unsigned_int
Definition: types.h:170
EvaluatorTensorProduct(const AlignedVector< Number > &shape_values, const AlignedVector< Number > &shape_gradients, const AlignedVector< Number > &shape_hessians, const unsigned int dummy1=0, const unsigned int dummy2=0)
EvaluatorTensorProduct(const AlignedVector< Number > &shape_values, const AlignedVector< Number > &shape_gradients, const AlignedVector< Number > &shape_hessians, const unsigned int dummy1=0, const unsigned int dummy2=0)
#define AssertIndexRange(index, range)
Definition: exceptions.h:1170
EvaluatorTensorProduct(const AlignedVector< Number > &shape_values, const AlignedVector< Number > &shape_gradients, const AlignedVector< Number > &shape_hessians, const unsigned int fe_degree, const unsigned int n_q_points_1d)
#define Assert(cond, exc)
Definition: exceptions.h:313
EvaluatorTensorProduct(const AlignedVector< Number > &shape_values, const AlignedVector< Number > &shape_gradients, const AlignedVector< Number > &shape_hessians, const unsigned int dummy1=0, const unsigned int dummy2=0)
static ::ExceptionBase & ExcNotImplemented()