Panzer  Version of the Day
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
Panzer_IntegrationValues2.cpp
Go to the documentation of this file.
1 // @HEADER
2 // *****************************************************************************
3 // Panzer: A partial differential equation assembly
4 // engine for strongly coupled complex multiphysics systems
5 //
6 // Copyright 2011 NTESS and the Panzer contributors.
7 // SPDX-License-Identifier: BSD-3-Clause
8 // *****************************************************************************
9 // @HEADER
10 
12 #include "Panzer_UtilityAlgs.hpp"
13 
14 #include "Shards_CellTopology.hpp"
15 
16 #include "Kokkos_DynRankView.hpp"
17 #include "Intrepid2_FunctionSpaceTools.hpp"
18 #include "Intrepid2_RealSpaceTools.hpp"
19 #include "Intrepid2_CellTools.hpp"
20 #include "Intrepid2_ArrayTools.hpp"
21 #include "Intrepid2_CubatureControlVolume.hpp"
22 #include "Intrepid2_CubatureControlVolumeSide.hpp"
23 #include "Intrepid2_CubatureControlVolumeBoundary.hpp"
24 
26 #include "Panzer_Traits.hpp"
29 
30 // FIXME: There are some calls in Intrepid2 that require non-const arrays when they should be const - search for PHX::getNonConstDynRankViewFromConstMDField
31 #include "Phalanx_GetNonConstDynRankViewFromConstMDField.hpp"
32 
33 namespace panzer {
34 
35 namespace {
36 
38 getIntrepidCubature(const panzer::IntegrationRule & ir)
39 {
42 
43  Intrepid2::DefaultCubatureFactory cubature_factory;
44 
45  if(ir.getType() == ID::CV_SIDE){
46  ic = Teuchos::rcp(new Intrepid2::CubatureControlVolumeSide<PHX::Device::execution_space,double,double>(*ir.topology));
47  } else if(ir.getType() == ID::CV_VOLUME){
48  ic = Teuchos::rcp(new Intrepid2::CubatureControlVolume<PHX::Device::execution_space,double,double>(*ir.topology));
49  } else if(ir.getType() == ID::CV_BOUNDARY){
50  TEUCHOS_ASSERT(ir.isSide());
51  ic = Teuchos::rcp(new Intrepid2::CubatureControlVolumeBoundary<PHX::Device::execution_space,double,double>(*ir.topology,ir.getSide()));
52  } else if(ir.getType() == ID::VOLUME){
53  ic = cubature_factory.create<PHX::Device::execution_space,double,double>(*(ir.topology),ir.getOrder());
54  } else if(ir.getType() == ID::SIDE){
55  ic = cubature_factory.create<PHX::Device::execution_space,double,double>(*(ir.side_topology),ir.getOrder());
56  } else if(ir.getType() == ID::SURFACE){
57  // closed surface integrals don't exist in intrepid.
58  } else {
59  TEUCHOS_ASSERT(false);
60  }
61 
62  return ic;
63 }
64 
65 template<typename Scalar>
66 void
67 correctVirtualNormals(PHX::MDField<Scalar,Cell,IP,Dim> normals,
68  const int num_real_cells,
69  const int num_virtual_cells,
70  const shards::CellTopology & cell_topology,
71  const SubcellConnectivity & face_connectivity)
72 {
73  PANZER_FUNC_TIME_MONITOR_DIFF("panzer::integrationValues2::correctVirtualNormals()",corr_virt_norms);
74 
75  // What we want is for the adjoining face of the virtual cell to have normals that are the negated real cell's normals.
76  // we correct the normals here:
77  const int space_dim = cell_topology.getDimension();
78  const int faces_per_cell = cell_topology.getSubcellCount(space_dim-1);
79  const int points = normals.extent_int(1);
80  const int points_per_face = points / faces_per_cell;
81 
82  Kokkos::parallel_for(num_virtual_cells, KOKKOS_LAMBDA(const int & virtual_cell_ordinal){
83 
84  const int virtual_cell = virtual_cell_ordinal+num_real_cells;
85 
86  // Find the face and local face ids for the given virtual cell
87  // Note that virtual cells only connect to the owned cells through a single face
88  int face, virtual_lidx;
89  for (int local_face_id=0; local_face_id<faces_per_cell; local_face_id++){
90  // Faces that exist have positive indexes
91  face = face_connectivity.subcellForCell(virtual_cell, local_face_id);
92  if (face >= 0){
93  virtual_lidx = local_face_id;
94  break;
95  }
96  }
97 
98  // Indexes for real cell
99  const int real_side = (face_connectivity.cellForSubcell(face, 0) == virtual_cell) ? 1 : 0;
100  const int real_cell = face_connectivity.cellForSubcell(face,real_side);
101  const int real_lidx = face_connectivity.localSubcellForSubcell(face,real_side);
102 
103  // Make sure it is a real cell (it should actually be an owned cell)
104  KOKKOS_ASSERT(real_cell < num_real_cells);
105 
106  for(int point_ordinal=0; point_ordinal<points_per_face; point_ordinal++){
107 
108  // Point indexes for virtual and real point on face
109  const int virtual_cell_point = points_per_face * virtual_lidx + point_ordinal;
110  const int real_cell_point = points_per_face * real_lidx + point_ordinal;
111 
112  for (int d=0; d<space_dim; d++)
113  normals(virtual_cell,virtual_cell_point,d) = -normals(real_cell,real_cell_point,d);
114 
115  }
116 
117  // Clear other normals
118  for (int local_face_id=0; local_face_id<faces_per_cell; local_face_id++){
119 
120  if (local_face_id == virtual_lidx) continue;
121 
122  for (int point_ordinal=0; point_ordinal<points_per_face; point_ordinal++){
123  const int point = local_face_id * points_per_face + point_ordinal;
124  for (int dim=0; dim<space_dim; dim++)
125  normals(virtual_cell,point,dim) = 0.0;
126  }
127  }
128  });
129  PHX::Device::execution_space().fence();
130 }
131 
132 
133 template<typename Scalar>
134 void
135 correctVirtualRotationMatrices(PHX::MDField<Scalar,Cell,IP,Dim,Dim> rotation_matrices,
136  const int num_real_cells,
137  const int num_virtual_cells,
138  const shards::CellTopology & cell_topology,
139  const SubcellConnectivity & face_connectivity)
140 {
141  PANZER_FUNC_TIME_MONITOR_DIFF("panzer::integrationValues2::correctVirtualRotationMatrices()",corr_virt_rotmat);
142 
143  // What we want is for the adjoining face of the virtual cell to have normals that are the negated real cell's normals.
144  // we correct the normals here:
145  const int space_dim = cell_topology.getDimension();
146  const int faces_per_cell = cell_topology.getSubcellCount(space_dim-1);
147  const int points = rotation_matrices.extent_int(1);
148  const int points_per_face = points / faces_per_cell;
149 
150  Kokkos::parallel_for(num_virtual_cells, KOKKOS_LAMBDA(const int & virtual_cell_ordinal){
151 
152  const int virtual_cell = virtual_cell_ordinal+num_real_cells;
153 
154  // Find the face and local face ids for the given virtual cell
155  // Note that virtual cells only connect to the owned cells through a single face
156  int face, virtual_lidx;
157  for (int local_face_id=0; local_face_id<faces_per_cell; local_face_id++){
158  // Faces that exist have positive indexes
159  face = face_connectivity.subcellForCell(virtual_cell, local_face_id);
160  if (face >= 0){
161  virtual_lidx = local_face_id;
162  break;
163  }
164  }
165 
166  // The normals already have the correction applied, so we just need to zero out the rotation matrices on the other faces
167 
168  // Clear other rotation matrices
169  for (int local_face_id=0; local_face_id<faces_per_cell; local_face_id++){
170 
171  if (local_face_id == virtual_lidx) continue;
172 
173  for (int point_ordinal=0; point_ordinal<points_per_face; point_ordinal++){
174  const int point = local_face_id * points_per_face + point_ordinal;
175  for (int dim=0; dim<3; dim++)
176  for (int dim2=0; dim2<3; dim2++)
177  rotation_matrices(virtual_cell,point,dim,dim2) = 0.0;
178  }
179  }
180  });
181  PHX::Device::execution_space().fence();
182 }
183 
184 template<typename Scalar>
185 void
186 applyBasePermutation(PHX::MDField<Scalar,IP> field,
187  PHX::MDField<const int,Cell,IP> permutations)
188 {
189  PANZER_FUNC_TIME_MONITOR_DIFF("panzer::integrationValues2::applyBasePermutation(rank 1)",app_base_perm_r1);
190  MDFieldArrayFactory af("",true);
191 
192  const int num_ip = field.extent(0);
193 
194  auto scratch = af.template buildStaticArray<Scalar,IP>("scratch", num_ip);
195  scratch.deep_copy(field);
196  Kokkos::parallel_for(1, KOKKOS_LAMBDA(const int & ){
197  for(int ip=0; ip<num_ip; ++ip)
198  if (ip != permutations(0,ip))
199  field(ip) = scratch(permutations(0,ip));
200  });
201  PHX::Device::execution_space().fence();
202 }
203 
204 template<typename Scalar>
205 void
206 applyBasePermutation(PHX::MDField<Scalar,IP,Dim> field,
207  PHX::MDField<const int,Cell,IP> permutations)
208 {
209  PANZER_FUNC_TIME_MONITOR_DIFF("panzer::integrationValues2::applyBasePermutation(rank 2)",app_base_perm_r2);
210  MDFieldArrayFactory af("",true);
211 
212  const int num_ip = field.extent(0);
213  const int num_dim = field.extent(1);
214 
215  auto scratch = af.template buildStaticArray<Scalar,IP,Dim>("scratch", num_ip,num_dim);
216  scratch.deep_copy(field);
217  Kokkos::parallel_for(1, KOKKOS_LAMBDA(const int & ){
218  for(int ip=0; ip<num_ip; ++ip)
219  if (ip != permutations(0,ip))
220  for(int dim=0; dim<num_dim; ++dim)
221  field(ip,dim) = scratch(permutations(0,ip),dim);
222  });
223  PHX::Device::execution_space().fence();
224 }
225 
226 template<typename Scalar>
227 void
228 applyPermutation(PHX::MDField<Scalar,Cell,IP> field,
229  PHX::MDField<const int,Cell,IP> permutations)
230 {
231  PANZER_FUNC_TIME_MONITOR_DIFF("panzer::integrationValues2::applyPermutation(rank 2)",app_perm_r2);
232  MDFieldArrayFactory af("",true);
233 
234  const int num_cells = field.extent(0);
235  const int num_ip = field.extent(1);
236 
237  auto scratch = af.template buildStaticArray<Scalar,Cell,IP>("scratch", num_cells, num_ip);
238  scratch.deep_copy(field);
239  Kokkos::parallel_for(num_cells, KOKKOS_LAMBDA(const int & cell){
240  for(int ip=0; ip<num_ip; ++ip)
241  if (ip != permutations(cell,ip))
242  field(cell,ip) = scratch(cell,permutations(cell,ip));
243  });
244  PHX::Device::execution_space().fence();
245 }
246 
247 template<typename Scalar>
248 void
249 applyPermutation(PHX::MDField<Scalar,Cell,IP,Dim> field,
250  PHX::MDField<const int,Cell,IP> permutations)
251 {
252  PANZER_FUNC_TIME_MONITOR_DIFF("panzer::integrationValues2::applyPermutation(rank 3)",app_perm_r3);
253  MDFieldArrayFactory af("",true);
254 
255  const int num_cells = field.extent(0);
256  const int num_ip = field.extent(1);
257  const int num_dim = field.extent(2);
258 
259  auto scratch = af.template buildStaticArray<Scalar,Cell,IP,Dim>("scratch", num_cells, num_ip, num_dim);
260  scratch.deep_copy(field);
261  Kokkos::parallel_for(num_cells, KOKKOS_LAMBDA(const int & cell){
262  for(int ip=0; ip<num_ip; ++ip)
263  if (ip != permutations(cell,ip))
264  for(int dim=0; dim<num_dim; ++dim)
265  field(cell,ip,dim) = scratch(cell,permutations(cell,ip),dim);
266  });
267  PHX::Device::execution_space().fence();
268 }
269 
270 template<typename Scalar>
271 void
272 applyPermutation(PHX::MDField<Scalar,Cell,IP,Dim,Dim> field,
273  PHX::MDField<const int,Cell,IP> permutations)
274 {
275  PANZER_FUNC_TIME_MONITOR_DIFF("panzer::integrationValues2::applyPermutation(rank 4)",app_perm_r4);
276  MDFieldArrayFactory af("",true);
277 
278  const int num_cells = field.extent(0);
279  const int num_ip = field.extent(1);
280  const int num_dim = field.extent(2);
281  const int num_dim2 = field.extent(3);
282 
283  auto scratch = af.template buildStaticArray<Scalar,Cell,IP,Dim,Dim>("scratch", num_cells, num_ip, num_dim, num_dim2);
284  scratch.deep_copy(field);
285  Kokkos::parallel_for(num_cells, KOKKOS_LAMBDA(const int & cell){
286  for(int ip=0; ip<num_ip; ++ip)
287  if (ip != permutations(cell,ip))
288  for(int dim=0; dim<num_dim; ++dim)
289  for(int dim2=0; dim2<num_dim2; ++dim2)
290  field(cell,ip,dim,dim2) = scratch(cell,permutations(cell,ip),dim,dim2);
291  });
292  PHX::Device::execution_space().fence();
293 }
294 
295 
296 // Find the permutation that maps the set of points coords to other_coords. To
297 // avoid possible finite precision issues, == is not used, but rather
298 // min(norm(.)).
299 template <typename Scalar>
301 generatePermutations(const int num_cells,
304 {
305  PANZER_FUNC_TIME_MONITOR_DIFF("panzer::integrationValues2::generatePermutations()",gen_perms);
306 
307  const int num_ip = coords.extent(1);
308  const int num_dim = coords.extent(2);
309 
310  MDFieldArrayFactory af("",true);
311 
312  // This will store the permutations to go from coords to other_coords
313  auto permutation = af.template buildStaticArray<int,Cell,IP>("permutation", num_cells, num_ip);
314  permutation.deep_copy(0);
315 
316  // This is scratch space - there is likely a better way to do this
317  auto taken = af.template buildStaticArray<int,Cell,IP>("taken", num_cells, num_ip);
318  taken.deep_copy(0);
319 
320  Kokkos::parallel_for(num_cells, KOKKOS_LAMBDA(const int & cell){
321 
322  for (int ip = 0; ip < num_ip; ++ip) {
323  // Find an other point to associate with ip.
324  int i_min = 0;
325  Scalar d_min = -1;
326  for (int other_ip = 0; other_ip < num_ip; ++other_ip) {
327  // For speed, skip other points that are already associated.
328  if(taken(cell,other_ip)) continue;
329  // Compute the distance between the two points.
330  Scalar d(0);
331  for (int dim = 0; dim < num_dim; ++dim) {
332  const Scalar diff = coords(cell, ip, dim) - other_coords(cell, other_ip, dim);
333  d += diff*diff;
334  }
335  if (d_min < 0 || d < d_min) {
336  d_min = d;
337  i_min = other_ip;
338  }
339  }
340  // Record the match.
341  permutation(cell,ip) = i_min;
342  // This point is now taken.
343  taken(cell,i_min) = 1;
344  }
345  });
346  PHX::Device::execution_space().fence();
347 
348  return permutation;
349 
350 }
351 
352 template <typename Scalar>
354 generateSurfacePermutations(const int num_cells,
355  const SubcellConnectivity face_connectivity,
357  PHX::MDField<const Scalar,Cell,IP,Dim,Dim> surface_rotation_matrices)
358 
359 {
360  PANZER_FUNC_TIME_MONITOR_DIFF("panzer::integrationValues2::generateSurfacePermutations()",gen_surf_perms);
361 
362  // The challenge for this call is handling wedge-based periodic boundaries
363  // We need to make sure that we can align points along faces that are rotated with respect to one another.
364  // Below we will see an algorithm that rotates two boundaries into a shared frame, then figures out
365  // how to permute the points on one face to line up with the other.
366 
367  const int num_points = surface_points.extent_int(1);
368  const int num_faces_per_cell = face_connectivity.numSubcellsOnCellHost(0);
369  const int num_points_per_face = num_points / num_faces_per_cell;
370  const int cell_dim = surface_points.extent(2);
371 
372  MDFieldArrayFactory af("",true);
373 
374  // This will store the permutations
375  auto permutation = af.template buildStaticArray<int,Cell,IP>("permutation", num_cells, num_points);
376  permutation.deep_copy(0);
377 
378  // Fill permutations with trivial values (i.e. no permutation - this will get overwritten for some cells)
379  Kokkos::parallel_for(num_cells,KOKKOS_LAMBDA (const int & cell) {
380  for(int point=0; point<num_points; ++point)
381  permutation(cell,point) = point;
382  });
383 
384  // Now we need to align the cubature points for each face
385  // If there is only one point there is no need to align things
386  if(num_points_per_face != 1) {
387  // To enforce that quadrature points on faces are aligned properly we will iterate through faces,
388  // map points to a plane shared by the faces, then re-order quadrature points on the "1" face to
389  // line up with the "0" face
390 
391  // Utility calls
392 #define PANZER_DOT(a,b) (a[0]*b[0] + a[1]*b[1] + a[2]*b[2])
393 #define PANZER_CROSS(a,b,c) {c[0] = a[1]*b[2] - a[2]*b[1]; c[1] = a[2]*b[0] - a[0]*b[2]; c[2] = a[0]*b[1] - a[1]*b[0];}
394 
395  // Iterate through faces
396  Kokkos::parallel_for("face iteration",face_connectivity.numSubcells(),KOKKOS_LAMBDA (const int face) {
397  // Cells for sides 0 and 1
398  const int cell_0 = face_connectivity.cellForSubcell(face,0);
399  const int cell_1 = face_connectivity.cellForSubcell(face,1);
400 
401  // If this face doesn't connect to anything we don't need to worry about alignment
402  if(cell_1 < 0)
403  return;
404 
405  // Local face index for sides 0 and 1
406  const int lidx_0 = face_connectivity.localSubcellForSubcell(face,0);
407  const int lidx_1 = face_connectivity.localSubcellForSubcell(face,1);
408 
409  // If the cell exists, then the local face index needs to exist
410  KOKKOS_ASSERT(lidx_1 >= 0);
411 
412  // To compare points on planes, it is good to center the planes around the origin
413  // Find the face center for the left and right cell (may not be the same point - e.g. periodic conditions)
414  // We also define a length scale r2 which gives an order of magnitude approximation to the cell size squared
415  Scalar xc0[3] = {0}, xc1[3] = {0}, r2 = 0;
416  for(int face_point=0; face_point<num_points_per_face; ++face_point){
417  Scalar dx2 = 0.;
418  for(int dim=0; dim<cell_dim; ++dim){
419  xc0[dim] += surface_points(cell_0,lidx_0*num_points_per_face + face_point,dim);
420  xc1[dim] += surface_points(cell_1,lidx_1*num_points_per_face + face_point,dim);
421  const Scalar dx = surface_points(cell_0,lidx_0*num_points_per_face + face_point,dim) - surface_points(cell_0,lidx_0*num_points_per_face,dim);
422  dx2 += dx*dx;
423  }
424 
425  // Check if the distance squared between these two points is larger than the others (doesn't need to be perfect)
426  r2 = (r2 < dx2) ? dx2 : r2;
427  }
428  for(int dim=0; dim<cell_dim; ++dim){
429  xc0[dim] /= double(num_points_per_face);
430  xc1[dim] /= double(num_points_per_face);
431  }
432 
433  // TODO: This needs to be adaptable to having curved faces - for now this will work
434 
435  // We need to define a plane with two vectors (transverse and binormal)
436  // These will be used with the face centers to construct a local reference frame for aligning points
437 
438  // We use the first point on the face to define the normal (may break for curved faces)
439  const int example_point_0 = lidx_0*num_points_per_face;
440  const int example_point_1 = lidx_1*num_points_per_face;
441 
442  // Load the transverse and binormal for the 0 cell (default)
443  Scalar t[3] = {surface_rotation_matrices(cell_0,example_point_0,1,0), surface_rotation_matrices(cell_0,example_point_0,1,1), surface_rotation_matrices(cell_0,example_point_0,1,2)};
444  Scalar b[3] = {surface_rotation_matrices(cell_0,example_point_0,2,0), surface_rotation_matrices(cell_0,example_point_0,2,1), surface_rotation_matrices(cell_0,example_point_0,2,2)};
445 
446  // In case the faces are not antiparallel (e.g. periodic wedge), we may need to change the transverse and binormal
447  {
448 
449  // Get the normals for each face for constructing one of the plane vectors (transverse)
450  const Scalar n0[3] = {surface_rotation_matrices(cell_0,example_point_0,0,0), surface_rotation_matrices(cell_0,example_point_0,0,1), surface_rotation_matrices(cell_0,example_point_0,0,2)};
451  const Scalar n1[3] = {surface_rotation_matrices(cell_1,example_point_1,0,0), surface_rotation_matrices(cell_1,example_point_1,0,1), surface_rotation_matrices(cell_1,example_point_1,0,2)};
452 
453  // n_0*n_1 == -1 (antiparallel), n_0*n_1 == 1 (parallel - bad), |n_0*n_1| < 1 (other)
454  const Scalar n0_dot_n1 = PANZER_DOT(n0,n1);
455 
456  // FIXME: Currently virtual cells will set their surface normal along the same direction as the cell they "reflect"
457  // This causes a host of issues (e.g. identifying 180 degree periodic wedges), but we have to support virtual cells as a priority
458  // Therefore, we will just assume that the ordering is fine (not valid for 180 degree periodic wedges)
459  if(Kokkos::fabs(n0_dot_n1 - 1.) < 1.e-8)
460  return;
461 
462  // Rotated faces case (e.g. periodic wedge) we need to check for non-antiparallel face normals
463  if(Kokkos::fabs(n0_dot_n1 + 1.) > 1.e-2){
464 
465  // Now we need to define an arbitrary transverse and binormal in the plane across which the faces are anti-symmetric
466  // We can do this by setting t = n_0 \times n_1
467  PANZER_CROSS(n0,n1,t);
468 
469  // Normalize the transverse vector
470  const Scalar mag_t = Kokkos::sqrt(PANZER_DOT(t,t));
471  t[0] /= mag_t;
472  t[1] /= mag_t;
473  t[2] /= mag_t;
474 
475  // The binormal will be the sum of the normals (does not need to be a right handed system)
476  b[0] = n0[0] + n1[0];
477  b[1] = n0[1] + n1[1];
478  b[2] = n0[2] + n1[2];
479 
480  // Normalize the binormal vector
481  const Scalar mag_b = Kokkos::sqrt(PANZER_DOT(b,b));
482  b[0] /= mag_b;
483  b[1] /= mag_b;
484  b[2] /= mag_b;
485 
486  }
487  }
488 
489  // Now that we have a reference coordinate plane in which to align our points
490  // Point on the transverse/binormal plane
491  Scalar p0[2] = {0};
492  Scalar p1[2] = {0};
493 
494  // Differential position in mesh
495  Scalar x0[3] = {0};
496  Scalar x1[3] = {0};
497 
498  // Iterate through points in cell 1 and find which point they align with in cell 0
499  for(int face_point_1=0; face_point_1<num_points_per_face; ++face_point_1){
500 
501  // Get the point index in the 1 cell
502  const int point_1 = lidx_1*num_points_per_face + face_point_1;
503 
504  // Load position shifted by face center
505  for(int dim=0; dim<cell_dim; ++dim)
506  x1[dim] = surface_points(cell_1,point_1,dim) - xc1[dim];
507 
508  // Convert position to transverse/binormal plane
509  p1[0] = PANZER_DOT(x1,t);
510  p1[1] = PANZER_DOT(x1,b);
511 
512  // Compare to points on the other surface
513  for(int face_point_0=0; face_point_0<num_points_per_face; ++face_point_0){
514 
515  // Get the point index in the 0 cell
516  const int point_0 = lidx_0*num_points_per_face + face_point_0;
517 
518  // Load position shifted by face center
519  for(int dim=0; dim<cell_dim; ++dim)
520  x0[dim] = surface_points(cell_0,point_0,dim) - xc0[dim];
521 
522  // Convert position to transverse/binormal plane
523  p0[0] = PANZER_DOT(x0,t);
524  p0[1] = PANZER_DOT(x0,b);
525 
526  // Find the distance squared between p0 and p1
527  const Scalar p012 = (p0[0]-p1[0])*(p0[0]-p1[0]) + (p0[1]-p1[1])*(p0[1]-p1[1]);
528 
529  // TODO: Should this be a constant value, or should we just find the minimum point?
530  // If the distance, compared to the size of the cell, is small, we assume these are the same points
531  if(p012 / r2 < 1.e-12){
532  permutation(cell_1,lidx_1*num_points_per_face+face_point_0) = point_1;
533  break;
534  }
535 
536  }
537  }
538  });
539  PHX::Device::execution_space().fence();
540  }
541 
542 #undef PANZER_DOT
543 #undef PANZER_CROSS
544 
545  return permutation;
546 }
547 
548 } // end anonymous namespace
549 
550 //template<typename DataType>
551 //using UnmanagedDynRankView = Kokkos::DynRankView<DataType,typename PHX::DevLayout<DataType>::type,PHX::Device,Kokkos::MemoryTraits<Kokkos::Unmanaged>>;
552 
553 template <typename Scalar>
555 IntegrationValues2(const std::string & pre,
556  const bool allocArrays):
557  num_cells_(0),
558  num_evaluate_cells_(0),
559  num_virtual_cells_(-1),
560  requires_permutation_(false),
561  alloc_arrays_(allocArrays),
562  prefix_(pre),
563  ddims_(1,0)
564 {
565  resetArrays();
566 }
567 
568 template <typename Scalar>
569 void
572 {
573  cub_points_evaluated_ = false;
574  side_cub_points_evaluated_ = false;
575  cub_weights_evaluated_ = false;
576  node_coordinates_evaluated_ = false;
577  jac_evaluated_ = false;
578  jac_inv_evaluated_ = false;
579  jac_det_evaluated_ = false;
580  weighted_measure_evaluated_ = false;
581  weighted_normals_evaluated_ = false;
582  surface_normals_evaluated_ = false;
583  surface_rotation_matrices_evaluated_ = false;
584  covarient_evaluated_ = false;
585  contravarient_evaluated_ = false;
586  norm_contravarient_evaluated_ = false;
587  ip_coordinates_evaluated_ = false;
588  ref_ip_coordinates_evaluated_ = false;
589 
590  // TODO: We need to clear the views
591 }
592 
593 template <typename Scalar>
596 {
597  PANZER_FUNC_TIME_MONITOR_DIFF("panzer::integrationValues2::setupArrays()",setup_arrays);
598 
599  MDFieldArrayFactory af(prefix_,alloc_arrays_);
600 
602 
603  int_rule = ir;
604 
605  const int num_nodes = ir->topology->getNodeCount();
606  const int num_cells = ir->workset_size;
607  const int num_space_dim = ir->topology->getDimension();
608 
609  // Specialize content if this is quadrature at a node
610  const bool is_node_rule = (num_space_dim==1) and ir->isSide();
611  if(not is_node_rule) {
612  TEUCHOS_ASSERT(ir->getType() != ID::NONE);
613  intrepid_cubature = getIntrepidCubature(*ir);
614  }
615 
616  const int num_ip = is_node_rule ? 1 : ir->num_points;
617 
618  cub_points = af.template buildStaticArray<Scalar,IP,Dim>("cub_points",num_ip, num_space_dim);
619 
620  if (ir->isSide() && ir->cv_type == "none")
621  side_cub_points = af.template buildStaticArray<Scalar,IP,Dim>("side_cub_points",num_ip,ir->side_topology->getDimension());
622 
623  cub_weights = af.template buildStaticArray<Scalar,IP>("cub_weights",num_ip);
624 
625  node_coordinates = af.template buildStaticArray<Scalar,Cell,BASIS,Dim>("node_coordinates",num_cells, num_nodes, num_space_dim);
626 
627  jac = af.template buildStaticArray<Scalar,Cell,IP,Dim,Dim>("jac",num_cells, num_ip, num_space_dim,num_space_dim);
628 
629  jac_inv = af.template buildStaticArray<Scalar,Cell,IP,Dim,Dim>("jac_inv",num_cells, num_ip, num_space_dim,num_space_dim);
630 
631  jac_det = af.template buildStaticArray<Scalar,Cell,IP>("jac_det",num_cells, num_ip);
632 
633  weighted_measure = af.template buildStaticArray<Scalar,Cell,IP>("weighted_measure",num_cells, num_ip);
634 
635  covarient = af.template buildStaticArray<Scalar,Cell,IP,Dim,Dim>("covarient",num_cells, num_ip, num_space_dim,num_space_dim);
636 
637  contravarient = af.template buildStaticArray<Scalar,Cell,IP,Dim,Dim>("contravarient",num_cells, num_ip, num_space_dim,num_space_dim);
638 
639  norm_contravarient = af.template buildStaticArray<Scalar,Cell,IP>("norm_contravarient",num_cells, num_ip);
640 
641  ip_coordinates = af.template buildStaticArray<Scalar,Cell,IP,Dim>("ip_coordiantes",num_cells, num_ip,num_space_dim);
642 
643  ref_ip_coordinates = af.template buildStaticArray<Scalar,Cell,IP,Dim>("ref_ip_coordinates",num_cells, num_ip,num_space_dim);
644 
645  weighted_normals = af.template buildStaticArray<Scalar,Cell,IP,Dim>("weighted_normal",num_cells,num_ip,num_space_dim);
646 
647  surface_normals = af.template buildStaticArray<Scalar,Cell,IP,Dim>("surface_normals",num_cells, num_ip,num_space_dim);
648 
649  surface_rotation_matrices = af.template buildStaticArray<Scalar,Cell,IP,Dim,Dim>("surface_rotation_matrices",num_cells, num_ip,3,3);
650 }
651 
652 
653 // ***********************************************************
654 // * Evaluation of values - NOT specialized
655 // ***********************************************************
656 template <typename Scalar>
659  const int in_num_cells,
660  const Teuchos::RCP<const SubcellConnectivity> & face_connectivity,
661  const int num_virtual_cells)
662 {
663  PANZER_FUNC_TIME_MONITOR_DIFF("panzer::integrationValues2::evaluateValues(with virtual cells)",eval_vals_with_virts);
664 
665  setup(int_rule, in_node_coordinates, in_num_cells);
666 
667  // Setup permutations (only required for surface integrators)
669  if((int_rule->getType() == ID::SURFACE) and (face_connectivity != Teuchos::null))
670  setupPermutations(face_connectivity, num_virtual_cells);
671 
672  // Evaluate everything once permutations are generated
673  evaluateEverything();
674 }
675 
676 template <typename Scalar>
677 void
680  const PHX::MDField<Scalar,Cell,IP,Dim>& other_ip_coordinates,
681  const int in_num_cells)
682 {
683  PANZER_FUNC_TIME_MONITOR_DIFF("panzer::integrationValues2::evaluateValues(no virtual cells)",eval_vals_no_virts);
684 
685  setup(int_rule, in_node_coordinates, in_num_cells);
686 
687  // Setup permutations
688  setupPermutations(other_ip_coordinates);
689 
690  // Evaluate everything once permutations are generated
691  evaluateEverything();
692 }
693 
694 template <typename Scalar>
695 void
698  const int num_virtual_cells)
699 {
700  PANZER_FUNC_TIME_MONITOR_DIFF("panzer::integrationValues2::setupPermutations(connectivity)",setup_perms_conn);
701 
702  TEUCHOS_ASSERT(not int_rule->isSide());
703  TEUCHOS_ASSERT(face_connectivity != Teuchos::null);
705  "IntegrationValues2::setupPermutations : Face connectivity based permutations are only required for surface integration schemes");
706  TEUCHOS_TEST_FOR_EXCEPT_MSG(num_virtual_cells_ >= 0,
707  "IntegrationValues2::setupPermutations : Number of virtual cells for surface permuations must be >= 0");
708  resetArrays();
709  side_connectivity_ = face_connectivity;
710  num_virtual_cells_ = num_virtual_cells;
711  requires_permutation_ = false;
712  permutations_ = generateSurfacePermutations<Scalar>(num_evaluate_cells_,*face_connectivity, getCubaturePoints(false,true), getSurfaceRotationMatrices(false,true));
713  requires_permutation_ = true;
714 }
715 
716 template <typename Scalar>
717 void
720 {
721  PANZER_FUNC_TIME_MONITOR_DIFF("panzer::integrationValues2::setupPermutations(other_coords)",setup_perms_coords);
722  resetArrays();
723  requires_permutation_ = false;
724  permutations_ = generatePermutations<Scalar>(num_evaluate_cells_, getCubaturePoints(false,true), other_ip_coordinates);
725  requires_permutation_ = true;
726 }
727 
728 
729 template <typename Scalar>
730 void
733  const PHX::MDField<Scalar,Cell,NODE,Dim> & cell_node_coordinates,
734  const int num_cells)
735 {
736  PANZER_FUNC_TIME_MONITOR_DIFF("panzer::integrationValues2::setup()",setup);
737 
738  // Clear arrays just in case we are rebuilding this object
739  resetArrays();
740 
741  num_cells_ = cell_node_coordinates.extent(0);
742  num_evaluate_cells_ = num_cells < 0 ? cell_node_coordinates.extent(0) : num_cells;
743  int_rule = ir;
744 
746  intrepid_cubature = getIntrepidCubature(*ir);
747 
748  // Copy node coordinates into owned allocation
749  {
750  MDFieldArrayFactory af(prefix_,true);
751 
752  const int num_space_dim = int_rule->topology->getDimension();
753  const int num_nodes = int_rule->topology->getNodeCount();
754  TEUCHOS_ASSERT(static_cast<int>(cell_node_coordinates.extent(1)) == num_nodes);
755 
756  auto aux = af.template buildStaticArray<Scalar,Cell,NODE,Dim>("node_coordinates",num_cells_,num_nodes, num_space_dim);
757  Kokkos::MDRangePolicy<PHX::Device,Kokkos::Rank<3>> policy({0,0,0},{num_evaluate_cells_,num_nodes,num_space_dim});
758  Kokkos::parallel_for(policy,KOKKOS_LAMBDA(const int & cell, const int & point, const int & dim){
759  aux(cell,point,dim) = cell_node_coordinates(cell,point,dim);
760  });
761  PHX::Device::execution_space().fence();
762  node_coordinates = aux;
763  }
764 
765 }
766 
767 template <typename Scalar>
771  const bool force,
772  const bool apply_permutation) const
773 {
774  if(cub_points_evaluated_ and (apply_permutation == requires_permutation_) and not force)
775  return cub_points;
776 
777  // Only log time if values computed (i.e. don't log if values are already cached)
778  PANZER_FUNC_TIME_MONITOR_DIFF("panzer::integrationValues2::getUniformCubaturePointsRef()",get_uniform_cub_pts_ref);
779 
780  Intrepid2::CellTools<PHX::Device::execution_space> cell_tools;
781  MDFieldArrayFactory af(prefix_,true);
782 
783  int num_space_dim = int_rule->topology->getDimension();
784  int num_ip = int_rule->num_points;
785 
786  auto aux = af.template buildStaticArray<Scalar,IP,Dim>("cub_points",num_ip, num_space_dim);
787 
788  if (int_rule->isSide() && num_space_dim==1) {
789  std::cout << "WARNING: 0-D quadrature rule infrastructure does not exist!!! Will not be able to do "
790  << "non-natural integration rules.";
791  return aux;
792  }
793 
794  TEUCHOS_TEST_FOR_EXCEPT_MSG(int_rule->cv_type != "none",
795  "IntegrationValues2::getUniformCubaturePointsRef : Cannot build reference cubature points for control volume integration scheme.");
796 
798  "IntegrationValues2::getUniformCubaturePointsRef : Cannot build reference cubature points for surface integration scheme.");
799 
800  auto weights = af.template buildStaticArray<Scalar,IP>("cub_weights",num_ip);
801 
802  if (!int_rule->isSide())
803  intrepid_cubature->getCubature(aux.get_view(), weights.get_view());
804  else {
805  auto s_cub_points = af.template buildStaticArray<Scalar,IP,Dim>("side_cub_points",num_ip, num_space_dim-1);
806 
807  intrepid_cubature->getCubature(s_cub_points.get_view(), weights.get_view());
808  cell_tools.mapToReferenceSubcell(aux.get_view(), s_cub_points.get_view(), num_space_dim-1, int_rule->getSide(), *(int_rule->topology));
809  }
810 
811  PHX::Device::execution_space().fence();
812 
813  if(apply_permutation and requires_permutation_)
814  applyBasePermutation(aux, permutations_);
815 
816  if(cache and (apply_permutation == requires_permutation_)){
817  cub_points = aux;
818  cub_points_evaluated_ = true;
819  }
820 
821  return aux;
822 
823 }
824 
825 template <typename Scalar>
829  const bool force,
830  const bool apply_permutation) const
831 {
832  if(side_cub_points_evaluated_ and (apply_permutation == requires_permutation_) and not force)
833  return side_cub_points;
834 
835  // Only log time if values computed (i.e. don't log if values are already cached)
836  PANZER_FUNC_TIME_MONITOR_DIFF("panzer::integrationValues2::getUniformSideCubaturePointsRef()",get_uniform_side_cub_pts_ref);
837 
838  MDFieldArrayFactory af(prefix_,true);
839 
840  int num_space_dim = int_rule->topology->getDimension();
841  int num_ip = int_rule->num_points;
842 
843  auto aux = af.template buildStaticArray<Scalar,IP,Dim>("side_cub_points",num_ip, num_space_dim-1);
844 
845  if (int_rule->isSide() && num_space_dim==1) {
846  std::cout << "WARNING: 0-D quadrature rule infrastructure does not exist!!! Will not be able to do "
847  << "non-natural integration rules.";
848  return aux;
849  }
850 
851  TEUCHOS_TEST_FOR_EXCEPT_MSG(int_rule->cv_type != "none",
852  "IntegrationValues2::getUniformSideCubaturePointsRef : Cannot build reference cubature points for control volume integration scheme.");
853 
855  "IntegrationValues2::getUniformSideCubaturePointsRef : Cannot build reference cubature points for surface integration scheme.");
856 
857  TEUCHOS_TEST_FOR_EXCEPT_MSG(!int_rule->isSide(),
858  "IntegrationValues2::getUniformSideCubaturePointsRef : Requested side points, which is not supported by integration rule.");
859 
860  auto weights = af.template buildStaticArray<Scalar,IP>("cub_weights",num_ip);
861 
862  intrepid_cubature->getCubature(aux.get_view(), weights.get_view());
863 
864  PHX::Device::execution_space().fence();
865 
866  if(apply_permutation and requires_permutation_)
867  applyBasePermutation(aux, permutations_);
868 
869  if(cache and (apply_permutation == requires_permutation_)){
870  side_cub_points = aux;
871  side_cub_points_evaluated_ = true;
872  }
873 
874  return aux;
875 }
876 
877 template <typename Scalar>
881  const bool force,
882  const bool apply_permutation) const
883 {
884  if(cub_weights_evaluated_ and (apply_permutation == requires_permutation_) and not force)
885  return cub_weights;
886 
887  // Only log time if values computed (i.e. don't log if values are already cached)
888  PANZER_FUNC_TIME_MONITOR_DIFF("panzer::integrationValues2::getUniformCubatureWeightRef()",get_uniform_cub_weights_ref);
889 
890  MDFieldArrayFactory af(prefix_,true);
891 
892  int num_space_dim = int_rule->topology->getDimension();
893  int num_ip = int_rule->num_points;
894 
895  auto aux = af.template buildStaticArray<Scalar,IP>("cub_weights",num_ip);
896 
897  if (int_rule->isSide() && num_space_dim==1) {
898  std::cout << "WARNING: 0-D quadrature rule infrastructure does not exist!!! Will not be able to do "
899  << "non-natural integration rules.";
900  return aux;
901  }
902 
903  TEUCHOS_TEST_FOR_EXCEPT_MSG(int_rule->cv_type != "none",
904  "IntegrationValues2::getUniformCubatureWeightsRef : Cannot build reference cubature weights for control volume integration scheme.");
905 
907  "IntegrationValues2::getUniformCubatureWeightsRef : Cannot build reference cubature weights for surface integration scheme.");
908 
909  auto points = af.template buildStaticArray<Scalar,IP,Dim>("cub_points",num_ip,num_space_dim);
910 
911  intrepid_cubature->getCubature(points.get_view(), aux.get_view());
912 
913  PHX::Device::execution_space().fence();
914 
915  if(apply_permutation and requires_permutation_)
916  applyBasePermutation(aux, permutations_);
917 
918  if(cache and (apply_permutation == requires_permutation_)){
919  cub_weights = aux;
920  cub_weights_evaluated_ = true;
921  }
922 
923  return aux;
924 
925 }
926 
927 template <typename Scalar>
931 {
932  return node_coordinates;
933 }
934 
935 template <typename Scalar>
938 getJacobian(const bool cache,
939  const bool force) const
940 {
941  if(jac_evaluated_ and not force)
942  return jac;
943 
944  // Only log time if values computed (i.e. don't log if values are already cached)
945  PANZER_FUNC_TIME_MONITOR_DIFF("panzer::integrationValues2::getJacobian()",get_jacobian);
946 
947  Intrepid2::CellTools<PHX::Device::execution_space> cell_tools;
948  MDFieldArrayFactory af(prefix_,true);
949 
950  int num_space_dim = int_rule->topology->getDimension();
951  int num_ip = int_rule->num_points;
952 
954  const bool is_cv = (int_rule->getType() == ID::CV_VOLUME) or (int_rule->getType() == ID::CV_SIDE) or (int_rule->getType() == ID::CV_BOUNDARY);
955  const bool is_surface = int_rule->getType() == ID::SURFACE;
956 
957  auto aux = af.template buildStaticArray<Scalar,Cell,IP,Dim,Dim>("jac",num_cells_, num_ip, num_space_dim,num_space_dim);
958 
959  if(is_cv or is_surface){
960 
961  // Don't forget that since we are not caching this, we have to make sure the managed view remains alive while we use the non-const wrapper
962  auto const_ref_coord = getCubaturePointsRef(false,force);
963  auto ref_coord = PHX::getNonConstDynRankViewFromConstMDField(const_ref_coord);
964  auto node_coord = PHX::getNonConstDynRankViewFromConstMDField(getNodeCoordinates());
965 
966  const auto cell_range = std::make_pair(0,num_evaluate_cells_);
967  auto s_ref_coord = Kokkos::subview(ref_coord, cell_range,Kokkos::ALL(),Kokkos::ALL());
968  auto s_node_coord = Kokkos::subview(node_coord, cell_range,Kokkos::ALL(),Kokkos::ALL());
969  auto s_jac = Kokkos::subview(aux.get_view(),cell_range,Kokkos::ALL(),Kokkos::ALL(),Kokkos::ALL());
970 
971  cell_tools.setJacobian(s_jac, s_ref_coord, s_node_coord,*(int_rule->topology));
972 
973  } else {
974 
975  // Don't forget that since we are not caching this, we have to make sure the managed view remains alive while we use the non-const wrapper
976  auto const_ref_coord = getUniformCubaturePointsRef(false,force,false);
977  auto ref_coord = PHX::getNonConstDynRankViewFromConstMDField(const_ref_coord);
978  auto node_coord = PHX::getNonConstDynRankViewFromConstMDField(getNodeCoordinates());
979 
980  const auto cell_range = std::make_pair(0,num_evaluate_cells_);
981  auto s_node_coord = Kokkos::subview(node_coord, cell_range,Kokkos::ALL(),Kokkos::ALL());
982  auto s_jac = Kokkos::subview(aux.get_view(),cell_range,Kokkos::ALL(),Kokkos::ALL(),Kokkos::ALL());
983 
984  cell_tools.setJacobian(s_jac, ref_coord, s_node_coord,*(int_rule->topology));
985 
986  if(requires_permutation_)
987  applyPermutation(aux, permutations_);
988 
989  }
990 
991  PHX::Device::execution_space().fence();
992 
993  if(cache){
994  jac = aux;
995  jac_evaluated_ = true;
996  }
997 
998  return aux;
999 }
1000 
1001 template <typename Scalar>
1004 getJacobianInverse(const bool cache,
1005  const bool force) const
1006 {
1007  if(jac_inv_evaluated_ and not force)
1008  return jac_inv;
1009 
1010  // Only log time if values computed (i.e. don't log if values are already cached)
1011  PANZER_FUNC_TIME_MONITOR_DIFF("panzer::integrationValues2::getJacobianInverse()",get_jacobian_inv);
1012 
1013  Intrepid2::CellTools<PHX::Device::execution_space> cell_tools;
1014  MDFieldArrayFactory af(prefix_,true);
1015 
1016  const int num_space_dim = int_rule->topology->getDimension();
1017  const int num_ip = int_rule->num_points;
1018 
1019  auto jacobian = getJacobian(false,force);
1020  auto aux = af.template buildStaticArray<Scalar,Cell,IP,Dim,Dim>("jac_inv",num_cells_, num_ip, num_space_dim,num_space_dim);
1021 
1022  const auto cell_range = std::make_pair(0,num_evaluate_cells_);
1023  auto s_jac = Kokkos::subview(jacobian.get_view(),cell_range,Kokkos::ALL(),Kokkos::ALL(),Kokkos::ALL());
1024  auto s_jac_inv = Kokkos::subview(aux.get_view(), cell_range,Kokkos::ALL(),Kokkos::ALL(),Kokkos::ALL());
1025 
1026  cell_tools.setJacobianInv(s_jac_inv, s_jac);
1027 
1028  PHX::Device::execution_space().fence();
1029 
1030  if(cache){
1031  jac_inv = aux;
1032  jac_inv_evaluated_ = true;
1033  }
1034 
1035  return aux;
1036 }
1037 
1038 template <typename Scalar>
1041 getJacobianDeterminant(const bool cache,
1042  const bool force) const
1043 {
1044  if(jac_det_evaluated_ and not force)
1045  return jac_det;
1046 
1047  // Only log time if values computed (i.e. don't log if values are already cached)
1048  PANZER_FUNC_TIME_MONITOR_DIFF("panzer::integrationValues2::getJacobianDeterminant()",get_jacobian_det);
1049 
1050  Intrepid2::CellTools<PHX::Device::execution_space> cell_tools;
1051  MDFieldArrayFactory af(prefix_,true);
1052 
1053  const int num_ip = int_rule->num_points;
1054 
1055  auto jacobian = getJacobian(false,force);
1056  auto aux = af.template buildStaticArray<Scalar,Cell,IP>("jac_det",num_cells_, num_ip);
1057 
1058  const auto cell_range = std::make_pair(0,num_evaluate_cells_);
1059  auto s_jac = Kokkos::subview(jacobian.get_view(),cell_range,Kokkos::ALL(),Kokkos::ALL(),Kokkos::ALL());
1060  auto s_jac_det = Kokkos::subview(aux.get_view(), cell_range,Kokkos::ALL());
1061 
1062  cell_tools.setJacobianDet(s_jac_det, s_jac);
1063 
1064  PHX::Device::execution_space().fence();
1065 
1066  if(cache){
1067  jac_det = aux;
1068  jac_det_evaluated_ = true;
1069  }
1070 
1071  return aux;
1072 }
1073 
1074 template <typename Scalar>
1077 getWeightedMeasure(const bool cache,
1078  const bool force) const
1079 {
1080  if(weighted_measure_evaluated_ and not force)
1081  return weighted_measure;
1082 
1083  // Only log time if values computed (i.e. don't log if values are already cached)
1084  PANZER_FUNC_TIME_MONITOR_DIFF("panzer::integrationValues2::getWeightedMeasure()",get_wt_meas);
1085 
1086  MDFieldArrayFactory af(prefix_,true);
1087 
1088  const int num_space_dim = int_rule->topology->getDimension();
1089  const int num_ip = int_rule->num_points;
1090 
1091  auto aux = af.template buildStaticArray<Scalar,Cell,IP>("weighted_measure",num_cells_, num_ip);
1092 
1093  if(int_rule->cv_type != "none"){
1094 
1095  TEUCHOS_TEST_FOR_EXCEPT_MSG(int_rule->cv_type == "side",
1096  "IntegrationValues2::getWeightedMeasure : Weighted measure not available for side control volume integrators. Use getWeightedNormals instead.");
1097 
1098  // CV integration uses a single call to map from physical space to the weighted measure - I assume this is slower than what we do with non-cv integration methods
1099 
1100  auto s_cub_points = af.template buildStaticArray<Scalar, Cell, IP, Dim>("cub_points",num_evaluate_cells_,num_ip,num_space_dim);
1101 
1102  auto node_coord = PHX::getNonConstDynRankViewFromConstMDField(getNodeCoordinates());
1103 
1104  const auto cell_range = std::make_pair(0,num_evaluate_cells_);
1105  auto s_node_coord = Kokkos::subview(node_coord, cell_range,Kokkos::ALL(),Kokkos::ALL());
1106  auto s_weighted_measure = Kokkos::subview(aux.get_view(),cell_range,Kokkos::ALL());
1107 
1108  intrepid_cubature->getCubature(s_cub_points.get_view(),s_weighted_measure,s_node_coord);
1109 
1110  } else if(int_rule->getType() == IntegrationDescriptor::SURFACE){
1111 
1112  const auto & cell_topology = *int_rule->topology;
1113  const int cell_dim = cell_topology.getDimension();
1114  const int num_sides = (cell_dim==1) ? 2 : cell_topology.getSideCount();
1115  const int cubature_order = int_rule->order();
1116  const int num_points_on_side = num_ip / num_sides;
1117 
1118  Intrepid2::DefaultCubatureFactory cubature_factory;
1119  auto jacobian = getJacobian(false,force);
1120 
1121  for(int side=0; side<num_sides; ++side) {
1122 
1123  const int point_offset=side*num_points_on_side;
1124 
1125  // Get the cubature for the side
1126  Kokkos::DynRankView<double,PHX::Device> side_cub_weights;
1127  if(cell_dim==1){
1128  side_cub_weights = Kokkos::DynRankView<double,PHX::Device>("side_cub_weights",num_points_on_side);
1129  auto tmp_side_cub_weights_host = Kokkos::create_mirror_view(side_cub_weights);
1130  tmp_side_cub_weights_host(0)=1.;
1131  Kokkos::deep_copy(side_cub_weights,tmp_side_cub_weights_host);
1132  } else {
1133 
1134  // Get the face topology from the cell topology
1135  const shards::CellTopology face_topology(cell_topology.getCellTopologyData(cell_dim-1,side));
1136 
1137  auto ic = cubature_factory.create<PHX::Device::execution_space,double,double>(face_topology,cubature_order);
1138 
1139  side_cub_weights = Kokkos::DynRankView<double,PHX::Device>("side_cub_weights",num_points_on_side);
1140  auto subcell_cub_points = Kokkos::DynRankView<double,PHX::Device>("subcell_cub_points",num_points_on_side,cell_dim-1);
1141 
1142  // Get the reference face points
1143  ic->getCubature(subcell_cub_points, side_cub_weights);
1144  }
1145 
1146  PHX::Device::execution_space().fence();
1147 
1148  // Iterating over face points
1149  Kokkos::MDRangePolicy<PHX::Device::execution_space,Kokkos::Rank<2>> policy({0,0},{num_evaluate_cells_,num_points_on_side});
1150 
1151  // Calculate measures (quadrature weights in physical space) for this side
1152  auto side_weighted_measure = Kokkos::DynRankView<Scalar,PHX::Device>("side_weighted_measure",num_evaluate_cells_,num_points_on_side);
1153  if(cell_dim == 1){
1154  auto side_cub_weights_host = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(),side_cub_weights);
1155  Kokkos::deep_copy(side_weighted_measure, side_cub_weights_host(0));
1156  } else {
1157 
1158  // Copy from complete jacobian to side jacobian
1159  auto side_jacobian = Kokkos::DynRankView<Scalar,PHX::Device>("side_jac",num_evaluate_cells_,num_points_on_side,cell_dim,cell_dim);
1160 
1161  Kokkos::parallel_for("Copy jacobian to side jacobian",policy,KOKKOS_LAMBDA (const int cell,const int point) {
1162  for(int dim=0;dim<cell_dim;++dim)
1163  for(int dim1=0;dim1<cell_dim;++dim1)
1164  side_jacobian(cell,point,dim,dim1) = jacobian(cell,point_offset+point,dim,dim1);
1165  });
1166 
1167  auto scratch = af.template buildStaticArray<Scalar,Point>("scratch_for_compute_measure", num_evaluate_cells_*num_points_on_side*num_space_dim*num_space_dim);
1168 
1169  if(cell_dim == 2){
1170  Intrepid2::FunctionSpaceTools<PHX::Device::execution_space>::
1171  computeEdgeMeasure(side_weighted_measure, side_jacobian, side_cub_weights,
1172  side,cell_topology,
1173  scratch.get_view());
1174  PHX::Device::execution_space().fence();
1175  } else if(cell_dim == 3){
1176  Intrepid2::FunctionSpaceTools<PHX::Device::execution_space>::
1177  computeFaceMeasure(side_weighted_measure, side_jacobian, side_cub_weights,
1178  side,cell_topology,
1179  scratch.get_view());
1180  PHX::Device::execution_space().fence();
1181  }
1182  }
1183 
1184 
1185  // Copy to the main array
1186  Kokkos::parallel_for("copy surface weighted measure values",policy,KOKKOS_LAMBDA (const int cell,const int point) {
1187  aux(cell,point_offset + point) = side_weighted_measure(cell,point);
1188  });
1189  PHX::Device::execution_space().fence();
1190  }
1191 
1192  } else {
1193 
1194  auto cell_range = std::make_pair(0,num_evaluate_cells_);
1195  auto s_weighted_measure = Kokkos::subview(aux.get_view(),cell_range,Kokkos::ALL());
1196  auto cubature_weights = getUniformCubatureWeightsRef(false,force,false);
1197 
1198  if (!int_rule->isSide()) {
1199 
1200  auto s_jac_det = Kokkos::subview(getJacobianDeterminant(false,force).get_view(),cell_range,Kokkos::ALL());
1201  Intrepid2::FunctionSpaceTools<PHX::Device::execution_space>::
1202  computeCellMeasure(s_weighted_measure, s_jac_det, cubature_weights.get_view());
1203 
1204  } else if(int_rule->spatial_dimension==3) {
1205 
1206  auto s_jac = Kokkos::subview(getJacobian(false,force).get_view(),cell_range,Kokkos::ALL(),Kokkos::ALL(),Kokkos::ALL());
1207  auto scratch = af.template buildStaticArray<Scalar,Point>("scratch_for_compute_measure", num_evaluate_cells_*num_ip*num_space_dim*num_space_dim);
1208  Intrepid2::FunctionSpaceTools<PHX::Device::execution_space>::
1209  computeFaceMeasure(s_weighted_measure, s_jac, cubature_weights.get_view(),
1210  int_rule->side, *int_rule->topology,
1211  scratch.get_view());
1212 
1213  } else if(int_rule->spatial_dimension==2) {
1214 
1215  auto s_jac = Kokkos::subview(getJacobian(false,force).get_view(),cell_range,Kokkos::ALL(),Kokkos::ALL(),Kokkos::ALL());
1216  auto scratch = af.template buildStaticArray<Scalar,Point>("scratch_for_compute_measure", num_evaluate_cells_*num_ip*num_space_dim*num_space_dim);
1217  Intrepid2::FunctionSpaceTools<PHX::Device::execution_space>::
1218  computeEdgeMeasure(s_weighted_measure, s_jac, cubature_weights.get_view(),
1219  int_rule->side,*int_rule->topology,
1220  scratch.get_view());
1221 
1222  } else {
1223  TEUCHOS_ASSERT(false);
1224  }
1225 
1226  }
1227 
1228  PHX::Device::execution_space().fence();
1229 
1230  // Apply permutations if necessary
1231  if(requires_permutation_)
1232  applyPermutation(aux, permutations_);
1233 
1234  if(cache){
1235  weighted_measure = aux;
1236  weighted_measure_evaluated_ = true;
1237  }
1238 
1239  return aux;
1240 }
1241 
1242 template <typename Scalar>
1245 getWeightedNormals(const bool cache,
1246  const bool force) const
1247 {
1248  if(weighted_normals_evaluated_ and not force)
1249  return weighted_normals;
1250 
1251  // Only log time if values computed (i.e. don't log if values are already cached)
1252  PANZER_FUNC_TIME_MONITOR_DIFF("panzer::integrationValues2::getWeightedNormals()",get_wt_normals);
1253 
1254  MDFieldArrayFactory af(prefix_,true);
1255 
1256  const int num_space_dim = int_rule->topology->getDimension();
1257  const int num_ip = int_rule->num_points;
1258 
1259  auto aux = af.template buildStaticArray<Scalar,Cell,IP,Dim>("weighted_normals",num_cells_,num_ip,num_space_dim);
1260 
1262  "IntegrationValues2::getWeightedNormals : Cannot build reference weighted normals for surface integration scheme.");
1263 
1264  auto points = af.template buildStaticArray<Scalar,Cell,IP,Dim>("cub_points",num_cells_,num_ip,num_space_dim);
1265 
1266  auto node_coord = PHX::getNonConstDynRankViewFromConstMDField(getNodeCoordinates());
1267 
1268  const auto cell_range = std::make_pair(0,num_evaluate_cells_);
1269  auto s_cub_points = Kokkos::subview(points.get_view(),cell_range, Kokkos::ALL(), Kokkos::ALL());
1270  auto s_weighted_normals = Kokkos::subview(aux.get_view(), cell_range, Kokkos::ALL(), Kokkos::ALL());
1271  auto s_node_coord = Kokkos::subview(node_coord, cell_range, Kokkos::ALL(), Kokkos::ALL());
1272 
1273  intrepid_cubature->getCubature(s_cub_points,s_weighted_normals,s_node_coord);
1274 
1275  PHX::Device::execution_space().fence();
1276 
1277  // Apply permutations if necessary
1278  if(requires_permutation_)
1279  applyPermutation(aux, permutations_);
1280 
1281  if(cache){
1282  weighted_normals = aux;
1283  weighted_normals_evaluated_ = true;
1284  }
1285 
1286  return aux;
1287 }
1288 
1289 template <typename Scalar>
1292 getSurfaceNormals(const bool cache,
1293  const bool force) const
1294 {
1295  if(surface_normals_evaluated_ and not force)
1296  return surface_normals;
1297 
1298  // Only log time if values computed (i.e. don't log if values are already cached)
1299  PANZER_FUNC_TIME_MONITOR_DIFF("panzer::integrationValues2::getSurfaceNormals()",get_surf_normals);
1300 
1301  TEUCHOS_TEST_FOR_EXCEPT_MSG(int_rule->isSide(),
1302  "IntegrationValues2::getSurfaceNormals : This call doesn't work with sides (only surfaces).");
1303 
1304  TEUCHOS_TEST_FOR_EXCEPT_MSG(int_rule->cv_type != "none",
1305  "IntegrationValues2::getSurfaceNormals : This call does not support control volume integration schemes.");
1306 
1307  TEUCHOS_TEST_FOR_EXCEPT_MSG(int_rule->getType() != IntegrationDescriptor::SURFACE,
1308  "IntegrationValues2::getSurfaceNormals : Can only build for surface integrators.");
1309 
1310  Intrepid2::CellTools<PHX::Device::execution_space> cell_tools;
1311  MDFieldArrayFactory af(prefix_,true);
1312 
1313  const shards::CellTopology & cell_topology = *(int_rule->topology);
1314  const int cell_dim = cell_topology.getDimension();
1315  const int subcell_dim = cell_topology.getDimension()-1;
1316  const int num_subcells = cell_topology.getSubcellCount(subcell_dim);
1317  const int num_space_dim = int_rule->topology->getDimension();
1318  const int num_ip = int_rule->num_points;
1319  const int num_points_on_face = num_ip / num_subcells;
1320 
1321  auto aux = af.template buildStaticArray<Scalar,Cell,IP,Dim>("surface_normals",num_cells_,num_ip,num_space_dim);
1322 
1323  // We only need the jacobian if we're not in 1D
1324  ConstArray_CellIPDimDim jacobian;
1325  if(cell_dim != 1)
1326  jacobian = getJacobian(false,force);
1327 
1328  // We get to build up our surface normals one 'side' at a time
1329  for(int subcell_index=0; subcell_index<num_subcells; ++subcell_index) {
1330 
1331  const int point_offset = subcell_index * num_points_on_face;;
1332 
1333  // Calculate normals
1334  auto side_normals = Kokkos::DynRankView<Scalar,PHX::Device>("side_normals",num_evaluate_cells_,num_points_on_face,cell_dim);
1335  if(cell_dim == 1){
1336 
1337  const int other_subcell_index = (subcell_index==0) ? 1 : 0;
1338  auto in_node_coordinates_k = getNodeCoordinates().get_view();
1339  const auto min = std::numeric_limits<typename Sacado::ScalarType<Scalar>::type>::min();
1340 
1341  Kokkos::parallel_for("compute normals 1D",num_evaluate_cells_,KOKKOS_LAMBDA (const int cell) {
1342  Scalar norm = (in_node_coordinates_k(cell,subcell_index,0) - in_node_coordinates_k(cell,other_subcell_index,0));
1343  side_normals(cell,0,0) = norm / fabs(norm + min);
1344  });
1345 
1346  } else {
1347 
1348  // Iterating over side points
1349  Kokkos::MDRangePolicy<PHX::Device::execution_space,Kokkos::Rank<2>> policy({0,0},{num_evaluate_cells_,num_points_on_face});
1350 
1351  auto side_jacobian = Kokkos::DynRankView<Scalar,PHX::Device>("side_jac",num_evaluate_cells_,num_points_on_face,cell_dim,cell_dim);
1352  Kokkos::parallel_for("Copy jacobian to side jacobian",policy,KOKKOS_LAMBDA (const int cell,const int point) {
1353  for(int dim=0;dim<cell_dim;++dim)
1354  for(int dim1=0;dim1<cell_dim;++dim1)
1355  side_jacobian(cell,point,dim,dim1) = jacobian(cell,point_offset+point,dim,dim1);
1356  });
1357 
1358  // Get the "physical side normals"
1359  cell_tools.getPhysicalSideNormals(side_normals,side_jacobian,subcell_index,cell_topology);
1360 
1361  // Normalize each normal
1362  Kokkos::parallel_for("Normalize the normals",policy,KOKKOS_LAMBDA (const int cell,const int point) {
1363  Scalar n = 0.;
1364  for(int dim=0;dim<cell_dim;++dim){
1365  n += side_normals(cell,point,dim)*side_normals(cell,point,dim);
1366  }
1367  // If n is zero then this is - hopefully - a virtual cell
1368  if(n > 0.){
1369  n = Kokkos::sqrt(n);
1370  for(int dim=0;dim<cell_dim;++dim)
1371  side_normals(cell,point,dim) /= n;
1372  }
1373  });
1374  }
1375 
1376  PHX::Device::execution_space().fence();
1377 
1378  Kokkos::MDRangePolicy<PHX::Device::execution_space,Kokkos::Rank<2>> policy({0,0},{num_evaluate_cells_,num_points_on_face});
1379  Kokkos::parallel_for("copy surface normals", policy,KOKKOS_LAMBDA (const int cell,const int point) {
1380  for(int dim=0;dim<cell_dim;++dim)
1381  aux(cell,point_offset + point,dim) = side_normals(cell,point,dim);
1382  });
1383  PHX::Device::execution_space().fence();
1384  }
1385 
1386  // Need to correct the virtual cells
1387  {
1388  TEUCHOS_TEST_FOR_EXCEPT_MSG(side_connectivity_ == Teuchos::null,
1389  "IntegrationValues2::getSurfaceNormals : Surface normals require a SubcellConnectivity object pass in through the setupPermutations call");
1390  TEUCHOS_TEST_FOR_EXCEPT_MSG(num_virtual_cells_ < 0,
1391  "IntegrationValues2::getSurfaceNormals : Number of virtual cells (see setupPermutations or evaluateValues) must be greater or equal to zero");
1392 
1393  // Virtual cell normals need to be reversed
1394  if(num_virtual_cells_ > 0)
1395  correctVirtualNormals(aux, num_evaluate_cells_ - num_virtual_cells_, num_virtual_cells_, *int_rule->topology, *side_connectivity_);
1396  }
1397 
1398  if(cache){
1399  surface_normals = aux;
1400  surface_normals_evaluated_ = true;
1401  }
1402 
1403  return aux;
1404 
1405 }
1406 
1407 template <typename Scalar>
1411  const bool force) const
1412 {
1413  if(surface_rotation_matrices_evaluated_ and not force)
1414  return surface_rotation_matrices;
1415 
1416  // Only log time if values computed (i.e. don't log if values are already cached)
1417  PANZER_FUNC_TIME_MONITOR_DIFF("panzer::integrationValues2::getSurfaceRotationMatrices()",get_surf_rot_mat);
1418 
1419  MDFieldArrayFactory af(prefix_,true);
1420 
1421  const int num_ip = int_rule->num_points;
1422  const int cell_dim = int_rule->topology->getDimension();
1423 
1424  // This call will handle all the error checking
1425  auto normals = getSurfaceNormals(false,force).get_static_view();
1426  auto aux = af.template buildStaticArray<Scalar,Cell,IP,Dim,Dim>("surface_rotation_matrices",num_cells_, num_ip, 3, 3);
1427 
1428  Kokkos::MDRangePolicy<PHX::Device::execution_space,Kokkos::Rank<2>> policy({0,0},{num_evaluate_cells_,num_ip});
1429  Kokkos::parallel_for("create surface rotation matrices",policy,KOKKOS_LAMBDA (const int cell,const int point) {
1430  Scalar normal[3];
1431  for(int i=0;i<3;i++)
1432  normal[i]=0.;
1433  for(int dim=0; dim<cell_dim; ++dim)
1434  normal[dim] = normals(cell,point,dim);
1435 
1436  Scalar transverse[3];
1437  Scalar binormal[3];
1438  panzer::convertNormalToRotationMatrix(normal,transverse,binormal);
1439 
1440  for(int dim=0; dim<3; ++dim){
1441  aux(cell,point,0,dim) = normal[dim];
1442  aux(cell,point,1,dim) = transverse[dim];
1443  aux(cell,point,2,dim) = binormal[dim];
1444  }
1445  });
1446  PHX::Device::execution_space().fence();
1447 
1448  // Need to correct the virtual cells
1449  {
1450  TEUCHOS_TEST_FOR_EXCEPT_MSG(side_connectivity_ == Teuchos::null,
1451  "IntegrationValues2::getSurfaceRotationMatrices : Surface normals require a SubcellConnectivity object pass in through the setupPermutations call");
1452  TEUCHOS_TEST_FOR_EXCEPT_MSG(num_virtual_cells_ < 0,
1453  "IntegrationValues2::getSurfaceRotationMatrices : Number of virtual cells (see setupPermutations or evaluateValues) must be greater or equal to zero");
1454 
1455  // Virtual cell normals need to be reversed
1456  if(num_virtual_cells_ > 0)
1457  correctVirtualRotationMatrices(aux, num_evaluate_cells_ - num_virtual_cells_, num_virtual_cells_, *int_rule->topology, *side_connectivity_);
1458  }
1459 
1460  if(cache){
1461  surface_rotation_matrices = aux;
1462  surface_rotation_matrices_evaluated_ = true;
1463  }
1464 
1465  return aux;
1466 }
1467 
1468 template <typename Scalar>
1471 getCovarientMatrix(const bool cache,
1472  const bool force) const
1473 {
1474  if(covarient_evaluated_ and not force)
1475  return covarient;
1476 
1477  // Only log time if values computed (i.e. don't log if values are already cached)
1478  PANZER_FUNC_TIME_MONITOR_DIFF("panzer::integrationValues2::getCovariantMatrix()",get_cov_mat);
1479 
1480  MDFieldArrayFactory af(prefix_,true);
1481 
1482  const int num_space_dim = int_rule->topology->getDimension();
1483  const int num_ip = int_rule->num_points;
1484 
1485  auto jacobian = getJacobian(false,force).get_static_view();
1486  auto aux = af.template buildStaticArray<Scalar,Cell,IP,Dim,Dim>("covarient",num_cells_, num_ip, num_space_dim,num_space_dim);
1487 
1488  Kokkos::MDRangePolicy<PHX::Device,Kokkos::Rank<2>> policy({0,0},{num_evaluate_cells_,num_ip});
1489  Kokkos::parallel_for("evalaute covarient metric tensor",policy,KOKKOS_LAMBDA (const int cell,const int ip) {
1490  // g^{ij} = \frac{\parital x_i}{\partial \chi_\alpha}\frac{\parital x_j}{\partial \chi_\alpha}
1491  for (int i = 0; i < num_space_dim; ++i) {
1492  for (int j = 0; j < num_space_dim; ++j) {
1493  Scalar value(0.0);
1494  for (int alpha = 0; alpha < num_space_dim; ++alpha)
1495  value += jacobian(cell,ip,i,alpha) * jacobian(cell,ip,j,alpha);
1496  aux(cell,ip,i,j) = value;
1497  }
1498  }
1499  });
1500  PHX::Device::execution_space().fence();
1501 
1502  if(cache){
1503  covarient = aux;
1504  covarient_evaluated_ = true;
1505  }
1506 
1507  return aux;
1508 }
1509 
1510 template <typename Scalar>
1513 getContravarientMatrix(const bool cache,
1514  const bool force) const
1515 {
1516  if(contravarient_evaluated_ and not force)
1517  return contravarient;
1518 
1519  // Only log time if values computed (i.e. don't log if values are already cached)
1520  PANZER_FUNC_TIME_MONITOR_DIFF("panzer::integrationValues2::getContravarientMatrix()",get_contra_mat);
1521 
1522  MDFieldArrayFactory af(prefix_,true);
1523 
1524  const int num_space_dim = int_rule->topology->getDimension();
1525  const int num_ip = int_rule->num_points;
1526 
1527  auto cov = getCovarientMatrix(false,force);
1528  auto aux = af.template buildStaticArray<Scalar,Cell,IP,Dim,Dim>("contravarient",num_cells_, num_ip, num_space_dim,num_space_dim);
1529 
1530  const auto cell_range = std::make_pair(0,num_evaluate_cells_);
1531  auto s_contravarient = Kokkos::subview(aux.get_view(), cell_range,Kokkos::ALL,Kokkos::ALL,Kokkos::ALL);
1532  auto s_covarient = Kokkos::subview(cov.get_view(), cell_range,Kokkos::ALL,Kokkos::ALL,Kokkos::ALL);
1533 
1534  Intrepid2::RealSpaceTools<PHX::Device::execution_space>::inverse(s_contravarient, s_covarient);
1535  PHX::Device::execution_space().fence();
1536 
1537  if(cache){
1538  contravarient = aux;
1539  contravarient_evaluated_ = true;
1540  }
1541 
1542  return aux;
1543 }
1544 
1545 template <typename Scalar>
1549  const bool force) const
1550 {
1551  if(norm_contravarient_evaluated_ and not force)
1552  return norm_contravarient;
1553 
1554  // Only log time if values computed (i.e. don't log if values are already cached)
1555  PANZER_FUNC_TIME_MONITOR_DIFF("panzer::integrationValues2::getNormContravarientMatrix()",get_norm_contr_mat);
1556 
1557  MDFieldArrayFactory af(prefix_,true);
1558 
1559  const int num_space_dim = int_rule->topology->getDimension();
1560  const int num_ip = int_rule->num_points;
1561 
1562  auto con = getContravarientMatrix(false,force).get_static_view();
1563  auto aux = af.template buildStaticArray<Scalar,Cell,IP>("norm_contravarient",num_cells_, num_ip);
1564 
1565  // norm of g_ij
1566  Kokkos::MDRangePolicy<PHX::Device,Kokkos::Rank<2>> policy({0,0},{num_evaluate_cells_,num_ip});
1567  Kokkos::parallel_for("evaluate norm_contravarient",policy,KOKKOS_LAMBDA (const int cell,const int ip) {
1568  aux(cell,ip) = 0.0;
1569  for (int i = 0; i < num_space_dim; ++i) {
1570  for (int j = 0; j < num_space_dim; ++j) {
1571  aux(cell,ip) += con(cell,ip,i,j) * con(cell,ip,i,j);
1572  }
1573  }
1574  aux(cell,ip) = Kokkos::sqrt(aux(cell,ip));
1575  });
1576  PHX::Device::execution_space().fence();
1577 
1578  if(cache){
1579  norm_contravarient = aux;
1580  norm_contravarient_evaluated_ = true;
1581  }
1582 
1583  return aux;
1584 }
1585 
1586 template <typename Scalar>
1589 getCubaturePoints(const bool cache,
1590  const bool force) const
1591 {
1592  if(ip_coordinates_evaluated_ and not force)
1593  return ip_coordinates;
1594 
1595  // Only log time if values computed (i.e. don't log if values are already cached)
1596  PANZER_FUNC_TIME_MONITOR_DIFF("panzer::integrationValues2::getCubaturePoints()",get_cub_pts);
1597 
1598  MDFieldArrayFactory af(prefix_,true);
1599 
1600  const int num_space_dim = int_rule->topology->getDimension();
1601  const int num_ip = int_rule->num_points;
1602 
1603  auto aux = af.template buildStaticArray<Scalar,Cell,IP,Dim>("ip_coordinates",num_cells_, num_ip, num_space_dim);
1604 
1606  const bool is_cv = (int_rule->getType() == ID::CV_VOLUME) or (int_rule->getType() == ID::CV_SIDE) or (int_rule->getType() == ID::CV_BOUNDARY);
1607  const bool is_surface = int_rule->getType() == ID::SURFACE;
1608 
1609  auto node_coord = PHX::getNonConstDynRankViewFromConstMDField(getNodeCoordinates());
1610 
1611  if(is_cv){
1612 
1613  // CV integration uses a single call to map from physical space to the weighted measure - I assume this is slower than what we do with non-cv integration methods
1614  const auto cell_range = std::make_pair(0,num_evaluate_cells_);
1615  auto s_node_coord = Kokkos::subview(node_coord, cell_range,Kokkos::ALL(),Kokkos::ALL());
1616  auto s_cub_points = Kokkos::subview(aux.get_view(),cell_range,Kokkos::ALL(),Kokkos::ALL());
1617 
1618  // TODO: We need to pull this apart for control volumes. Right now we calculate both weighted measures/norms and cubature points at the same time
1619  if(int_rule->cv_type == "side"){
1620  auto scratch = af.template buildStaticArray<Scalar,Cell,IP,Dim>("scratch",num_evaluate_cells_,num_ip,num_space_dim);
1621  intrepid_cubature->getCubature(s_cub_points,scratch.get_view(),s_node_coord);
1622  } else {
1623  // I think boundary is included as a weighted measure because it has a side embedded in intrepid_cubature
1624  TEUCHOS_ASSERT((int_rule->getType() == ID::CV_VOLUME) or (int_rule->getType() == ID::CV_BOUNDARY));
1625  auto scratch = af.template buildStaticArray<Scalar,Cell,IP>("scratch",num_evaluate_cells_,num_ip);
1626  intrepid_cubature->getCubature(s_cub_points,scratch.get_view(),s_node_coord);
1627  }
1628 
1629  } else if(is_surface){
1630 
1631  // Don't forget that since we are not caching this, we have to make sure the managed view remains alive while we use the non-const wrapper
1632  auto const_ref_coord = getCubaturePointsRef(false,force);
1633  auto ref_coord = PHX::getNonConstDynRankViewFromConstMDField(const_ref_coord);
1634 
1635  const auto cell_range = std::make_pair(0,num_evaluate_cells_);
1636  auto s_ref_coord = Kokkos::subview(ref_coord, cell_range,Kokkos::ALL(),Kokkos::ALL());
1637  auto s_coord = Kokkos::subview(aux.get_view(),cell_range,Kokkos::ALL(),Kokkos::ALL());
1638  auto s_node_coord = Kokkos::subview(node_coord, cell_range,Kokkos::ALL(),Kokkos::ALL());
1639 
1640  Intrepid2::CellTools<PHX::Device::execution_space> cell_tools;
1641  cell_tools.mapToPhysicalFrame(s_coord, s_ref_coord, s_node_coord, *(int_rule->topology));
1642 
1643  } else {
1644 
1645  // Don't forget that since we are not caching this, we have to make sure the managed view remains alive while we use the non-const wrapper
1646  auto const_ref_coord = getUniformCubaturePointsRef(false,force,false);
1647  auto ref_coord = PHX::getNonConstDynRankViewFromConstMDField(const_ref_coord);
1648 
1649  const auto cell_range = std::make_pair(0,num_evaluate_cells_);
1650  auto s_coord = Kokkos::subview(aux.get_view(),cell_range,Kokkos::ALL(),Kokkos::ALL());
1651  auto s_node_coord = Kokkos::subview(node_coord, cell_range,Kokkos::ALL(),Kokkos::ALL());
1652 
1653  Intrepid2::CellTools<PHX::Device::execution_space> cell_tools;
1654  cell_tools.mapToPhysicalFrame(s_coord, ref_coord, s_node_coord, *(int_rule->topology));
1655 
1656  if(requires_permutation_)
1657  applyPermutation(aux, permutations_);
1658 
1659  }
1660 
1661  PHX::Device::execution_space().fence();
1662 
1663  if(cache){
1664  ip_coordinates = aux;
1665  ip_coordinates_evaluated_ = true;
1666  }
1667 
1668  return aux;
1669 }
1670 
1671 
1672 template <typename Scalar>
1675 getCubaturePointsRef(const bool cache,
1676  const bool force) const
1677 {
1678  if(ref_ip_coordinates_evaluated_ and not force)
1679  return ref_ip_coordinates;
1680 
1681  // Only log time if values computed (i.e. don't log if values are already cached)
1682  PANZER_FUNC_TIME_MONITOR_DIFF("panzer::integrationValues2::getCubaturePointsRef()",get_cub_pts_ref);
1683 
1685  const bool is_surface = int_rule->getType() == ID::SURFACE;
1686  const bool is_cv = (int_rule->getType() == ID::CV_VOLUME) or (int_rule->getType() == ID::CV_SIDE) or (int_rule->getType() == ID::CV_BOUNDARY);
1687 
1688  const int num_space_dim = int_rule->topology->getDimension();
1689  const int num_ip = int_rule->num_points;
1690 
1691  MDFieldArrayFactory af(prefix_,true);
1692 
1693  Intrepid2::CellTools<PHX::Device::execution_space> cell_tools;
1694 
1695  auto aux = af.template buildStaticArray<Scalar,Cell,IP,Dim>("ref_ip_coordinates",num_cells_, num_ip, num_space_dim);
1696 
1697  if(is_cv){
1698 
1699  // Control volume reference points are actually generated from the physical points (i.e. reverse to everything else)
1700 
1701  auto node_coord = PHX::getNonConstDynRankViewFromConstMDField(getNodeCoordinates());
1702 
1703  // Don't forget that since we are not caching this, we have to make sure the managed view remains alive while we use the non-const wrapper
1704  auto const_coord = getCubaturePoints(false,force);
1705  auto coord = PHX::getNonConstDynRankViewFromConstMDField(const_coord);
1706 
1707  const auto cell_range = std::make_pair(0,num_evaluate_cells_);
1708  auto s_ref_coord = Kokkos::subview(aux.get_view(),cell_range,Kokkos::ALL(),Kokkos::ALL());
1709  auto s_coord = Kokkos::subview(coord, cell_range,Kokkos::ALL(),Kokkos::ALL());
1710  auto s_node_coord = Kokkos::subview(node_coord, cell_range,Kokkos::ALL(),Kokkos::ALL());
1711 
1712  cell_tools.mapToReferenceFrame(s_ref_coord, s_coord, s_node_coord, *(int_rule->topology));
1713 
1714  } else if(is_surface){
1715 
1716  const auto & cell_topology = *int_rule->topology;
1717  const int cell_dim = cell_topology.getDimension();
1718  const int num_sides = (cell_dim==1) ? 2 : cell_topology.getSideCount();
1719  const int subcell_dim = cell_dim-1;
1720  const int num_points_on_face = num_ip / num_sides;
1721  const int order = int_rule->getOrder();
1722 
1723  // Scratch space for storing the points for each side of the cell
1724  auto side_cub_points2 = af.template buildStaticArray<Scalar,IP,Dim>("side_cub_points",num_points_on_face,cell_dim);
1725 
1726  Intrepid2::DefaultCubatureFactory cubature_factory;
1727 
1728  // We get to build up our cubature one side at a time
1729  for(int side=0; side<num_sides; ++side) {
1730 
1731  const int point_offset = side*num_points_on_face;
1732 
1733  // Get the cubature for the side
1734  if(cell_dim==1){
1735  // In 1D the surface point is either on the left side of the cell, or the right side
1736  auto side_cub_points_host = Kokkos::create_mirror_view(side_cub_points2.get_view());
1737  side_cub_points_host(0,0) = (side==0)? -1. : 1.;
1738  Kokkos::deep_copy(side_cub_points2.get_view(),side_cub_points_host);
1739  } else {
1740 
1741  // Get the face topology from the cell topology
1742  const shards::CellTopology face_topology(cell_topology.getCellTopologyData(subcell_dim,side));
1743 
1744  // Create a cubature for the face of the cell
1745  auto ic = cubature_factory.create<PHX::Device::execution_space,double,double>(face_topology,order);
1746  auto tmp_side_cub_weights = Kokkos::DynRankView<double,PHX::Device>("tmp_side_cub_weights",num_points_on_face);
1747  auto tmp_side_cub_points = Kokkos::DynRankView<double,PHX::Device>("tmp_side_cub_points",num_points_on_face,subcell_dim);
1748 
1749  // Get the reference face points
1750  ic->getCubature(tmp_side_cub_points, tmp_side_cub_weights);
1751 
1752  // Convert from reference face points to reference cell points
1753  cell_tools.mapToReferenceSubcell(side_cub_points2.get_view(), tmp_side_cub_points, subcell_dim, side, cell_topology);
1754  }
1755 
1756  PHX::Device::execution_space().fence();
1757 
1758  // Copy from the side allocation to the surface allocation
1759  Kokkos::MDRangePolicy<PHX::Device::execution_space,Kokkos::Rank<3>> policy({0,0,0},{num_evaluate_cells_,num_points_on_face, num_space_dim});
1760  Kokkos::parallel_for("copy values",policy,KOKKOS_LAMBDA (const int cell,const int point, const int dim) {
1761  aux(cell,point_offset + point,dim) = side_cub_points2(point,dim);
1762  });
1763  PHX::Device::execution_space().fence();
1764  }
1765 
1766  } else {
1767 
1768  // Haven't set this up yet
1769  TEUCHOS_TEST_FOR_EXCEPT_MSG(int_rule->isSide() && num_space_dim==1,
1770  "ERROR: 0-D quadrature rule infrastructure does not exist!!! Will not be able to do "
1771  << "non-natural integration rules.");
1772 
1773  auto cub_points2 = getUniformCubaturePointsRef(false,force,false);
1774 
1775  Kokkos::MDRangePolicy<PHX::Device,Kokkos::Rank<3>> policy({0,0,0},{num_evaluate_cells_,num_ip,num_space_dim});
1776  Kokkos::parallel_for(policy, KOKKOS_LAMBDA(const int & cell, const int & ip, const int & dim){
1777  aux(cell,ip,dim) = cub_points2(ip,dim);
1778  });
1779  }
1780 
1781  PHX::Device::execution_space().fence();
1782 
1783  if(requires_permutation_)
1784  applyPermutation(aux, permutations_);
1785 
1786  if(cache){
1787  ref_ip_coordinates = aux;
1788  ref_ip_coordinates_evaluated_ = true;
1789  }
1790 
1791  return aux;
1792 }
1793 
1794 template <typename Scalar>
1795 void
1798 {
1799  PANZER_FUNC_TIME_MONITOR_DIFF("panzer::integrationValues2::evaluateEverything()",eval_everything);
1800 
1802  const bool is_surface = int_rule->getType() == ID::SURFACE;
1803  const bool is_cv = (int_rule->getType() == ID::CV_VOLUME) or (int_rule->getType() == ID::CV_SIDE) or (int_rule->getType() == ID::CV_BOUNDARY);
1804  const bool is_side = int_rule->isSide();
1805 
1806  // This will force all values to be re-evaluated
1807  resetArrays();
1808 
1809  // Base cubature stuff
1810  if(is_cv){
1811  getCubaturePoints(true);
1812  getCubaturePointsRef(true);
1813  } else {
1814  if(not is_surface){
1815  getUniformCubaturePointsRef(true,true,requires_permutation_);
1816  getUniformCubatureWeightsRef(true,true,requires_permutation_);
1817  if(is_side)
1818  getUniformSideCubaturePointsRef(true,true,requires_permutation_);
1819  }
1820  getCubaturePointsRef(true);
1821  getCubaturePoints(true);
1822  }
1823 
1824  // Measure stuff
1825  getJacobian(true);
1826  getJacobianDeterminant(true);
1827  getJacobianInverse(true);
1828  if(int_rule->cv_type == "side")
1829  getWeightedNormals(true);
1830  else
1831  getWeightedMeasure(true);
1832 
1833  // Surface stuff
1834  if(is_surface){
1835  getSurfaceNormals(true);
1836  getSurfaceRotationMatrices(true);
1837  }
1838 
1839  // Stabilization stuff
1840  if(not (is_surface or is_cv)){
1841  getContravarientMatrix(true);
1842  getCovarientMatrix(true);
1843  getNormContravarientMatrix(true);
1844  }
1845 }
1846 
1847 #define INTEGRATION_VALUES2_INSTANTIATION(SCALAR) \
1848  template class IntegrationValues2<SCALAR>;
1849 
1851 
1852 // Disabled FAD support due to long build times on cuda (in debug mode
1853 // it takes multiple hours on some platforms). If we need
1854 // sensitivities wrt coordinates, we can reenable.
1855 
1856 // INTEGRATION_VALUES2_INSTANTIATION(panzer::Traits::FadType)
1857 
1858 }
ConstArray_IPDim getUniformSideCubaturePointsRef(const bool cache=true, const bool force=false, const bool apply_permutation=true) const
Get the uniform cubature points for a side.
void setupPermutations(const Teuchos::RCP< const SubcellConnectivity > &face_connectivity, const int num_virtual_cells)
Initialize the permutation arrays given a face connectivity.
ConstArray_CellIPDimDim getContravarientMatrix(const bool cache=true, const bool force=false) const
Get the contravarient matrix.
void setup(const Teuchos::RCP< const panzer::IntegrationRule > &ir, const PHX::MDField< Scalar, Cell, NODE, Dim > &cell_node_coordinates, const int num_cells=-1)
Main setup call for the lazy evaluation interface.
ConstArray_CellIP getWeightedMeasure(const bool cache=true, const bool force=false) const
Get the weighted measure (integration weights)
const int & getType() const
Get type of integrator.
ConstArray_CellIPDimDim getJacobianInverse(const bool cache=true, const bool force=false) const
Get the inverse of the Jacobian matrix evaluated at the cubature points.
#define INTEGRATION_VALUES2_INSTANTIATION(SCALAR)
ConstArray_CellIPDimDim getCovarientMatrix(const bool cache=true, const bool force=false) const
Get the covarient matrix.
TEUCHOS_DEPRECATED RCP< T > rcp(T *p, Dealloc_T dealloc, bool owns_mem)
#define PANZER_DOT(a, b)
#define TEUCHOS_TEST_FOR_EXCEPT_MSG(throw_exception_test, msg)
ConstArray_CellIPDim getWeightedNormals(const bool cache=true, const bool force=false) const
Get the weighted normals.
ConstArray_CellIP getNormContravarientMatrix(const bool cache=true, const bool force=false) const
Get the contravarient matrix.
ConstArray_CellIPDimDim getSurfaceRotationMatrices(const bool cache=true, const bool force=false) const
Get the surface rotation matrices.
#define PANZER_CROSS(a, b, c)
KOKKOS_FORCEINLINE_FUNCTION array_type get_static_view()
Teuchos::RCP< const shards::CellTopology > topology
KOKKOS_INLINE_FUNCTION void convertNormalToRotationMatrix(const Scalar normal[3], Scalar transverse[3], Scalar binormal[3])
ConstArray_CellIPDim getCubaturePoints(const bool cache=true, const bool force=false) const
Get the cubature points in physical space.
void setupArrays(const Teuchos::RCP< const panzer::IntegrationRule > &ir)
Sizes/allocates memory for arrays.
ConstArray_CellIPDim getCubaturePointsRef(const bool cache=true, const bool force=false) const
Get the cubature points in the reference space.
const int & getSide() const
Get side associated with integration - this is for backward compatibility.
ConstArray_IPDim getUniformCubaturePointsRef(const bool cache=true, const bool force=false, const bool apply_permutation=true) const
Get the uniform cubature points.
ConstArray_CellIPDimDim getJacobian(const bool cache=true, const bool force=false) const
Get the Jacobian matrix evaluated at the cubature points.
ConstArray_CellIP getJacobianDeterminant(const bool cache=true, const bool force=false) const
Get the determinant of the Jacobian matrix evaluated at the cubature points.
PHX::MDField< ScalarT, panzer::Cell, panzer::BASIS > field
A field to which we&#39;ll contribute, or in which we&#39;ll store, the result of computing this integral...
IntegrationValues2(const std::string &pre="", const bool allocArrays=false)
Base constructor.
Teuchos::RCP< shards::CellTopology > side_topology
#define TEUCHOS_ASSERT(assertion_test)
ConstArray_CellBASISDim getNodeCoordinates() const
Get the node coordinates describing the geometry of the mesh.
void evaluateValues(const PHX::MDField< Scalar, Cell, NODE, Dim > &cell_node_coordinates, const int num_cells=-1, const Teuchos::RCP< const SubcellConnectivity > &face_connectivity=Teuchos::null, const int num_virtual_cells=-1)
Evaluate basis values.
ConstArray_IP getUniformCubatureWeightsRef(const bool cache=true, const bool force=false, const bool apply_permutation=true) const
Get the uniform cubature weights.
ConstArray_CellIPDim getSurfaceNormals(const bool cache=true, const bool force=false) const
Get the surface normals.
const int & getOrder() const
Get order of integrator.