void
  OrientationTools<SpT>::
  modifyBasisByOrientation(/**/  Kokkos::DynRankView<outputValueType,outputProperties...> output,
                           const Kokkos::DynRankView<inputValueType, inputProperties...>  input,
                           const Kokkos::DynRankView<ortValueType,   ortProperties...>    orts,
                           const BasisPtrType basis ) {
#ifdef HAVE_INTREPID2_DEBUG
    {
      INTREPID2_TEST_FOR_EXCEPTION( input.rank() != output.rank(), std::invalid_argument,
                                    ">>> ERROR (OrientationTools::modifyBasisByOrientation): Input and output rank are not 3.");
      for (ordinal_type i=0;i<input.rank();++i)
        INTREPID2_TEST_FOR_EXCEPTION( input.dimension(i) != output.dimension(i), std::invalid_argument,
                                      ">>> ERROR (OrientationTools::modifyBasisByOrientation): Input and output dimension does not match.");

      INTREPID2_TEST_FOR_EXCEPTION( input.dimension(1) != basis->getCardinality(), std::invalid_argument,
                                    ">>> ERROR (OrientationTools::modifyBasisByOrientation): Field dimension of input/output does not match to basis cardinality.");
      INTREPID2_TEST_FOR_EXCEPTION( input.dimension(3) != basis->getBaseCellTopology().getDimension(), std::invalid_argument,
                                    ">>> ERROR (OrientationTools::modifyBasisByOrientation): Space dimension of input/output does not match to topology dimension.");
    }
#endif
    if (basis->requireOrientation()) {
      auto ordinalToTag = Kokkos::create_mirror_view(typename SpT::memory_space(), basis->getAllDofTags());
      auto tagToOrdinal = Kokkos::create_mirror_view(typename SpT::memory_space(), basis->getAllDofOrdinal());
      
      Kokkos::deep_copy(ordinalToTag, basis->getAllDofTags());
      Kokkos::deep_copy(tagToOrdinal, basis->getAllDofOrdinal());
      
      const ordinal_type 
        numCells  = output.dimension(0),
        //numBasis  = output.dimension(1),
        numPoints = output.dimension(2),
        dimBasis  = output.dimension(3);
      
      const CoeffMatrixDataViewType matData = createCoeffMatrix(basis);
      const shards::CellTopology cellTopo = basis->getBaseCellTopology();
      
      const ordinal_type 
        numVerts = cellTopo.getVertexCount(), 
        numEdges = cellTopo.getEdgeCount(),
        numFaces = cellTopo.getFaceCount();
      
      const ordinal_type intrDim = ( numEdges == 0 ? 1 : 
                                     numFaces == 0 ? 2 : 
                                     /**/            3 );
      
      for (auto cell=0;cell<numCells;++cell) {
        auto out = Kokkos::subview(output, cell, Kokkos::ALL(), Kokkos::ALL(), Kokkos::ALL());
        auto in  = Kokkos::subview(input,  cell, Kokkos::ALL(), Kokkos::ALL(), Kokkos::ALL());
        
        // vertex copy (no orientation)
        for (auto vertId=0;vertId<numVerts;++vertId) {
          const auto i = tagToOrdinal(0, vertId, 0);
          if (i != -1) // if dof does not exist i returns with -1
            for (auto j=0;j<numPoints;++j)
              for (auto k=0;k<dimBasis;++k)
                out(i, j, k) = in(i, j, k);
        }
        
        // interior copy
        {
          const auto ordIntr = tagToOrdinal(intrDim, 0, 0);
          if (ordIntr != -1) {
            const auto ndofIntr = ordinalToTag(ordIntr, 3);
            for (auto i=0;i<ndofIntr;++i) {
              const auto ii = tagToOrdinal(intrDim, 0, i);
              for (auto j=0;j<numPoints;++j)
                for (auto k=0;k<dimBasis;++k)
                  out(ii, j, k) = in(ii, j, k);
            }
          }
        }
        
        // edge transformation
        if (numEdges > 0) {
          ordinal_type ortEdges[12];
          orts(cell).getEdgeOrientation(ortEdges, numEdges);
          
          // apply coeff matrix
          for (auto edgeId=0;edgeId<numEdges;++edgeId) {
            const auto ordEdge = tagToOrdinal(1, edgeId, 0);
            
            if (ordEdge != -1) {
              const auto ndofEdge = ordinalToTag(ordEdge, 3);
              const auto mat = Kokkos::subview(matData, 
                                               edgeId, ortEdges[edgeId], 
                                               Kokkos::ALL(), Kokkos::ALL());
              
              for (auto j=0;j<numPoints;++j) 
                for (auto i=0;i<ndofEdge;++i) {
                  const auto ii = tagToOrdinal(1, edgeId, i);
                  
                  for (auto k=0;k<dimBasis;++k) {
                    double temp = 0.0;
                    for (auto l=0;l<ndofEdge;++l) {
                      const auto ll = tagToOrdinal(1, edgeId, l);
                      temp += mat(i,l)*in(ll, j, k);
                    }
                    out(ii, j, k) = temp;
                  }
                }
            }
          }
        }
        
        // face transformation
        if (numFaces > 0) {
          ordinal_type ortFaces[12];
          orts(cell).getFaceOrientation(ortFaces, numFaces);
          
          // apply coeff matrix
          for (auto faceId=0;faceId<numFaces;++faceId) {
            const auto ordFace = tagToOrdinal(2, faceId, 0);
            
            if (ordFace != -1) {
              const auto ndofFace = ordinalToTag(ordFace, 3);
              const auto mat = Kokkos::subview(matData, 
                                               numEdges+faceId, ortFaces[faceId], 
                                               Kokkos::ALL(), Kokkos::ALL());
              
              for (auto j=0;j<numPoints;++j) 
                for (auto i=0;i<ndofFace;++i) {
                  const auto ii = tagToOrdinal(2, faceId, i);
                  
                  for (auto k=0;k<dimBasis;++k) {
                    double temp = 0.0;
                    for (auto l=0;l<ndofFace;++l) {
                      const auto ll = tagToOrdinal(2, faceId, l);
                      temp += mat(i,l)*in(ll, j, k);
                    }
                    out(ii, j, k) = temp;
                  }
                }
            }
          }
        }
        
      }
    } else {
      Kokkos::deep_copy(output, input);      
    }
  }
Beispiel #2
0
// ----------------------------------------------------------------------
// Main
//
//
int main(int argc, char *argv[]) {
  Teuchos::GlobalMPISession mpiSession(&argc, &argv);

  Kokkos::initialize();

  // This little trick lets us print to std::cout only if a (dummy) command-line argument is provided.
  int iprint = argc - 1;

  for (int i=0;i<argc;++i) {
    if ((strcmp(argv[i],"--nelement")          == 0)) { nelement = atoi(argv[++i]); continue;}
    if ((strcmp(argv[i],"--apply-orientation") == 0)) { apply_orientation  = atoi(argv[++i]); continue;}
    if ((strcmp(argv[i],"--verbose")           == 0)) { verbose  = atoi(argv[++i]); continue;}
    if ((strcmp(argv[i],"--maxp")              == 0)) { maxp     = atoi(argv[++i]); continue;}
  }

  Teuchos::RCP<std::ostream> outStream;
  Teuchos::oblackholestream bhs; // outputs nothing

  if (iprint > 0)
    outStream = Teuchos::rcp(&std::cout, false);
  else
    outStream = Teuchos::rcp(&bhs, false);

  // Save the format state of the original std::cout.
  Teuchos::oblackholestream oldFormatState;
  oldFormatState.copyfmt(std::cout);
  *outStream << std::scientific;
  *outStream \
    << "===============================================================================\n" \
    << "|                                                                             |\n" \
    << "|                    Unit Test (Basis_HGRAD_TRI_Cn_FEM)                       |\n" \
    << "|                                                                             |\n" \
    << "|     1) Patch test involving mass and stiffness matrices,                    |\n" \
    << "|        for the Neumann problem on a triangular patch                        |\n" \
    << "|        Omega with boundary Gamma.                                           |\n" \
    << "|                                                                             |\n" \
    << "|        - div (grad u) + u = f  in Omega,  (grad u) . n = g  on Gamma        |\n" \
    << "|                                                                             |\n" \
    << "|  Questions? Contact  Pavel Bochev  ([email protected]),                    |\n" \
    << "|                      Denis Ridzal  ([email protected]),                    |\n" \
    << "|                      Kara Peterson ([email protected]).                    |\n" \
    << "|                      Kyungjoo Kim  ([email protected]).                     |\n" \
    << "|                                                                             |\n" \
    << "|  Intrepid's website: http://trilinos.sandia.gov/packages/intrepid           |\n" \
    << "|  Trilinos website:   http://trilinos.sandia.gov                             |\n" \
    << "|                                                                             |\n" \
    << "===============================================================================\n" \
    << "| TEST 4: Patch test for high order assembly                                  |\n" \
    << "===============================================================================\n";

  int r_val = 0;

  // precision control
  outStream->precision(3);

#if defined( INTREPID_USING_EXPERIMENTAL_HIGH_ORDER )

  try {
    // test setup
    const int ndim = 2;
    FieldContainer<value_type> base_nodes(1, 4, ndim);
    base_nodes(0, 0, 0) = 0.0;
    base_nodes(0, 0, 1) = 0.0;

    base_nodes(0, 1, 0) = 1.0;
    base_nodes(0, 1, 1) = 0.0;

    base_nodes(0, 2, 0) = 0.0;
    base_nodes(0, 2, 1) = 1.0;

    base_nodes(0, 3, 0) = 1.0;
    base_nodes(0, 3, 1) = 1.0;

    // element 0 has globally permuted edge node
    const int elt_0[2][3] = { { 0, 1, 2 },
                              { 0, 2, 1 } };
    
    // element 1 is locally permuted
    int elt_1[3] = { 1, 2, 3 };

    DefaultCubatureFactory<value_type> cubature_factory;

    // for all test orders
    for (int nx=0;nx<=maxp;++nx) {
      for (int ny=0;ny<=maxp-nx;++ny) {
        // polynomial order of approximation
        const int minp = std::max(nx+ny, 1);

        // test for all basis above order p
        const EPointType pointtype[] = { POINTTYPE_EQUISPACED, POINTTYPE_WARPBLEND };
        for (int ptype=0;ptype<2;++ptype) {
          for (int p=minp;p<=maxp;++p) {
            *outStream << "\n"                                              \
                       << "===============================================================================\n" \
                       << "  Order (nx,ny,p) = " << nx << ", " << ny << ", " << p << " , PointType = " << EPointTypeToString(pointtype[ptype]) << "\n" \
                       << "===============================================================================\n";

            BasisSet_HGRAD_TRI_Cn_FEM<value_type,FieldContainer<value_type> > basis_set(p, pointtype[ptype]);
            const auto& basis = basis_set.getCellBasis();
            const shards::CellTopology cell = basis.getBaseCellTopology();

            const int nbf = basis.getCardinality();

            const int nvert = cell.getVertexCount();
            const int nedge = cell.getEdgeCount();

            FieldContainer<value_type> nodes(1, 4, ndim);
            FieldContainer<value_type> cell_nodes(1, nvert, ndim);

            // ignore the subdimension; the matrix is always considered as 1D array
            FieldContainer<value_type> A(1, nbf, nbf), b(1, nbf);

            // ***** Test for different orientations *****
            for (int conf0=0;conf0<2;++conf0) {
              for (int ino=0;ino<3;++ino) {
                nodes(0, elt_0[conf0][ino], 0) = base_nodes(0, ino, 0);
                nodes(0, elt_0[conf0][ino], 1) = base_nodes(0, ino, 1);
              }
              nodes(0, 3, 0) = base_nodes(0, 3, 0);
              nodes(0, 3, 1) = base_nodes(0, 3, 1);

              // reset element connectivity
              elt_1[0] = 1;
              elt_1[1] = 2;
              elt_1[2] = 3;

              // for all permuations of element 1
              for (int conf1=0;conf1<6;++conf1) {
                // filter out left handed element
                fill_cell_nodes(cell_nodes, 
                                nodes, 
                                elt_1,
                                nvert, ndim);
                if (OrientationTools<value_type>::isLeftHandedCell(cell_nodes)) {
                  // skip left handed
                } else {
                  const int *element[2] = { elt_0[conf0], elt_1 };
                  *outStream << "\n"                                \
                             << "  Element 0 is configured " << conf0 << " "
                             << "(" << element[0][0] << ","<< element[0][1] << "," << element[0][2] << ")"
                             << "  Element 1 is configured " << conf1 << " "
                             << "(" << element[1][0] << ","<< element[1][1] << "," << element[1][2] << ")"
                             << "\n";

                  if (verbose) {
                    *outStream << " - Element nodal connectivity - \n";
                    for (int iel=0;iel<nelement;++iel)
                      *outStream << " iel = " << std::setw(4) << iel
                                 << ", nodes = "
                                 << std::setw(4) << element[iel][0]
                                 << std::setw(4) << element[iel][1]
                                 << std::setw(4) << element[iel][2]
                                 << "\n";
                  }

                  // Step 0: count one-to-one mapping between high order nodes and dofs
                  Example::ToyMesh mesh;
                  int local2global[2][8][2], boundary[2][3], off_global = 0;

                  const int nnodes_per_element
                    = cell.getVertexCount()
                    + cell.getEdgeCount()
                    + 1;

                  for (int iel=0;iel<nelement;++iel)
                    mesh.getLocalToGlobalMap(local2global[iel], off_global, basis, element[iel]);

                  for (int iel=0;iel<nelement;++iel)
                    mesh.getBoundaryFlags(boundary[iel], cell, element[iel]);

                  if (verbose) {
                    *outStream << " - Element one-to-one local2global map -\n";
                    for (int iel=0;iel<nelement;++iel) {
                      *outStream << " iel = " << std::setw(4) << iel << "\n";
                      for (int i=0;i<(nnodes_per_element+1);++i) {
                        *outStream << "   local = " << std::setw(4) << local2global[iel][i][0]
                                   << "   global = " << std::setw(4) << local2global[iel][i][1]
                                   << "\n";
                      }
                    }
                    *outStream << " - Element boundary flags -\n";
                    const int nside = cell.getSideCount();
                    for (int iel=0;iel<nelement;++iel) {
                      *outStream << " iel = " << std::setw(4) << iel << "\n";
                      for (int i=0;i<nside;++i) {
                        *outStream << "   side = " << std::setw(4) << i
                                   << "   boundary = " << std::setw(4) << boundary[iel][i]
                                   << "\n";
                      }
                    }
                  }

                  // Step 1: assembly
                  const int ndofs = off_global;
                  FieldContainer<value_type> A_asm(1, ndofs, ndofs), b_asm(1, ndofs);

                  for (int iel=0;iel<nelement;++iel) {
                    // Step 1.1: create element matrices
                    Orientation ort = Orientation::getOrientation(cell, element[iel]);

                    // set element nodal coordinates
                    fill_cell_nodes(cell_nodes, 
                                    nodes, 
                                    element[iel], 
                                    nvert, ndim);

                    build_element_matrix_and_rhs(A, b,
                                                 cubature_factory,
                                                 basis_set,
                                                 element[iel],
                                                 boundary[iel],
                                                 cell_nodes,
                                                 ort,
                                                 nx, ny);
                    // if p is bigger than 4, not worth to look at the matrix
                    if (verbose && p < 5) {
                      *outStream << " - Element matrix and rhs, iel = " << iel << "\n";
                      *outStream << std::showpos;
                      for (int i=0;i<nbf;++i) {
                        for (int j=0;j<nbf;++j)
                          *outStream << MatVal(A, i, j) << " ";
                        *outStream << ":: " << MatVal(b, i, 0) << "\n";
                      }
                      *outStream << std::noshowpos;
                    }

                    // Step 1.2: assemble high order elements
                    assemble_element_matrix_and_rhs(A_asm, b_asm,
                                                    A, b,
                                                    local2global[iel],
                                                    nnodes_per_element);
                  }

                  if (verbose && p < 5) {
                    *outStream << " - Assembled element matrix and rhs -\n";
                    *outStream << std::showpos;
                    for (int i=0;i<ndofs;++i) {
                      for (int j=0;j<ndofs;++j)
                        *outStream << MatVal(A_asm, i, j) << " ";
                      *outStream << ":: " << MatVal(b_asm, i, 0) << "\n";
                    }
                    *outStream << std::noshowpos;
                  }

                  // Step 2: solve the system of equations
                  int info = 0;
                  Teuchos::LAPACK<int,value_type> lapack;
                  FieldContainer<int> ipiv(ndofs);
                  lapack.GESV(ndofs, 1, &A_asm(0,0,0), ndofs, &ipiv(0,0), &b_asm(0,0), ndofs, &info);
                  TEUCHOS_TEST_FOR_EXCEPTION( info != 0, std::runtime_error,
                                              ">>> ERROR (Intrepid::HGRAD_TRI_Cn::Test 04): " \
                                              "LAPACK solve fails");

                  // Step 3: construct interpolant and check solutions
                  magnitude_type interpolation_error = 0, solution_norm =0;
                  for (int iel=0;iel<nelement;++iel) {
                    retrieve_element_solution(b,
                                              b_asm,
                                              local2global[iel],
                                              nnodes_per_element);

                    if (verbose && p < 5) {
                      *outStream << " - Element solution, iel = " << iel << "\n";
                      *outStream << std::showpos;
                      for (int i=0;i<nbf;++i) {
                        *outStream << MatVal(b, i, 0) << "\n";
                      }
                      *outStream << std::noshowpos;
                    }

                    magnitude_type
                      element_interpolation_error = 0,
                      element_solution_norm = 0;

                    Orientation ort = Orientation::getOrientation(cell, element[iel]);

                    // set element nodal coordinates
                    fill_cell_nodes(cell_nodes, 
                                    nodes, 
                                    element[iel], 
                                    nvert, ndim);

                    compute_element_error(element_interpolation_error,
                                          element_solution_norm,
                                          element[iel],
                                          cell_nodes,
                                          basis_set,
                                          b,
                                          ort,
                                          nx, ny);

                    interpolation_error += element_interpolation_error;
                    solution_norm       += element_solution_norm;

                    {
                      int edge_orts[3];
                      ort.getEdgeOrientation(edge_orts, nedge);
                      *outStream << "   iel = " << std::setw(4) << iel
                                 << ", orientation = "
                                 << edge_orts[0]
                                 << edge_orts[1]
                                 << edge_orts[2]
                                 << " , error = " << element_interpolation_error
                                 << " , solution norm = " << element_solution_norm
                                 << " , relative error = " << (element_interpolation_error/element_solution_norm)
                                 << "\n";
                    }
                    const magnitude_type relative_error = interpolation_error/solution_norm;
                    const magnitude_type tol = p*p*100*INTREPID_TOL;

                    if (relative_error > tol) {
                      ++r_val;
                      *outStream << "\n\nPatch test failed: \n"
                                 << "    exact polynomial (nx, ny) = " << std::setw(4) << nx << ", " << std::setw(4) << ny << "\n"
                                 << "    basis order               = " << std::setw(4) << p << "\n"
                                 << "    orientation configuration = " << std::setw(4) << conf0 << std::setw(4) << conf1 << "\n"
                                 << "    relative error            = " << std::setw(4) << relative_error << "\n"
                                 << "    tolerance                 = " << std::setw(4) << tol << "\n";
                    }
                  }
                } 

                // for next iteration
                std::next_permutation(elt_1, elt_1+3);
              } // end of conf1
            } // end of conf0
          } // end of p
        } // end of point type
      } // end of ny
    } // end of nx
  }
  catch (std::logic_error err) {
    *outStream << err.what() << "\n\n";
    r_val = -1000;
  };
#else
  *outStream << "\t This test is for high order element assembly. \n"
             << "\t Use -D INTREPID_USING_EXPERIMENTAL_HIGH_ORDER in CMAKE_CXX_FLAGS \n";
#endif

  if (r_val != 0)
    std::cout << "End Result: TEST FAILED  :: r_val = " << r_val << "\n";
  else
    std::cout << "End Result: TEST PASSED\n";

  // reset format state of std::cout
  std::cout.copyfmt(oldFormatState);

  Kokkos::finalize();

  return r_val;
}
Beispiel #3
0
      void getLocalToGlobalMap(int (*local2global)[2],
                               int &off_global,
                               const Basis<Scalar,ArrayType> &basis,
                               const int *element) {
        const int local = 0, global = 1;
        const int nbf = basis.getCardinality();
        const shards::CellTopology cell = basis.getBaseCellTopology();
        const int dim = cell.getDimension();

        int cnt = 0, off_element = 0;
        int subcell_verts[4], nids;

        const int nvert = cell.getVertexCount();
        for (int i=0;i<nvert;++i) {
          const int ord_vert = (off_element < nbf ? basis.getDofOrdinal(0, i, 0) : 0);
          const int dof_vert = (off_element < nbf ? basis.getDofTag(ord_vert)[3] : 0);
      
          local2global[cnt][local] = off_element;
          off_element += dof_vert;
          Orientation::getElementNodeMap(subcell_verts, nids,
                                         cell, element,
                                         0, i);
      
          if (!findNode(local2global[cnt][global], subcell_verts, nids, true)) {
            addNode(subcell_verts, nids, off_global);
            local2global[cnt][global] = off_global;
            off_global += dof_vert;
          }
          ++cnt;
        }
        const int nedge = cell.getEdgeCount();
        for (int i=0;i<nedge;++i) {
          const int ord_edge = (off_element < nbf ? basis.getDofOrdinal(1, i, 0) : 0);
          const int dof_edge = (off_element < nbf ? basis.getDofTag(ord_edge)[3] : 0);
      
          local2global[cnt][local] = off_element;
          off_element += dof_edge;
          Orientation::getElementNodeMap(subcell_verts, nids,
                                         cell, element,
                                         1, i);
      
          if (!findNode(local2global[cnt][global], subcell_verts, nids, true)) {
            addNode(subcell_verts, nids, off_global);
            local2global[cnt][global] = off_global;
            off_global += dof_edge;
          }
          ++cnt;
        }
        const int nface = cell.getFaceCount();
        for (int i=0;i<nface;++i) {
          const int ord_face = (off_element < nbf ? basis.getDofOrdinal(2, i, 0) : 0);
          const int dof_face = (off_element < nbf ? basis.getDofTag(ord_face)[3] : 0);
      
          local2global[cnt][local] = off_element;
          off_element += dof_face;
          Orientation::getElementNodeMap(subcell_verts, nids,
                                         cell, element,
                                         2, i);
      
          if (!findNode(local2global[cnt][global], subcell_verts, nids, true)) {
            addNode(subcell_verts, nids, off_global);
            local2global[cnt][global] = off_global;
            off_global += dof_face;
          }
          ++cnt;
        }
        {
          const int i = 0;
          const int ord_intr = (off_element < nbf ? basis.getDofOrdinal(dim, i, 0) : 0);
          const int dof_intr = (off_element < nbf ? basis.getDofTag(ord_intr)[3]   : 0);
      
          local2global[cnt][local] = off_element;
          off_element += dof_intr;
          Orientation::getElementNodeMap(subcell_verts, nids,
                                         cell, element,
                                         dim, i);
      
          if (!findNode(local2global[cnt][global], subcell_verts, nids, true)) {
            addNode(subcell_verts, nids, off_global);
            local2global[cnt][global] = off_global;
            off_global += dof_intr;
          }
          ++cnt;
        }
    
        // add the last offset
        local2global[cnt][local] = off_element;
        local2global[cnt][global] = -1; // invalid values
      }
Beispiel #4
0
/** \brief Tests for experimental assembly procedure matching basis values.
    \param argc [in] - number of command-line arguments
    \param argv [in] - command-line arguments
*/
int main(int argc, char *argv[]) {
  Teuchos::GlobalMPISession mpiSession(&argc, &argv);

  Kokkos::initialize();

  // This little trick lets us print to std::cout only if a (dummy) command-line argument is provided.
  int iprint = argc - 1;

  bool verbose = false;
  int  maxp = INTREPID2_MAX_ORDER;
  for (int i=0;i<argc;++i) {
    if ((strcmp(argv[i],"--verbose")           == 0)) { verbose  = atoi(argv[++i]); continue;}
    if ((strcmp(argv[i],"--maxp")              == 0)) { maxp     = atoi(argv[++i]); continue;}
  }
  
  Teuchos::RCP<std::ostream> outStream;
  Teuchos::oblackholestream bhs; // outputs nothing

  if (iprint > 0)
    outStream = Teuchos::rcp(&std::cout, false);
  else
    outStream = Teuchos::rcp(&bhs, false);

  // Save the format state of the original std::cout.
  Teuchos::oblackholestream oldFormatState;
  oldFormatState.copyfmt(std::cout);

  *outStream \
    << "===============================================================================\n" \
    << "|                                                                             |\n" \
    << "|                           Unit Test HGRAD_TET_Cn_FEM                        |\n" \
    << "|                                                                             |\n" \
    << "|     1) High order assembly                                                  |\n" \
    << "|                                                                             |\n" \
    << "|  Questions? Contact  Pavel Bochev ([email protected]) or                   |\n" \
    << "|                      Denis Ridzal ([email protected]) or                   |\n" \
    << "|                      Robert Kirby ([email protected]) or               |\n" \
    << "|                      Kyungjoo Kim ([email protected])                       |\n" \
    << "|                                                                             |\n" \
    << "|  Intrepid's website: http://trilinos.sandia.gov/packages/intrepid           |\n" \
    << "|  Trilinos website:   http://trilinos.sandia.gov                             |\n" \
    << "|                                                                             |\n" \
    << "===============================================================================\n";


  int r_val = 0;

#if defined( INTREPID_USING_EXPERIMENTAL_HIGH_ORDER )
  typedef double value_type;

  // Let's instantiate a basis
  try {
    OrientationTools<value_type>::verboseStreamPtr = outStream.get();
    //for (int test_order=1;test_order<=10;++test_order) {
    for (int test_order=1;test_order<=maxp;++test_order) {
      // Step 0 : construct basis function set
      const int order = test_order;
      
      BasisSet_HGRAD_TET_Cn_FEM<double,FieldContainer<double> > basis_set(order , POINTTYPE_EQUISPACED);
      const auto& cell_basis = basis_set.getCellBasis();
      const auto& face_basis = basis_set.getTriangleBasis();
      
      const shards::CellTopology cell_topo = cell_basis.getBaseCellTopology();
      const shards::CellTopology face_topo = face_basis.getBaseCellTopology();
      
      const int nbf_cell = cell_basis.getCardinality();
      const int nbf_face = face_basis.getCardinality();
      
      const int ndim_cell  = cell_topo.getDimension();
      const int ndim_face  = face_topo.getDimension();
      
      const int npts = PointTools::getLatticeSize(face_topo, order, 1);
      
      for (int test_face=0;test_face<4;++test_face) {
        // tricky part
        const bool left_handed = cell_topo.getNodeMap(2, test_face, 1) > cell_topo.getNodeMap(2, test_face, 2);

        for (int test_ort=0;test_ort<6;++test_ort) {
          *outStream << "\n"                                            \
                     << "===============================================================================\n" \
                     << "  Order = " << test_order << " , Face = " << test_face << " , Orientation = " << test_ort << "\n" \
                     << "===============================================================================\n";
          
          // Step 1 : create reference and modified triangle points

          // reference triangle points
          FieldContainer<value_type> ref_face_pts(npts, ndim_face);
          PointTools::getLattice<value_type>(ref_face_pts,
                                             face_topo,
                                             order, 1);

          // modified triangle points
          const int left_ort[] = { 0, 2, 1, 3, 5, 4 };
          FieldContainer<value_type> ort_face_pts(npts, ndim_face);
          OrientationTools<value_type>::mapToModifiedReference(ort_face_pts,
                                                               ref_face_pts,
                                                               face_topo,
                                                               (left_handed ? left_ort[test_ort] : test_ort));


          // Step 2 : map face points to cell points appropriately
          const int nface = cell_topo.getFaceCount();

          // create orientation object
          int orts[4] = {};
          orts[test_face] = test_ort;
          
          Orientation ort;
          ort.setFaceOrientation(nface, orts);

          // map triangle points and modified points to reference coordinates
          FieldContainer<value_type> ref_cell_pts(npts, ndim_cell);
          CellTools<value_type>::mapToReferenceSubcell(ref_cell_pts,
                                                       ref_face_pts,
                                                       ndim_face,
                                                       test_face,
                                                       cell_topo);

          // Step 3 : evaluate modified basis functions with orientation for reference cell points
          FieldContainer<double> ort_cell_vals(nbf_cell, npts);
          {
            // temporary cell workspace
            FieldContainer<double> tmp_cell_vals(nbf_cell, npts);

            cell_basis.getValues(tmp_cell_vals, ref_cell_pts, OPERATOR_VALUE);
            OrientationTools<value_type>::getBasisFunctionsByTopology(ort_cell_vals,
                                                                      tmp_cell_vals,
                                                                      cell_basis);

            for (int i=0;i<nbf_cell;++i)
              for (int j=0;j<npts;++j)
                tmp_cell_vals(i, j) = ort_cell_vals(i, j);

            OrientationTools<value_type>::verbose = verbose;
            OrientationTools<value_type>::reverse = true; // for point matching only
            OrientationTools<value_type>::getModifiedBasisFunctions(ort_cell_vals,
                                                                    tmp_cell_vals,
                                                                    basis_set,
                                                                    ort);
            OrientationTools<value_type>::verbose = false;
          }

          // Step 4 : evaluate reference face basis functions for modified face points
          FieldContainer<double> ref_face_vals(nbf_face, npts);
          {
            // temporary face workspace
            FieldContainer<double> tmp_face_vals(nbf_face, npts);

            face_basis.getValues(tmp_face_vals, ort_face_pts, OPERATOR_VALUE);
            OrientationTools<value_type>::getBasisFunctionsByTopology(ref_face_vals,
                                                                      tmp_face_vals,
                                                                      face_basis);
          }

          // Step 5 : compare the basis functions to face functions
          {
            // strip the range of cell DOFs
            int off_cell = 0;
            {
              const int nvert = cell_topo.getVertexCount();
              for (int i=0;i<nvert;++i) {
                const int ord_vert = cell_basis.getDofOrdinal(0, i, 0);
                off_cell += cell_basis.getDofTag(ord_vert)[3];
              }
              if (off_cell < nbf_cell) {
                const int nedge = cell_topo.getEdgeCount();
                for (int i=0;i<nedge;++i) {
                  const int ord_edge = cell_basis.getDofOrdinal(1, i, 0);
                  off_cell += cell_basis.getDofTag(ord_edge)[3];
                }
              }
              if (off_cell < nbf_cell) {
                for (int i=0;i<test_face;++i) {
                  const int ord_face = cell_basis.getDofOrdinal(2, i, 0);
                  off_cell += cell_basis.getDofTag(ord_face)[3];
                }
              }
            }

            // strip the range of face DOFs
            int off_face = 0;
            {
              const int nvert = face_topo.getVertexCount();
              for (int i=0;i<nvert;++i) {
                const int ord_vert = face_basis.getDofOrdinal(0, i, 0);
                off_face += face_basis.getDofTag(ord_vert)[3];
              }
              if (off_face < nbf_face) {
                const int nedge = face_topo.getEdgeCount();
                for (int i=0;i<nedge;++i) {
                  const int ord_edge = face_basis.getDofOrdinal(1, i, 0);
                  off_face += face_basis.getDofTag(ord_edge)[3];
                }
              }
            }

            const int ndof = nbf_face - off_face;
            for (int i=0;i<ndof;++i) {
              for (int j=0;j<npts;++j) {
                const value_type diff = std::abs(ort_cell_vals(i+off_cell,j) - ref_face_vals(i+off_face,j));
                if (diff > INTREPID_TOL) {
                  ++r_val;
                  *outStream << std::setw(70) << "^^^^----FAILURE!" << "\n";
                  *outStream << " Basis function " << i << " at point " << j << " does not match each other\n";
                }
              }
            }
          }
        } // test ort
      } // test face
    } // test order
  }
  catch (std::exception err) {
    std::cout << " Exeption\n";
    *outStream << err.what() << "\n\n";
    r_val = -1000;
  }
#else
  *outStream << "\t This test is for high order element assembly. \n"
             << "\t Use -D INTREPID_USING_EXPERIMENTAL_HIGH_ORDER in CMAKE_CXX_FLAGS \n";
#endif
  if (r_val != 0)
    std::cout << "End Result: TEST FAILED, r_val = " << r_val << "\n";
  else
    std::cout << "End Result: TEST PASSED\n";

  // reset format state of std::cout
  std::cout.copyfmt(oldFormatState);

  Kokkos::finalize();

  return r_val;
}