diff --git a/.gitmodules b/.gitmodules index 2afd5b7d..c0b643c1 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,4 @@ [submodule "MATAR"] path = matar url = https://github.com/lanl/MATAR.git - branch = main + branch = main \ No newline at end of file diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index bb673b82..819c57ba 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -4,4 +4,5 @@ add_subdirectory(average) add_subdirectory(decomp_example) add_subdirectory(point_connectivity) add_subdirectory(mesh_form) +add_subdirectory(reference_element) # add_subdirectory(other_example) # Future examples go here \ No newline at end of file diff --git a/examples/average/include/mesh_io.h b/examples/average/include/mesh_io.h index f184a635..7affdd1c 100644 --- a/examples/average/include/mesh_io.h +++ b/examples/average/include/mesh_io.h @@ -142,7 +142,7 @@ inline int PointIndexFromIJK(int i, int j, int k, const int* order) /// ///////////////////////////////////////////////////////////////////////////// void build_3d_box( - swage::Mesh& mesh, + swage::Mesh_t& mesh, node_t& node, double origin[3], double length[3], @@ -212,7 +212,7 @@ void build_3d_box( node.coords.update_host(); // initialize elem variables - mesh.initialize_elems(num_elems, num_dim); + mesh.initialize_elems(num_elems); // populate the point data structures FOR_ALL(k, 0, num_elems_k, @@ -268,7 +268,7 @@ void build_3d_box( /// \param rank rank /// ///////////////////////////////////////////////////////////////////////////// - void write_vtk(swage::Mesh& mesh, + void write_vtk(swage::Mesh_t& mesh, node_t& node, int rank) { @@ -526,7 +526,7 @@ void build_3d_box( /// \param comm MPI communicator /// ///////////////////////////////////////////////////////////////////////////// -void write_vtu(swage::Mesh& mesh, +void write_vtu(swage::Mesh_t& mesh, node_t& node, GaussPoint_t& gauss_point, int rank, @@ -843,7 +843,7 @@ void write_vtu(swage::Mesh& mesh, /// \param Number of dimensions /// ///////////////////////////////////////////////////////////////////////////// - void read_vtk_mesh(swage::Mesh& mesh, + void read_vtk_mesh(swage::Mesh_t& mesh, node_t& node, int num_dims, std::string mesh_file_) @@ -942,7 +942,7 @@ void write_vtu(swage::Mesh& mesh, printf("Number of elements read in %zu\n", num_elem); // initialize elem variables - mesh.initialize_elems(num_elem, num_dims); + mesh.initialize_elems(num_elem); found=true; } // end if @@ -1001,9 +1001,9 @@ void write_vtu(swage::Mesh& mesh, mesh.nodes_in_elem.update_device(); - // initialize corner variables - size_t num_corners = num_elem * num_nodes_in_elem; - mesh.initialize_corners(num_corners); + // initialize corner variables (it is set in the elems initialization) + //size_t num_corners = num_elem * num_nodes_in_elem; + //mesh.initialize_corners(num_corners); // Build connectivity diff --git a/examples/average/src/average.cpp b/examples/average/src/average.cpp index bfedc2a9..70515622 100644 --- a/examples/average/src/average.cpp +++ b/examples/average/src/average.cpp @@ -44,14 +44,18 @@ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "state.h" + int main(int argc, char** argv) { MATAR_INITIALIZE(argc, argv); { // MATAR scope std::cout<<"Hello, Average Example!"<& node_coords, double origin[3], double length[3], @@ -243,11 +243,11 @@ void build_3d_box( node_coords.update_host(); // initialize elem variables - if (Pn_order == 0){ - mesh.initialize_elems(num_elems, num_dim); + if (Pn_order == 0){ // BUG: Pn_order=1 is needed for coding above to work correctly + mesh.initialize_elems(num_elems); Pn_order = 1; } else { - mesh.initialize_elems_Pn(num_elems, num_dim, Pn_order); + mesh.initialize_elems_Pn(num_elems, Pn_order, 2*Pn_order); } // populate the point data structures @@ -321,7 +321,7 @@ void build_3d_box( /// ///////////////////////////////////////////////////////////////////////////// void build_2d_polar( - swage::Mesh& mesh, + swage::Mesh_t& mesh, MPICArrayKokkos& node_coords, double& inner_radius, double& outer_radius, @@ -398,7 +398,7 @@ void build_2d_polar( node_coords.update_device(); // initialize elem variables - mesh.initialize_elems(num_elems, num_dim); + mesh.initialize_elems(num_elems); // populate the elem center data structures for (int j = 0; j < num_elems_j; j++) { @@ -432,9 +432,9 @@ void build_2d_polar( // update device side mesh.nodes_in_elem.update_device(); - // intialize corner variables - int num_corners = num_elems * mesh.num_nodes_in_elem; - mesh.initialize_corners(num_corners); + // intialize corner variables (this is initialized in initialize_elems) + //int num_corners = num_elems * mesh.num_nodes_in_elem; + //mesh.initialize_corners(num_corners); // corner.initialize(num_corners, num_dim); // Build connectivity @@ -455,7 +455,7 @@ void build_2d_polar( /// \param comm MPI communicator /// ///////////////////////////////////////////////////////////////////////////// -void write_vtu(swage::Mesh& mesh, +void write_vtu(swage::Mesh_t& mesh, node_t& node, GaussPoint_t& gauss_point, int rank, diff --git a/examples/decomp_example/src/mesh_decomp_example.cpp b/examples/decomp_example/src/mesh_decomp_example.cpp index 7fad86d7..a10177c1 100644 --- a/examples/decomp_example/src/mesh_decomp_example.cpp +++ b/examples/decomp_example/src/mesh_decomp_example.cpp @@ -59,11 +59,13 @@ int main(int argc, char** argv) { int num_elems_j = 40; // Initial mesh built on rank zero - swage::Mesh initial_mesh; + swage::Mesh_t initial_mesh; + initial_mesh.num_dims = num_dims; MPICArrayKokkos initial_node_coords; // Mesh partitioned by pt-scotch, including ghost - swage::Mesh final_mesh; + swage::Mesh_t final_mesh; + final_mesh.num_dims = num_dims; node_t final_node; MPICArrayKokkos final_node_coords; diff --git a/examples/mesh_form/include/mesh_io.h b/examples/mesh_form/include/mesh_io.h index 69a6e490..261092d2 100644 --- a/examples/mesh_form/include/mesh_io.h +++ b/examples/mesh_form/include/mesh_io.h @@ -164,7 +164,7 @@ int PointIndexFromIJK(int i, int j, int k, const int* order) /// ///////////////////////////////////////////////////////////////////////////// void build_3d_box( - swage::Mesh& mesh, + swage::Mesh_t& mesh, DCArrayKokkos& node_coords, double origin[3], double length[3], @@ -251,9 +251,9 @@ void build_3d_box( // initialize elem variables if (is_linear){ - mesh.initialize_elems(num_elems, num_dim); + mesh.initialize_elems(num_elems); } else { - mesh.initialize_elems_Pn(num_elems, num_dim, Pn_order); + mesh.initialize_elems_Pn(num_elems, Pn_order, 2*Pn_order); } // populate the point data structures @@ -310,8 +310,9 @@ void build_3d_box( // initialize corner variables (corner = element-node pair) // used for per-corner data like corner_delta in mesh_mold.cpp - int num_corners = num_elems * mesh.num_nodes_in_elem; - mesh.initialize_corners(num_corners); + // the corners are initialized in initialize_elems + //int num_corners = num_elems * mesh.num_nodes_in_elem; + //mesh.initialize_corners(num_corners); // Build connectivity mesh.build_connectivity(); @@ -332,7 +333,7 @@ void build_3d_box( /// ///////////////////////////////////////////////////////////////////////////// void build_2d_polar( - swage::Mesh& mesh, + swage::Mesh_t& mesh, MPICArrayKokkos& node_coords, double& inner_radius, double& outer_radius, @@ -409,7 +410,7 @@ void build_2d_polar( node_coords.update_device(); // initialize elem variables - mesh.initialize_elems(num_elems, num_dim); + mesh.initialize_elems(num_elems); // populate the elem center data structures for (int j = 0; j < num_elems_j; j++) { @@ -444,8 +445,9 @@ void build_2d_polar( mesh.nodes_in_elem.update_device(); // intialize corner variables - int num_corners = num_elems * mesh.num_nodes_in_elem; - mesh.initialize_corners(num_corners); + // this is initialized in initialize_elems + //int num_corners = num_elems * mesh.num_nodes_in_elem; + //mesh.initialize_corners(num_corners); // corner.initialize(num_corners, num_dim); // Build connectivity @@ -466,7 +468,7 @@ void build_2d_polar( /// \param comm MPI communicator /// ///////////////////////////////////////////////////////////////////////////// -void write_vtu(swage::Mesh& mesh, +void write_vtu(swage::Mesh_t& mesh, node_t& node, GaussPoint_t& gauss_point, int rank, @@ -864,7 +866,7 @@ void write_vtu(swage::Mesh& mesh, /// \param Number of dimensions /// ///////////////////////////////////////////////////////////////////////////// -void read_vtk_mesh(swage::Mesh& mesh, +void read_vtk_mesh(swage::Mesh_t& mesh, DCArrayKokkos& node_coords, int num_dims, std::string mesh_file_) @@ -964,7 +966,7 @@ found=false; throw std::invalid_argument("Failed to parse CELLS header line: \"" + line + "\""); } printf("Number of elements read in %zu\n", num_elem); - mesh.initialize_elems(num_elem, num_dims); + mesh.initialize_elems(num_elem); found = true; break; } @@ -1097,8 +1099,9 @@ mesh.nodes_in_elem.update_device(); // initialize corner variables -size_t num_corners = num_elem * num_nodes_in_elem; -mesh.initialize_corners(num_corners); +// this is initialized in initialize_elems +//size_t num_corners = num_elem * num_nodes_in_elem; +//mesh.initialize_corners(num_corners); // Build connectivity diff --git a/examples/mesh_form/src/mesh_form.cpp b/examples/mesh_form/src/mesh_form.cpp index b6e0f0d9..fee50ebb 100644 --- a/examples/mesh_form/src/mesh_form.cpp +++ b/examples/mesh_form/src/mesh_form.cpp @@ -68,7 +68,7 @@ int main(int argc, char** argv) { // Initial mesh built on rank zero - swage::Mesh mesh; + swage::Mesh_t mesh; node_t node; DCArrayKokkos node_coords; diff --git a/examples/reference_element/CMakeLists.txt b/examples/reference_element/CMakeLists.txt new file mode 100644 index 00000000..464116fc --- /dev/null +++ b/examples/reference_element/CMakeLists.txt @@ -0,0 +1,37 @@ +# examples/average/CMakeLists.txt + +# 1. Define the executable +# Point directly to the source file inside 'src/' +add_executable(remap_fv_test src/remap_fv_test.cpp) +add_executable(remap_dg_test src/remap_dg_test.cpp) +add_executable(ref_flux_test src/ref_flux_test.cpp) +add_executable(ref_plus_mesh_test src/ref_plus_mesh_test.cpp) +add_executable(integration_test src/integration_test.cpp) +add_executable(gradient_test src/gradient_test.cpp) +add_executable(interpolation_test src/interpolation_test.cpp) +add_executable(kronecker_delta_test src/kronecker_delta_test.cpp) +add_executable(partition_unity_test src/partition_unity_test.cpp) + +# 2. Add this example's specific include path +# This allows main.cpp to find headers in examples/point_connectivity/include/ +target_include_directories(remap_fv_test PRIVATE include) +target_include_directories(remap_dg_test PRIVATE include) +target_include_directories(ref_flux_test PRIVATE include) +target_include_directories(ref_plus_mesh_test PRIVATE include) +target_include_directories(integration_test PRIVATE include) +target_include_directories(gradient_test PRIVATE include) +target_include_directories(interpolation_test PRIVATE include) +target_include_directories(kronecker_delta_test PRIVATE include) +target_include_directories(partition_unity_test PRIVATE include) + +# 3. Link against the main library (ELEMENTS) +# This pulls in Kokkos, MATAR, and the main library headers automatically. +target_link_libraries(remap_fv_test PRIVATE ELEMENTS) +target_link_libraries(remap_dg_test PRIVATE ELEMENTS) +target_link_libraries(ref_flux_test PRIVATE ELEMENTS) +target_link_libraries(ref_plus_mesh_test PRIVATE ELEMENTS) +target_link_libraries(integration_test PRIVATE ELEMENTS) +target_link_libraries(gradient_test PRIVATE ELEMENTS) +target_link_libraries(interpolation_test PRIVATE ELEMENTS) +target_link_libraries(kronecker_delta_test PRIVATE ELEMENTS) +target_link_libraries(partition_unity_test PRIVATE ELEMENTS) \ No newline at end of file diff --git a/examples/reference_element/src/gradient_test.cpp b/examples/reference_element/src/gradient_test.cpp new file mode 100644 index 00000000..14d803d4 --- /dev/null +++ b/examples/reference_element/src/gradient_test.cpp @@ -0,0 +1,443 @@ +/********************************************************************************************** +© 2020. Triad National Security, LLC. All rights reserved. +This program was produced under U.S. Government contract 89233218CNA000001 for Los Alamos +National Laboratory (LANL), which is operated by Triad National Security, LLC for the U.S. +Department of Energy/National Nuclear Security Administration. All rights in the program are +reserved by Triad National Security, LLC, and the U.S. Department of Energy/National Nuclear +Security Administration. The Government is granted for itself and others acting on its behalf a +nonexclusive, paid-up, irrevocable worldwide license in this material to reproduce, prepare +derivative works, distribute copies to the public, perform publicly and display publicly, and +to permit others to do so. +This program is open source under the BSD-3 License. +Redistribution and use in source and binary forms, with or without modification, are permitted +provided that the following conditions are met: +1. Redistributions of source code must retain the above copyright notice, this list of +conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, this list of +conditions and the following disclaimer in the documentation and/or other materials +provided with the distribution. +3. Neither the name of the copyright holder nor the names of its contributors may be used +to endorse or promote products derived from this software without specific prior +written permission. +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**********************************************************************************************/ +#include +#include +#include + +// This pulls in kokkos, matar, mesh, hash, ref_elem stuff, and PT-Scotch +#include "ELEMENTS.h" + +//#undef NDEBUG // Ensures NDEBUG is turned off + +using namespace mtr; +using namespace swage; // unstructured mesh and hash +using namespace elements; + +const bool Verbose = false; +const size_t max_num = 19; // max number of quadrature points to test up to, the limit is 19 +const size_t max_order = 19; // max polynomial order to test, limit is 19th-order with Legendre + +// polynomial with terms <= p_order +KOKKOS_INLINE_FUNCTION +double polynomial(const CArrayKokkos &coeff, const double x, const size_t p_order){ + + double result = 0.0; + for (int i = 0; i <= p_order; ++i) { + result += coeff(i) * pow(x, (double)i); + } + return result; +} // end polynomial + +KOKKOS_INLINE_FUNCTION +double polynomial(const CArrayKokkos &coeff, const double x, const double y, const size_t p_order){ + + double result = 0.0; + for (int j = 0; j <= p_order; ++j) + for (int i = 0; i <= p_order-j; ++i) { + result += coeff(i,j) * pow(x, (double)i) * pow(y, (double)j); + + } // end for + return result; +} // end polynomial + +KOKKOS_INLINE_FUNCTION +double polynomial(const CArrayKokkos &coeff, const double x, const double y, const double z, const size_t p_order){ + + double result = 0.0; + for (int k = 0; k <= p_order; ++k) + for (int j = 0; j <= p_order-k; ++j) + for (int i = 0; i <= p_order-j-k; ++i) { + result += coeff(i,j,k) * pow(x, (double)i) * pow(y, (double)j) * pow(z, (double)k); + } // end for + + return result; +} // end polynomial + + +// polynomial with terms <= p_order +KOKKOS_INLINE_FUNCTION +double grad_polynomial_x(const CArrayKokkos &coeff, const double x, const size_t p_order){ + + double result = 0.0; + if(p_order==0) return 0.0; + + for (int i = 1; i <= p_order; ++i) { + result += (double)i*coeff(i) * pow(x, (double)i-1); + } + + return result; +} // end polynomial + +// --- 2D grad +KOKKOS_INLINE_FUNCTION +double grad_polynomial_x(const CArrayKokkos &coeff, const double x, const double y, const size_t p_order){ + + double result = 0.0; + if(p_order==0) return 0.0; + + for (int j = 0; j <= p_order; ++j) + for (int i = 1; i <= p_order-j; ++i) { + result += (double)i*coeff(i,j) * pow(x, (double)i-1) * pow(y, (double)j); + } // end for + + return result; +} // end polynomial + +KOKKOS_INLINE_FUNCTION +double grad_polynomial_y(const CArrayKokkos &coeff, const double x, const double y, const size_t p_order){ + + double result = 0.0; + if(p_order==0) return 0.0; + + for (int j = 1; j <= p_order; ++j) + for (int i = 0; i <= p_order-j; ++i) { + result += (double)j*coeff(i,j) * pow(x, (double)i) * pow(y, (double)j-1); + } // end for + + return result; +} // end polynomial + +// --- 3D grad +KOKKOS_INLINE_FUNCTION +double grad_polynomial_x(const CArrayKokkos &coeff, const double x, const double y, const double z, const size_t p_order){ + + double result = 0.0; + if(p_order==0) return 0.0; + + for (int k = 0; k <= p_order; ++k) + for (int j = 0; j <= p_order-k; ++j) + for (int i = 1; i <= p_order-j-k; ++i) { + result += (double)i*coeff(i,j,k) * pow(x, (double)i-1.) * pow(y, (double)j) * pow(z, (double)k); + } // end for + + return result; +} // end polynomial + +KOKKOS_INLINE_FUNCTION +double grad_polynomial_y(const CArrayKokkos &coeff, const double x, const double y, const double z, const size_t p_order){ + + double result = 0.0; + if(p_order==0) return 0.0; + + for (int k = 0; k <= p_order; ++k) + for (int j = 1; j <= p_order-k; ++j) + for (int i = 0; i <= p_order-j-k; ++i) { + result += (double)j*coeff(i,j,k) * pow(x, (double)i) * pow(y, (double)j-1.) * pow(z, (double)k); + } // end for + + return result; +} // end polynomial + +KOKKOS_INLINE_FUNCTION +double grad_polynomial_z(const CArrayKokkos &coeff, const double x, const double y, const double z, const size_t p_order){ + + double result = 0.0; + if(p_order==0) return 0.0; + + for (int k = 1; k <= p_order; ++k) + for (int j = 0; j <= p_order-k; ++j) + for (int i = 0; i <= p_order-j-k; ++i) { + result += (double)k*coeff(i,j,k) * pow(x, (double)i) * pow(y, (double)j) * pow(z, (double)k-1.); + } // end for + + return result; +} // end polynomial + + +// Test: interpolate a polynomial that the basis can represent exactly +void test_gradient(const Quadrature_t& Quad, + const ReferenceElement_t& RefElem) { + + // For polynomial order p, Lagrange basis can represent + // any polynomial of degree <= p exactly + + const size_t p_order = RefElem.num_dofs_1d - 1; + + CArrayKokkos dof_values(RefElem.num_dofs_in_elem); + CArrayKokkos coeff; + + + if(RefElem.elem_dims==1){ + coeff = CArrayKokkos(p_order+1); + } + else if (RefElem.elem_dims==2){ + coeff = CArrayKokkos(p_order+1, p_order+1); + } + else { + coeff = CArrayKokkos(p_order+1, p_order+1, p_order+1); + } + coeff.set_values(0.78914567); // a radom value + + + + // populate polynomial values at DOFs of the ref element + FOR_ALL(dof, 0, RefElem.num_dofs_in_elem, { + + double value=0.0; + if(RefElem.elem_dims==1){ + const double xi = RefElem.dof_positions(dof, 0); + value = polynomial(coeff, xi, p_order); + } + else if (RefElem.elem_dims==2){ + const double xi = RefElem.dof_positions(dof, 0); + const double eta = RefElem.dof_positions(dof, 1); + value = polynomial(coeff, xi, eta, p_order); + } + else { + const double xi = RefElem.dof_positions(dof, 0); + const double eta = RefElem.dof_positions(dof, 1); + const double mu = RefElem.dof_positions(dof, 2); + value = polynomial(coeff, xi, eta, mu, p_order); + } + + dof_values(dof) = value; // the value at the ref elem node + + }); // end parallel + + + // compare interpoloation with exact solution + FOR_ALL(qpt, 0, Quad.num_qpts_in_elem, { + + double sum[3]; // gradient value + sum[0] = 0.0; + sum[1] = 0.0; + sum[2] = 0.0; + + for (size_t dof = 0; dof < RefElem.num_dofs_in_elem; dof++) { + for(size_t dim=0; dim tolerance) { + printf("Error: interpolation failed at qpt id = %d with order = %zu \n", qpt, p_order); + printf("interpolated = %f vs exact value = %f, error = %f \n", sum[dim], exact_value[dim], sum[dim]-exact_value[dim]); + Kokkos::abort("Interpolation failed at quadrature point "); + } + if(Verbose)printf("Grad in dim = %zu: interpolated = %f vs exact value = %f, error = %f \n", dim, sum[dim], exact_value[dim], sum[dim]-exact_value[dim]); + } + + }); // end loop over qpts +} // end function + +int main(int argc, char** argv) { + +MATAR_INITIALIZE(argc, argv); +{ // MATAR scope + + printf("\n--- Gradient tests ---\n"); + + + printf("\n--- DG element with Legendre Quadrature & Legendre DOFs ---\n"); + for(size_t num_qpts_1D = 1; num_qpts_1D<=max_num; num_qpts_1D++){ + + if(Verbose)printf("num quadrature points in 1D = %zu \n", num_qpts_1D); + + Quadrature_t Quad; + + // elem_dims=1,2,3 + for(size_t elem_dims_test = 1; elem_dims_test<=3; elem_dims_test++){ + Quad.initialize_quadrature(reference_space::GaussLegendre, + num_qpts_1D, + elem_dims_test); + + // build reference elements of varing orders, + for (size_t p_order = 0; p_order +#include +#include + +// This pulls in kokkos, matar, mesh, hash, ref_elem stuff, and PT-Scotch +#include "ELEMENTS.h" + +//#undef NDEBUG // Ensures NDEBUG is turned off + +using namespace mtr; +using namespace swage; // unstructured mesh and hash +using namespace elements; + +const bool Verbose = false; +const size_t max_num = 19; // max number of quadrature points to test up to, the limit is 19 +const size_t max_order = 19; // max polynomial order to test, limit is 19th-order with Legendre + + +// polynomial with terms <= p_order +KOKKOS_INLINE_FUNCTION +double polynomial(const CArrayKokkos &coeff, const double x, const size_t p_order){ + + double result = 0.0; + for (int i = 0; i <= p_order; ++i) { + result += coeff(i) * pow(x, (double)i); + } + return result; +} // end polynomial + +// Analytical integration of polynomial over [-1, 1] +KOKKOS_INLINE_FUNCTION +double integrate_polynomial_1D(const CArrayKokkos &coeff, const size_t p_order){ + double result = 0.0; + for (int i = 0; i <= p_order; ++i) { + // Integral of x^i from -1 to 1 + // = [x^(i+1)/(i+1)] from -1 to 1 + // = (1^(i+1) - (-1)^(i+1))/(i+1) + if (i % 2 == 0) { // even power: (-1)^i = 1 + result += coeff(i) * 2.0 / (double)(i + 1); + } + // odd powers integrate to zero over symmetric interval + } + return result; +} // end integrate + +// 2D polynomial for QUADS +KOKKOS_INLINE_FUNCTION +double polynomial(const CArrayKokkos &coeff, const double x, + const double y, const size_t p_order){ + double result = 0.0; + for (int j = 0; j <= p_order; ++j) // Full range + for (int i = 0; i <= p_order; ++i) { // Full range - NO "-j" + result += coeff(i,j) * pow(x, (double)i) * pow(y, (double)j); + } + return result; +} + +// 2D analytical integration for QUADS +KOKKOS_INLINE_FUNCTION +double integrate_polynomial_2D_quad(const CArrayKokkos &coeff, + const size_t p_order){ + double result = 0.0; + for (int j = 0; j <= p_order; ++j) // Full range + for (int i = 0; i <= p_order; ++i) { // Full range - NO "-j" + double xi_integral = 0.0; + double eta_integral = 0.0; + + if (i % 2 == 0) xi_integral = 2.0 / (double)(i + 1); + if (j % 2 == 0) eta_integral = 2.0 / (double)(j + 1); + + result += coeff(i,j) * xi_integral * eta_integral; + } + return result; +} + +// 3D polynomial for HEXES +KOKKOS_INLINE_FUNCTION +double polynomial(const CArrayKokkos &coeff, const double x, + const double y, const double z, const size_t p_order){ + double result = 0.0; + for (int k = 0; k <= p_order; ++k) // Full range + for (int j = 0; j <= p_order; ++j) // Full range + for (int i = 0; i <= p_order; ++i) { // Full range + result += coeff(i,j,k) * pow(x, (double)i) * + pow(y, (double)j) * + pow(z, (double)k); + } + return result; +} + +// 3D analytical integration for HEXES +KOKKOS_INLINE_FUNCTION +double integrate_polynomial_3D_hex(const CArrayKokkos &coeff, + const size_t p_order){ + double result = 0.0; + for (int k = 0; k <= p_order; ++k) // Full range + for (int j = 0; j <= p_order; ++j) // Full range + for (int i = 0; i <= p_order; ++i) { // Full range + double xi_integral = 0.0; + double eta_integral = 0.0; + double mu_integral = 0.0; + + if (i % 2 == 0) xi_integral = 2.0 / (double)(i + 1); + if (j % 2 == 0) eta_integral = 2.0 / (double)(j + 1); + if (k % 2 == 0) mu_integral = 2.0 / (double)(k + 1); + + result += coeff(i,j,k) * xi_integral * eta_integral * mu_integral; + } + return result; +} + + +// Test: interpolate a polynomial that the basis can represent exactly +void test_integration(const Quadrature_t& Quad, + const ReferenceElement_t& RefElem) { + + // For polynomial order p, Lagrange basis can represent + // any polynomial of degree <= p exactly + + const size_t p_order = RefElem.num_dofs_1d - 1; + + CArrayKokkos coeff; + + if(RefElem.elem_dims==1){ + coeff = CArrayKokkos(p_order+1); + } + else if (RefElem.elem_dims==2){ + coeff = CArrayKokkos(p_order+1, p_order+1); + } + else { + coeff = CArrayKokkos(p_order+1, p_order+1, p_order+1); + } + coeff.set_values(0.78914567); // a radom value + + // Compute numerical integral using quadratur + double numerical_integral = 0.0; + double sum_lcl = 0.0; + + // evaluate polynomial at quadrature point and sum + FOR_REDUCE_SUM(qpt, 0, Quad.num_qpts_in_elem, sum_lcl, { + + double value = 0.0; + if(RefElem.elem_dims==1){ + const double xi = Quad.qpt_positions(qpt, 0); + value = polynomial(coeff, xi, p_order); + } + else if (RefElem.elem_dims==2){ + const double xi = Quad.qpt_positions(qpt, 0); + const double eta = Quad.qpt_positions(qpt, 1); + value = polynomial(coeff, xi, eta, p_order); + } + else { + const double xi = Quad.qpt_positions(qpt, 0); + const double eta = Quad.qpt_positions(qpt, 1); + const double mu = Quad.qpt_positions(qpt, 2); + value = polynomial(coeff, xi, eta, mu, p_order); + } + + sum_lcl += value*Quad.qpt_weights(qpt); + + }, numerical_integral); // end parallel + + + RUN({ + + // Compute analytical integral + double exact_integral = 0.0; + + if(RefElem.elem_dims==1){ + exact_integral = integrate_polynomial_1D(coeff, p_order); + } + else if (RefElem.elem_dims==2){ + exact_integral = integrate_polynomial_2D_quad(coeff, p_order); + } + else { + exact_integral = integrate_polynomial_3D_hex(coeff, p_order); + } + + // Compare results + const double error = fabs(numerical_integral - exact_integral); + const double tolerance = fmax(1.e-10, 1.e-10 * (double)p_order); + + if (error > tolerance) { + printf("Error: integration failed with order = %zu \n", p_order); + printf("tolerance = %.15e \n", tolerance); + printf("numerical = %.15e vs exact = %.15e, error = %.15e \n", + numerical_integral, exact_integral, error); + Kokkos::abort("Integration test failed"); + } + + if (Verbose){ + printf("Integration: numerical = %.15e vs exact = %.15e, error = %.15e \n", + numerical_integral, exact_integral, error); + } + + }); // end RUN on device +} // end integration test + + +// Diagnostic: Check weight sums in all dimensions +void check_quadrature(const Quadrature_t& Quad) { + + double weight_sum = 0.0; + double weight_sum_lcl = 0.0; + + FOR_REDUCE_SUM(qpt, 0, Quad.num_qpts_in_elem, weight_sum_lcl, { + weight_sum_lcl += Quad.qpt_weights(qpt); + }, weight_sum); + Kokkos::fence(); + + + double expected = pow(2.0, (double)Quad.elem_dims); + double error = fabs(weight_sum - expected); + if (error > 1.e-12) { + printf("Error: quadrature weights don't correctly tally for number = %zu \n", Quad.num_qpts_in_elem); + printf("numerical = %.15e vs exact = %.15e, error = %.15e \n", + weight_sum, expected, error); + Kokkos::abort("Quadrature weights test failed"); + } + + if(Verbose){ + printf("%zuD: Sum of weights = %.15e ", Quad.elem_dims, weight_sum); + if(Quad.elem_dims == 1) printf("(expected: 2.0)\n"); + else if(Quad.elem_dims == 2) printf("(expected: 4.0 for quad)\n"); + else if(Quad.elem_dims == 3) printf("(expected: 8.0 for hex)\n"); + } +} // end check quadrature + + + +int main(int argc, char** argv) { + +MATAR_INITIALIZE(argc, argv); +{ // MATAR scope + + printf("\n--- integration tests ---\n"); + + + printf("\n--- checking Lengendre quadrature weight tallies ---\n"); + for(size_t elem_dims_test = 1; elem_dims_test<=3; elem_dims_test++){ + + Quadrature_t Quad; + + for(size_t num_qpts_1D = 1; num_qpts_1D<=max_num; num_qpts_1D++){ + Quad.initialize_quadrature(reference_space::GaussLegendre, + num_qpts_1D, + elem_dims_test); + + check_quadrature(Quad); + } + + } // end for dim + + + printf("\n--- checking Lobbatto quadrature weight tallies ---\n"); + for(size_t elem_dims_test = 1; elem_dims_test<=3; elem_dims_test++){ + + Quadrature_t Quad; + + for(size_t num_qpts_1D = 2; num_qpts_1D<=max_num; num_qpts_1D++){ + Quad.initialize_quadrature(reference_space::GaussLobatto, + num_qpts_1D, + elem_dims_test); + + check_quadrature(Quad); + } + + } // end for dim + + + printf("\n--- DG element with Legendre Quadrature & Legendre DOFs ---\n"); + for(size_t num_qpts_1D = 1; num_qpts_1D<=max_num; num_qpts_1D++){ + + if(Verbose)printf("num quadrature points in 1D = %zu \n", num_qpts_1D); + + Quadrature_t Quad; + + // elem_dims=1,2,3 + for(size_t elem_dims_test = 1; elem_dims_test<=3; elem_dims_test++){ + Quad.initialize_quadrature(reference_space::GaussLegendre, + num_qpts_1D, + elem_dims_test); + + // build reference elements of varing orders, + + size_t p_order_ceil = 2*num_qpts_1D-1; // Legendre + if(max_order +#include +#include + +// This pulls in kokkos, matar, mesh, hash, ref_elem stuff, and PT-Scotch +#include "ELEMENTS.h" + +//#undef NDEBUG // Ensures NDEBUG is turned off + +using namespace mtr; +using namespace swage; // unstructured mesh and hash +using namespace elements; + +const bool Verbose = false; +const size_t max_num = 19; // max number of quadrature points to test up to, the limit is 19 +const size_t max_order = 19; // max polynomial order to test, limit is 19th-order with Legendre + + +// polynomial with terms <= p_order +KOKKOS_INLINE_FUNCTION +double polynomial(const CArrayKokkos &coeff, const double x, const size_t p_order){ + + double result = 0.0; + for (int i = 0; i <= p_order; ++i) { + result += coeff(i) * pow(x, (double)i); + } + return result; +} // end polynomial + +KOKKOS_INLINE_FUNCTION +double polynomial(const CArrayKokkos &coeff, const double x, const double y, const size_t p_order){ + + double result = 0.0; + for (int j = 0; j <= p_order; ++j) + for (int i = 0; i <= p_order-j; ++i) { + result += coeff(i,j) * pow(x, (double)i) * pow(y, (double)j); + + } // end for + return result; +} // end polynomial + +KOKKOS_INLINE_FUNCTION +double polynomial(const CArrayKokkos &coeff, const double x, const double y, const double z, const size_t p_order){ + + double result = 0.0; + for (int k = 0; k <= p_order; ++k) + for (int j = 0; j <= p_order-k; ++j) + for (int i = 0; i <= p_order-j-k; ++i) { + result += coeff(i,j,k) * pow(x, (double)i) * pow(y, (double)j) * pow(z, (double)k); + } // end for + + return result; +} // end polynomial + + +// Test: interpolate a polynomial that the basis can represent exactly +void test_interpolation(const Quadrature_t& Quad, + const ReferenceElement_t& RefElem) { + + // For polynomial order p, Lagrange basis can represent + // any polynomial of degree <= p exactly + + const size_t p_order = RefElem.num_dofs_1d - 1; + + CArrayKokkos dof_values(RefElem.num_dofs_in_elem); + CArrayKokkos coeff; + + + if(RefElem.elem_dims==1){ + coeff = CArrayKokkos(p_order+1); + } + else if (RefElem.elem_dims==2){ + coeff = CArrayKokkos(p_order+1, p_order+1); + } + else { + coeff = CArrayKokkos(p_order+1, p_order+1, p_order+1); + } + coeff.set_values(0.78914567); // a radom value + + + + // populate polynomial values at DOFs of the ref element + FOR_ALL(dof, 0, RefElem.num_dofs_in_elem, { + + double value=0.0; + if(RefElem.elem_dims==1){ + const double xi = RefElem.dof_positions(dof, 0); + value = polynomial(coeff, xi, p_order); + } + else if (RefElem.elem_dims==2){ + const double xi = RefElem.dof_positions(dof, 0); + const double eta = RefElem.dof_positions(dof, 1); + value = polynomial(coeff, xi, eta, p_order); + } + else { + const double xi = RefElem.dof_positions(dof, 0); + const double eta = RefElem.dof_positions(dof, 1); + const double mu = RefElem.dof_positions(dof, 2); + value = polynomial(coeff, xi, eta, mu, p_order); + } + + dof_values(dof) = value; // the value at the ref elem node + + }); // end parallel + + + // compare interpoloation with exact solution + FOR_ALL(qpt, 0, Quad.num_qpts_in_elem, { + + double sum = 0; // interpolated value + + for (size_t dof = 0; dof < RefElem.num_dofs_in_elem; dof++) { + sum += RefElem.qpt_basis(qpt, dof)*dof_values(dof); + } + + double exact_value=0.0; + if(RefElem.elem_dims==1){ + const double xi = Quad.qpt_positions(qpt, 0); + exact_value = polynomial(coeff, xi, p_order); + } + else if (RefElem.elem_dims==2){ + const double xi = Quad.qpt_positions(qpt, 0); + const double eta = Quad.qpt_positions(qpt, 1); + exact_value = polynomial(coeff, xi, eta, p_order); + } + else { + const double xi = Quad.qpt_positions(qpt, 0); + const double eta = Quad.qpt_positions(qpt, 1); + const double mu = Quad.qpt_positions(qpt, 2); + exact_value = polynomial(coeff, xi, eta, mu, p_order); + } + + // round off gets bad at high p_order's due to polynomial sensativity + const double error = fabs(sum - exact_value); + const double tolerance = fmax(1.e-11, 1.e-11 * (double)p_order); + + if (error > tolerance) { + printf("Error: interpolation failed at qpt id = %d with order = %zu \n", qpt, p_order); + printf("interpolated = %f vs exact value = %f, error = %f \n", sum, exact_value, sum-exact_value); + Kokkos::abort("Interpolation failed at quadrature point "); + } + if(Verbose)printf("interpolated = %f vs exact value = %f \n", sum, exact_value); + + }); // end loop over qpts +} + +int main(int argc, char** argv) { + +MATAR_INITIALIZE(argc, argv); +{ // MATAR scope + + printf("\n--- interpolation tests ---\n"); + + + printf("\n--- DG element with Legendre Quadrature & Legendre DOFs ---\n"); + for(size_t num_qpts_1D = 1; num_qpts_1D<=max_num; num_qpts_1D++){ + + if(Verbose)printf("num quadrature points in 1D = %zu \n", num_qpts_1D); + + Quadrature_t Quad; + + // elem_dims=1,2,3 + for(size_t elem_dims_test = 1; elem_dims_test<=3; elem_dims_test++){ + Quad.initialize_quadrature(reference_space::GaussLegendre, + num_qpts_1D, + elem_dims_test); + + // build reference elements of varing orders, + for (size_t p_order = 0; p_order +#include +#include + +// This pulls in kokkos, matar, mesh, hash, ref_elem stuff, and PT-Scotch +#include "ELEMENTS.h" + +//#undef NDEBUG // Ensures NDEBUG is turned off + +using namespace mtr; +using namespace swage; // unstructured mesh and hash +using namespace elements; + +const bool Verbose = false; +const size_t max_num = 19; // max number of quadrature points to test up to, the limit is 19 + + +int main(int argc, char** argv) { + +MATAR_INITIALIZE(argc, argv); +{ // MATAR scope + + printf("\n--- kronecker delta test ---\n"); + + // Note: quadrature points will be collocated with kinematic DOFs + for(size_t num_qpts_1D = 2; num_qpts_1D<=max_num; num_qpts_1D++){ + + Quadrature_t Quad; + + // elem_dims=1,2,3 + for(size_t elem_dims_test = 1; elem_dims_test<=3; elem_dims_test++){ + Quad.initialize_quadrature(reference_space::GaussLobatto, + num_qpts_1D, + elem_dims_test); + + // build reference element with collocated DOFs + const size_t p_order = num_qpts_1D - 1; + + if(Verbose)printf("p_order = %zu: \n", p_order); + ReferenceElement_t FERefElem; + + // p_order is the basis order for Lagrange polynomial + FERefElem.initialize_ref_elem(reference_space::arbitraryOrderElement, + reference_space::LagrangeLobatto, + Quad, + p_order); + + + // Check: should be 1 at i, 0 elsewhere + FOR_ALL(basis, 0, FERefElem.num_dofs_in_elem, { + + if(Verbose) printf("basis DOF id = %d \n", basis); + for(int dof_pt=0; dof_pt 1.0e-12) { + printf("ERROR: expected val = %f, basis val = %f \n", expected, FERefElem.qpt_basis(dof_pt, basis)); + Kokkos::abort("ERROR: Kronecker delta property violated"); + } + } // end loop over other nodes + + }); + + } // end elem_dims + } // end number of quadrature points + + printf("\nAll kronecker delta checks passed.\n"); + +} +MATAR_FINALIZE(); + +} // end main + diff --git a/examples/reference_element/src/partition_unity_test.cpp b/examples/reference_element/src/partition_unity_test.cpp new file mode 100644 index 00000000..1175d2c9 --- /dev/null +++ b/examples/reference_element/src/partition_unity_test.cpp @@ -0,0 +1,260 @@ +/********************************************************************************************** +© 2020. Triad National Security, LLC. All rights reserved. +This program was produced under U.S. Government contract 89233218CNA000001 for Los Alamos +National Laboratory (LANL), which is operated by Triad National Security, LLC for the U.S. +Department of Energy/National Nuclear Security Administration. All rights in the program are +reserved by Triad National Security, LLC, and the U.S. Department of Energy/National Nuclear +Security Administration. The Government is granted for itself and others acting on its behalf a +nonexclusive, paid-up, irrevocable worldwide license in this material to reproduce, prepare +derivative works, distribute copies to the public, perform publicly and display publicly, and +to permit others to do so. +This program is open source under the BSD-3 License. +Redistribution and use in source and binary forms, with or without modification, are permitted +provided that the following conditions are met: +1. Redistributions of source code must retain the above copyright notice, this list of +conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, this list of +conditions and the following disclaimer in the documentation and/or other materials +provided with the distribution. +3. Neither the name of the copyright holder nor the names of its contributors may be used +to endorse or promote products derived from this software without specific prior +written permission. +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**********************************************************************************************/ +#include +#include +#include + +// This pulls in kokkos, matar, mesh, point cloud, ref_elem stuff, and PT-Scotch +#include "ELEMENTS.h" + +//#undef NDEBUG // Ensures NDEBUG is turned off + +using namespace mtr; +using namespace swage; // unstructured mesh and point cloud +using namespace elements; + +const bool Verbose = false; +const size_t max_num = 19; // max number of quadrature points to test up to, the limit is 19 +const size_t max_order = 19; // max polynomial order to test, limit is 19th-order with Legendre + + +void verify_partition_of_unity(const Quadrature_t& Quad, + const ReferenceElement_t& RefElem) { + + // device parallel loop + FOR_ALL(qpt, 0, Quad.num_qpts_in_elem, { + double sum = 0.0; + + for (size_t basis = 0; basis < RefElem.num_dofs_in_elem; basis++) { + sum += RefElem.qpt_basis(qpt, basis); + } + if (fabs(sum - 1.0) > 1.e-12) { + printf("Error: partion of unity failed, sum of basis = %f at qpt id = %d with order = %zu \n", sum, qpt, RefElem.num_dofs_1d-1); + Kokkos::abort("Partition of unity failed at quadrature point "); + } + if(Verbose)printf("sum = %f \n", sum); + }); // end loop over qpts + +} // end function + +void verify_gradient(const Quadrature_t& Quad, + const ReferenceElement_t& RefElem) { + + // device parallel loop + FOR_ALL(qpt, 0, Quad.num_qpts_in_elem, { + double sum[3]; + sum[0] = 0.0; + sum[1] = 0.0; + sum[2] = 0.0; + + for(size_t dim=0; dim 1.e-12) { + printf("Error: gradient failed, sum of gradient basis = %.15e at qpt id = %d, for dim = %zu, with order = %zu \n", sum[dim], qpt, dim, RefElem.num_dofs_1d); + Kokkos::abort("Gradient of basis failed at quadrature point "); + } + if(Verbose)printf("dim = %zu, sum = %.15e \n", dim, sum[dim]); + } // for dim + + }); // end loop over qpts + +} // end function + +int main(int argc, char** argv) { + +MATAR_INITIALIZE(argc, argv); +{ // MATAR scope + + printf("\n--- partition unity and gradient test ---\n"); + + printf("\n--- DG element with Legendre Quadrature & Legendre DOFs ---\n"); + for(size_t num_qpts_1D = 1; num_qpts_1D<=max_num; num_qpts_1D++){ + + if(Verbose)printf("num quadrature points in 1D = %zu \n", num_qpts_1D); + + Quadrature_t Quad; + + // elem_dims=1,2,3 + for(size_t elem_dims_test = 1; elem_dims_test<=3; elem_dims_test++){ + Quad.initialize_quadrature(reference_space::GaussLegendre, + num_qpts_1D, + elem_dims_test); + + // build reference elements of varing orders + for (size_t p_order = 0; p_order +#include +#include + +// This pulls in kokkos, matar, mesh, ref_elem stuff, and PT-Scotch +#include "ELEMENTS.h" +#include "cramers_rule.hpp" // det and solvers + +using namespace mtr; +using namespace swage; // unstructured mesh and point cloud +using namespace elements; // reference element space + + +// 8x8x8 linear mesh +// num_patches = 8*8*9*3 = 1728 +// bdy_patches = 8*8*6 = 384 + +int main(int argc, char** argv) { + +MATAR_INITIALIZE(argc, argv); +{ // MATAR scope + std::cout<<"Reference Element Flux Example!"< node_coords(Mesh.num_nodes, Mesh.num_dims); + const double h = 1.0/((double)num_nodes_1D); + + // create indexing for a Pn order mesh + + // Step 1: Initialize ALL node coordinates once (no race condition) + FOR_ALL(kc, 0, num_nodes_1D, + jc, 0, num_nodes_1D, + ic, 0, num_nodes_1D, { + + size_t node_gid = ic + (jc + kc*num_nodes_1D)*num_nodes_1D; + + node_coords(node_gid, 0) = (double)ic * h; + node_coords(node_gid, 1) = (double)jc * h; + node_coords(node_gid, 2) = (double)kc * h; + }); + + // Step 2: Build element connectivity + FOR_ALL(i, 0, num_elems_1D, + j, 0, num_elems_1D, + k, 0, num_elems_1D, { + + size_t elem_gid = i + (j + k*num_elems_1D)*num_elems_1D; + size_t node_lid = 0; + + // FIX: Multiply by elem_order to space elements correctly + for(size_t kc = k*elem_order; kc <= k*elem_order + elem_order; kc++) + for(size_t jc = j*elem_order; jc <= j*elem_order + elem_order; jc++) + for(size_t ic = i*elem_order; ic <= i*elem_order + elem_order; ic++){ + size_t node_gid = ic + (jc + kc*num_nodes_1D)*num_nodes_1D; + Mesh.nodes_in_elem(elem_gid, node_lid) = node_gid; + node_lid++; + } + }); + + std::cout<<"Building corner connectivity \n"; + Mesh.build_corner_connectivity(); + std::cout<<"Building element element connectivity \n"; + Mesh.build_elem_elem_connectivity(); + std::cout<<"Building surface connectivity \n"; + Mesh.build_surf_connectivity(); + + // check mesh index sizes + if(Mesh.num_nodes!=num_nodes){ + printf("num nodes = %zu and mesh.num_nodes = %zu", num_nodes, Mesh.num_nodes); + Kokkos::abort("ERROR: wrong number of mesh nodes"); + } + if(Mesh.num_gauss_in_elem!=Quad.num_qpts_in_elem){ + Kokkos::abort("ERROR: wrong number of Gauss points in elem"); + } + if(Mesh.num_patches!=1728){ + Kokkos::abort("ERROR: wrong number of patches"); + } + if(Mesh.num_bdy_patches!=384){ + Kokkos::abort("ERROR: wrong number of boundary patches"); + } + + + // State arrays for this test + const size_t num_qpts_in_elem = Quad.num_qpts_in_elem; + + const size_t num_surfs = Mesh.num_surfs; + const size_t num_qpts_in_surf = SurfQuad.num_qpts_in_surf; + + const size_t num_nodes_in_elem = Mesh.num_nodes_in_elem; + const size_t num_surfs_in_elem = Mesh.num_surfs_in_elem; + + if(num_nodes_in_elem != FERefElem.num_dofs_in_elem) Kokkos::abort("ERROR: mismatch in DOFs and num nodes in elem \n"); + + DCArrayKokkos elem_jac(num_elems, num_qpts_in_elem, elem_dims, elem_dims, "elem_jacobian"); + DCArrayKokkos elem_vol(num_elems, "elem_vol"); + + + DCArrayKokkos surf_jac(num_surfs, num_qpts_in_surf, elem_dims, elem_dims, "surf_jacobian"); + DCArrayKokkos surf_inv_jac(num_surfs, num_qpts_in_surf, elem_dims, elem_dims, "surf_inv_jacobian"); + + + printf("\n=== TEST: Surface Integration ===\n"); + + FOR_ALL(elem_gid, 0, num_elems, { + + elem_vol(elem_gid) = 0.0; + + ViewCArrayKokkos nodes_in_elem(&Mesh.nodes_in_elem(elem_gid,0), num_nodes_in_elem); + + // calculate volume + for(size_t qpt_lid=0; qpt_lid a_grad_basis(&FERefElem.qpt_grad_basis(qpt_lid,0,0), + num_nodes_in_elem, 3); + + // extract the basis at a single quadrature point (qpt,dof) + ViewCArrayKokkos a_basis(&FERefElem.qpt_basis(qpt_lid,0), + num_nodes_in_elem); + + // jacobian matrix + ViewCArrayKokkos jac(&elem_jac(elem_gid,qpt_lid,0,0),3,3); + + jacobian(jac, + node_coords, + nodes_in_elem, + a_grad_basis); + + const double det_jac = det_3x3(jac); + + elem_vol(elem_gid) += det_jac*Quad.qpt_weights(qpt_lid); + + } // end for + + const double vol_q = elem_vol(elem_gid); + double vol_h = 1.0; + for(size_t dim=0; dim 1.0e-12){ + printf("wrong elem volume using gauss quadrature, error = %.15f \n", vol_q-vol_h ); + Kokkos::abort("ERROR: wrong volume \n"); + } + + + double tally_div=0; + double tally_flux[3][3]; + double tally_normal[3]; + + double eye[3][3]; + + for(size_t j=0; j a_grad_basis(&RefSurf.qpt_grad_basis(face_lid,qpt_lid,0,0), + num_nodes_in_elem, 3); + + // extract the basis at a single quadrature point (surf,qpt,dof) + ViewCArrayKokkos a_basis(&RefSurf.qpt_basis(face_lid,qpt_lid,0), + num_nodes_in_elem); + + ViewCArrayKokkos jac(&surf_jac(surf_gid,qpt_lid,0,0),3,3); + ViewCArrayKokkos inv_jac(&surf_inv_jac(surf_gid,qpt_lid,0,0),3,3); + + jacobian(jac, + node_coords, + nodes_in_elem, + a_grad_basis); + + const double det_jac = det_3x3(jac); + + invert_3x3(jac, inv_jac, det_jac); + + // Nanson's formula: s*J^-1*j*f*w + double area_normal[3]; + area_normal[0] = 0.; + area_normal[1] = 0.; + area_normal[2] = 0.; + for(size_t j=0; j identity & trace=3 + + double qpt_coords[3]; + for(size_t dim=0; dim= 1.e-12){ + printf("Tally_normal of area normals in elem (should=0) = (%f, %f, %f) \n", tally_normal[0], tally_normal[1], tally_normal[2]); + Kokkos::abort("ERROR: no conservation of surface normals \n"); + } // end check on tally_normal + } // end for dim + + + // Test: check grad x = eye + bool passed = true; + for(size_t i=0; i= 1.e-12) passed = false; + } + if(!passed){ + printf("\n"); + printf("eye = gradx = \n "); + for(size_t i=0; i= 1.e-10){ + printf("Tally_div = %.15f \n",tally_div/elem_vol(elem_gid)-3.0); + Kokkos::abort("ERROR: divergence of Gradx should be equal to 3 \n"); + } + + }); // end parallel for elements + + + printf("\n Surface flux test finished.\n"); + + +} // end MATAR scope +MATAR_FINALIZE(); + +return 0; +} diff --git a/examples/reference_element/src/ref_plus_mesh_test.cpp b/examples/reference_element/src/ref_plus_mesh_test.cpp new file mode 100644 index 00000000..07324bf0 --- /dev/null +++ b/examples/reference_element/src/ref_plus_mesh_test.cpp @@ -0,0 +1,1121 @@ +/********************************************************************************************** +© 2020. Triad National Security, LLC. All rights reserved. +This program was produced under U.S. Government contract 89233218CNA000001 for Los Alamos +National Laboratory (LANL), which is operated by Triad National Security, LLC for the U.S. +Department of Energy/National Nuclear Security Administration. All rights in the program are +reserved by Triad National Security, LLC, and the U.S. Department of Energy/National Nuclear +Security Administration. The Government is granted for itself and others acting on its behalf a +nonexclusive, paid-up, irrevocable worldwide license in this material to reproduce, prepare +derivative works, distribute copies to the public, perform publicly and display publicly, and +to permit others to do so. +This program is open source under the BSD-3 License. +Redistribution and use in source and binary forms, with or without modification, are permitted +provided that the following conditions are met: +1. Redistributions of source code must retain the above copyright notice, this list of +conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, this list of +conditions and the following disclaimer in the documentation and/or other materials +provided with the distribution. +3. Neither the name of the copyright holder nor the names of its contributors may be used +to endorse or promote products derived from this software without specific prior +written permission. +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**********************************************************************************************/ +#include +#include +#include + +// This pulls in kokkos, matar, mesh, ref_elem stuff, and PT-Scotch +#include "ELEMENTS.h" +#include "cramers_rule.hpp" // det and solvers + +using namespace mtr; +using namespace swage; // unstructured mesh and point cloud +using namespace elements; // reference element space + + + + +// Convert from ensight FE elem to IJK elem +const size_t convert_ensight_to_ijk[8] = + {0, + 1, + 3, + 2, + 4, + 5, + 7, + 6}; + +/////////////////////////////////////////////////////////////////////////////// +/// Test 1: 3D Affine Transformation +/// Maps reference cube [-1,1]³ to a skewed, rotated, stretched parallelepiped +/////////////////////////////////////////////////////////////////////////////// +void test_affine_transformation() { + printf("\n=== TEST: Affine Transformation ===\n"); + + // Reference element: unit cube [-1,1]^3 + // Physical element: defined by affine map x = A*xi + b + // where A is the transformation matrix + + Quadrature_t Quad; + ReferenceElement_t RefElem; + + // create quadrature, it is a single point at this time + Quad.initialize_quadrature(reference_space::GaussLegendre, + 1, + 3); + + // create ref element + RefElem.initialize_ref_elem(reference_space::arbitraryOrderElement, + reference_space::LagrangeLobatto, + Quad, + 1); + + // Transformation matrix (rotation + stretch + shear) + real_t A[3][3] = { + { 0.5, 0.3, 0.2}, // dx/dxi, dx/deta, dx/dzeta + { 0.2, 0.6, 0.1}, // dy/dxi, dy/deta, dy/dzeta + { 0.1, 0.2, 0.4} // dz/dxi, dz/deta, dz/dzeta + }; + + real_t b[3] = {1.0, 2.0, 3.0}; // Translation + + // Generate 8 corner nodes of transformed hexahedron + real_t node_coords[8][3]; + real_t ref_corners[8][3] = { + {-1, -1, -1}, {1, -1, -1}, {1, 1, -1}, {-1, 1, -1}, // bottom + {-1, -1, 1}, {1, -1, 1}, {1, 1, 1}, {-1, 1, 1} // top + }; + + DCArrayKokkos node_coords_dual(8,3); + DCArrayKokkos node_in_elem_dual(8); + + for(size_t n = 0; n < 8; n++) { + for(size_t i = 0; i < 3; i++) { + node_coords[n][i] = b[i]; + for(size_t j = 0; j < 3; j++) { + node_coords[n][i] += A[i][j] * ref_corners[n][j]; + } + + // get i,j,k node lid for this node + size_t node_lid = convert_ensight_to_ijk[n]; + node_coords_dual.host(node_lid,i) = node_coords[n][i]; + node_in_elem_dual.host(node_lid) = node_lid; + } + } + node_coords_dual.update_device(); + node_in_elem_dual.update_device(); + + + printf("Node coordinates (Ensight FE element ordering):\n"); + for(size_t n = 0; n < 8; n++) { + printf(" Node %zu: [%8.4f, %8.4f, %8.4f]\n", + n, node_coords[n][0], node_coords[n][1], node_coords[n][2]); + } + + // For an AFFINE transformation, Jacobian is CONSTANT everywhere + // and equals the transformation matrix A + printf("\nExpected Jacobian (constant, equals A):\n"); + for(int i = 0; i < 3; i++) { + printf(" "); + for(int j = 0; j < 3; j++) { + printf("%10.6f ", A[i][j]); + } + printf("\n"); + } + + real_t expected_det = A[0][0]*(A[1][1]*A[2][2] - A[1][2]*A[2][1]) + - A[0][1]*(A[1][0]*A[2][2] - A[1][2]*A[2][0]) + + A[0][2]*(A[1][0]*A[2][1] - A[1][1]*A[2][0]); + + printf("Expected determinant: %12.8f\n", expected_det); + + // Now compute Jacobian at element center (xi=0, eta=0, zeta=0) + // and verify it matches A + + // Call jacobian function and then compare results + DCArrayKokkos jac(3, 3); + RUN({ + // exact the basis and grad basis for this quadrature point + //ViewCArrayKokkos a_basis(&RefElem.qpt_basis(0,0),RefElem.num_dofs_in_elem); + ViewCArrayKokkos a_grad_basis(&RefElem.qpt_grad_basis(0,0,0),RefElem.num_dofs_in_elem,3); + + jacobian(jac, + node_coords_dual, + node_in_elem_dual, + a_grad_basis); + + printf("\n"); + printf("calculated jacobian matrix = \n"); + for(size_t i = 0; i < 3; i++){ // looping over dimension + for(size_t j = 0; j < 3; j++){ // looping over dimension + printf("%10.6f, ", jac(i, j)); + } + printf("\n"); + } // end for + + double det = det_3x3(jac); + printf("Calculated determinant: %12.8f\n", det); + }); // end RUN on device + jac.update_host(); + + for(size_t i = 0; i < 3; i++) // looping over dimension + for(size_t j = 0; j < 3; j++){ // looping over dimension + if(fabs(jac.host(i, j)-A[i][j])>1e-12){ + Kokkos::abort("ERROR: Jacobian calculation in affine test failed\n"); + } + } // end for + + printf("\nTest criteria:\n"); + printf(" 1. J should equal A within tolerance (~1e-12)\n"); + printf(" 2. det(J) should equal %12.8f\n", expected_det); + printf(" 3. J should be same at ALL quadrature points (affine property)\n"); + + printf("\nAffine test: Passes\n\n"); +} // end function + + +/////////////////////////////////////////////////////////////////////////////// +/// Test 2: Heavily Skewed Hexahedron +/// Creates a non-trivial, fully-populated Jacobian +/////////////////////////////////////////////////////////////////////////////// +void test_skewed_hexahedron() { + printf("\n=== TEST: Heavily Skewed Hexahedron ===\n"); + + // Define a highly skewed but valid element + real_t node_coords[8][3] = { + // Bottom face (z=0 plane, but skewed) + {0.0, 0.0, 0.0}, // node 0 + {1.0, 0.2, 0.1}, // node 1 + {1.1, 1.0, 0.15}, // node 2 + {0.1, 0.9, 0.05}, // node 3 + // Top face (z~1 plane, but twisted and stretched) + {0.1, 0.1, 0.9}, // node 4 + {1.2, 0.1, 1.0}, // node 5 + {1.3, 1.1, 1.1}, // node 6 + {0.0, 1.0, 0.95} // node 7 + }; + + printf("Node coordinates (Ensight FE element ordering):\n"); + for(int n = 0; n < 8; n++) { + printf(" Node %d: [%8.4f, %8.4f, %8.4f]\n", + n, node_coords[n][0], node_coords[n][1], node_coords[n][2]); + } + + // Compute Jacobian at center + printf("\nJacobian at element center (xi=eta=zeta=0):\n"); + printf("Expected properties:\n"); + printf(" - All 9 entries should be non-zero\n"); + printf(" - Determinant should be positive\n"); + printf(" - Off-diagonal terms significant (non-trivial mapping)\n"); + + // Analytical approximation (for verification): + // At center, with trilinear shape functions: + // J ~ (1/8) * sum of differences in each direction + + real_t J_approx[3][3]; + for(int i = 0; i < 3; i++) { + J_approx[i][0] = 0.125 * ((node_coords[1][i] + node_coords[2][i] + + node_coords[5][i] + node_coords[6][i]) - + (node_coords[0][i] + node_coords[3][i] + + node_coords[4][i] + node_coords[7][i])); + + J_approx[i][1] = 0.125 * ((node_coords[2][i] + node_coords[3][i] + + node_coords[6][i] + node_coords[7][i]) - + (node_coords[0][i] + node_coords[1][i] + + node_coords[4][i] + node_coords[5][i])); + + J_approx[i][2] = 0.125 * ((node_coords[4][i] + node_coords[5][i] + + node_coords[6][i] + node_coords[7][i]) - + (node_coords[0][i] + node_coords[1][i] + + node_coords[2][i] + node_coords[3][i])); + } + + printf("\nApproximate Jacobian (analytical):\n"); + for(int i = 0; i < 3; i++) { + printf(" "); + for(int j = 0; j < 3; j++) { + printf("%10.6f ", J_approx[i][j]); + } + printf("\n"); + } + + real_t det_approx = J_approx[0][0]*(J_approx[1][1]*J_approx[2][2] - + J_approx[1][2]*J_approx[2][1]) + - J_approx[0][1]*(J_approx[1][0]*J_approx[2][2] - + J_approx[1][2]*J_approx[2][0]) + + J_approx[0][2]*(J_approx[1][0]*J_approx[2][1] - + J_approx[1][1]*J_approx[2][0]); + + printf("Approximate determinant: %12.8f\n", det_approx); + + // ======================= + // using ELEMENTS + // ======================= + DCArrayKokkos node_coords_dual(8,3); + DCArrayKokkos node_in_elem_dual(8); + + for(size_t n = 0; n < 8; n++) { + for(size_t i = 0; i < 3; i++) { + // get i,j,k node lid for this node + size_t node_lid = convert_ensight_to_ijk[n]; + node_coords_dual.host(node_lid,i) = node_coords[n][i]; + node_in_elem_dual.host(node_lid) = node_lid; + } + } + node_coords_dual.update_device(); + node_in_elem_dual.update_device(); + + Quadrature_t Quad; + ReferenceElement_t RefElem; + + // create quadrature, it is a single point at this time + Quad.initialize_quadrature(reference_space::GaussLegendre, + 1, + 3); + + // create ref element + RefElem.initialize_ref_elem(reference_space::arbitraryOrderElement, + reference_space::LagrangeLobatto, + Quad, + 1); + + // Call jacobian function and then compare results + DCArrayKokkos jac(3, 3); + RUN({ + // exact the basis and grad basis for this quadrature point + //ViewCArrayKokkos a_basis(&RefElem.qpt_basis(0,0),RefElem.num_dofs_in_elem); + ViewCArrayKokkos a_grad_basis(&RefElem.qpt_grad_basis(0,0,0),RefElem.num_dofs_in_elem,3); + + jacobian(jac, + node_coords_dual, + node_in_elem_dual, + a_grad_basis); + + printf("\n"); + printf("calculated jacobian matrix = \n"); + for(size_t i = 0; i < 3; i++){ // looping over dimension + for(size_t j = 0; j < 3; j++){ // looping over dimension + printf("%10.6f, ", jac(i, j)); + } + printf("\n"); + } // end for + + double det = det_3x3(jac); + printf("Calculated determinant: %12.8f\n", det); + }); // end RUN on device + jac.update_host(); + + for(size_t i = 0; i < 3; i++) // looping over dimension + for(size_t j = 0; j < 3; j++){ // looping over dimension + if(fabs(jac.host(i, j)-J_approx[i][j])>1.e-10){ + Kokkos::abort("ERROR: Jacobian calculation in skewed hex test failed\n"); + } + } // end for + + printf("\nSkewed hex test: Passes\n\n"); + +} // end function + + + +/////////////////////////////////////////////////////////////////////////////// +/// Test 3: Rotated and Scaled Cube +/// Apply known rotation matrix + scaling +/////////////////////////////////////////////////////////////////////////////// +void test_rotated_scaled_cube() { + printf("\n=== TEST: Rotated and Scaled Cube ===\n"); + + // Rotation angle + const real_t theta = M_PI / 6.0; // 30 degrees + const real_t phi = M_PI / 4.0; // 45 degrees + + // Scale factors + const real_t sx = 0.5, sy = 0.8, sz = 1.2; + + // Combined transformation matrix: R_z(phi) * R_y(theta) * Scale + real_t cos_t = cos(theta), sin_t = sin(theta); + real_t cos_p = cos(phi), sin_p = sin(phi); + + real_t T[3][3] = { + {sx * cos_t * cos_p, sx * (-sin_p), sx * sin_t * cos_p}, + {sy * cos_t * sin_p, sy * cos_p, sy * sin_t * sin_p}, + {sz * (-sin_t), sz * 0.0, sz * cos_t} + }; + + printf("Transformation matrix (Rotation + Scale):\n"); + for(int i = 0; i < 3; i++) { + printf(" "); + for(int j = 0; j < 3; j++) { + printf("%10.6f ", T[i][j]); + } + printf("\n"); + } + + // Reference cube vertices + real_t ref_verts[8][3] = { + {-1, -1, -1}, {1, -1, -1}, {1, 1, -1}, {-1, 1, -1}, + {-1, -1, 1}, {1, -1, 1}, {1, 1, 1}, {-1, 1, 1} + }; + + // Transform vertices + real_t node_coords[8][3]; + for(int n = 0; n < 8; n++) { + for(int i = 0; i < 3; i++) { + node_coords[n][i] = 0.0; + for(int j = 0; j < 3; j++) { + node_coords[n][i] += T[i][j] * ref_verts[n][j]; + } + } + } + + printf("\nTransformed node coordinates:\n"); + for(int n = 0; n < 8; n++) { + printf(" Node %d: [%8.4f, %8.4f, %8.4f]\n", + n, node_coords[n][0], node_coords[n][1], node_coords[n][2]); + } + + // Expected Jacobian = T (constant for affine transformation) + printf("\nExpected Jacobian (equals T):\n"); + for(int i = 0; i < 3; i++) { + printf(" "); + for(int j = 0; j < 3; j++) { + printf("%10.6f ", T[i][j]); + } + printf("\n"); + } + + // Expected determinant + real_t expected_det = sx * sy * sz * + (cos_t * cos_t * cos_p * cos_p + + cos_t * cos_t * sin_p * sin_p + + sin_t * sin_t); + + // Simplified: det(R) = 1, det(Scale) = sx*sy*sz + expected_det = sx * sy * sz; + + printf("\nExpected determinant: %12.8f\n", expected_det); + printf("(Should equal sx*sy*sz = %f * %f * %f = 0.48)\n", sx, sy, sz); + + + // ======================= + // using ELEMENTS + // ======================= + DCArrayKokkos node_coords_dual(8,3); + DCArrayKokkos node_in_elem_dual(8); + + for(size_t n = 0; n < 8; n++) { + for(size_t i = 0; i < 3; i++) { + // get i,j,k node lid for this node + size_t node_lid = convert_ensight_to_ijk[n]; + node_coords_dual.host(node_lid,i) = node_coords[n][i]; + node_in_elem_dual.host(node_lid) = node_lid; + } + } + node_coords_dual.update_device(); + node_in_elem_dual.update_device(); + + Quadrature_t Quad; + ReferenceElement_t RefElem; + + // create quadrature, it is a single point at this time + Quad.initialize_quadrature(reference_space::GaussLegendre, + 1, + 3); + + // create ref element + RefElem.initialize_ref_elem(reference_space::arbitraryOrderElement, + reference_space::LagrangeLobatto, + Quad, + 1); + + // Call jacobian function and then compare results + DCArrayKokkos jac(3, 3); + RUN({ + // exact the basis and grad basis for this quadrature point + //ViewCArrayKokkos a_basis(&RefElem.qpt_basis(0,0),RefElem.num_dofs_in_elem); + ViewCArrayKokkos a_grad_basis(&RefElem.qpt_grad_basis(0,0,0),RefElem.num_dofs_in_elem,3); + + jacobian(jac, + node_coords_dual, + node_in_elem_dual, + a_grad_basis); + + printf("\n"); + printf("calculated jacobian matrix = \n"); + for(size_t i = 0; i < 3; i++){ // looping over dimension + for(size_t j = 0; j < 3; j++){ // looping over dimension + printf("%10.6f, ", jac(i, j)); + } + printf("\n"); + } // end for + + double det = det_3x3(jac); + printf("Calculated determinant: %12.8f\n", det); + }); // end RUN on device + jac.update_host(); + + for(size_t i = 0; i < 3; i++) // looping over dimension + for(size_t j = 0; j < 3; j++){ // looping over dimension + if(fabs(jac.host(i, j)-T[i][j])>1.e-10){ + Kokkos::abort("ERROR: Jacobian calculation in rotated and scaled test failed\n"); + } + } // end for + + printf("\nRotated and scaled hex test: Passes\n\n"); + +} // end function + + +void test_manufactured_solution() { + printf("\n=== TEST: Method of Manufactured Solutions ===\n"); + + // Define physical node positions in ENSIGHT order (your input) + real_t node_coords_ensight[8][3] = { + {0.0, 0.0, 0.0}, // node 0: ref(-1,-1,-1) + {1.0, 0.1, 0.05}, // node 1: ref( 1,-1,-1) + {0.9, 1.0, 0.1}, // node 2: ref( 1, 1,-1) + {0.05, 0.95, 0.05}, // node 3: ref(-1, 1,-1) + {0.1, 0.05, 1.0}, // node 4: ref(-1,-1, 1) + {1.05, 0.15, 0.95}, // node 5: ref( 1,-1, 1) + {1.0, 1.05, 1.05}, // node 6: ref( 1, 1, 1) + {0.1, 1.0, 0.95} // node 7: ref(-1, 1, 1) + }; + + // Convert to IJK ordering for BOTH analytical and numerical + real_t node_coords_ijk[8][3]; + for(size_t n = 0; n < 8; n++) { + size_t ijk_lid = convert_ensight_to_ijk[n]; + for(size_t i = 0; i < 3; i++) { + node_coords_ijk[ijk_lid][i] = node_coords_ensight[n][i]; + } + } + + printf("Physical node coordinates (IJK ordering):\n"); + for(int n = 0; n < 8; n++) { + printf(" Node %d: [%9.4f, %9.4f, %9.4f]\n", + n, node_coords_ijk[n][0], node_coords_ijk[n][1], node_coords_ijk[n][2]); + } + + // Compute basis function gradients in IJK ordering + auto compute_hex8_basis_gradients_ijk = [](real_t xi, real_t eta, real_t zeta, + real_t grad_basis[8][3]) { + // IJK reference coordinates + real_t ref_coords[8][3] = { + {-1, -1, -1}, // 0: i=0, j=0, k=0 + { 1, -1, -1}, // 1: i=1, j=0, k=0 + {-1, 1, -1}, // 2: i=0, j=1, k=0 + { 1, 1, -1}, // 3: i=1, j=1, k=0 + {-1, -1, 1}, // 4: i=0, j=0, k=1 + { 1, -1, 1}, // 5: i=1, j=0, k=1 + {-1, 1, 1}, // 6: i=0, j=1, k=1 + { 1, 1, 1} // 7: i=1, j=1, k=1 + }; + + for(int i = 0; i < 8; i++) { + real_t xi_i = ref_coords[i][0]; + real_t eta_i = ref_coords[i][1]; + real_t zeta_i = ref_coords[i][2]; + + grad_basis[i][0] = 0.125 * xi_i * (1.0 + eta_i*eta) * (1.0 + zeta_i*zeta); + grad_basis[i][1] = 0.125 * (1.0 + xi_i*xi) * eta_i * (1.0 + zeta_i*zeta); + grad_basis[i][2] = 0.125 * (1.0 + xi_i*xi) * (1.0 + eta_i*eta) * zeta_i; + } + }; + + // Compute ANALYTICAL Jacobian using IJK ordering + auto compute_analytical_jacobian = [&](real_t xi, real_t eta, real_t zeta, + real_t J[3][3]) { + real_t grad_basis[8][3]; + compute_hex8_basis_gradients_ijk(xi, eta, zeta, grad_basis); + + // J_ij = Sum_k x_k^i * (partial N_k/partial xi_j) + for(int i = 0; i < 3; i++) { + for(int j = 0; j < 3; j++) { + J[i][j] = 0.0; + for(int k = 0; k < 8; k++) { + J[i][j] += node_coords_ijk[k][i] * grad_basis[k][j]; + } + } + } + }; + + // Setup Kokkos arrays with IJK ordering + DCArrayKokkos node_coords_dual(8,3, "node coords dual"); + DCArrayKokkos node_in_elem_dual(8, "nodes in elem dual"); + DCArrayKokkos jac(3, 3, "jacobian"); + + for(size_t n = 0; n < 8; n++) { + node_in_elem_dual.host(n) = n; + for(size_t i = 0; i < 3; i++) { + node_coords_dual.host(n,i) = node_coords_ijk[n][i]; // Already in IJK order! + } + } + node_coords_dual.update_device(); + node_in_elem_dual.update_device(); + + // ---- reference volume ---- + Quadrature_t Quad; + ReferenceElement_t RefElem; + + Quad.initialize_quadrature(reference_space::GaussLegendre, 3, 3); //(type,num_qpts_1d,elem_dims) + + RefElem.initialize_ref_elem(reference_space::arbitraryOrderElement, + reference_space::LagrangeLobatto, + Quad, + 1); + + // ---- reference surface ---- + SurfaceQuadrature_t SurfQuad; + ReferenceSurface_t RefSurf; + + SurfQuad.initialize_quadrature(reference_space::GaussLegendre, 3, 3); //(type,num_qpts_1d,elem_dims) + + RefSurf.initialize_ref_surf(SurfQuad, + RefElem); + + // Test points + const double qpt = 0.774596669241483377035853079956; // sqrt(3/5) + real_t test_points[3][3] = { + {-qpt, -qpt, -qpt}, // qpt_id = 0 + {0.0, 0.0, 0.0}, // qpt_id = 13 + {qpt, qpt, qpt}, // qpt_id = 26 + }; + + printf("\nTesting Jacobian at quadrature points:\n"); + printf("%s\n", std::string(80, '=').c_str()); + + size_t qpt_id = 0; + bool all_passed = true; + + for(int p = 0; p < 3; p++) { + real_t xi = test_points[p][0]; + real_t eta = test_points[p][1]; + real_t zeta = test_points[p][2]; + + printf("\n Point %d: (xi=%7.4f, eta=%7.4f, zeta=%7.4f)\n", p+1, xi, eta, zeta); + printf(" %s\n", std::string(70, '-').c_str()); + + // Analytical + real_t J_analytical[3][3]; + compute_analytical_jacobian(xi, eta, zeta, J_analytical); + + printf(" Analytical Jacobian:\n"); + for(int i = 0; i < 3; i++) { + printf(" "); + for(int j = 0; j < 3; j++) { + printf("%10.6f ", J_analytical[i][j]); + } + printf("\n"); + } + + // Numerical + RUN({ + ViewCArrayKokkos a_grad_basis(&RefElem.qpt_grad_basis(qpt_id,0,0), + RefElem.num_dofs_in_elem, 3); + + jacobian(jac, + node_coords_dual, + node_in_elem_dual, + a_grad_basis); + }); + Kokkos::fence(); + jac.update_host(); + + printf("\n Numerical Jacobian:\n"); + for(size_t i = 0; i < 3; i++){ + printf(" "); + for(size_t j = 0; j < 3; j++){ + printf("%10.6f ", jac.host(i, j)); + } + printf("\n"); + } + + // Compare + double max_error = 0.0; + printf("\n Absolute Error:\n"); + for(int i = 0; i < 3; i++) { + printf(" "); + for(int j = 0; j < 3; j++) { + double error = fabs(J_analytical[i][j] - jac.host(i,j)); + max_error = fmax(max_error, error); + printf("%10.2e ", error); + } + printf("\n"); + } + + // Determinant + auto det_analytical = J_analytical[0][0]*(J_analytical[1][1]*J_analytical[2][2] - + J_analytical[1][2]*J_analytical[2][1]) + - J_analytical[0][1]*(J_analytical[1][0]*J_analytical[2][2] - + J_analytical[1][2]*J_analytical[2][0]) + + J_analytical[0][2]*(J_analytical[1][0]*J_analytical[2][1] - + J_analytical[1][1]*J_analytical[2][0]); + + DCArrayKokkos det_numerical(1, "det_numerical dual"); + RUN({ + det_numerical(0) = det_3x3(jac); + }); + det_numerical.update_host(); + + double det_error = fabs(det_analytical - det_numerical.host(0)); + + printf("\n Determinant:\n"); + printf(" Analytical: %12.8f\n", det_analytical); + printf(" Numerical: %12.8f\n", det_numerical.host(0)); + printf(" Error: %12.2e\n", det_error); + + const double tol = 1e-10; + bool passed = (max_error < tol) && (det_error < tol); + + printf("\n Max Error: %12.2e\n", max_error); + printf(" Result: %s\n", passed ? " PASSED" : "X FAILED"); + + if(passed==false)Kokkos::abort("test failed \n"); + + if(!passed) all_passed = false; + + + qpt_id += 13; + } // end for qpt loop + + // =============================== + // Now testing a surface element + // =============================== + + real_t surf_test_points[18][3] = { + // side 0 (xi=-1) + {-1, -qpt, -qpt}, // qpt_id = 0 + {-1, 0.0, 0.0}, // qpt_id = 4 + {-1, qpt, qpt}, // qpt_id = 8 + // side 1 (xi=1) + {1, -qpt, -qpt}, // qpt_id = 0 + {1, 0.0, 0.0}, // qpt_id = 4 + {1, qpt, qpt}, // qpt_id = 8 + // side 2 (eta=-1) + {-qpt, -1, -qpt}, // qpt_id = 0 + {0.0, -1, 0.0}, // qpt_id = 4 + {qpt, -1, qpt}, // qpt_id = 8 + // side 3 (eta=1) + {-qpt, 1, -qpt}, // qpt_id = 0 + {0.0, 1, 0.0}, // qpt_id = 4 + {qpt, 1, qpt}, // qpt_id = 8 + //side 4 (mu=-1) + {-qpt, -qpt, -1}, // qpt_id = 0 + {0.0, 0.0, -1}, // qpt_id = 4 + {qpt, qpt, -1}, // qpt_id = 8 + //side 5 (mu=1) + {-qpt, -qpt, 1}, // qpt_id = 0 + {0.0, 0.0, 1}, // qpt_id = 4 + {qpt, qpt, 1} // qpt_id = 8 + }; + + printf("\nTesting Jacobian at quadrature points:\n"); + printf("%s\n", std::string(80, '=').c_str()); + + // reset helper vars + qpt_id = 0; + all_passed = true; + + for(int side = 0; side<6; side++){ + printf("\n\n side = %d\n", side); + + for(int lid = 0; lid < 3; lid++) { + int p = lid + side*3; + real_t xi = surf_test_points[p][0]; + real_t eta = surf_test_points[p][1]; + real_t zeta = surf_test_points[p][2]; + + printf("\n Point %d: (xi=%7.4f, eta=%7.4f, zeta=%7.4f)\n", p+1, xi, eta, zeta); + // CPU prints only after here for verifying tests: + //printf(" QPt # %zu: (xi=%7.4f, eta=%7.4f, zeta=%7.4f)\n", + // qpt_id, + // SurfQuad.qpt_positions(side,qpt_id,0), + // SurfQuad.qpt_positions(side,qpt_id,1), + // SurfQuad.qpt_positions(side,qpt_id,2) + // ); + printf(" %s\n", std::string(70, '-').c_str()); + + // Analytical + real_t J_analytical[3][3]; + compute_analytical_jacobian(xi, eta, zeta, J_analytical); + + printf(" Analytical Jacobian:\n"); + for(int i = 0; i < 3; i++) { + printf(" "); + for(int j = 0; j < 3; j++) { + printf("%10.6f ", J_analytical[i][j]); + } + printf("\n"); + } + + // Numerical + RUN({ + // extract the grad_basis at a single quadrature point (num_dofs,3D) + ViewCArrayKokkos a_grad_basis(&RefSurf.qpt_grad_basis(side,qpt_id,0,0), + RefElem.num_dofs_in_elem, 3); + + jacobian(jac, + node_coords_dual, + node_in_elem_dual, + a_grad_basis); + }); + Kokkos::fence(); + jac.update_host(); + + printf("\n Numerical Jacobian:\n"); + for(size_t i = 0; i < 3; i++){ + printf(" "); + for(size_t j = 0; j < 3; j++){ + printf("%10.6f ", jac.host(i, j)); + } + printf("\n"); + } + + // Compare + double max_error = 0.0; + printf("\n Absolute Error:\n"); + for(int i = 0; i < 3; i++) { + printf(" "); + for(int j = 0; j < 3; j++) { + double error = fabs(J_analytical[i][j] - jac.host(i,j)); + max_error = fmax(max_error, error); + printf("%10.2e ", error); + } + printf("\n"); + } + + // Determinant + auto det_analytical = J_analytical[0][0]*(J_analytical[1][1]*J_analytical[2][2] - + J_analytical[1][2]*J_analytical[2][1]) + - J_analytical[0][1]*(J_analytical[1][0]*J_analytical[2][2] - + J_analytical[1][2]*J_analytical[2][0]) + + J_analytical[0][2]*(J_analytical[1][0]*J_analytical[2][1] - + J_analytical[1][1]*J_analytical[2][0]); + + DCArrayKokkos det_numerical(1, "det_numerical"); + RUN({ + det_numerical(0) = det_3x3(jac); + }); + det_numerical.update_host(); + double det_error = fabs(det_analytical - det_numerical.host(0)); + + printf("\n Determinant:\n"); + printf(" Analytical: %12.8f\n", det_analytical); + printf(" Numerical: %12.8f\n", det_numerical.host(0)); + printf(" Error: %12.2e\n", det_error); + + const double tol = 1e-10; + bool passed = (max_error < tol) && (det_error < tol); + + printf("\n Max Error: %12.2e\n", max_error); + printf(" Result: %s\n", passed ? " PASSED" : "X FAILED"); + + if(passed==false)Kokkos::abort("test failed \n"); + + if(!passed) all_passed = false; + + qpt_id += 4; + if(qpt_id>8) qpt_id=0; // restart local counter on the surface + } // end for qpt loop + } // end for side of ref element + +} // end function + + + +int main(int argc, char** argv) { + +MATAR_INITIALIZE(argc, argv); +{ // MATAR scope + std::cout<<"Reference Element plus Mesh Example!"< node_coords(Mesh.num_nodes, Mesh.num_dims); + const double h = 1.0/((double)num_nodes_1D); + + // create indexing for a Pn order mesh + + // Step 1: Initialize ALL node coordinates once (no race condition) + FOR_ALL(kc, 0, num_nodes_1D, + jc, 0, num_nodes_1D, + ic, 0, num_nodes_1D, { + + size_t node_gid = ic + (jc + kc*num_nodes_1D)*num_nodes_1D; + + node_coords(node_gid, 0) = (double)ic * h; + node_coords(node_gid, 1) = (double)jc * h; + node_coords(node_gid, 2) = (double)kc * h; + }); + + // Step 2: Build element connectivity + FOR_ALL(i, 0, num_elems_1D, + j, 0, num_elems_1D, + k, 0, num_elems_1D, { + + size_t elem_gid = i + (j + k*num_elems_1D)*num_elems_1D; + size_t node_lid = 0; + + // FIX: Multiply by elem_order to space elements correctly + for(size_t kc = k*elem_order; kc <= k*elem_order + elem_order; kc++) + for(size_t jc = j*elem_order; jc <= j*elem_order + elem_order; jc++) + for(size_t ic = i*elem_order; ic <= i*elem_order + elem_order; ic++){ + size_t node_gid = ic + (jc + kc*num_nodes_1D)*num_nodes_1D; + Mesh.nodes_in_elem(elem_gid, node_lid) = node_gid; + node_lid++; + } + }); + + std::cout<<"Building corner connectivity \n"; + Mesh.build_corner_connectivity(); + std::cout<<"Building element element connectivity \n"; + Mesh.build_elem_elem_connectivity(); + std::cout<<"Building surface connectivity connectivity \n"; + Mesh.build_surf_connectivity(); + + // check mesh index sizes + if(Mesh.num_nodes!=num_nodes){ + printf("num nodes = %zu and mesh.num_nodes = %zu", num_nodes, Mesh.num_nodes); + Kokkos::abort("ERROR: wrong number of mesh nodes"); + } + if(Mesh.num_gauss_in_elem!=Quad.num_qpts_in_elem){ + Kokkos::abort("ERROR: wrong number of Gauss points in elem"); + } + + + printf("\n=== TEST: 2x2x2 Cubic Mesh Face Pair Test ===\n"); + + // Face 1 of element 0 matches face 0 of element 1 + const size_t elem_nodes_in_face_match[16] = + {3, 10, 17, 24, 52, 59, 66, 73, 101, 108, 115, 122, 150, 157, 164, 171}; + + // Check surfaces and patches + FOR_ALL(elem_gid, 0, Mesh.num_elems, { + + for(size_t face_lid = 0; face_lid < Mesh.num_surfs_in_elem; face_lid++) { + + // Only test the specific faces we want to validate + if(!((elem_gid == 0 && face_lid == 1) || (elem_gid == 1 && face_lid == 0))) { + continue; + } + + size_t count = 0; // Count unique nodes found across all patches in this face + bool found[16] = {false}; // Track which of the 16 face nodes we've found + + for(size_t patch_surf_lid = 0; patch_surf_lid < Mesh.num_patches_in_surf; patch_surf_lid++) { + + const size_t patch_lid = face_lid * Mesh.num_patches_in_surf + patch_surf_lid; // FIX: num_patches_in_surf + const size_t patch_gid = Mesh.patches_in_elem(elem_gid, patch_lid); + const size_t surf_gid = Mesh.surf_in_patch(patch_gid); + + // Validate surface ID + if(surf_gid > 5) { + printf("ERROR: should be <= 5, but surf_gid = %d \n", (int)surf_gid); + Kokkos::abort("Surface gid is out of bounds\n"); + } + + // Check each node in this patch + for(size_t node_lid = 0; node_lid < 4; node_lid++) { + const size_t node_gid = Mesh.nodes_in_patch(patch_gid, node_lid); + + // Check if this node is in our expected face list + for(size_t i = 0; i < 16; i++) { + if(elem_nodes_in_face_match[i] == node_gid && !found[i]) { + found[i] = true; + count++; + break; + } + } + } + } // end patch loop + + // Now check that we found all 16 unique nodes + if(count != 16) { + printf("ERROR: Elem %d, Face %zu found only %zu/16 nodes\n", + elem_gid, face_lid, count); + Kokkos::abort("Face nodes don't match!\n"); + } + + } // end face loop + }); + Kokkos::fence(); + + printf("Face matching test PASSED!\n\n"); + + + + // Test bidirectional surface-element mapping consistency + DCArrayKokkos bdy_surf_counter(1); + bdy_surf_counter.set_values(0); + + FOR_ALL(surf_gid, 0, Mesh.num_surfs, { + + const size_t num_elems_in_surf = Mesh.num_elems_in_surf(surf_gid); + if(num_elems_in_surf==1) Kokkos::atomic_add(&bdy_surf_counter(0),1); + + // Test should work for both boundary (1 elem) and interior (2 elems) surfaces + for(size_t side_lid = 0; side_lid < num_elems_in_surf; side_lid++) { + + const int elem_gid = Mesh.elems_in_surf(surf_gid, side_lid); + const int face_lid = Mesh.faces_in_surf(surf_gid, side_lid); + + // Verify elem_gid is valid + if(elem_gid < 0 || elem_gid >= Mesh.num_elems) { + printf("ERROR: surf %d side_lid %zu has invalid elem_gid = %d\n", + surf_gid, side_lid, elem_gid); + Kokkos::abort("Invalid element in surface\n"); + } + + // Verify face_lid is valid + if(face_lid < 0 || face_lid >= Mesh.num_surfs_in_elem) { + printf("ERROR: surf %d side_lid %zu has invalid face_lid = %d\n", + surf_gid, side_lid, face_lid); + Kokkos::abort("Invalid face in surface\n"); + } + + // *** CRITICAL TEST: Verify reverse mapping *** + const size_t reverse_surf_gid = Mesh.surfs_in_elem(elem_gid, face_lid); + if(reverse_surf_gid != surf_gid) { + printf("ERROR: Mapping inconsistency!\n"); + printf(" surf %d -> elem %d, face %d\n", surf_gid, elem_gid, face_lid); + printf(" BUT elem %d, face %d -> surf %zu\n", + elem_gid, face_lid, reverse_surf_gid); + Kokkos::abort("Failed reverse mapping surf->elem->surf\n"); + } + + } // end side_lid loop + + }); // end surf loop + Kokkos::fence(); + bdy_surf_counter.update_host(); + printf("Surface-Element bidirectional mapping test PASSED!\n"); + + if(Mesh.num_bdy_surfs!=bdy_surf_counter.host(0)){ + Kokkos::abort("Failed to find correct number of boundary surfaces\n"); + } + if(Mesh.num_bdy_patches!=bdy_surf_counter.host(0)*Mesh.num_patches_in_surf) Kokkos::abort("Failed to find correct number of boundary patches\n"); + printf("Boundary surfaces and patches test PASSED!\n"); + + // for this 2x2x2 mesh, there are: + // 12 interior surfaces + 24 boundary surfaces + // 3 planes of 2x2 in x-dir = 12 + // 3 planes of 2x2 in y-dir = 12 + // 3 planes of 2x2 in z-dir = 12 + // 36 total surfaces + if(Mesh.num_surfs!=36) Kokkos::abort("Wrong number of surfaces\n"); + if(Mesh.num_bdy_surfs!=24) Kokkos::abort("Wrong number of boundary surfaces\n"); + printf("\n\n"); + + + // ========================================== + // Create state on the unstructured mesh structure + + DCArrayKokkos node_scalar(Mesh.num_nodes); + DCArrayKokkos gauss_scalar(Mesh.num_gauss_in_elem); + + + // =========== + RUN({ + + size_t elem_gid = 0; // testing first element + size_t qpt = 0; // testing geometry at first quadrature point + + const size_t num_nodes_in_elem = FERefElem.num_dofs_in_elem; + + // get Jacobian + double j_1D[9]; + ViewCArrayKokkos jac(&j_1D[0], 3, 3); + + // make views on device, extacting only element and quadrature point values + ViewCArrayKokkos nodes_in_an_elem(&Mesh.nodes_in_elem(elem_gid,0), num_nodes_in_elem); + ViewCArrayKokkos a_grad_basis(&FERefElem.qpt_grad_basis(qpt, 0, 0), num_nodes_in_elem, 3); + + jacobian(jac, + node_coords, + nodes_in_an_elem, + a_grad_basis); + + printf("jacobian matrix = \n"); + for(size_t i = 0; i < elem_dims; i++){ // looping over dimension + for(size_t j = 0; j < elem_dims; j++){ // looping over dimension + printf("%f, ", jac(i, j)); + } + printf("\n"); + } // end for + }); + + + printf("\nReference plus mesh test finished.\n"); + + // running unit tests of Jacobian + test_affine_transformation(); + test_skewed_hexahedron(); + test_rotated_scaled_cube(); + test_manufactured_solution(); + + +} // end MATAR scope +MATAR_FINALIZE(); + +return 0; +} diff --git a/examples/reference_element/src/remap_dg_test.cpp b/examples/reference_element/src/remap_dg_test.cpp new file mode 100644 index 00000000..96410af6 --- /dev/null +++ b/examples/reference_element/src/remap_dg_test.cpp @@ -0,0 +1,1130 @@ +/********************************************************************************************** +© 2020. Triad National Security, LLC. All rights reserved. +This program was produced under U.S. Government contract 89233218CNA000001 for Los Alamos +National Laboratory (LANL), which is operated by Triad National Security, LLC for the U.S. +Department of Energy/National Nuclear Security Administration. All rights in the program are +reserved by Triad National Security, LLC, and the U.S. Department of Energy/National Nuclear +Security Administration. The Government is granted for itself and others acting on its behalf a +nonexclusive, paid-up, irrevocable worldwide license in this material to reproduce, prepare +derivative works, distribute copies to the public, perform publicly and display publicly, and +to permit others to do so. +This program is open source under the BSD-3 License. +Redistribution and use in source and binary forms, with or without modification, are permitted +provided that the following conditions are met: +1. Redistributions of source code must retain the above copyright notice, this list of +conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, this list of +conditions and the following disclaimer in the documentation and/or other materials +provided with the distribution. +3. Neither the name of the copyright holder nor the names of its contributors may be used +to endorse or promote products derived from this software without specific prior +written permission. +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**********************************************************************************************/ +#include +#include +#include + +// for VTU writing +#include +#include +#include +#include +#include + +// This pulls in kokkos, matar, mesh, ref_elem stuff, and PT-Scotch +#include "ELEMENTS.h" +#include "cramers_rule.hpp" // det and solvers +#include "lu_solver.hpp" + +using namespace mtr; +using namespace swage; // unstructured mesh and point cloud +using namespace elements; // reference element space + + +void write_lagrange_hex_mesh( + const std::string& filename, + const DCArrayKokkos& node_coords, // All node coordinates [num_nodes][3] + const size_t num_nodes, + const DCArrayKokkos& nodes_in_elem, // Connectivity + const size_t num_elems, + const size_t order, + const DCArrayKokkos& node_data, // Nodal data + const std::string& node_data_name, + const DCArrayKokkos& elem_data, // Element center data (NEW) + const std::string& elem_data_name); // Element data name (NEW) + + +void write_lagrange_cells(std::ofstream& file, + const DCArrayKokkos& nodes_in_elem, + size_t num_elems, + size_t order, + const DCArrayKokkos& elem_data, // Element data + const std::string& elem_data_name); // Element data name + +void write_points(std::ofstream& file, + const DCArrayKokkos& coords, + size_t num_nodes); + + +void write_point_data(std::ofstream& file, + const DCArrayKokkos& data, + size_t num_nodes, + const std::string& name); + +void reorder_ijk_to_vtk_lagrange(const DCArrayKokkos& nodes_in_elem, + CArray& vtk_nodes, + const size_t elem_gid, + const size_t order); + +inline int PointIndexFromIJK(int i, int j, int k, const int* order); + +int main(int argc, char** argv) { + +MATAR_INITIALIZE(argc, argv); +{ // MATAR scope + std::cout<<"Reference Element Remap Example!"< node_coords(Mesh.num_nodes, Mesh.num_dims); + const double h = 1.0/((double)num_nodes_1D-1); + + // create indexing for a Pn order mesh + + // Step 1: Initialize ALL node coordinates once (no race condition) + FOR_ALL(kc, 0, num_nodes_1D, + jc, 0, num_nodes_1D, + ic, 0, num_nodes_1D, { + + size_t node_gid = ic + (jc + kc*num_nodes_1D)*num_nodes_1D; + + node_coords(node_gid, 0) = (double)ic * h; + node_coords(node_gid, 1) = (double)jc * h; + node_coords(node_gid, 2) = (double)kc * h; + }); + + // Step 2: Build element connectivity + FOR_ALL(i, 0, num_elems_1D, + j, 0, num_elems_1D, + k, 0, num_elems_1D, { + + size_t elem_gid = i + (j + k*num_elems_1D)*num_elems_1D; + size_t node_lid = 0; + + // FIX: Multiply by elem_order to space elements correctly + for(size_t kc = k*elem_order; kc <= k*elem_order + elem_order; kc++) + for(size_t jc = j*elem_order; jc <= j*elem_order + elem_order; jc++) + for(size_t ic = i*elem_order; ic <= i*elem_order + elem_order; ic++){ + size_t node_gid = ic + (jc + kc*num_nodes_1D)*num_nodes_1D; + Mesh.nodes_in_elem(elem_gid, node_lid) = node_gid; + node_lid++; + } + }); + Kokkos::fence(); + Mesh.nodes_in_elem.update_host(); + + + std::cout<<"Building corner connectivity \n"; + Mesh.build_corner_connectivity(); + std::cout<<"Building element element connectivity \n"; + Mesh.build_elem_elem_connectivity(); + std::cout<<"Building surface connectivity \n"; + Mesh.build_surf_connectivity(); + + // check mesh index sizes + if(Mesh.num_nodes!=num_nodes){ + printf("num nodes = %zu and mesh.num_nodes = %zu", num_nodes, Mesh.num_nodes); + Kokkos::abort("ERROR: wrong number of mesh nodes"); + } + if(Mesh.num_gauss_in_elem!=Quad.num_qpts_in_elem){ + Kokkos::abort("ERROR: wrong number of Gauss points in elem"); + } + + + // State arrays for this test + const size_t num_qpts_in_elem = Quad.num_qpts_in_elem; + + const size_t num_surfs = Mesh.num_surfs; + const size_t num_qpts_in_surf = SurfQuad.num_qpts_in_surf; + + const size_t num_nodes_in_elem = Mesh.num_nodes_in_elem; + const size_t num_surfs_in_elem = Mesh.num_surfs_in_elem; + + const size_t num_corners = Mesh.num_corners; + + if(num_nodes_in_elem != FERefElem.num_dofs_in_elem) Kokkos::abort("ERROR: mismatch in DOFs and num nodes in elem \n"); + + DCArrayKokkos elem_jac(num_elems, num_qpts_in_elem, elem_dims, elem_dims, "elem_jacobian"); + DCArrayKokkos elem_inv_jac(num_elems, num_qpts_in_elem, elem_dims, elem_dims, "elem_jacobian"); + DCArrayKokkos elem_det_jac(num_elems, num_qpts_in_elem, "elem_det_jacobian"); + DCArrayKokkos elem_vol(num_elems, "elem_vol"); + + DCArrayKokkos surf_jac(num_surfs, num_qpts_in_surf, elem_dims, elem_dims, "surf_jacobian"); + DCArrayKokkos surf_inv_jac(num_surfs, num_qpts_in_surf, elem_dims, elem_dims, "surf_inv_jacobian"); + DCArrayKokkos surf_flux(num_surfs, "surf_flux"); + + DCArrayKokkos elem_field(num_elems, "elem_field"); + DCArrayKokkos node_field(num_nodes, "node_field"); // for displaying field results + DCArrayKokkos node_velocity(num_nodes, elem_dims, "node_velocity"); + DCArrayKokkos node_velocity_n(num_nodes, elem_dims, "node_velocity_n"); + DCArrayKokkos node_coords_n(num_nodes, elem_dims, "node_coords_n"); + + DCArrayKokkos corner_field(num_corners, "corner_field"); + DCArrayKokkos corner_field_n(num_corners, "corner_field_n"); + DCArrayKokkos elem_vol_matrix(num_elems, num_nodes_in_elem, num_nodes_in_elem, "elem_vol_matrix"); + DCArrayKokkos elem_inv_vol_matrix(num_elems, num_nodes_in_elem, num_nodes_in_elem, "elem_inv_vol_matrix"); + DCArrayKokkos elem_vol_matrix_n(num_elems, num_nodes_in_elem, num_nodes_in_elem, "elem_vol_matrix_n"); + + CArrayKokkos perm_elem(num_elems,num_nodes_in_elem, "perm"); // permutation array + CArrayKokkos vv_elem(num_elems,num_nodes_in_elem, "vv"); // temp arrary for LU solver + + // Calculate RHS_surf_flux + CArrayKokkos RHS_surf_flux(num_elems, num_surfs_in_elem, num_qpts_in_surf, "RHS_surf_flux"); // used to build RHS vector + CArrayKokkos RHS_elem(num_elems, num_nodes_in_elem, "RHS_elem"); // RHS vector + + + // ================================================================ + // Build qpt to qpt connectivity on surfaces of elements + + CArrayKokkos surf_qpt_qpt_map(num_surfs,2,num_qpts_in_surf); + surf_qpt_qpt_map.set_values(-1); + + build_quadrature_point_connectivity(Mesh, + RefSurf, + surf_qpt_qpt_map, + node_coords); + + + // ================================================================ + // Step 1: build the volume matrix for nodal DG at t=0 + + elem_vol.set_values(0.0); + elem_vol_matrix.set_values(0.0); + FOR_ALL(elem_gid, 0, num_elems, { + + ViewCArrayKokkos nodes_in_elem(&Mesh.nodes_in_elem(elem_gid,0), num_nodes_in_elem); + + // Vol_matrix = \int (phi_q \phi_p j w) + for(size_t dof_lid=0; dof_lid a_grad_basis(&FERefElem.qpt_grad_basis(qpt_lid,0,0), + num_nodes_in_elem, 3); + + // extract the basis at a single quadrature point (qpt,dof) + ViewCArrayKokkos a_basis(&FERefElem.qpt_basis(qpt_lid,0), + num_nodes_in_elem); + + // jacobian matrix + ViewCArrayKokkos jac(&elem_jac(elem_gid,qpt_lid,0,0),3,3); + + jacobian(jac, + node_coords, + nodes_in_elem, + a_grad_basis); + + // calculate det_J + elem_det_jac(elem_gid, qpt_lid) = det_3x3(jac); + + // volume contribution from qpt + const double vol_qpt = elem_det_jac(elem_gid, qpt_lid)*Quad.qpt_weights(qpt_lid); + + elem_vol(elem_gid) += vol_qpt; + + elem_vol_matrix(elem_gid, dof_lid, node_lid) += a_basis(dof_lid)*a_basis(node_lid)*vol_qpt; + } // end for + + }); // end parallel for + Kokkos::fence(); + + + // ----------------------------------------------------- + const double max_vel = 1.0; // the CFL velocity used for calculating dt + double h_cfl = h/(double)num_nodes_1D; // the CFL length scale for calculating dt + double dt = 0.2*h_cfl/max_vel; // dt from CFL at start, this time is psuedo time + + + // ----------------------------------------------------- + double time = 0; // the time + double time_output = graphics_dt; // the time for graphics outputs + size_t output_id = 0; // the file id for the outputs + size_t rk_num_stages = 1; // runge kutta time integration levels + + + // ================================================================ + // Step 0: Set the initial conditions + + FOR_ALL(elem_gid, 0, num_elems, { + + + ViewCArrayKokkos nodes_in_elem(&Mesh.nodes_in_elem(elem_gid,0), num_nodes_in_elem); + + for(size_t corner_lid=0; corner_lid nodes_in_elem(&Mesh.nodes_in_elem(elem_gid,0), num_nodes_in_elem); + + for(size_t dof_lid=0; dof_lid a_grad_basis(&RefSurf.qpt_grad_basis(face_lid,qpt_lid,0,0), + num_nodes_in_elem, 3); + + // extract the basis at a single quadrature point (surf,qpt,dof) + ViewCArrayKokkos a_basis(&RefSurf.qpt_basis(face_lid,qpt_lid,0), + num_nodes_in_elem); + + ViewCArrayKokkos jac(&surf_jac(surf_gid,qpt_lid,0,0),3,3); + ViewCArrayKokkos inv_jac(&surf_inv_jac(surf_gid,qpt_lid,0,0),3,3); + + jacobian(jac, + node_coords, + nodes_in_elem, + a_grad_basis); + + const double det_jac_qpt = det_3x3(jac); + + invert_3x3(jac, inv_jac, det_jac_qpt); + + // Nanson's formula: s*J^-1*j*f*w + double area_normal[3]; + area_normal[0] = 0.; + area_normal[1] = 0.; + area_normal[2] = 0.; + for(size_t j=0; j a_nbr_basis(&RefSurf.qpt_basis(nbr_face_lid,nbr_qpt_lid,0), + num_nodes_in_elem); + + // reconstruct the fields + double qpt_field = 0.0; + double nbr_qpt_field = 0.0; + + for(size_t node_lid=0; node_lid nodes_in_elem(&Mesh.nodes_in_elem(elem_gid,0), num_nodes_in_elem); + + // ---------------------------------------------- + // 4a. Initialize RHS = M*u^n + + for(size_t dof_lid = 0; dof_lid < num_nodes_in_elem; dof_lid++){ + + // Add M * u^n term + for(size_t node_lid = 0; node_lid < num_nodes_in_elem; node_lid++){ + const size_t corner_gid = Mesh.corners_in_elem(elem_gid, node_lid); + RHS_elem(elem_gid, dof_lid) += + elem_vol_matrix_n(elem_gid, dof_lid, node_lid) * corner_field_n(corner_gid); + } + } + + // ---------------------------------------------- + // 4b. Subtract the VOLUME integral: \int (\nabal phi_q)J^{-1}*(v*U) dV + + for(size_t dof_lid = 0; dof_lid < num_nodes_in_elem; dof_lid++) + for(size_t qpt_lid = 0; qpt_lid < num_qpts_in_elem; qpt_lid++){ + + ViewCArrayKokkos a_grad_basis(&FERefElem.qpt_grad_basis(qpt_lid,0,0), + num_nodes_in_elem, 3); + ViewCArrayKokkos a_basis(&FERefElem.qpt_basis(qpt_lid,0), + num_nodes_in_elem); + + // jacobian matrix and inver + ViewCArrayKokkos jac(&elem_jac(elem_gid,qpt_lid,0,0),3,3); + ViewCArrayKokkos inv_jac(&elem_inv_jac(elem_gid,qpt_lid,0,0),3,3); + + jacobian(jac, + node_coords, + nodes_in_elem, + a_grad_basis); + + elem_det_jac(elem_gid,qpt_lid) = det_3x3(jac); + + invert_3x3(jac, inv_jac, elem_det_jac(elem_gid,qpt_lid)); + + // Reconstruct field at quadrature point + double qpt_field = 0.0; + for(size_t node_lid = 0; node_lid < num_nodes_in_elem; node_lid++){ + const size_t corner_gid = Mesh.corners_in_elem(elem_gid, node_lid); + qpt_field += a_basis(node_lid) * corner_field(corner_gid); + } + + // Reconstruct velocity at quadrature point + double qpt_vel[3]; + qpt_vel[0] = 0.0; + qpt_vel[1] = 0.0; + qpt_vel[2] = 0.0; + + for(size_t node_lid = 0; node_lid < num_nodes_in_elem; node_lid++){ + const size_t node_gid = Mesh.nodes_in_elem(elem_gid, node_lid); + for(size_t dim = 0; dim < elem_dims; dim++){ + qpt_vel[dim] += a_basis(node_lid) * node_velocity(node_gid, dim); + } + } + + // transform the gradient to the physical space + double physical_grad[3]; + physical_grad[0] = 0.0; + physical_grad[1] = 0.0; + physical_grad[2] = 0.0; + for(size_t i = 0; i < elem_dims; i++) + for(size_t j = 0; j < elem_dims; j++){ + physical_grad[i] += a_grad_basis(dof_lid, j)*inv_jac(j,i); + } + + // Compute (\nabal phi_q)*J^-1*(v*U) + // From Anderson et. al. paper + double grad_dot_flux = 0.0; + for(size_t dim = 0; dim < elem_dims; dim++){ + grad_dot_flux += physical_grad[dim] * qpt_vel[dim] * qpt_field; + } + + const double vol_qpt = elem_det_jac(elem_gid,qpt_lid) * Quad.qpt_weights(qpt_lid); + RHS_elem(elem_gid, dof_lid) -= rk_alpha * dt * grad_dot_flux * vol_qpt; + } // end for dof_lid and qpt + + // ---------------------------------------------- + // 4c. Add SURFACE flux contribution + + for(size_t dof_lid = 0; dof_lid < num_nodes_in_elem; dof_lid++) + for(size_t face_lid = 0; face_lid < num_surfs_in_elem; face_lid++) + for(size_t qpt_lid = 0; qpt_lid < num_qpts_in_surf; qpt_lid++){ + + ViewCArrayKokkos a_basis(&RefSurf.qpt_basis(face_lid, qpt_lid, 0), + num_nodes_in_elem); + + // surface flux (note: RHS_surf_flux already has correct sign) + RHS_elem(elem_gid, dof_lid) += + rk_alpha * dt * RHS_surf_flux(elem_gid, face_lid, qpt_lid) * a_basis(dof_lid); + } + + }); + Kokkos::fence(); + + + + // ================================================================ + // Step 5: Move the mesh to the new location + FOR_ALL(node_gid, 0, num_nodes,{ + // new position of the mesh + node_coords(node_gid, 0) = node_coords_n(node_gid, 0) + 0.5*(node_velocity(node_gid, 0)+node_velocity_n(node_gid, 0)) * rk_alpha * dt; + node_coords(node_gid, 1) = node_coords_n(node_gid, 1) + 0.5*(node_velocity(node_gid, 1)+node_velocity_n(node_gid, 1)) * rk_alpha * dt; + // z-coords never change + }); + Kokkos::fence(); + + + // ================================================================ + // Step 6: build the volume matrix for nodal DG after the mesh moved + elem_vol.set_values(0.0); + elem_vol_matrix.set_values(0.0); + FOR_ALL(elem_gid, 0, num_elems, { + + ViewCArrayKokkos nodes_in_elem(&Mesh.nodes_in_elem(elem_gid,0), num_nodes_in_elem); + + // Vol_matrix = \int (phi_q \phi_p j w) + for(size_t dof_lid=0; dof_lid a_grad_basis(&FERefElem.qpt_grad_basis(qpt_lid,0,0), + num_nodes_in_elem, 3); + + // extract the basis at a single quadrature point (qpt,dof) + ViewCArrayKokkos a_basis(&FERefElem.qpt_basis(qpt_lid,0), + num_nodes_in_elem); + + // jacobian matrix + ViewCArrayKokkos jac(&elem_jac(elem_gid,qpt_lid,0,0),3,3); + + jacobian(jac, + node_coords, + nodes_in_elem, + a_grad_basis); + + // calculate det_J + elem_det_jac(elem_gid, qpt_lid) = det_3x3(jac); + + // volume contribution from qpt + const double vol_qpt = elem_det_jac(elem_gid, qpt_lid)*Quad.qpt_weights(qpt_lid); + + elem_vol(elem_gid) += vol_qpt; + + elem_vol_matrix(elem_gid, dof_lid, node_lid) += a_basis(dof_lid)*a_basis(node_lid)*vol_qpt; + } // end for + + }); // end parallel for + Kokkos::fence(); + + + // ----------------------------------------------------- + // 7. Solve M * u^{n+1} = RHS using LU decomposition + + FOR_ALL(elem_gid, 0, num_elems,{ + + // populate the matrix to invert + for(size_t dof_lid=0; dof_lid perm(&perm_elem(elem_gid,0), num_nodes_in_elem); + ViewCArrayKokkos vv(&vv_elem(elem_gid,0), num_nodes_in_elem); + + ViewCArrayKokkos A(&elem_inv_vol_matrix(elem_gid, 0, 0), + num_nodes_in_elem, num_nodes_in_elem); + ViewCArrayKokkos b(&RHS_elem(elem_gid,0), num_nodes_in_elem); + + singular = LU_decompose(A, perm, vv, parity); + if(singular == 0){ + Kokkos::abort("ERROR: matrix is singular \n"); + } + + //printf("\nA = \n"); + //for(size_t i=0; i1.e-12) Kokkos::abort("ERROR: Mass is not conserved"); + + + // ================================================================ + // Step 8: write outputs + if( time-time_output >= -1.e-8 ){ + + elem_field.set_values(0.0); + FOR_ALL(elem_gid,0,num_elems,{ + for(size_t node_lid=0; node_lid= max_time ){ + printf("Domain mass at time=%f: %f \n", time, domain_mass_time); + break; + } + + } // end loop over cycle + printf(" time = %.4f ", time); + + printf("\n Remap test finished.\n"); + + +} // end MATAR scope +MATAR_FINALIZE(); + +return 0; +} // end function + + + +// +void write_lagrange_hex_mesh( + const std::string& filename, + const DCArrayKokkos& node_coords, // All node coordinates [num_nodes][3] + const size_t num_nodes, + const DCArrayKokkos& nodes_in_elem, // Connectivity + const size_t num_elems, + const size_t order, + const DCArrayKokkos& node_data, // Nodal data + const std::string& node_data_name, + const DCArrayKokkos& elem_data, // Element center data (NEW) + const std::string& elem_data_name) // Element data name (NEW) +{ + std::ofstream vtu_file(filename); + if (!vtu_file.is_open()) { + std::cerr << "Error: Cannot open file " << filename << std::endl; + return; + } + + vtu_file << std::fixed << std::setprecision(8); + + // Header + vtu_file << "\n"; + vtu_file << "\n"; + vtu_file << " \n"; + vtu_file << " \n"; + + // Write Points + write_points(vtu_file, node_coords, num_nodes); + + // Write Cells (connectivity, types, AND cell data) + write_lagrange_cells(vtu_file, nodes_in_elem, num_elems, order, + elem_data, elem_data_name); // Pass element data + + // Write Point Data + write_point_data(vtu_file, node_data, num_nodes, node_data_name); + + // Footer + vtu_file << " \n"; + vtu_file << " \n"; + vtu_file << "\n"; + + vtu_file.close(); + std::cout << "Wrote VTU file: " << filename << std::endl; +} + +void write_points(std::ofstream& file, const DCArrayKokkos& coords, size_t num_nodes) +{ + file << " \n"; + file << " \n"; + + for (size_t i = 0; i < num_nodes; i++) { + file << " " << coords.host(i, 0) << " " + << coords.host(i, 1) << " " + << coords.host(i, 2) << "\n"; + } + + file << " \n"; + file << " \n"; +} + +void write_lagrange_cells(std::ofstream& file, + const DCArrayKokkos& nodes_in_elem, + size_t num_elems, + size_t order, + const DCArrayKokkos& elem_data, // Element data + const std::string& elem_data_name) // Element data name +{ + const size_t nodes_per_elem = (order + 1) * (order + 1) * (order + 1); + const int VTK_LAGRANGE_HEXAHEDRON = 72; + + file << " \n"; + + // Connectivity + file << " \n"; + + CArray vtk_nodes(nodes_per_elem); + + for (size_t elem = 0; elem < num_elems; elem++) { + // Convert to VTK ordering + reorder_ijk_to_vtk_lagrange(nodes_in_elem, vtk_nodes, elem, order); + + file << " "; + for (size_t i = 0; i < nodes_per_elem; i++) { + file << vtk_nodes(i) << " "; + } + file << "\n"; + } + + file << " \n"; + + // Offsets + file << " \n"; + file << " "; + for (size_t elem = 0; elem < num_elems; elem++) { + file << (elem + 1) * nodes_per_elem << " "; + } + file << "\n \n"; + + // Cell types + file << " \n"; + file << " "; + for (size_t elem = 0; elem < num_elems; elem++) { + file << VTK_LAGRANGE_HEXAHEDRON << " "; + } + file << "\n \n"; + + file << " \n"; + + // CellData section with HigherOrderDegrees AND user data + file << " \n"; + + // HigherOrderDegrees (CRITICAL for Lagrange elements!) + file << " \n"; + file << " "; + for (size_t elem = 0; elem < num_elems; elem++) { + file << order << " " << order << " " << order << " "; + } + file << "\n \n"; + + // User-provided element center data + file << " \n"; + file << " "; + for (size_t elem = 0; elem < num_elems; elem++) { + file << elem_data.host(elem) << " "; + } + file << "\n \n"; + + file << " \n"; +} + +void write_point_data(std::ofstream& file, + const DCArrayKokkos& data, + size_t num_nodes, + const std::string& name) +{ + file << " \n"; + file << " \n"; + + // writing node field data + for (size_t i = 0; i < num_nodes; i++) { + file << " " << data.host(i) << "\n"; + } + + file << " \n"; + file << " \n"; +} + +// Keep your existing helper functions unchanged +void reorder_ijk_to_vtk_lagrange(const DCArrayKokkos& nodes_in_elem, + CArray& vtk_nodes, + const size_t elem_gid, + const size_t order) +{ + const int n = order + 1; + int ord[3] = {(int)order, (int)order, (int)order}; + + std::vector> vtk_to_ijk; + + for(int k = 0; k < n; k++){ + for(int j = 0; j < n; j++){ + for(int i = 0; i < n; i++){ + int vtk_pos = PointIndexFromIJK(i, j, k, ord); + size_t ijk_linear = i + j*n + k*n*n; + vtk_to_ijk.push_back({vtk_pos, ijk_linear}); + } + } + } + + std::sort(vtk_to_ijk.begin(), vtk_to_ijk.end()); + + for(size_t v = 0; v < vtk_to_ijk.size(); v++){ + size_t ijk_linear = vtk_to_ijk[v].second; + vtk_nodes(v) = nodes_in_elem.host(elem_gid, ijk_linear); + } +} + +inline int PointIndexFromIJK(int i, int j, int k, const int* order) +{ + bool ibdy = (i == 0 || i == order[0]); + bool jbdy = (j == 0 || j == order[1]); + bool kbdy = (k == 0 || k == order[2]); + int nbdy = (ibdy ? 1 : 0) + (jbdy ? 1 : 0) + (kbdy ? 1 : 0); + + if (nbdy == 3) { // Vertex DOF + return (i ? (j ? 2 : 1) : (j ? 3 : 0)) + (k ? 4 : 0); + } + + int offset = 8; + if (nbdy == 2) { // Edge DOF + if (!ibdy) { + return (i - 1) + (j ? order[0] - 1 + order[1] - 1 : 0) + + (k ? 2 * (order[0] - 1 + order[1] - 1) : 0) + offset; + } + if (!jbdy) { + return (j - 1) + (i ? order[0] - 1 : 2 * (order[0] - 1) + order[1] - 1) + + (k ? 2 * (order[0] - 1 + order[1] - 1) : 0) + offset; + } + offset += 4 * (order[0] - 1) + 4 * (order[1] - 1); + return (k - 1) + (order[2] - 1) * (i ? (j ? 3 : 1) : (j ? 2 : 0)) + offset; + } + + offset += 4 * (order[0] - 1 + order[1] - 1 + order[2] - 1); + if (nbdy == 1) { // Face DOF + if (ibdy) { + return (j - 1) + ((order[1] - 1) * (k - 1)) + + (i ? (order[1] - 1) * (order[2] - 1) : 0) + offset; + } + offset += 2 * (order[1] - 1) * (order[2] - 1); + if (jbdy) { + return (i - 1) + ((order[0] - 1) * (k - 1)) + + (j ? (order[2] - 1) * (order[0] - 1) : 0) + offset; + } + offset += 2 * (order[2] - 1) * (order[0] - 1); + return (i - 1) + ((order[0] - 1) * (j - 1)) + + (k ? (order[0] - 1) * (order[1] - 1) : 0) + offset; + } + + // Interior DOF + offset += 2 * ((order[1] - 1) * (order[2] - 1) + (order[2] - 1) * (order[0] - 1) + + (order[0] - 1) * (order[1] - 1)); + return offset + (i - 1) + (order[0] - 1) * ((j - 1) + (order[1] - 1) * (k - 1)); +} \ No newline at end of file diff --git a/examples/reference_element/src/remap_fv_test.cpp b/examples/reference_element/src/remap_fv_test.cpp new file mode 100644 index 00000000..4350e16d --- /dev/null +++ b/examples/reference_element/src/remap_fv_test.cpp @@ -0,0 +1,802 @@ +/********************************************************************************************** +© 2020. Triad National Security, LLC. All rights reserved. +This program was produced under U.S. Government contract 89233218CNA000001 for Los Alamos +National Laboratory (LANL), which is operated by Triad National Security, LLC for the U.S. +Department of Energy/National Nuclear Security Administration. All rights in the program are +reserved by Triad National Security, LLC, and the U.S. Department of Energy/National Nuclear +Security Administration. The Government is granted for itself and others acting on its behalf a +nonexclusive, paid-up, irrevocable worldwide license in this material to reproduce, prepare +derivative works, distribute copies to the public, perform publicly and display publicly, and +to permit others to do so. +This program is open source under the BSD-3 License. +Redistribution and use in source and binary forms, with or without modification, are permitted +provided that the following conditions are met: +1. Redistributions of source code must retain the above copyright notice, this list of +conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, this list of +conditions and the following disclaimer in the documentation and/or other materials +provided with the distribution. +3. Neither the name of the copyright holder nor the names of its contributors may be used +to endorse or promote products derived from this software without specific prior +written permission. +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**********************************************************************************************/ +#include +#include +#include + +// for VTU writing +#include +#include +#include +#include +#include + +// This pulls in kokkos, matar, mesh, ref_elem stuff, and PT-Scotch +#include "ELEMENTS.h" +#include "cramers_rule.hpp" // det and solvers + +using namespace mtr; +using namespace swage; // unstructured mesh and point cloud +using namespace elements; // reference element space + + +void write_lagrange_hex_mesh( + const std::string& filename, + const DCArrayKokkos& node_coords, // All node coordinates [num_nodes][3] + const size_t num_nodes, + const DCArrayKokkos& nodes_in_elem, // Connectivity + const size_t num_elems, + const size_t order, + const DCArrayKokkos& node_data, // Nodal data + const std::string& node_data_name, + const DCArrayKokkos& elem_data, // Element center data (NEW) + const std::string& elem_data_name); // Element data name (NEW) + + +void write_lagrange_cells(std::ofstream& file, + const DCArrayKokkos& nodes_in_elem, + size_t num_elems, + size_t order, + const DCArrayKokkos& elem_data, // Element data + const std::string& elem_data_name); // Element data name + +void write_points(std::ofstream& file, + const DCArrayKokkos& coords, + size_t num_nodes); + + +void write_point_data(std::ofstream& file, + const DCArrayKokkos& data, + size_t num_nodes, + const std::string& name); + +void reorder_ijk_to_vtk_lagrange(const DCArrayKokkos& nodes_in_elem, + CArray& vtk_nodes, + const size_t elem_gid, + const size_t order); + +inline int PointIndexFromIJK(int i, int j, int k, const int* order); + +int main(int argc, char** argv) { + +MATAR_INITIALIZE(argc, argv); +{ // MATAR scope + std::cout<<"Reference Element Remap Example!"< node_coords(Mesh.num_nodes, Mesh.num_dims); + const double h = 1.0/((double)num_nodes_1D-1); + + // create indexing for a Pn order mesh + + // Step 1: Initialize ALL node coordinates once (no race condition) + FOR_ALL(kc, 0, num_nodes_1D, + jc, 0, num_nodes_1D, + ic, 0, num_nodes_1D, { + + size_t node_gid = ic + (jc + kc*num_nodes_1D)*num_nodes_1D; + + node_coords(node_gid, 0) = (double)ic * h; + node_coords(node_gid, 1) = (double)jc * h; + node_coords(node_gid, 2) = (double)kc * h; + }); + + // Step 2: Build element connectivity + FOR_ALL(i, 0, num_elems_1D, + j, 0, num_elems_1D, + k, 0, num_elems_1D, { + + size_t elem_gid = i + (j + k*num_elems_1D)*num_elems_1D; + size_t node_lid = 0; + + // FIX: Multiply by elem_order to space elements correctly + for(size_t kc = k*elem_order; kc <= k*elem_order + elem_order; kc++) + for(size_t jc = j*elem_order; jc <= j*elem_order + elem_order; jc++) + for(size_t ic = i*elem_order; ic <= i*elem_order + elem_order; ic++){ + size_t node_gid = ic + (jc + kc*num_nodes_1D)*num_nodes_1D; + Mesh.nodes_in_elem(elem_gid, node_lid) = node_gid; + node_lid++; + } + }); + Kokkos::fence(); + Mesh.nodes_in_elem.update_host(); + + + std::cout<<"Building corner connectivity \n"; + Mesh.build_corner_connectivity(); + std::cout<<"Building element element connectivity \n"; + Mesh.build_elem_elem_connectivity(); + std::cout<<"Building surface connectivity \n"; + Mesh.build_surf_connectivity(); + + // check mesh index sizes + if(Mesh.num_nodes!=num_nodes){ + printf("num nodes = %zu and mesh.num_nodes = %zu", num_nodes, Mesh.num_nodes); + Kokkos::abort("ERROR: wrong number of mesh nodes"); + } + if(Mesh.num_gauss_in_elem!=Quad.num_qpts_in_elem){ + Kokkos::abort("ERROR: wrong number of Gauss points in elem"); + } + + + // State arrays for this test + const size_t num_qpts_in_elem = Quad.num_qpts_in_elem; + + const size_t num_surfs = Mesh.num_surfs; + const size_t num_qpts_in_surf = SurfQuad.num_qpts_in_surf; + + const size_t num_nodes_in_elem = Mesh.num_nodes_in_elem; + const size_t num_surfs_in_elem = Mesh.num_surfs_in_elem; + + if(num_nodes_in_elem != FERefElem.num_dofs_in_elem) Kokkos::abort("ERROR: mismatch in DOFs and num nodes in elem \n"); + + DCArrayKokkos elem_jac(num_elems, num_qpts_in_elem, elem_dims, elem_dims, "elem_jacobian"); + DCArrayKokkos elem_det_jac(num_elems, num_qpts_in_elem, "elem_det_jacobian"); + DCArrayKokkos elem_vol(num_elems, "elem_vol"); + DCArrayKokkos elem_vol_n(num_elems, "elem_vol_n"); + + + DCArrayKokkos surf_jac(num_surfs, num_qpts_in_surf, elem_dims, elem_dims, "surf_jacobian"); + DCArrayKokkos surf_inv_jac(num_surfs, num_qpts_in_surf, elem_dims, elem_dims, "surf_inv_jacobian"); + DCArrayKokkos surf_flux(num_surfs, "surf_flux"); + + DCArrayKokkos elem_field(num_elems, "elem_field"); // P0 field in the element, this is a FV method on high-order meshes + DCArrayKokkos elem_field_n(num_elems, "elem_field_n"); // P0 field in the element, this is a FV method on high-order meshes + DCArrayKokkos node_velocity(num_nodes, elem_dims, "node_velocity"); + + + const double max_vel = 1.0; + double h_cfl = h; + double dt = 0.2*h_cfl/max_vel; // dt from CFL at start, this time is psuedo time + + FOR_ALL(elem_gid, 0, num_elems, { + + ViewCArrayKokkos nodes_in_elem(&Mesh.nodes_in_elem(elem_gid,0), num_nodes_in_elem); + + double elem_coords[3]; + for(size_t dim=0; dim nodes_in_elem(&Mesh.nodes_in_elem(elem_gid,0), num_nodes_in_elem); + + // calculate volume + for(size_t qpt_lid=0; qpt_lid a_grad_basis(&FERefElem.qpt_grad_basis(qpt_lid,0,0), + num_nodes_in_elem, 3); + + // extract the basis at a single quadrature point (qpt,dof) + ViewCArrayKokkos a_basis(&FERefElem.qpt_basis(qpt_lid,0), + num_nodes_in_elem); + + // jacobian matrix + ViewCArrayKokkos jac(&elem_jac(elem_gid,qpt_lid,0,0),3,3); + jacobian(jac, + node_coords, + nodes_in_elem, + a_grad_basis); + + // calculate det_J + elem_det_jac(elem_gid, qpt_lid) = det_3x3(jac); + + elem_vol(elem_gid) += elem_det_jac(elem_gid, qpt_lid)*Quad.qpt_weights(qpt_lid); + + } // end for + + }); + Kokkos::fence(); + + + ////--------/// + double time = 0; + double time_output = graphics_dt; + size_t output_id = 0; + + + { + // writing the initial mesh and state + node_velocity.set_values(0.0); + + node_coords.update_host(); + node_velocity.update_host(); + elem_field.update_host(); + + printf(" Writing output at time = %.4f. ", time); + + char filename[100]; + snprintf(filename, sizeof(filename), "output_time_%04zu.vtu", output_id); + + // Write the mesh state + write_lagrange_hex_mesh( + filename, + node_coords, + Mesh.num_nodes, + Mesh.nodes_in_elem, + Mesh.num_elems, + elem_order, + node_velocity, // only x-comp of vel written + "Vel", + elem_field, // element center data + "Density" // element data name + ); + output_id += 1; + } // end graphics dump scope + + + for(size_t cycle=0; cycle nodes_in_elem(&Mesh.nodes_in_elem(elem_gid,0), num_nodes_in_elem); + + for(size_t qpt_lid=0; qpt_lid a_grad_basis(&RefSurf.qpt_grad_basis(face_lid,qpt_lid,0,0), + num_nodes_in_elem, 3); + + // extract the basis at a single quadrature point (surf,qpt,dof) + ViewCArrayKokkos a_basis(&RefSurf.qpt_basis(face_lid,qpt_lid,0), + num_nodes_in_elem); + + ViewCArrayKokkos jac(&surf_jac(surf_gid,qpt_lid,0,0),3,3); + ViewCArrayKokkos inv_jac(&surf_inv_jac(surf_gid,qpt_lid,0,0),3,3); + + jacobian(jac, + node_coords, + nodes_in_elem, + a_grad_basis); + + const double det_jac_qpt = det_3x3(jac); + + invert_3x3(jac, inv_jac, det_jac_qpt); + + // Nanson's formula: s*J^-1*j*f*w + double area_normal[3]; + area_normal[0] = 0.; + area_normal[1] = 0.; + area_normal[2] = 0.; + for(size_t j=0; j nodes_in_elem(&Mesh.nodes_in_elem(elem_gid,0), num_nodes_in_elem); + + // calculate volume + for(size_t qpt_lid=0; qpt_lid a_grad_basis(&FERefElem.qpt_grad_basis(qpt_lid,0,0), + num_nodes_in_elem, 3); + + // extract the basis at a single quadrature point (qpt,dof) + ViewCArrayKokkos a_basis(&FERefElem.qpt_basis(qpt_lid,0), + num_nodes_in_elem); + + // jacobian matrix + ViewCArrayKokkos jac(&elem_jac(elem_gid,qpt_lid,0,0),3,3); + jacobian(jac, + node_coords, + nodes_in_elem, + a_grad_basis); + + // calculate det_J + elem_det_jac(elem_gid, qpt_lid) = det_3x3(jac); + + elem_vol(elem_gid) += elem_det_jac(elem_gid, qpt_lid)*Quad.qpt_weights(qpt_lid); + + } // end for + + }); + Kokkos::fence(); + + + // Step 5: update the elem field + FOR_ALL(elem_gid, 0, num_elems,{ + + // Start with conservative quantity from previous time + double conservative_quantity = elem_field_n(elem_gid) * elem_vol_n(elem_gid); + + // Accumulate fluxes from all faces + for(size_t face_lid=0; face_lid= -1.e-8 ){ + + node_coords.update_host(); + node_velocity.update_host(); + elem_field.update_host(); + + + printf(" Writing output at time = %.4f. ", time); + + // After your cycle loop ends, add: + char filename[100]; + snprintf(filename, sizeof(filename), "output_time_%04zu.vtu", output_id); + + // Write the mesh state + write_lagrange_hex_mesh( + filename, + node_coords, + Mesh.num_nodes, + Mesh.nodes_in_elem, + Mesh.num_elems, + elem_order, + node_velocity, // only x-comp of vel written + "Vel", + elem_field, // element center data + "Density" // element data name + ); + time_output += graphics_dt; + output_id += 1; + + } // end if + + if (time >= max_time ) break; + + } // end loop over cycle + printf(" time = %.4f ", time); + + printf("\n Remap test finished.\n"); + + +} // end MATAR scope +MATAR_FINALIZE(); + +return 0; +} // end function + + + +// +void write_lagrange_hex_mesh( + const std::string& filename, + const DCArrayKokkos& node_coords, // All node coordinates [num_nodes][3] + const size_t num_nodes, + const DCArrayKokkos& nodes_in_elem, // Connectivity + const size_t num_elems, + const size_t order, + const DCArrayKokkos& node_data, // Nodal data + const std::string& node_data_name, + const DCArrayKokkos& elem_data, // Element center data (NEW) + const std::string& elem_data_name) // Element data name (NEW) +{ + std::ofstream vtu_file(filename); + if (!vtu_file.is_open()) { + std::cerr << "Error: Cannot open file " << filename << std::endl; + return; + } + + vtu_file << std::fixed << std::setprecision(8); + + // Header + vtu_file << "\n"; + vtu_file << "\n"; + vtu_file << " \n"; + vtu_file << " \n"; + + // Write Points + write_points(vtu_file, node_coords, num_nodes); + + // Write Cells (connectivity, types, AND cell data) + write_lagrange_cells(vtu_file, nodes_in_elem, num_elems, order, + elem_data, elem_data_name); // Pass element data + + // Write Point Data + write_point_data(vtu_file, node_data, num_nodes, node_data_name); + + // Footer + vtu_file << " \n"; + vtu_file << " \n"; + vtu_file << "\n"; + + vtu_file.close(); + std::cout << "Wrote VTU file: " << filename << std::endl; +} + +void write_points(std::ofstream& file, const DCArrayKokkos& coords, size_t num_nodes) +{ + file << " \n"; + file << " \n"; + + for (size_t i = 0; i < num_nodes; i++) { + file << " " << coords.host(i, 0) << " " + << coords.host(i, 1) << " " + << coords.host(i, 2) << "\n"; + } + + file << " \n"; + file << " \n"; +} + +void write_lagrange_cells(std::ofstream& file, + const DCArrayKokkos& nodes_in_elem, + size_t num_elems, + size_t order, + const DCArrayKokkos& elem_data, // Element data + const std::string& elem_data_name) // Element data name +{ + const size_t nodes_per_elem = (order + 1) * (order + 1) * (order + 1); + const int VTK_LAGRANGE_HEXAHEDRON = 72; + + file << " \n"; + + // Connectivity + file << " \n"; + + CArray vtk_nodes(nodes_per_elem); + + for (size_t elem = 0; elem < num_elems; elem++) { + // Convert to VTK ordering + reorder_ijk_to_vtk_lagrange(nodes_in_elem, vtk_nodes, elem, order); + + file << " "; + for (size_t i = 0; i < nodes_per_elem; i++) { + file << vtk_nodes(i) << " "; + } + file << "\n"; + } + + file << " \n"; + + // Offsets + file << " \n"; + file << " "; + for (size_t elem = 0; elem < num_elems; elem++) { + file << (elem + 1) * nodes_per_elem << " "; + } + file << "\n \n"; + + // Cell types + file << " \n"; + file << " "; + for (size_t elem = 0; elem < num_elems; elem++) { + file << VTK_LAGRANGE_HEXAHEDRON << " "; + } + file << "\n \n"; + + file << " \n"; + + // CellData section with HigherOrderDegrees AND user data + file << " \n"; + + // HigherOrderDegrees (CRITICAL for Lagrange elements!) + file << " \n"; + file << " "; + for (size_t elem = 0; elem < num_elems; elem++) { + file << order << " " << order << " " << order << " "; + } + file << "\n \n"; + + // User-provided element center data + file << " \n"; + file << " "; + for (size_t elem = 0; elem < num_elems; elem++) { + file << elem_data.host(elem) << " "; + } + file << "\n \n"; + + file << " \n"; +} + +void write_point_data(std::ofstream& file, + const DCArrayKokkos& data, + size_t num_nodes, + const std::string& name) +{ + file << " \n"; + file << " \n"; + + // writing X-component of node data + for (size_t i = 0; i < num_nodes; i++) { + file << " " << data.host(i, 0) << "\n"; + } + + file << " \n"; + file << " \n"; +} + +// Keep your existing helper functions unchanged +void reorder_ijk_to_vtk_lagrange(const DCArrayKokkos& nodes_in_elem, + CArray& vtk_nodes, + const size_t elem_gid, + const size_t order) +{ + const int n = order + 1; + int ord[3] = {(int)order, (int)order, (int)order}; + + std::vector> vtk_to_ijk; + + for(int k = 0; k < n; k++){ + for(int j = 0; j < n; j++){ + for(int i = 0; i < n; i++){ + int vtk_pos = PointIndexFromIJK(i, j, k, ord); + size_t ijk_linear = i + j*n + k*n*n; + vtk_to_ijk.push_back({vtk_pos, ijk_linear}); + } + } + } + + std::sort(vtk_to_ijk.begin(), vtk_to_ijk.end()); + + for(size_t v = 0; v < vtk_to_ijk.size(); v++){ + size_t ijk_linear = vtk_to_ijk[v].second; + vtk_nodes(v) = nodes_in_elem.host(elem_gid, ijk_linear); + } +} + +inline int PointIndexFromIJK(int i, int j, int k, const int* order) +{ + bool ibdy = (i == 0 || i == order[0]); + bool jbdy = (j == 0 || j == order[1]); + bool kbdy = (k == 0 || k == order[2]); + int nbdy = (ibdy ? 1 : 0) + (jbdy ? 1 : 0) + (kbdy ? 1 : 0); + + if (nbdy == 3) { // Vertex DOF + return (i ? (j ? 2 : 1) : (j ? 3 : 0)) + (k ? 4 : 0); + } + + int offset = 8; + if (nbdy == 2) { // Edge DOF + if (!ibdy) { + return (i - 1) + (j ? order[0] - 1 + order[1] - 1 : 0) + + (k ? 2 * (order[0] - 1 + order[1] - 1) : 0) + offset; + } + if (!jbdy) { + return (j - 1) + (i ? order[0] - 1 : 2 * (order[0] - 1) + order[1] - 1) + + (k ? 2 * (order[0] - 1 + order[1] - 1) : 0) + offset; + } + offset += 4 * (order[0] - 1) + 4 * (order[1] - 1); + return (k - 1) + (order[2] - 1) * (i ? (j ? 3 : 1) : (j ? 2 : 0)) + offset; + } + + offset += 4 * (order[0] - 1 + order[1] - 1 + order[2] - 1); + if (nbdy == 1) { // Face DOF + if (ibdy) { + return (j - 1) + ((order[1] - 1) * (k - 1)) + + (i ? (order[1] - 1) * (order[2] - 1) : 0) + offset; + } + offset += 2 * (order[1] - 1) * (order[2] - 1); + if (jbdy) { + return (i - 1) + ((order[0] - 1) * (k - 1)) + + (j ? (order[2] - 1) * (order[0] - 1) : 0) + offset; + } + offset += 2 * (order[2] - 1) * (order[0] - 1); + return (i - 1) + ((order[0] - 1) * (j - 1)) + + (k ? (order[0] - 1) * (order[1] - 1) : 0) + offset; + } + + // Interior DOF + offset += 2 * ((order[1] - 1) * (order[2] - 1) + (order[2] - 1) * (order[0] - 1) + + (order[0] - 1) * (order[1] - 1)); + return offset + (i - 1) + (order[0] - 1) * ((j - 1) + (order[1] - 1) * (k - 1)); +} \ No newline at end of file diff --git a/matar b/matar index 5c0be4cb..9926149c 160000 --- a/matar +++ b/matar @@ -1 +1 @@ -Subproject commit 5c0be4cb76231ba2e51e789d91de95ac4feed08d +Subproject commit 9926149c7819e112420e51e30df3b62ea85cbe09 diff --git a/src/ELEMENTS.h b/src/ELEMENTS.h index 60964c9c..99a86f82 100644 --- a/src/ELEMENTS.h +++ b/src/ELEMENTS.h @@ -8,7 +8,6 @@ #include "swage/unstructured_mesh.h" #include "swage/point_cloud.h" #include "elements/ref_elem.h" -#include "elements/ref_surf_elem.h" #include "elements/ref_quadrature.h" diff --git a/src/decomp_utilities/decomp_utils.h b/src/decomp_utilities/decomp_utils.h index ca15f34a..a028b361 100644 --- a/src/decomp_utilities/decomp_utils.h +++ b/src/decomp_utilities/decomp_utils.h @@ -50,9 +50,9 @@ namespace elements */ inline void naive_partition_mesh( - swage::Mesh& initial_mesh, + swage::Mesh_t& initial_mesh, MPICArrayKokkos& initial_node_coords, - swage::Mesh& naive_mesh, + swage::Mesh_t& naive_mesh, MPICArrayKokkos& naive_node_coords, CArrayDual& elems_in_elem_on_rank, CArrayDual& num_elems_in_elem_per_rank, @@ -490,9 +490,9 @@ inline void naive_partition_mesh( // ****************************************************************************************** naive_mesh.initialize_nodes(num_nodes_on_rank); if (initial_mesh.Pn > 1){ - naive_mesh.initialize_elems_Pn(num_elements_on_rank, num_dim, initial_mesh.Pn); + naive_mesh.initialize_elems_Pn(num_elements_on_rank, initial_mesh.Pn, 2*initial_mesh.Pn); } else { - naive_mesh.initialize_elems(num_elements_on_rank, num_dim); + naive_mesh.initialize_elems(num_elements_on_rank); } naive_mesh.local_to_global_node_mapping = DCArrayKokkos(num_nodes_on_rank, "naive_mesh.local_to_global_node_mapping"); naive_mesh.local_to_global_elem_mapping = DCArrayKokkos(num_elements_on_rank, "naive_mesh.local_to_global_elem_mapping"); @@ -589,8 +589,8 @@ inline void naive_partition_mesh( /// @note Performance: O(n_local_elements * n_nodes_per_element) for local operations, /// plus O(n_global_elements) for global MPI collective operations inline void build_ghost( - swage::Mesh& input_mesh, - swage::Mesh& output_mesh, + swage::Mesh_t& input_mesh, + swage::Mesh_t& output_mesh, MPICArrayKokkos& input_node_coords, MPICArrayKokkos& output_node_coords, CommunicationPlan& element_communication_plan, @@ -1126,10 +1126,10 @@ inline void build_ghost( // (Pn == 1) use initialize_elems to keep the element kind/layout consistent // with naive_mesh and intermediate_mesh. if (input_mesh.Pn > 1){ - output_mesh.initialize_elems_Pn(total_extended_elems, input_mesh.num_dims, input_mesh.Pn); + output_mesh.initialize_elems_Pn(total_extended_elems, input_mesh.Pn, 2*input_mesh.Pn); } else { - output_mesh.initialize_elems(total_extended_elems, input_mesh.num_dims); + output_mesh.initialize_elems(total_extended_elems); } output_mesh.local_to_global_node_mapping = DCArrayKokkos(total_extended_nodes); output_mesh.local_to_global_elem_mapping = DCArrayKokkos(total_extended_elems); @@ -1734,8 +1734,8 @@ inline void build_ghost( */ inline void partition_mesh( - swage::Mesh& initial_mesh, - swage::Mesh& final_mesh, + swage::Mesh_t& initial_mesh, + swage::Mesh_t& final_mesh, MPICArrayKokkos& initial_node_coords, MPICArrayKokkos& final_node_coords, CommunicationPlan& element_communication_plan, @@ -1759,13 +1759,13 @@ inline void partition_mesh( // Create mesh, gauss points, and node data structures on each rank // This is the initial partitioned mesh - swage::Mesh naive_mesh; + swage::Mesh_t naive_mesh; naive_mesh.num_dims = initial_mesh.num_dims; naive_mesh.Pn = initial_mesh.Pn; MPICArrayKokkos naive_node_coords; // Mesh partitioned by pt-scotch, not including ghost - swage::Mesh intermediate_mesh; + swage::Mesh_t intermediate_mesh; MPICArrayKokkos intermediate_node_coords; // Helper arrays to hold element-element connectivity for naive partitioning that include what would be ghost, without having to build the full mesh @@ -2287,9 +2287,9 @@ inline void partition_mesh( // arbitrary_tensor_element layout whose connectivity-build path writes // 8 nodes-per-zone unconditionally and would corrupt the heap in 2D. if (initial_mesh.Pn > 1){ - intermediate_mesh.initialize_elems_Pn(num_new_elems, naive_mesh.num_dims, initial_mesh.Pn); + intermediate_mesh.initialize_elems_Pn(num_new_elems, initial_mesh.Pn, 2*initial_mesh.Pn); } else { - intermediate_mesh.initialize_elems(num_new_elems, naive_mesh.num_dims); + intermediate_mesh.initialize_elems(num_new_elems); } intermediate_mesh.local_to_global_node_mapping = DCArrayKokkos(num_new_nodes, "intermediate_mesh.local_to_global_node_mapping"); intermediate_mesh.local_to_global_elem_mapping = DCArrayKokkos(num_new_elems, "intermediate_mesh.local_to_global_elem_mapping"); diff --git a/src/elements/ref_elem.h b/src/elements/ref_elem.h index 1dcb1317..2248f03f 100644 --- a/src/elements/ref_elem.h +++ b/src/elements/ref_elem.h @@ -38,1312 +38,1475 @@ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "matar.h" #include "ref_quadrature.h" -namespace elements -{ using namespace mtr; -// Constructs kinematic and thermodynamic basis functions in the element. -// Kinematic basis will be referenced as basis -// Thermodynamic basis is referenced as elem_basis, since the thermodynamic quantities are internal to the elements -struct fe_ref_elem_t +namespace reference_space { - size_t num_dim; + enum ElementType + { + linearElement = 0, // single quadrature point element + arbitraryOrderElement = 1 // fully integrated arbitrary-order element + }; + + // Location of basis DOFs + enum BasisType + { + LagrangeLobatto = 0, // GLL in Steven's solver (C0 kinematic space) + LagrangeLegendre = 1, // GL in Steven's solver (DG thermo space) + }; + + enum QuadratureType + { + GaussLobatto = 0, + GaussLegendre = 1 + }; +} // end ref_elem namespace - // Kinematic Dofs - size_t num_dofs_1d; - size_t num_dofs_in_elem; - // size_t num_dofs_in_surf; - // Thermodynamic Dofs - size_t num_dg_dofs_1d; - size_t num_dg_dofs_in_elem; +namespace elements +{ - // Gauss Points - size_t num_lobotto_1d; - size_t num_dual_lobotto_1d; - size_t num_lobotto_in_elem; - size_t num_dual_lobotto_in_elem; + ///////////////////////////////////////////////////////////////////////////// + /// + /// \fn get_qpt_rid + /// + /// \brief Compute the 1D array index for a quadrature point in a 3D volume + /// element. + /// + /// Calculates the row-major flat index corresponding to a quadrature point + /// at the position (i, j, k) within an element. This function is typically + /// used for accessing basis functions, positions, and weights defined on + /// the tensor-product grid of quadrature points in the reference element, + /// which is common in high-order finite element and spectral methods. + /// + /// \param i Local quadrature index in the first (xi) coordinate direction. + /// \param j Local quadrature index in the second (eta) coordinate direction. + /// \param k Local quadrature index in the third (mu) coordinate direction. + /// + /// \return The row-major offset index for the quadrature point in this element. + /// + ///////////////////////////////////////////////////////////////////////////// + KOKKOS_INLINE_FUNCTION + size_t get_qpt_rid(size_t i, size_t j, size_t k, size_t num_qpts_1d) + { + return i + (j + k * num_qpts_1d) * num_qpts_1d; + } // end function - size_t num_gauss_1d; - size_t num_gauss_in_elem; - // Zones - size_t num_zones_1d; - size_t num_zones_in_elem; + ///////////////////////////////////////////////////////////////////////////// + /// + /// \fn get_qpt_rid + /// + /// \brief Compute the 1D array index for a quadrature point in a 2D volume + /// element + /// + /// Calculates the row-major flat index corresponding to a quadrature point + /// at the position (i, j) within a 2D volume element. This function + /// is typically used for accessing basis functions, positions, + /// and weights defined on the tensor-product grid of quadrature points in + /// the reference element, which is common in high-order finite element and + /// spectral methods. + /// + /// \param i Local quadrature index in the first (xi) coordinate direction. + /// \param j Local quadrature index in the second (eta) coordinate direction. + /// + /// \return The row-major offset index for the quadrature point in this element. + /// + ///////////////////////////////////////////////////////////////////////////// + KOKKOS_INLINE_FUNCTION + size_t get_qpt_rid(size_t i, size_t j, size_t num_qpts_1d) + { + return i + j * num_qpts_1d; + } // end function - // Num basis functions - size_t num_basis; - size_t num_dg_basis; - // Kinematic basis evaluation at nodes // evaluation at points? - CArrayKokkos lobotto_point_basis; - CArrayKokkos gauss_point_basis; + ///////////////////////////////////////////////////////////////////////////// + /// + /// \fn get_dof_rid + /// + /// \brief Compute the 1D array index for a degree of freedom (DOF) in an element. + /// + /// Calculates the flat row-major index corresponding to a DOF located at + /// position (i, j, k) in the element, for continuous fields. This is used + /// for basis functions and data fields that are continuous across element + /// boundaries. + /// + /// \param i Local DOF index in the first (xi) coordinate direction. + /// \param j Local DOF index in the second (eta) coordinate direction. + /// \param k Local DOF index in the third (mu) coordinate direction. + /// + /// \return The row-major offset index for the DOF in this element. + /// + ///////////////////////////////////////////////////////////////////////////// + KOKKOS_INLINE_FUNCTION + size_t get_dof_rid(size_t i, size_t j, size_t k, size_t num_dofs_1d) + { + return i + (j + k * num_dofs_1d) * num_dofs_1d; + } + - // Thermodynamic basis evaluation at nodes // evaluations at points? - CArrayKokkos lobotto_point_dg_basis; - CArrayKokkos gauss_point_dg_basis; + ///////////////////////////////////////////////////////////////////////////// + /// + /// \fn get_dof_rid + /// + /// \brief Compute the 1D array index for a degree of freedom (DOF) in an + /// element. + /// + /// Calculates the flat row-major index corresponding to a DOF located at + /// position (i, j, k) in the element, for continuous fields. This is used for + /// basis functions and data fields that are continuous across element + /// boundaries. + /// + /// \param i Local DOF index in the first (xi) coordinate direction. + /// \param j Local DOF index in the second (eta) coordinate direction. + /// + /// \return The row-major offset index for the DOF in this element. + /// + ///////////////////////////////////////////////////////////////////////////// + KOKKOS_INLINE_FUNCTION + size_t get_dof_rid(size_t i, size_t j, size_t num_dofs_1d) + { + return i + j * num_dofs_1d; + } - // Gradient of basis - CArrayKokkos lobotto_point_grad_basis; - CArrayKokkos gauss_point_grad_basis; - // Gauss and DOF positions - CArrayKokkos lob_points_1D; // lobatto points in 1D - CArrayKokkos dual_lob_points_1D; - CArrayKokkos leg_nodes_1D; // Gauss legendre points in 1D + ///////////////////////////////////////////////////////////////////////////// + /// + /// \fn lagrange_basis_1D + /// + /// \brief Computes the Lagrange basis functions in 1D at a given point. + /// + /// This function evaluates the values of the 1D Lagrange basis functions at + /// a specified point within the reference element, using the nodal positions. + /// For each basis node, it computes the interpolation value (the product + /// over all other node positions) and stores the result in the provided + /// array. + /// + /// \param interp Output array to store the value of each basis function at + /// the specified point. + /// \param dof_positions_1d the positions of the DOFs in reference coordinates + /// \param x_point Point at which to evaluate the basis functions. + /// + /// \return void + /// + ///////////////////////////////////////////////////////////////////////////// - CArrayKokkos lobotto_point_positions; - CArrayKokkos gauss_point_positions; - // CArrayKokkos gauss_surf_positions; + KOKKOS_FUNCTION + void lagrange_basis_1D( + const CArrayKokkos& interp, // interpolant from each basis + const CArrayKokkos& dof_positions_1d, // location of basis DOFs in ref elem + const double x_point) // point of interest in element + // calculate the basis value associated with each node_i + { + const size_t num_dofs_1d = dof_positions_1d.dims(0); - CArrayKokkos dof_positions; - CArrayKokkos dof_positions_1d; + for (size_t vert_i = 0; vert_i < num_dofs_1d; vert_i++) { + double numerator = 1.0; // placeholder numerator + double denominator = 1.0; // placeholder denominator + double interpolant = 1.0; // placeholder value of numerator/denominator - CArrayKokkos dg_dof_positions; - CArrayKokkos dg_dof_positions_1d; + for (size_t vert_j = 0; vert_j < num_dofs_1d; vert_j++) { // looping over the verts !=vert_i + if (vert_j != vert_i) { + // Calculate the numerator + numerator = numerator * (x_point - dof_positions_1d(vert_j)); - // Quadrature Weights - CArrayKokkos lob_weights_1D; // lobatto weights in 1D - CArrayKokkos leg_weights_1D; // Gauss legendre weights in 1D + // Calculate the denominator + denominator = denominator * (dof_positions_1d(vert_i) - dof_positions_1d(vert_j)); + } // end if - CArrayKokkos lobotto_point_weights; - CArrayKokkos gauss_point_weights; - // CArrayKokkos gauss_surf_weights; + + } // end looping over nodes != vert_i + interpolant = numerator / denominator; // storing a single value for interpolation for node vert_i - CArrayKokkos dof_lobatto_map; - CArrayKokkos dual_dof_lobatto_map; + // writing value to vectors for later use + interp(vert_i) = interpolant; // Interpolant value at given point + } // end loop over all nodes + } // end of Lagrange_1D function ///////////////////////////////////////////////////////////////////////////// /// - /// \fn init + /// \fn lagrange_derivative_1D /// - /// \brief Initialize the reference element with polynomial order and dimension. + /// \brief Computes the values of the derivatives of the 1D Lagrange basis functions at a given point. /// - /// Initializes internal data members for the reference element according - /// to the given polynomial order and spatial dimension. Sets up the number - /// of points, degrees of freedom, and zones for Gauss-Legendre, Gauss-Lobatto, - /// and dual Gauss-Lobatto quadrature, as well as associated sizes and counts - /// for basis function evaluations, according to the dimension and order. + /// This function evaluates the first derivatives of the 1D Lagrange basis functions associated + /// with the element's degrees of freedom at a specified point within the reference element. + /// For each basis node, it computes the derivative of the basis function using the nodal + /// positions and stores the results in the provided array. /// - /// \param p_order The order of polynomial approximation, i.e., the finite element order. - /// \param num_dim_inp The number of spatial dimensions (e.g., 1, 2, or 3). + /// \param derivative Output array to store the value of each 1D basis function derivative at the given point. + /// \param x_point Point at which to evaluate the derivatives of the basis functions. /// /// \return void /// ///////////////////////////////////////////////////////////////////////////// - void init(int p_order, int num_dim_inp) + KOKKOS_INLINE_FUNCTION + void lagrange_derivative_1D( + const CArrayKokkos& derivative, // derivative + const CArrayKokkos& dof_positions_1d, // location of basis DOFs in ref elem + const double x_point) // point of interest in element { - // CArrayKokkos lob_points_1D; - - num_dim = num_dim_inp; - - if (p_order == 0) { - num_lobotto_1d = 2; // num gauss lobatto points in 1d - num_dual_lobotto_1d = 1; - num_gauss_1d = 1; - num_dofs_1d = 2; - num_dg_dofs_1d = 1; - num_zones_1d = 1; - num_zones_in_elem = num_zones_1d * num_zones_1d * num_zones_1d; - } - else{ - num_lobotto_1d = 2 * p_order + 1; // num gauss lobatto points in 1d - num_dual_lobotto_1d = 2 * p_order - 1; - num_gauss_1d = 2 * p_order; + const size_t num_dofs_1d = dof_positions_1d.dims(0); - num_dofs_1d = p_order + 1; - num_dg_dofs_1d = p_order; + for (size_t vert_i = 0; vert_i < num_dofs_1d; vert_i++) { // looping over the nodes + double denominator = 1.0; // placeholder denominator + double num_gradient = 0.0; // placeholder for numerator of the gradient + double gradient = 0.0; - num_zones_1d = p_order; - num_zones_in_elem = num_zones_1d * num_zones_1d * num_zones_1d; - } + for (size_t vert_j = 0; vert_j < num_dofs_1d; vert_j++) { // looping over the nodes !=vert_i + if (vert_j != vert_i) { + // Calculate the denominator that is the same for + // both the basis and the gradient of the basis + denominator = denominator * (dof_positions_1d(vert_i) - dof_positions_1d(vert_j)); + + double product_gradient = 1.0; - num_lobotto_in_elem = 1; + // Calculate the numerator of the gradient + for (size_t N = 0; N < num_dofs_1d; N++) { // looping over the nodes !=vert_i + if (N != vert_j && N != vert_i) { + product_gradient = product_gradient * (x_point - dof_positions_1d(N)); + } // end if + } // end for - num_dual_lobotto_in_elem = 1; + // Sum over the product of the numerator + // contributions from each node + num_gradient += product_gradient; + } // end if - num_gauss_in_elem = 1; + + } // end looping over nodes != vert_i + + gradient = (num_gradient / denominator); // storing the derivative of the interpolating function + + // writing value to vectors for later use + derivative(vert_i) = gradient; // derivative of each function + } // end loop over all nodes + } // end of Lagrange_1D function - num_dofs_in_elem = 1; - // num_dofs_in_surf = 1; + ///////////////////////////////////////////////////////////////////////////// + /// + /// \fn get_basis + /// + /// \brief Computes the tensor-product nodal basis values at an arbitrary point. + /// + /// This function evaluates the Lagrange basis functions at a specified point + /// within the reference element and assembles the tensor-product basis values + /// for all degrees of freedom (DOFs). Basis values in each coordinate + /// direction are computed independently using the 1D Lagrange basis, and then + /// combined to form the full multi-dimensional basis. The results are written + /// to the provided output array. + /// + /// \param basis Reference to the output CArrayKokkos to hold full + /// tensor-product basis values, sized for all DOFs in the element. + /// \param val_1d Temporary CArrayKokkos for holding 1D basis values + /// (as workspace). + /// \param val_3d Temporary CArrayKokkos for holding basis values for + /// each direction; shape should be (num_dofs_1d, 3). + /// \param point Reference to CArrayKokkos representing the + /// coordinates (xi, eta, mu) at which the basis is evaluated (size 3). + /// + /// \return void + /// + ///////////////////////////////////////////////////////////////////////////// + KOKKOS_FUNCTION + void get_basis(const CArrayKokkos& basis, + const CArrayKokkos& dof_positions_1d, + const CArrayKokkos& val_1d, + const CArrayKokkos& val_Nd, + const CArrayKokkos& point) + { + const size_t num_dofs_1d = dof_positions_1d.dims(0); + const size_t elem_dims = point.dims(0); - num_dg_dofs_in_elem = 1; + + for(size_t dim=0; dim(num_dofs_in_elem, num_dim, "dof_positions"); - dof_positions_1d = CArrayKokkos(num_dofs_1d, "dof_positions_1d"); + } // end loop over dims - dg_dof_positions = CArrayKokkos(num_dg_dofs_in_elem, num_dim, "dg_dof_positions"); - dg_dof_positions_1d = CArrayKokkos(num_dg_dofs_1d, "dg_dof_positions_1d"); - lobotto_point_weights = CArrayKokkos(num_lobotto_in_elem, "lobotto_point_weights"); - gauss_point_weights = CArrayKokkos(num_gauss_in_elem, "gauss_point_weights"); + if(elem_dims==3){ + // Multiply the i, j, k components of the basis from each node + // to get the tensor product basis for the node + for (size_t k = 0; k < num_dofs_1d; k++) + for (size_t j = 0; j < num_dofs_1d; j++) + for (size_t i = 0; i < num_dofs_1d; i++) { + const size_t rid = get_dof_rid(i, j, k, num_dofs_1d); + basis(rid) = val_Nd(i, 0) * val_Nd(j, 1) * val_Nd(k, 2); - // Memory for gradients - lobotto_point_grad_basis = CArrayKokkos(num_lobotto_in_elem, num_basis, num_dim, "lobotto_point_grad_basis"); - gauss_point_grad_basis = CArrayKokkos(num_gauss_in_elem, num_basis, num_dim, "gauss_point_grad_basis"); + } + } // end if 3D + else if(elem_dims==2){ + // Multiply the i, j components of the basis from each node + // to get the tensor product basis for the node + for (size_t j = 0; j < num_dofs_1d; j++) + for (size_t i = 0; i < num_dofs_1d; i++) { + const size_t rid = get_dof_rid(i, j, num_dofs_1d); + basis(rid) = val_Nd(i, 0) * val_Nd(j, 1); + } + } // end if 2D + else{ + for (size_t i = 0; i < num_dofs_1d; i++) { + const size_t rid = i; + basis(rid) = val_Nd(i, 0); + } + } // end if 1D - // Basis evaluation at the nodes - lobotto_point_basis = CArrayKokkos(num_lobotto_in_elem, num_basis, "lobotto_point_basis"); - gauss_point_basis = CArrayKokkos(num_gauss_in_elem, num_basis, "gauss_point_basis"); - lobotto_point_dg_basis = CArrayKokkos(num_lobotto_in_elem, num_dg_basis, "lobotto_point_dg_basis"); - gauss_point_dg_basis = CArrayKokkos(num_gauss_in_elem, num_dg_basis, "gauss_point_dg_basis"); + // reset values to 0.0 + for (size_t i = 0; i < num_dofs_1d; i++) { + val_1d(i) = 0.0; + for(size_t dim=0; dim(num_lobotto_in_elem, num_dim, "lobotto_point_positions"); - gauss_point_positions = CArrayKokkos(num_gauss_in_elem, num_dim, "gauss_point_positions"); + } // end get basis function - dof_lobatto_map = CArrayKokkos(num_dofs_in_elem, "dof to lobatto map"); - dual_dof_lobatto_map = CArrayKokkos(num_dg_dofs_in_elem, "Thermo dof to lobatto map"); - // --- build gauss nodal positions and weights --- + ///////////////////////////////////////////////////////////////////////////// + /// + /// \fn partial_xi_basis + /// + /// \brief Compute the partial derivative of the basis function with respect + /// to the xi coordinate. + /// + /// This function evaluates the tensor-product Lagrange basis function + /// derivatives in the xi direction at a given point within the reference + /// element. The computation is performed by first evaluating the 1D + /// derivatives and basis functions for each coordinate and then combining + /// them using the chain rule for tensor products. The result is stored in + /// the provided array for all degrees of freedom. + /// + /// \param partial_xi Array to store the value of the partial derivative with + /// respect to xi for each basis function. + /// \param val_1d Temporary workspace array for 1D basis evaluations. + /// \param val_Nd Temporary workspace array for 1D/2D/3D basis component + /// evaluations. + /// \param Dval_1d Temporary workspace array for 1D derivative evaluations. + /// \param Dval_Nd Temporary workspace array for 1D/2D/3D derivative component + /// evaluations. + /// \param point Input array specifying the coordinates (xi, eta, mu) at which + ///. to evaluate the derivative. + /// + /// \return void + /// + ///////////////////////////////////////////////////////////////////////////// + KOKKOS_FUNCTION + void partial_xi_basis(const CArrayKokkos& partial_xi, + const CArrayKokkos& dof_positions_1d, + const CArrayKokkos& val_1d, + const CArrayKokkos& val_Nd, + const CArrayKokkos& Dval_1d, + const CArrayKokkos& Dval_Nd, + const CArrayKokkos& point) + { + const size_t num_dofs_1d = dof_positions_1d.dims(0); + const size_t elem_dims = point.dims(0); - lob_points_1D = CArrayKokkos(num_lobotto_1d, "lob_points_1d"); + // get grad basis + for (size_t i = 0; i < num_dofs_1d; i++) { + Dval_1d(i) = 0.0; + } - dual_lob_points_1D = CArrayKokkos(num_dual_lobotto_1d, "dual_lob_points_1d"); + // Calculate 1D partial w.r.t. xi for the X coordinate of the point + lagrange_derivative_1D(Dval_1d, dof_positions_1d, point(0)); - RUN_CLASS({ - get_lobatto_nodes_1D(lob_points_1D, num_lobotto_1d); - }); + // Save the grad basis value at the point to a temp array and zero out the temp array + for (size_t i = 0; i < num_dofs_1d; i++) { + Dval_Nd(i, 0) = Dval_1d(i); + } - RUN_CLASS({ - get_lobatto_nodes_1D(dual_lob_points_1D, num_dual_lobotto_1d); - }); + // get Y and Z basis, the latter only if elem_dims = 3 + for(size_t dim=1; dim(num_lobotto_1d, "lob_weights_1d"); - RUN_CLASS({ - get_lobatto_weights_1D(lob_weights_1D, num_lobotto_1d); - }); + for (size_t i = 0; i < num_dofs_1d; i++) { + val_1d(i) = 0.0; + } - leg_nodes_1D = CArrayKokkos(num_gauss_1d, "leg_nodes_1d"); - RUN_CLASS({ - get_legendre_nodes_1D(leg_nodes_1D, num_gauss_1d); - }); + // Calculate 1D basis for the this dim coordinate of the point + lagrange_basis_1D(val_1d, dof_positions_1d, point(dim)); - leg_weights_1D = CArrayKokkos(num_gauss_1d, "leg_weights_1d"); - RUN_CLASS({ - get_legendre_weights_1D(leg_weights_1D, num_gauss_1d); - }); + // Save the basis value at the point to a temp array and zero out the temp array + for (size_t i = 0; i < num_dofs_1d; i++) { + val_Nd(i, dim) = val_1d(i); + } - // //WARNING WARNING WARNING may need to add lobotto_elem_positions etc for BV matrix to get control coefficients // + } // end loop over dims + + if(elem_dims==3){ + // Multiply the i, j, k components of the basis and partial_xi from each node + // to get the tensor product partial derivatives of the basis at each node + for (size_t k = 0; k < num_dofs_1d; k++) + for (size_t j = 0; j < num_dofs_1d; j++) + for (size_t i = 0; i < num_dofs_1d; i++) { + const size_t rid = get_dof_rid(i, j, k, num_dofs_1d); + + // Partial w.r.t xi + partial_xi(rid) = Dval_Nd(i, 0) * val_Nd(j, 1) * val_Nd(k, 2); + } // end for + + for (size_t i = 0; i < num_dofs_1d; i++) { + val_1d(i) = 0.0; + val_Nd(i, 0) = 0.0; + val_Nd(i, 1) = 0.0; + val_Nd(i, 2) = 0.0; + Dval_1d(i) = 0.0; + Dval_Nd(i, 0) = 0.0; + Dval_Nd(i, 1) = 0.0; + Dval_Nd(i, 2) = 0.0; + } // end for + } //end if 3D + else if(elem_dims==2){ + // Multiply the i, j components of the basis and partial_xi from each node + // to get the tensor product partial derivatives of the basis at each node + for (size_t j = 0; j < num_dofs_1d; j++) + for (size_t i = 0; i < num_dofs_1d; i++) { + const size_t rid = get_dof_rid(i, j, num_dofs_1d); + + // Partial w.r.t xi + partial_xi(rid) = Dval_Nd(i, 0) * val_Nd(j, 1); + } // end for + + for (size_t i = 0; i < num_dofs_1d; i++) { + val_1d(i) = 0.0; + val_Nd(i, 0) = 0.0; + val_Nd(i, 1) = 0.0; + Dval_1d(i) = 0.0; + Dval_Nd(i, 0) = 0.0; + Dval_Nd(i, 1) = 0.0; + } // end for + } // end if 2D + else { + // Multiply the i components of the basis and partial_xi from each node + // to get the tensor product partial derivatives of the basis at each node + for (size_t i = 0; i < num_dofs_1d; i++) { + const size_t rid = i; + + // Partial w.r.t xi + partial_xi(rid) = Dval_Nd(i, 0); + } // end for + + for (size_t i = 0; i < num_dofs_1d; i++) { + val_1d(i) = 0.0; + val_Nd(i, 0) = 0.0; + Dval_1d(i) = 0.0; + Dval_Nd(i, 0) = 0.0; + } // end for + } // end if 1D + + } // end partial_xi_basis - // // --- build reference index spaces for 3D --- - if (num_dim == 3) { - FOR_ALL_CLASS(k, 0, num_lobotto_1d, - j, 0, num_lobotto_1d, - i, 0, num_lobotto_1d, { - int lob_rid = lobatto_rid(i, j, k); - lobotto_point_positions(lob_rid, 0) = lob_points_1D(i); - lobotto_point_positions(lob_rid, 1) = lob_points_1D(j); - lobotto_point_positions(lob_rid, 2) = lob_points_1D(k); + ///////////////////////////////////////////////////////////////////////////// + /// + /// \fn partial_eta_basis + /// + /// \brief Computes the partial derivative of the Lagrange basis functions + /// with respect to eta at a given point. + /// + /// This function evaluates the partial derivative of the tensor-product + /// Lagrange basis functions with respect to the eta (second) coordinate + /// at a given point in the reference element. It builds up the 3D basis + /// and its derivatives using repeated calls to 1D basis and derivative + /// evaluations, and computes the tensor-product forms to populate the + /// provided partial_eta array with the values of the partials at every + /// reference node. + /// + /// \param partial_eta Output array to store the partial derivatives with + /// respect to eta at each basis node. + /// \param val_1d Temporary array used for storing 1D basis values + /// during calculation. + /// \param val_3d Temporary 2D array used for storing intermediate 3D + /// basis values. + /// \param Dval_1d Temporary array used for storing 1D derivative + /// values during calculation. + /// \param Dval_3d Temporary 2D array used for storing intermediate 3D + /// derivative values. + /// \param point Input array representing the coordinates (xi, eta, zeta) + /// at which to evaluate the derivative. + /// + /// \return void + /// + ///////////////////////////////////////////////////////////////////////////// + KOKKOS_FUNCTION + void partial_eta_basis(const CArrayKokkos& partial_eta, + const CArrayKokkos& dof_positions_1d, + const CArrayKokkos& val_1d, + const CArrayKokkos& val_Nd, + const CArrayKokkos& Dval_1d, + const CArrayKokkos& Dval_Nd, + const CArrayKokkos& point) + { + const size_t num_dofs_1d = dof_positions_1d.dims(0); + const size_t elem_dims = point.dims(0); - lobotto_point_weights(lob_rid) = lob_weights_1D(i) * lob_weights_1D(j) * lob_weights_1D(k); - }); - Kokkos::fence(); + // get X and Z basis values, the latter only if elem_dims = 3D + for(size_t dim=0; dim 0 ... - RUN_CLASS({ - size_t dof_rid = 0; - for (int k = 0; k < num_lobotto_1d; k = k + 2) { - for (int j = 0; j < num_lobotto_1d; j = j + 2) { - for (int i = 0; i < num_lobotto_1d; i = i + 2) { - size_t lob_rid = lobatto_rid(i, j, k); - dof_lobatto_map(dof_rid) = lob_rid; - dof_rid++; - } // i - } // j - } // k - }); // RUN_CLASS - Kokkos::fence(); + for (size_t i = 0; i < num_dofs_1d; i++) { + val_1d(i) = 0.0; + } - if (p_order == 1) { - RUN_CLASS({ - size_t dual_dof_rid = 0; - for (int k = 0; k < num_dual_lobotto_1d; k++) { - for (int j = 0; j < num_dual_lobotto_1d; j++) { - for (int i = 0; i < num_dual_lobotto_1d; i++) { - size_t dual_lob_rid = dual_lobatto_rid(i, j, k); - dual_dof_lobatto_map(dual_dof_rid) = dual_lob_rid; - dual_dof_rid++; - } // i - } // j - } // k - }); // RUN_CLASS - Kokkos::fence(); - } // if p=0 - if (p_order > 1) { - RUN_CLASS({ - size_t dual_dof_rid = 0; - for (int k = 0; k < num_dual_lobotto_1d; k = k + 2) { - for (int j = 0; j < num_dual_lobotto_1d; j = j + 2) { - for (int i = 0; i < num_dual_lobotto_1d; i = i + 2) { - size_t dual_lob_rid = dual_lobatto_rid(i, j, k); - dual_dof_lobatto_map(dual_dof_rid) = dual_lob_rid; - dual_dof_rid++; - } // i - } // j - } // k - }); // RUN_CLASS - Kokkos::fence(); - } // p > 1 - - FOR_ALL_CLASS(k, 0, num_gauss_1d, - j, 0, num_gauss_1d, - i, 0, num_gauss_1d, { - int leg_rid = legendre_rid(i, j, k); - - // printf(" leg_node_1D value = %f \n", leg_nodes_1D(i) ); - gauss_point_positions(leg_rid, 0) = leg_nodes_1D(i); - gauss_point_positions(leg_rid, 1) = leg_nodes_1D(j); - gauss_point_positions(leg_rid, 2) = leg_nodes_1D(k); - // printf(" leg_weight: %f \n", leg_weights_1D(i)); - gauss_point_weights(leg_rid) = leg_weights_1D(i) * leg_weights_1D(j) * leg_weights_1D(k); - }); - Kokkos::fence(); + // Calculate 1D basis for the this dim coordinate of the point + lagrange_basis_1D(val_1d, dof_positions_1d, point(dim)); - // Saving vertex positions in 1D - if (p_order == 0) { - // dofs same as lobatto quadrature points - FOR_ALL_CLASS(i, 0, num_lobotto_1d, { - dof_positions_1d(i) = lob_points_1D(i); - dg_dof_positions_1d(i) = dual_lob_points_1D(i); - }); + // Save the basis value at the point to a temp array and zero out the temp array + for (size_t i = 0; i < num_dofs_1d; i++) { + val_Nd(i, dim) = val_1d(i); } - else{ - RUN_CLASS({ - int dof_id = 0; - for (int i = 0; i < num_lobotto_1d; i = i + 2) { - dof_positions_1d(dof_id) = lob_points_1D(i); + } // end loop over dims - dof_id++; - } - }); + // get grad basis + for (size_t i = 0; i < num_dofs_1d; i++) { + Dval_1d(i) = 0.0; + } - RUN_CLASS({ - int dof_id = 0; + // Calculate 1D partial w.r.t. eta for the Y coordinate of the point + lagrange_derivative_1D(Dval_1d, dof_positions_1d, point(1)); - for (int i = 0; i < num_dual_lobotto_1d; i = i + 2) { - dg_dof_positions_1d(dof_id) = dual_lob_points_1D(i); + // Save the grad basis value at the point to a temp array and zero out the temp array + for (size_t i = 0; i < num_dofs_1d; i++) { + Dval_Nd(i, 1) = Dval_1d(i); + } - dof_id++; - } - }); + + if(elem_dims==3){ + // Multiply the i, j, k components of the basis and partial_eta from each node + // to get the tensor product partial derivatives of the basis at each node + for (size_t k = 0; k < num_dofs_1d; k++) + for (size_t j = 0; j < num_dofs_1d; j++) + for (size_t i = 0; i < num_dofs_1d; i++) { + size_t rid = get_dof_rid(i, j, k, num_dofs_1d); + + // Partial w.r.t xi + partial_eta(rid) = val_Nd(i, 0) * Dval_Nd(j, 1) * val_Nd(k, 2); + } // end for + + for (size_t i = 0; i < num_dofs_1d; i++) { + val_1d(i) = 0.0; + val_Nd(i, 0) = 0.0; + val_Nd(i, 1) = 0.0; + val_Nd(i, 2) = 0.0; + Dval_1d(i) = 0.0; + Dval_Nd(i, 0) = 0.0; + Dval_Nd(i, 1) = 0.0; + Dval_Nd(i, 2) = 0.0; } - Kokkos::fence(); + } // end if 3D + else if(elem_dims==2){ + // Multiply the i, j components of the basis and partial_eta from each node + // to get the tensor product partial derivatives of the basis at each node + for (size_t j = 0; j < num_dofs_1d; j++) + for (size_t i = 0; i < num_dofs_1d; i++) { + size_t rid = get_dof_rid(i, j, num_dofs_1d); + + // Partial w.r.t xi + partial_eta(rid) = val_Nd(i, 0) * Dval_Nd(j, 1); + } // end for + + for (size_t i = 0; i < num_dofs_1d; i++) { + val_1d(i) = 0.0; + val_Nd(i, 0) = 0.0; + val_Nd(i, 1) = 0.0; + Dval_1d(i) = 0.0; + Dval_Nd(i, 0) = 0.0; + Dval_Nd(i, 1) = 0.0; + } + } // end if 1D + else { + // Note: there is no 1D with this function, its valid for only 2D & 3D + } // end if 1D - FOR_ALL_CLASS(num_k, 0, num_dofs_1d, - num_j, 0, num_dofs_1d, - num_i, 0, num_dofs_1d, { - int dof_rlid = dof_rid(num_i, num_j, num_k); + } // end partial_eta_basis - dof_positions(dof_rlid, 0) = dof_positions_1d(num_i); - dof_positions(dof_rlid, 1) = dof_positions_1d(num_j); - dof_positions(dof_rlid, 2) = dof_positions_1d(num_k); - }); - Kokkos::fence(); - // basis and grad basis evaluations done at points // + ///////////////////////////////////////////////////////////////////////////// + /// + /// \fn partial_mu_basis + /// + /// \brief Compute the tensor-product partial derivative of the basis functions + /// with respect to the mu (z) coordinate at a given point in the reference element. + /// + /// This function calculates the partial derivatives of the high-order + /// tensor-product Lagrange basis functions with respect to the mu (z) coordinate + /// at a specified point in the reference element, using their 1D basis and derivative + /// representations. The result is stored in the provided array. Temporary arrays + /// for 1D and 3D basis/derivative values are passed in for workspace re-use. + /// + /// \param partial_mu Array to store the computed partial derivatives with respect to mu. + /// \param val_1d Workspace array for 1D Lagrange basis evaluations. + /// \param val_3d Workspace array for 3D tensor-product basis evaluations. + /// \param Dval_1d Workspace array for 1D Lagrange basis derivatives. + /// \param Dval_3d Workspace array for 3D tensor-product basis derivatives. + /// \param point Coordinates in the reference element where the basis partials are evaluated. + /// + /// \return void + /// + ///////////////////////////////////////////////////////////////////////////// + KOKKOS_FUNCTION + void partial_mu_basis(const CArrayKokkos& partial_mu, + const CArrayKokkos& dof_positions_1d, + const CArrayKokkos& val_1d, + const CArrayKokkos& val_3d, + const CArrayKokkos& Dval_1d, + const CArrayKokkos& Dval_3d, + const CArrayKokkos& point) + { + // this routine is only valid for 3D ref elems - // temp variables hold evaluations at a single point for each dof // - CArrayKokkos temp_nodal_basis(num_dofs_in_elem); - CArrayKokkos temp_elem_basis(num_dg_dofs_in_elem); + const size_t num_dofs_1d = dof_positions_1d.dims(0); + const size_t elem_dims = point.dims(0); - CArrayKokkos val_1d(num_dofs_1d); - CArrayKokkos val_3d(num_dofs_1d, 3); + // get X and Y basis + for(size_t dim=0; dim elem_val_1d(num_dg_dofs_1d); - CArrayKokkos elem_val_3d(num_dg_dofs_1d, 3); + for (size_t i = 0; i < num_dofs_1d; i++) { + val_1d(i) = 0.0; + } - CArrayKokkos point(3); + // Calculate 1D basis for the this dim coordinate of the point + lagrange_basis_1D(val_1d, dof_positions_1d, point(dim)); - RUN_CLASS({ - for (int lobotto_rid = 0; lobotto_rid < num_lobotto_in_elem; lobotto_rid++) { - // Get the nodal coordinates - for (int dim = 0; dim < 3; dim++) { - point(dim) = lobotto_point_positions(lobotto_rid, dim); - // printf(" point value = %f \n", point(dim) ); - } + // Save the basis value at the point to a temp array and zero out the temp array + for (size_t i = 0; i < num_dofs_1d; i++) { + val_3d(i, dim) = val_1d(i); + } - get_basis(temp_nodal_basis, val_1d, val_3d, point); - // double check_basis = 0.0; + } // end loop over dims - for (int basis_id = 0; basis_id < num_dofs_in_elem; basis_id++) { - // printf(" computed basis value = %f \n", temp_nodal_basis(basis_id) ); - lobotto_point_basis(lobotto_rid, basis_id) = temp_nodal_basis(basis_id); - // check_basis += temp_nodal_basis(basis_id); - temp_nodal_basis(basis_id) = 0.0; - } - // printf(" basis tally = %f \n", check_basis ); - } - }); - Kokkos::fence(); - RUN_CLASS({ - for (int gauss_rid = 0; gauss_rid < num_gauss_in_elem; gauss_rid++) { - // Get the nodal coordinates - for (int dim = 0; dim < 3; dim++) { - point(dim) = gauss_point_positions(gauss_rid, dim); - // printf(" point value = %f \n", point(dim) ); - } + // get grad basis + for (size_t i = 0; i < num_dofs_1d; i++) { + Dval_1d(i) = 0.0; + } + + // Calculate 1D partial w.r.t. mu for the Z coordinate of the point + lagrange_derivative_1D(Dval_1d, dof_positions_1d, point(2)); - get_basis(temp_nodal_basis, val_1d, val_3d, point); + // Save the basis value at the point to a temp array and zero out the temp array + for (size_t i = 0; i < num_dofs_1d; i++) { + Dval_3d(i, 2) = Dval_1d(i); + } - // double check_basis = 0.0; + // Multiply the i, j, k components of the basis and partial_xi from each node + // to get the tensor product partial derivatives of the basis at each node + for (size_t k = 0; k < num_dofs_1d; k++) + for (size_t j = 0; j < num_dofs_1d; j++) + for (size_t i = 0; i < num_dofs_1d; i++) { + size_t rid = get_dof_rid(i, j, k, num_dofs_1d); - for (int basis_id = 0; basis_id < num_dofs_in_elem; basis_id++) { - // printf(" computed basis value = %f \n", temp_nodal_basis(basis_id) ); - gauss_point_basis(gauss_rid, basis_id) = temp_nodal_basis(basis_id); - // check_basis += temp_nodal_basis(basis_id); - temp_nodal_basis(basis_id) = 0.0; - } + // Partial w.r.t mu + partial_mu(rid) = val_3d(i, 0) * val_3d(j, 1) * Dval_3d(k, 2); + } // end for - // printf(" basis tally = %f \n", check_basis ); - } - }); - Kokkos::fence(); + for (size_t i = 0; i < num_dofs_1d; i++) { + val_1d(i) = 0.0; + val_3d(i, 0) = 0.0; + val_3d(i, 1) = 0.0; + val_3d(i, 2) = 0.0; + Dval_1d(i) = 0.0; + Dval_3d(i, 0) = 0.0; + Dval_3d(i, 1) = 0.0; + Dval_3d(i, 2) = 0.0; + } + } // end partial_mu_basis - RUN_CLASS({ - for (int gauss_rid = 0; gauss_rid < num_gauss_in_elem; gauss_rid++) { - // Get the nodal coordinates - for (int dim = 0; dim < 3; dim++) { - point(dim) = gauss_point_positions(gauss_rid, dim); - } - get_elem_basis(temp_elem_basis, elem_val_1d, elem_val_3d, point); - // get_bernstein_basis(temp_elem_basis, elem_val_1d, elem_val_3d, point); - // double check_basis = 0.0; + ///////////////////////////////////////////////////////////////////////////// + /// + /// \fn get_basis_and_grad_basis + /// + /// \brief Fill the reference element with basis and grad basis values + /// + /// This function works for building basis and grad basis values in a volume + /// reference element or reference surface. + /// + /// The DOF's are the volume element and can be the Lagrange basis can be at + /// Lobatto or Legendre points, those points are in most cases are spatially + /// different from the quadrature points. + /// + /// \param qpt_basis quadrature point basis, 2DArray(qpts,dofs) + /// \param qpt_grad_basis quadrature point grad basis, 3DArray(qpts,dofs,dims) + /// \param qpt_positions all quadarature pt positions, 2DArray(qpts,dims) + /// \param dof_positions_1D The 1D dof positions in the reference coords + /// + /// \return void + /// + ///////////////////////////////////////////////////////////////////////////// + template + void get_basis_and_grad_basis(T1& qpt_basis, + T2& qpt_grad_basis, + const T3& qpt_positions, + const T4& dof_positions_1d) + { + + const size_t num_dofs_1d = dof_positions_1d.dims(0); - for (int basis_id = 0; basis_id < num_dg_dofs_in_elem; basis_id++) { - gauss_point_dg_basis(gauss_rid, basis_id) = temp_elem_basis(basis_id); - // check_basis += temp_elem_basis(basis_id); - temp_elem_basis(basis_id) = 0.0; - } + const size_t num_qpts_in_elem = qpt_grad_basis.dims(0); + const size_t num_dofs_in_elem = qpt_grad_basis.dims(1); + const size_t elem_dims = qpt_grad_basis.dims(2); + + // ----------------------------------------------------------------------- + // Step 2: Calculate the basis values at quadrature points + // ----------------------------------------------------------------------- + + // temporary arrays to hold evaluations at a single point for each dof + CArrayKokkos temp_basis(num_dofs_in_elem); + CArrayKokkos temp_val_1d(num_dofs_1d); + CArrayKokkos temp_val_Nd(num_dofs_1d, elem_dims); // 2D or 3D + + CArrayKokkos point(elem_dims); - // printf(" basis tally = %f \n", check_basis ); + + RUN({ + for (size_t qpt_rid = 0; qpt_rid < num_qpts_in_elem; qpt_rid++) { + + // Get the evaluation coordinates + for (size_t dim = 0; dim < elem_dims; dim++) { + point(dim) = qpt_positions(qpt_rid, dim); } - }); - Kokkos::fence(); - RUN_CLASS({ - for (int lobotto_rid = 0; lobotto_rid < num_lobotto_in_elem; lobotto_rid++) { - // Get the nodal coordinates - for (int dim = 0; dim < 3; dim++) { - point(dim) = lobotto_point_positions(lobotto_rid, dim); - } + get_basis(temp_basis, dof_positions_1d, temp_val_1d, temp_val_Nd, point); - get_elem_basis(temp_elem_basis, elem_val_1d, elem_val_3d, point); - // get_bernstein_basis(temp_elem_basis, elem_val_1d, elem_val_3d, point); - // double check_basis = 0.0; + for (size_t basis_id = 0; basis_id < num_dofs_in_elem; basis_id++) { + qpt_basis(qpt_rid, basis_id) = temp_basis(basis_id); + temp_basis(basis_id) = 0.0; + } + } // end for over qpts in elem + }); + Kokkos::fence(); - for (int basis_id = 0; basis_id < num_dg_dofs_in_elem; basis_id++) { - lobotto_point_dg_basis(lobotto_rid, basis_id) = temp_elem_basis(basis_id); - // check_basis += temp_elem_basis(basis_id); - temp_elem_basis(basis_id) = 0.0; - } + // ----------------------------------------------------------------------- + // Step 3: Calculate the grad basis values at quadrature points + // ----------------------------------------------------------------------- + - // printf(" basis tally = %f \n", check_basis ); + // temporary arrays to hold evaluations at a single point for each dof + CArrayKokkos temp_partial_xi(num_dofs_in_elem); + CArrayKokkos temp_partial_eta(num_dofs_in_elem); + CArrayKokkos temp_partial_mu(num_dofs_in_elem); + + CArrayKokkos temp_Dval_1d(num_dofs_1d); + CArrayKokkos temp_Dval_Nd(num_dofs_1d, elem_dims); + + + RUN({ + for (size_t qpt_rid = 0; qpt_rid < num_qpts_in_elem; qpt_rid++) { + + // Get the evaluation coordinates + for (size_t dim = 0; dim < elem_dims; dim++) { + point(dim) = qpt_positions(qpt_rid, dim); } - }); - Kokkos::fence(); - // --- evaluate grad_basis functions at the lobatto points --- + partial_xi_basis(temp_partial_xi, + dof_positions_1d, + temp_val_1d, + temp_val_Nd, + temp_Dval_1d, + temp_Dval_Nd, + point); + + if(elem_dims>1) partial_eta_basis(temp_partial_eta, + dof_positions_1d, + temp_val_1d, + temp_val_Nd, + temp_Dval_1d, + temp_Dval_Nd, + point); + + if(elem_dims>2) partial_mu_basis(temp_partial_mu, + dof_positions_1d, + temp_val_1d, + temp_val_Nd, + temp_Dval_1d, + temp_Dval_Nd, + point); + + for (size_t basis_id = 0; basis_id < num_dofs_in_elem; basis_id++) { + qpt_grad_basis(qpt_rid, basis_id, 0) = temp_partial_xi(basis_id); + if(elem_dims>1)qpt_grad_basis(qpt_rid, basis_id, 1) = temp_partial_eta(basis_id); + if(elem_dims>2)qpt_grad_basis(qpt_rid, basis_id, 2) = temp_partial_mu(basis_id); + + temp_partial_xi(basis_id) = 0.0; + if(elem_dims>1) temp_partial_eta(basis_id) = 0.0; + if(elem_dims>2) temp_partial_mu(basis_id) = 0.0; + } // end loop over basis functions + + + } // end for qpts in elem + }); + Kokkos::fence(); - CArrayKokkos temp_partial_xi(num_dofs_in_elem); - CArrayKokkos temp_partial_eta(num_dofs_in_elem); - CArrayKokkos temp_partial_mu(num_dofs_in_elem); + } // end get_basis_and_grad_basis function - CArrayKokkos Dval_1d(num_dofs_1d); - CArrayKokkos Dval_3d(num_dofs_1d, 3); - RUN_CLASS({ - for (int lobotto_rid = 0; lobotto_rid < num_lobotto_in_elem; lobotto_rid++) { - // Get the lobatto coordinates - for (int dim = 0; dim < 3; dim++) { - point(dim) = lobotto_point_positions(lobotto_rid, dim); - } + // Quadrature rules for elems + struct Quadrature_t + { + reference_space::QuadratureType QuadratureType; + + size_t elem_dims = 0; + size_t num_qpts_in_elem = 0; + size_t num_qpts_1d = 0; + + CArrayKokkos qpt_positions; + CArrayKokkos qpt_weights; + + ///////////////////////////////////////////////////////////////////////////// + /// + /// \fn initialize_quadrature + /// + /// \brief Set up quadrature in a volume element + /// + /// \param TypeInp The type of quadrature (e.g., Lobatto or Legendre) + /// \param num_qpts_1d_inp The number of quadrature in 1D, applied to each direction. + /// \param elem_dims_in The number dimensions + /// + ///////////////////////////////////////////////////////////////////////////// + void initialize_quadrature(const reference_space::QuadratureType TypeInp, + const size_t num_qpts_1d_inp, + const size_t elem_dims_in) + { + QuadratureType = TypeInp; + + elem_dims = elem_dims_in; + num_qpts_1d = num_qpts_1d_inp; + if(num_qpts_1d==0) throw std::runtime_error("ERROR: zero quadrature points specified \n"); + + num_qpts_in_elem = 1; + for(size_t dim=0; dim(num_qpts_in_elem, elem_dims, "qpt_positions"); + qpt_weights = CArrayKokkos(num_qpts_in_elem, "qpt_weights"); - // double check[3]; - // for (int i = 0; i < 3; i++) check[i] = 0.0; + // temporary 1D variables to build 3D element + CArrayKokkos qpt_positions_1d(num_qpts_1d, "qpt_positions_1d"); + CArrayKokkos qpt_weights_1d (num_qpts_1d, "qpt_weights_1d"); - partial_xi_basis(temp_partial_xi, val_1d, val_3d, Dval_1d, Dval_3d, point); - partial_eta_basis(temp_partial_eta, val_1d, val_3d, Dval_1d, Dval_3d, point); - partial_mu_basis(temp_partial_mu, val_1d, val_3d, Dval_1d, Dval_3d, point); + if(QuadratureType == reference_space::GaussLegendre){ + RUN_CLASS({ + get_legendre_nodes_1D(qpt_positions_1d, num_qpts_1d); + get_legendre_weights_1D(qpt_weights_1d, num_qpts_1d); + }); + } + else if(QuadratureType == reference_space::GaussLobatto){ + RUN_CLASS({ + get_lobatto_nodes_1D(qpt_positions_1d, num_qpts_1d); + get_lobatto_weights_1D(qpt_weights_1d, num_qpts_1d); + }); + } + else + { + throw std::runtime_error("ERROR: unsupported quadrature set specified \n"); + } - for (int basis_id = 0; basis_id < num_dofs_in_elem; basis_id++) { - lobotto_point_grad_basis(lobotto_rid, basis_id, 0) = temp_partial_xi(basis_id); - lobotto_point_grad_basis(lobotto_rid, basis_id, 1) = temp_partial_eta(basis_id); - lobotto_point_grad_basis(lobotto_rid, basis_id, 2) = temp_partial_mu(basis_id); + // 3D volume element + if(elem_dims==3){ + FOR_ALL_CLASS(k, 0, num_qpts_1d, + j, 0, num_qpts_1d, + i, 0, num_qpts_1d, { - // check[0] += temp_partial_xi(basis_id); - // check[1] += temp_partial_eta(basis_id); - // check[2] += temp_partial_mu(basis_id); + const size_t rid = get_qpt_rid(i, j, k, num_qpts_1d); - temp_partial_xi(basis_id) = 0.0; - temp_partial_eta(basis_id) = 0.0; - temp_partial_mu(basis_id) = 0.0; - } + qpt_positions(rid, 0) = qpt_positions_1d(i); + qpt_positions(rid, 1) = qpt_positions_1d(j); + qpt_positions(rid, 2) = qpt_positions_1d(k); - // printf(" grad_basis tally = %f, %f, %f \n", check[0], check[1], check[2]); - } - }); - Kokkos::fence(); + qpt_weights(rid) = qpt_weights_1d(i) * qpt_weights_1d(j) * qpt_weights_1d(k); + }); + Kokkos::fence(); + } + // 2D volume + else if (elem_dims==2){ + FOR_ALL_CLASS(j, 0, num_qpts_1d, + i, 0, num_qpts_1d, { - RUN_CLASS({ - for (int gauss_rid = 0; gauss_rid < num_gauss_in_elem; gauss_rid++) { - // Get the nodal coordinates - for (int dim = 0; dim < 3; dim++) { - point(dim) = gauss_point_positions(gauss_rid, dim); - } + const size_t rid = get_qpt_rid(i, j, num_qpts_1d); - partial_xi_basis(temp_partial_xi, val_1d, val_3d, Dval_1d, Dval_3d, point); - partial_eta_basis(temp_partial_eta, val_1d, val_3d, Dval_1d, Dval_3d, point); - partial_mu_basis(temp_partial_mu, val_1d, val_3d, Dval_1d, Dval_3d, point); + qpt_positions(rid, 0) = qpt_positions_1d(i); + qpt_positions(rid, 1) = qpt_positions_1d(j); - double check[3]; - for (int i = 0; i < 3; i++) { - check[i] = 0.0; - } + qpt_weights(rid) = qpt_weights_1d(i) * qpt_weights_1d(j); + }); + Kokkos::fence(); + } + // 1D volume + else if (elem_dims==1) { + FOR_ALL_CLASS(i, 0, num_qpts_1d, { - for (int basis_id = 0; basis_id < num_dofs_in_elem; basis_id++) { - gauss_point_grad_basis(gauss_rid, basis_id, 0) = temp_partial_xi(basis_id); - // printf(" grad basis value : %f \n ", gauss_point_grad_basis(gauss_rid, basis_id, 0) ); - gauss_point_grad_basis(gauss_rid, basis_id, 1) = temp_partial_eta(basis_id); - // printf(" grad basis value : %f \n ", gauss_point_grad_basis(gauss_rid, basis_id, 1) ); - gauss_point_grad_basis(gauss_rid, basis_id, 2) = temp_partial_mu(basis_id); - // printf(" grad basis value : %f \n ", gauss_point_grad_basis(gauss_rid, basis_id, 2) ); - - check[0] += temp_partial_xi(basis_id); - check[1] += temp_partial_eta(basis_id); - check[2] += temp_partial_mu(basis_id); - - temp_partial_xi(basis_id) = 0.0; - temp_partial_eta(basis_id) = 0.0; - temp_partial_mu(basis_id) = 0.0; - } + const size_t rid = i; - // printf(" grad_basis tally = %f, %f, %f \n", check[0], check[1], check[2]); - } - }); - Kokkos::fence(); - } // end 3d scope - } // end of member function + qpt_positions(rid, 0) = qpt_positions_1d(i); - + qpt_weights(rid) = qpt_weights_1d(i); + }); + Kokkos::fence(); + } + else{ + throw std::runtime_error("ERROR: unsupported quadrature elem dims \n"); + } - // --- ref index access member functions --- - ///////////////////////////////////////////////////////////////////////////// - /// - /// \fn dof_rid - /// - /// \brief Compute the 1D array index for a degree of freedom (DOF) in an element. - /// - /// Calculates the flat row-major index corresponding to a DOF located at position (i, j, k) - /// in the element, for continuous fields. This is used for basis functions and data fields - /// that are continuous across element boundaries. - /// - /// \param i Local DOF index in the first (xi) coordinate direction. - /// \param j Local DOF index in the second (eta) coordinate direction. - /// \param k Local DOF index in the third (mu) coordinate direction. - /// - /// \return The row-major offset index for the DOF in this element. - /// - ///////////////////////////////////////////////////////////////////////////// - KOKKOS_INLINE_FUNCTION - int dof_rid(int i, int j, int k) const - { - return i + j * num_dofs_1d + k * num_dofs_1d * num_dofs_1d; - } + } // init fcn quadrature - ///////////////////////////////////////////////////////////////////////////// - /// - /// \fn elem_dof_rid - /// - /// \brief Compute the 1D array index for a discontinuous Galerkin degree of freedom (DG DOF) in an element. - /// - /// Calculates the flat row-major index corresponding to a DG DOF located at position (i, j, k) - /// in the element, for discontinuous fields. This is used for basis functions and data fields - /// that are discontinuous across element boundaries. - /// - /// \param i Local DG DOF index in the first (xi) coordinate direction. - /// \param j Local DG DOF index in the second (eta) coordinate direction. - /// \param k Local DG DOF index in the third (mu) coordinate direction. - /// - /// \return The row-major offset index for the DG DOF in this element. - /// - ///////////////////////////////////////////////////////////////////////////// - KOKKOS_INLINE_FUNCTION - int elem_dof_rid(int i, int j, int k) const - { - return i + j * num_dg_dofs_1d + k * num_dg_dofs_1d * num_dg_dofs_1d; - } + }; // end Quadrature_t - ///////////////////////////////////////////////////////////////////////////// - /// - /// \fn lobatto_rid - /// - /// \brief Compute the 1D array index for a Gauss-Lobatto quadrature point in an element. - /// - /// Calculates the row-major flat index corresponding to a Gauss-Lobatto quadrature - /// point at the position (i, j, k) within an element. Typically used for accessing - /// basis, position, and weight arrays defined on the tensor-product grid of - /// Gauss-Lobatto quadrature points in the reference element. - /// - /// \param i Local index in the first (xi) coordinate direction. - /// \param j Local index in the second (eta) coordinate direction. - /// \param k Local index in the third (mu) coordinate direction. - /// - /// \return The row-major offset index for the Gauss-Lobatto quadrature point in this element. - /// - ///////////////////////////////////////////////////////////////////////////// - KOKKOS_INLINE_FUNCTION - int lobatto_rid(int i, int j, int k) const - { - return i + j * num_lobotto_1d + k * num_lobotto_1d * num_lobotto_1d; - } - ///////////////////////////////////////////////////////////////////////////// - /// - /// \fn dual_lobatto_rid - /// - /// \brief Compute the 1D array index for a dual Gauss-Lobatto quadrature point in an element. - /// - /// Calculates the row-major flat index for accessing data associated with a dual Gauss-Lobatto - /// quadrature point at the position (i, j, k) within the element. This is used for accessing - /// basis functions, positions, and weights at dual Gauss-Lobatto quadrature points, which are - /// ancillary points in high-order finite element or spectral methods. - /// - /// \param i Local dual Gauss-Lobatto index in the first (xi) coordinate direction. - /// \param j Local dual Gauss-Lobatto index in the second (eta) coordinate direction. - /// \param k Local dual Gauss-Lobatto index in the third (mu) coordinate direction. - /// - /// \return The row-major offset index for the dual Gauss-Lobatto quadrature point in this element. - /// - ///////////////////////////////////////////////////////////////////////////// - KOKKOS_INLINE_FUNCTION - int dual_lobatto_rid(int i, int j, int k) const - { - return i + j * num_dual_lobotto_1d + k * num_dual_lobotto_1d * num_dual_lobotto_1d; - } - ///////////////////////////////////////////////////////////////////////////// - /// - /// \fn legendre_rid - /// - /// \brief Compute the 1D array index for a Gauss-Legendre quadrature point in an element. - /// - /// Calculates the row-major flat index corresponding to a Gauss-Legendre quadrature - /// point at the position (i, j, k) within an element. This function is typically used - /// for accessing basis functions, positions, and weights defined on the tensor-product - /// grid of Gauss-Legendre quadrature points in the reference element, which is common - /// in high-order finite element and spectral methods. - /// - /// \param i Local Gauss-Legendre index in the first (xi) coordinate direction. - /// \param j Local Gauss-Legendre index in the second (eta) coordinate direction. - /// \param k Local Gauss-Legendre index in the third (mu) coordinate direction. - /// - /// \return The row-major offset index for the Gauss-Legendre quadrature point in this element. - /// - ///////////////////////////////////////////////////////////////////////////// - KOKKOS_INLINE_FUNCTION - int legendre_rid(int i, int j, int k) const + // reference element data structure + struct ReferenceElement_t { - return i + j * num_gauss_1d + k * num_gauss_1d * num_gauss_1d; - } - ///////////////////////////////////////////////////////////////////////////// - /// - /// \fn legendre_rid_2D - /// - /// \brief Compute the 1D array index for a Gauss-Legendre quadrature point in 2D. - /// - /// Calculates the row-major flat index corresponding to a Gauss-Legendre quadrature - /// point at the position (i, j) within a two-dimensional element. This function is - /// typically used for accessing basis functions, positions, and weights defined on the - /// tensor-product grid of Gauss-Legendre quadrature points in the reference element, - /// for high-order finite element and spectral methods in 2D. - /// - /// \param i Local Gauss-Legendre index in the first (xi) coordinate direction. - /// \param j Local Gauss-Legendre index in the second (eta) coordinate direction. - /// - /// \return The row-major offset index for the Gauss-Legendre quadrature point in this element (2D). - /// - ///////////////////////////////////////////////////////////////////////////// - KOKKOS_INLINE_FUNCTION - int legendre_rid_2D(int i, int j) const - { - return i + j * num_gauss_1d; - } + reference_space::ElementType ElementType = reference_space::linearElement; ///< The type of element + reference_space::BasisType BasisType = reference_space::LagrangeLobatto; /// dof_positions; + CArrayKokkos dof_positions_1d; + + + // Basis evaluation at quadrature points + CArrayKokkos qpt_basis; + CArrayKokkos qpt_grad_basis; + + + ///////////////////////////////////////////////////////////////////////////// + /// + /// \fn initialize_ref_elem + /// + /// \brief Initialize the reference element with polynomial order and dimension. + /// It is the companion structure for the unstructured_mesh.h + /// + /// Initializes internal data members for the reference element according + /// to the given polynomial order and spatial dimension. Sets up the number of + /// degrees of freedom (DOF), as well as associated sizes and counts for basis + /// function evaluations, according to the dimension and polynomial order. If + /// the polynomial order is less than element order, it is in a discontinous + /// space. For that case, user must create multiple reference elements, one + /// that defines the position and one for the discontinous fields. + /// + /// The DOF's for the Lagrange basis can be at Lobatto or Legendre points, + /// those points are in most cases are spatially different from the quadrature + /// points. + /// + /// \param ElemTypeInp The element type (classical linear or arbitrary-order) + /// \param BasisTypeInp The location of basis DOFs + /// \param Quadrature_t Quadrature set in the element + /// \param p_order The element order e.g., constant = 0, linear = 1, quadratic = 2, ... + /// + /// \return void + /// + ///////////////////////////////////////////////////////////////////////////// + void initialize_ref_elem(const reference_space::ElementType ElemTypeInp, + const reference_space::BasisType BasisTypeInp, + const struct Quadrature_t& Quadrature, + const size_t p_order) + { + // set element and basis type + ElementType = ElemTypeInp; + BasisType = BasisTypeInp; + + elem_dims = Quadrature.elem_dims; + if(elem_dims==0) throw std::runtime_error("ERROR: quadrature not correctly specified \n"); + if(elem_dims>3) throw std::runtime_error("ERROR: only 1D, 2D, and 3D reference elements supported \n"); + + // ----------------------------------------------------------------------- + // Step 1a: determine the number of DOFs in 3D + // ----------------------------------------------------------------------- + Pn = p_order; + num_dofs_1d = p_order + 1; - ///////////////////////////////////////////////////////////////////////////// - /// - /// \fn get_basis - /// - /// \brief Computes the tensor-product nodal basis values at an arbitrary point. - /// - /// This function evaluates the Lagrange basis functions at a specified point within - /// the reference element and assembles the tensor-product basis values for all degrees - /// of freedom (DOFs). Basis values in each coordinate direction are computed independently - /// using the 1D Lagrange basis, and then combined to form the full multi-dimensional basis. - /// The results are written to the provided output array. - /// - /// \param basis Reference to the output CArrayKokkos to hold full tensor-product basis values, sized for all DOFs in the element. - /// \param val_1d Temporary CArrayKokkos for holding 1D basis values (as workspace). - /// \param val_3d Temporary CArrayKokkos for holding basis values for each direction; shape should be (num_dofs_1d, 3). - /// \param point Reference to CArrayKokkos representing the coordinates (xi, eta, mu) at which the basis is evaluated (size 3). - /// - /// \return void - /// - ///////////////////////////////////////////////////////////////////////////// - KOKKOS_FUNCTION - void get_basis(const CArrayKokkos& basis, - const CArrayKokkos& val_1d, - const CArrayKokkos& val_3d, - const CArrayKokkos& point) const - { - // initialize to zero // - for (int i = 0; i < num_dofs_1d; i++) { - val_1d(i) = 0.0; - } + num_dofs_in_elem = 1; // Initialize to 1 + for (size_t dim = 0; dim < elem_dims; dim++) { + num_dofs_in_elem *= num_dofs_1d; + } // end for - // Calculate 1D basis for the X coordinate of the point - lagrange_basis_1D(val_1d, point(0)); + // ----------------------------------------------------------------------- + // Step 1b: get the positions in reference space for the DOFs + // ----------------------------------------------------------------------- + dof_positions = CArrayKokkos(num_dofs_in_elem, elem_dims, "dof_positions"); + dof_positions_1d = CArrayKokkos (num_dofs_1d, "dof_positions_1d"); - // Save the basis value at the point to a temp array and zero out the temp array - for (int i = 0; i < num_dofs_1d; i++) { - val_3d(i, 0) = val_1d(i); - val_1d(i) = 0.0; - } + // dof positions can be at legendre or lobatto locations in elem + if(BasisTypeInp == reference_space::LagrangeLegendre){ + RUN_CLASS({ + get_legendre_nodes_1D(dof_positions_1d, num_dofs_1d); + }); + } + else if(BasisTypeInp == reference_space::LagrangeLobatto){ + RUN_CLASS({ + get_lobatto_nodes_1D(dof_positions_1d, num_dofs_1d); + }); + } + else + { + throw std::runtime_error("ERROR: unsupported basis DOF locations specified \n"); + } - // Calculate 1D basis for the Y coordinate of the point - lagrange_basis_1D(val_1d, point(1)); + // 3D volume element + if(elem_dims==3){ + FOR_ALL_CLASS(k, 0, num_dofs_1d, + j, 0, num_dofs_1d, + i, 0, num_dofs_1d, { - // Save the basis value at the point to a temp array and zero out the temp array - for (int i = 0; i < num_dofs_1d; i++) { - val_3d(i, 1) = val_1d(i); - val_1d(i) = 0.0; - } + const size_t rid = get_dof_rid(i, j, k, num_dofs_1d); - // Calculate 1D basis for the Z coordinate of the point - lagrange_basis_1D(val_1d, point(2)); + dof_positions(rid, 0) = dof_positions_1d(i); + dof_positions(rid, 1) = dof_positions_1d(j); + dof_positions(rid, 2) = dof_positions_1d(k); + }); + } // end if 3D + // 2D volume or 2D surface element + else if (elem_dims==2){ + FOR_ALL_CLASS(j, 0, num_dofs_1d, + i, 0, num_dofs_1d, { - // Save the basis value at the point to a temp array and zero out the temp array - for (int i = 0; i < num_dofs_1d; i++) { - val_3d(i, 2) = val_1d(i); - val_1d(i) = 0.0; - } + const size_t rid = get_dof_rid(i, j, num_dofs_1d); - // Multiply the i, j, k components of the basis from each node - // to get the tensor product basis for the node - for (int k = 0; k < num_dofs_1d; k++) { - for (int j = 0; j < num_dofs_1d; j++) { - for (int i = 0; i < num_dofs_1d; i++) { - int dof_rlid = dof_rid(i, j, k); - basis(dof_rlid) = val_3d(i, 0) * val_3d(j, 1) * val_3d(k, 2); - } - } - } + dof_positions(rid, 0) = dof_positions_1d(i); + dof_positions(rid, 1) = dof_positions_1d(j); + }); + } // end if 1D + // 1D volume, edge, or 1D surface element + else { + FOR_ALL_CLASS(i, 0, num_dofs_1d, { - for (int i = 0; i < num_dofs_1d; i++) { - val_1d(i) = 0.0; - val_3d(i, 0) = 0.0; - val_3d(i, 1) = 0.0; - val_3d(i, 2) = 0.0; - } - } + const size_t rid = i; - ///////////////////////////////////////////////////////////////////////////// - /// - /// \fn get_elem_basis - /// - /// \brief Compute tensor-product DG element basis function values at a given point. - /// - /// Calculates the tensor-product Lagrange basis functions associated with the - /// discontinuous (DG) element DOFs at the specified point in reference coordinates. - /// This involves evaluating the 1D basis polynomials in each coordinate direction, - /// then forming the full multidimensional basis by taking the product over all - /// directions for each DOF. Intermediate storage is used for efficient calculation. - /// - /// \param basis [out] CArrayKokkos& - Output array for the computed basis values - /// for each DOF in the element. - /// \param val_1d [in,out] CArrayKokkos& - Temporary storage for 1D basis values - /// per coordinate direction; this will be overwritten during the calculation. - /// \param val_3d [in,out] CArrayKokkos& - Temporary storage for 1D basis results - /// in all 3 coordinate directions, size [num_dg_dofs_1d, 3]; - /// will be overwritten during calculation. - /// \param point [in] const CArrayKokkos& - The reference space coordinates - /// (xi, eta, mu) at which to evaluate the basis functions. - /// - /// \return void - /// - ///////////////////////////////////////////////////////////////////////////// - KOKKOS_FUNCTION - void get_elem_basis(const CArrayKokkos& basis, - const CArrayKokkos& val_1d, - const CArrayKokkos& val_3d, - const CArrayKokkos& point) const - { - // initialize to zero // - for (int i = 0; i < num_dg_dofs_1d; i++) { - val_1d(i) = 0.0; - } + dof_positions(rid, 0) = dof_positions_1d(i); + }); + } // end if 1D + Kokkos::fence(); - // Calculate 1D basis for the X coordinate of the point - lagrange_elem_basis_1D(val_1d, point(0)); - // Save the basis value at the point to a temp array and zero out the temp array - for (int i = 0; i < num_dg_dofs_1d; i++) { - val_3d(i, 0) = val_1d(i); - val_1d(i) = 0.0; - } + // build basis and grad basis in the reference element + qpt_basis = CArrayKokkos(Quadrature.num_qpts_in_elem, num_dofs_in_elem, "qpt_basis"); + qpt_grad_basis = CArrayKokkos(Quadrature.num_qpts_in_elem, num_dofs_in_elem, elem_dims, "qpt_grad_basis"); + + get_basis_and_grad_basis(qpt_basis, + qpt_grad_basis, + Quadrature.qpt_positions, + dof_positions_1d); - // Calculate 1D basis for the Y coordinate of the point - lagrange_elem_basis_1D(val_1d, point(1)); + } // end of member function - // Save the basis value at the point to a temp array and zero out the temp array - for (int i = 0; i < num_dg_dofs_1d; i++) { - val_3d(i, 1) = val_1d(i); - val_1d(i) = 0.0; - } + }; // end struct - // Calculate 1D basis for the Z coordinate of the point - lagrange_elem_basis_1D(val_1d, point(2)); - // Save the basis value at the point to a temp array and zero out the temp array - for (int i = 0; i < num_dg_dofs_1d; i++) { - val_3d(i, 2) = val_1d(i); - val_1d(i) = 0.0; - } + // Quadrature rules for surfaces of a volume element + struct SurfaceQuadrature_t + { + reference_space::QuadratureType QuadratureType; + + size_t elem_dims = 0; + size_t num_qpts_in_surf = 0; + size_t num_qpts_1d = 0; + + size_t num_ref_surfs = 0; + + CArrayKokkos qpt_positions; + CArrayKokkos qpt_weights; + + + ///////////////////////////////////////////////////////////////////////////// + /// + /// \fn initialize_quadrature + /// + /// \brief Set up quadrature for surface elements + /// + /// \param TypeInp The type of quadrature (e.g., Lobatto or Legendre) + /// \param num_qpts_1d_inp The number of quadrature in 1D, applied to each direction. + /// \param elem_dims_in The number dimensions + /// + ///////////////////////////////////////////////////////////////////////////// + void initialize_quadrature(const reference_space::QuadratureType TypeInp, + const size_t num_qpts_1d_inp, + const size_t elem_dims_in) + { + QuadratureType = TypeInp; + + elem_dims = elem_dims_in; + num_qpts_1d = num_qpts_1d_inp; + if(num_qpts_1d==0) throw std::runtime_error("ERROR: zero quadrature points specified \n"); + + num_qpts_in_surf = 1; + num_ref_surfs = 0; + for(size_t dim=0; dim(num_ref_surfs, num_qpts_in_surf, elem_dims, "qpt_positions"); + qpt_weights = CArrayKokkos(num_ref_surfs, num_qpts_in_surf, "qpt_weights"); + + + // temporary 1D variables to build 3D element + CArrayKokkos qpt_positions_1d(num_qpts_1d, "qpt_positions_1d"); + CArrayKokkos qpt_weights_1d (num_qpts_1d, "qpt_weights_1d"); + + if(QuadratureType == reference_space::GaussLegendre){ + RUN_CLASS({ + get_legendre_nodes_1D(qpt_positions_1d, num_qpts_1d); + get_legendre_weights_1D(qpt_weights_1d, num_qpts_1d); + }); + } + else if(QuadratureType == reference_space::GaussLobatto){ + RUN_CLASS({ + get_lobatto_nodes_1D(qpt_positions_1d, num_qpts_1d); + get_lobatto_weights_1D(qpt_weights_1d, num_qpts_1d); + }); + } + else + { + throw std::runtime_error("ERROR: unsupported quadrature set specified \n"); } - } - for (int i = 0; i < num_dg_dofs_1d; i++) { - val_1d(i) = 0.0; - val_3d(i, 0) = 0.0; - val_3d(i, 1) = 0.0; - val_3d(i, 2) = 0.0; - } - } - ///////////////////////////////////////////////////////////////////////////// - /// - /// \fn partial_xi_basis - /// - /// \brief Compute the partial derivative of the basis function with respect to the xi coordinate. - /// - /// This function evaluates the tensor-product Lagrange basis function derivatives in the xi direction - /// at a given point within the reference element. The computation is performed by first evaluating the - /// 1D derivatives and basis functions for each coordinate and then combining them using the chain rule - /// for tensor products. The result is stored in the provided array for all degrees of freedom. - /// - /// \param partial_xi Array to store the value of the partial derivative with respect to xi for each basis function. - /// \param val_1d Temporary workspace array for 1D basis evaluations. - /// \param val_3d Temporary workspace array for 3D basis component evaluations. - /// \param Dval_1d Temporary workspace array for 1D derivative evaluations. - /// \param Dval_3d Temporary workspace array for 3D derivative component evaluations. - /// \param point Input array specifying the coordinates (xi, eta, mu) at which to evaluate the derivative. - /// - /// \return void - /// - ///////////////////////////////////////////////////////////////////////////// - KOKKOS_FUNCTION - void partial_xi_basis(const CArrayKokkos& partial_xi, - const CArrayKokkos& val_1d, - const CArrayKokkos& val_3d, - const CArrayKokkos& Dval_1d, - const CArrayKokkos& Dval_3d, - const CArrayKokkos& point) const - { - // initialize// - for (int i = 0; i < num_dofs_1d; i++) { - val_1d(i) = 0.0; - Dval_1d(i) = 0.0; - } + + // surface of 3D volume element + if(elem_dims==3){ + size_t face; - // Calculate 1D partial w.r.t. xi for the X coordinate of the point - lagrange_derivative_1D(Dval_1d, point(0)); + // xi-minus has coords (-1, eta, mu) + face = 0; + FOR_ALL_CLASS(k, 0, num_qpts_1d, + j, 0, num_qpts_1d, { - // Save the basis value at the point to a temp array and zero out the temp array - for (int i = 0; i < num_dofs_1d; i++) { - Dval_3d(i, 0) = Dval_1d(i); - Dval_1d(i) = 0.0; - } + const size_t rid=get_qpt_rid(j, k, num_qpts_1d); - // Calculate 1D basis for the Y coordinate of the point - lagrange_basis_1D(val_1d, point(1)); + qpt_positions(face, rid, 0) = -1.; + qpt_positions(face, rid, 1) = qpt_positions_1d(j); + qpt_positions(face, rid, 2) = qpt_positions_1d(k); - // Save the basis value at the point to a temp array and zero out the temp array - for (int i = 0; i < num_dofs_1d; i++) { - val_3d(i, 1) = val_1d(i); - val_1d(i) = 0.0; - } + qpt_weights(face,rid) = qpt_weights_1d(j) * qpt_weights_1d(k); - // Calculate 1D basis for the Z coordinate of the point - lagrange_basis_1D(val_1d, point(2)); + }); // end for + + // xi-plus has coords (+1, eta, mu) + face = 1; + FOR_ALL_CLASS(k, 0, num_qpts_1d, + j, 0, num_qpts_1d, { - // Save the basis value at the point to a temp array and zero out the temp array - for (int i = 0; i < num_dofs_1d; i++) { - val_3d(i, 2) = val_1d(i); - val_1d(i) = 0.0; - } + const size_t rid=get_qpt_rid(j, k, num_qpts_1d); - // Multiply the i, j, k components of the basis and partial_xi from each node - // to get the tensor product partial derivatives of the basis at each node - for (int k = 0; k < num_dofs_1d; k++) { - for (int j = 0; j < num_dofs_1d; j++) { - for (int i = 0; i < num_dofs_1d; i++) { - int dof_rlid = dof_rid(i, j, k); + qpt_positions(face, rid, 0) = 1.; + qpt_positions(face, rid, 1) = qpt_positions_1d(j); + qpt_positions(face, rid, 2) = qpt_positions_1d(k); - // Partial w.r.t xi - partial_xi(dof_rlid) = Dval_3d(i, 0) * val_3d(j, 1) * val_3d(k, 2); - } - } - } + qpt_weights(face,rid) = qpt_weights_1d(j) * qpt_weights_1d(k); - for (int i = 0; i < num_dofs_1d; i++) { - val_1d(i) = 0.0; - val_3d(i, 0) = 0.0; - val_3d(i, 1) = 0.0; - val_3d(i, 2) = 0.0; - Dval_1d(i) = 0.0; - Dval_3d(i, 0) = 0.0; - Dval_3d(i, 1) = 0.0; - Dval_3d(i, 2) = 0.0; - } - } + }); // end for - ///////////////////////////////////////////////////////////////////////////// - /// - /// \fn partial_eta_basis - /// - /// \brief Computes the partial derivative of the Lagrange basis functions with respect to eta at a given point. - /// - /// This function evaluates the partial derivative of the tensor-product Lagrange basis - /// functions with respect to the eta (second) coordinate at a given point in the reference element. - /// It builds up the 3D basis and its derivatives using repeated calls to 1D basis and derivative - /// evaluations, and computes the tensor-product forms to populate the provided partial_eta array - /// with the values of the partials at every reference node. - /// - /// \param partial_eta Output array to store the partial derivatives with respect to eta at each basis node. - /// \param val_1d Temporary array used for storing 1D basis values during calculation. - /// \param val_3d Temporary 2D array used for storing intermediate 3D basis values. - /// \param Dval_1d Temporary array used for storing 1D derivative values during calculation. - /// \param Dval_3d Temporary 2D array used for storing intermediate 3D derivative values. - /// \param point Input array representing the coordinates (xi, eta, zeta) at which to evaluate the derivative. - /// - /// \return void - /// - ///////////////////////////////////////////////////////////////////////////// - KOKKOS_FUNCTION - void partial_eta_basis(const CArrayKokkos& partial_eta, - const CArrayKokkos& val_1d, - const CArrayKokkos& val_3d, - const CArrayKokkos& Dval_1d, - const CArrayKokkos& Dval_3d, - const CArrayKokkos& point) const - { - // initialize// - for (int i = 0; i < num_dofs_1d; i++) { - val_1d(i) = 0.0; - Dval_1d(i) = 0.0; - } + // eta-minus has coords (xi, -1, mu) + face = 2; + FOR_ALL_CLASS(k, 0, num_qpts_1d, + i, 0, num_qpts_1d, { - // Calculate 1D basis for the Y coordinate of the point - lagrange_basis_1D(val_1d, point(0)); + const size_t rid=get_qpt_rid(i, k, num_qpts_1d); - // Save the basis value at the point to a temp array and zero out the temp array - for (int i = 0; i < num_dofs_1d; i++) { - val_3d(i, 0) = val_1d(i); - val_1d(i) = 0.0; - } + qpt_positions(face, rid, 0) = qpt_positions_1d(i); + qpt_positions(face, rid, 1) = -1.; + qpt_positions(face, rid, 2) = qpt_positions_1d(k); - // Calculate 1D partial w.r.t. eta for the Y coordinate of the point - lagrange_derivative_1D(Dval_1d, point(1)); + qpt_weights(face,rid) = qpt_weights_1d(i) * qpt_weights_1d(k); - // Save the basis value at the point to a temp array and zero out the temp array - for (int i = 0; i < num_dofs_1d; i++) { - Dval_3d(i, 1) = Dval_1d(i); + }); // end for - Dval_1d(i) = 0.0; - } + // eta-plus has coords (xi,+1, mu) + face = 3; + FOR_ALL_CLASS(k, 0, num_qpts_1d, + i, 0, num_qpts_1d, { - // Calculate 1D basis for the Z coordinate of the point - lagrange_basis_1D(val_1d, point(2)); + const size_t rid=get_qpt_rid(i, k, num_qpts_1d); - // Save the basis value at the point to a temp array and zero out the temp array - for (int i = 0; i < num_dofs_1d; i++) { - val_3d(i, 2) = val_1d(i); - val_1d(i) = 0.0; - } + qpt_positions(face, rid, 0) = qpt_positions_1d(i); + qpt_positions(face, rid, 1) = 1.; + qpt_positions(face, rid, 2) = qpt_positions_1d(k); - // Multiply the i, j, k components of the basis and partial_eta from each node - // to get the tensor product partial derivatives of the basis at each node - for (int k = 0; k < num_dofs_1d; k++) { - for (int j = 0; j < num_dofs_1d; j++) { - for (int i = 0; i < num_dofs_1d; i++) { - int dof_rlid = dof_rid(i, j, k); + qpt_weights(face,rid) = qpt_weights_1d(i) * qpt_weights_1d(k); - // Partial w.r.t xi - partial_eta(dof_rlid) = val_3d(i, 0) * Dval_3d(j, 1) * val_3d(k, 2); - } + }); // end for + + + // mu-minus has coords (xi, eta, -1) + face = 4; + FOR_ALL_CLASS(j, 0, num_qpts_1d, + i, 0, num_qpts_1d, { + + const size_t rid=get_qpt_rid(i, j, num_qpts_1d); + + qpt_positions(face, rid, 0) = qpt_positions_1d(i); + qpt_positions(face, rid, 1) = qpt_positions_1d(j); + qpt_positions(face, rid, 2) = -1.; + + qpt_weights(face,rid) = qpt_weights_1d(i) * qpt_weights_1d(j); + + }); // end for + + // mu-plus has coords (xi, eta, +1) + face = 5; + FOR_ALL_CLASS(j, 0, num_qpts_1d, + i, 0, num_qpts_1d, { + + const size_t rid=get_qpt_rid(i, j, num_qpts_1d); + + qpt_positions(face, rid, 0) = qpt_positions_1d(i); + qpt_positions(face, rid, 1) = qpt_positions_1d(j); + qpt_positions(face, rid, 2) = 1.; + + qpt_weights(face,rid) = qpt_weights_1d(i) * qpt_weights_1d(j); + + }); // end for + } - } + // surface of 2D element (line) + else if (elem_dims==2){ - for (int i = 0; i < num_dofs_1d; i++) { - val_1d(i) = 0.0; - val_3d(i, 0) = 0.0; - val_3d(i, 1) = 0.0; - val_3d(i, 2) = 0.0; - Dval_1d(i) = 0.0; - Dval_3d(i, 0) = 0.0; - Dval_3d(i, 1) = 0.0; - Dval_3d(i, 2) = 0.0; - } - } + size_t face; - ///////////////////////////////////////////////////////////////////////////// - /// - /// \fn partial_mu_basis - /// - /// \brief Compute the tensor-product partial derivative of the basis functions - /// with respect to the mu (z) coordinate at a given point in the reference element. - /// - /// This function calculates the partial derivatives of the high-order - /// tensor-product Lagrange basis functions with respect to the mu (z) coordinate - /// at a specified point in the reference element, using their 1D basis and derivative - /// representations. The result is stored in the provided array. Temporary arrays - /// for 1D and 3D basis/derivative values are passed in for workspace re-use. - /// - /// \param partial_mu Array to store the computed partial derivatives with respect to mu. - /// \param val_1d Workspace array for 1D Lagrange basis evaluations. - /// \param val_3d Workspace array for 3D tensor-product basis evaluations. - /// \param Dval_1d Workspace array for 1D Lagrange basis derivatives. - /// \param Dval_3d Workspace array for 3D tensor-product basis derivatives. - /// \param point Coordinates in the reference element where the basis partials are evaluated. - /// - /// \return void - /// - ///////////////////////////////////////////////////////////////////////////// - KOKKOS_FUNCTION - void partial_mu_basis(const CArrayKokkos& partial_mu, - const CArrayKokkos& val_1d, - const CArrayKokkos& val_3d, - const CArrayKokkos& Dval_1d, - const CArrayKokkos& Dval_3d, - const CArrayKokkos& point) const - { - // initialize// - for (int i = 0; i < num_dofs_1d; i++) { - val_1d(i) = 0.0; - Dval_1d(i) = 0.0; - } + // xi-minus has coords (-1, eta) + face = 0; + FOR_ALL_CLASS(j, 0, num_qpts_1d, { + const size_t rid=j; - // Calculate 1D basis for the X coordinate of the point - lagrange_basis_1D(val_1d, point(0)); + qpt_positions(face, rid, 0) = -1.; + qpt_positions(face, rid, 1) = qpt_positions_1d(j); - // Save the basis value at the point to a temp array and zero out the temp array - for (int i = 0; i < num_dofs_1d; i++) { - val_3d(i, 0) = val_1d(i); - val_1d(i) = 0.0; - } + qpt_weights(face,rid) = qpt_weights_1d(j); - // Calculate 1D basis for the Y coordinate of the point - lagrange_basis_1D(val_1d, point(1)); + }); // end for + + // xi-plus has coords (+1, eta) + face = 1; + FOR_ALL_CLASS(j, 0, num_qpts_1d, { + const size_t rid=j; - // Save the basis value at the point to a temp array and zero out the temp array - for (int i = 0; i < num_dofs_1d; i++) { - val_3d(i, 1) = val_1d(i); - val_1d(i) = 0.0; - } + qpt_positions(face, rid, 0) = 1.; + qpt_positions(face, rid, 1) = qpt_positions_1d(j); - // Calculate 1D partial w.r.t. mu for the Z coordinate of the point - lagrange_derivative_1D(Dval_1d, point(2)); + qpt_weights(face,rid) = qpt_weights_1d(j); - // Save the basis value at the point to a temp array and zero out the temp array - for (int i = 0; i < num_dofs_1d; i++) { - Dval_3d(i, 2) = Dval_1d(i); - Dval_1d(i) = 0.0; - } + }); // end for - // Multiply the i, j, k components of the basis and partial_xi from each node - // to get the tensor product partial derivatives of the basis at each node - for (int k = 0; k < num_dofs_1d; k++) { - for (int j = 0; j < num_dofs_1d; j++) { - for (int i = 0; i < num_dofs_1d; i++) { - int dof_rlid = dof_rid(i, j, k); + // eta-minus has coords (xi, -1) + face = 2; + FOR_ALL_CLASS(i, 0, num_qpts_1d, { + const size_t rid=i; + + qpt_positions(face, rid, 0) = qpt_positions_1d(i); + qpt_positions(face, rid, 1) = -1.; + + qpt_weights(face,rid) = qpt_weights_1d(i); + + }); // end for + + + // eta-plus has coords (xi,+1) + face = 3; + FOR_ALL_CLASS(i, 0, num_qpts_1d, { + const size_t rid=i; + + qpt_positions(face, rid, 0) = qpt_positions_1d(i); + qpt_positions(face, rid, 1) = 1.; + + qpt_weights(face,rid) = qpt_weights_1d(i); + + }); // end for - // Partial w.r.t mu - partial_mu(dof_rlid) = val_3d(i, 0) * val_3d(j, 1) * Dval_3d(k, 2); - } } - } + // surface of 1D element is a point + else if (elem_dims==1) { + size_t face = 0; + const size_t rid = 0; + qpt_positions(face, rid, 0) = -1.; + qpt_weights(face,rid) = 1.0; + + face = 1; + qpt_positions(face, rid, 0) = 1.; + qpt_weights(face,rid) = 1.0; + } + else{ + throw std::runtime_error("ERROR: unsupported quadrature surface dims \n"); + } - for (int i = 0; i < num_dofs_1d; i++) { - val_1d(i) = 0.0; - val_3d(i, 0) = 0.0; - val_3d(i, 1) = 0.0; - val_3d(i, 2) = 0.0; - Dval_1d(i) = 0.0; - Dval_3d(i, 0) = 0.0; - Dval_3d(i, 1) = 0.0; - Dval_3d(i, 2) = 0.0; - } - } + } // init fcn quadrature -// KOKKOS_FUNCTION -// void get_bernstein_basis(const CArrayKokkos &elem_basis, -// const CArrayKokkos &elem_val_1d, -// const CArrayKokkos &elem_val_3d, -// const CArrayKokkos &point) const { - -// // initialize to zero // -// for (int i =0; i< num_dg_dofs_1d; i++){ -// elem_val_1d(i) = 0.0; -// } - -// // Calculate 1D basis for the X coordinate of the point -// bernstein_basis_1D(elem_val_1d, point(0)); - -// // Save the basis value at the point to a temp array and zero out the temp array -// for(int i = 0; i < num_dg_dofs_1d; i++){ -// elem_val_3d(i,0) = elem_val_1d(i); -// elem_val_1d(i) = 0.0; -// } - -// // Calculate 1D basis for the Y coordinate of the point -// bernstein_basis_1D(elem_val_1d, point(1)); - -// // Save the basis value at the point to a temp array and zero out the temp array -// for(int i = 0; i < num_dg_dofs_1d; i++){ -// elem_val_3d(i,1) = elem_val_1d(i); -// elem_val_1d(i) = 0.0; -// } - -// // Calculate 1D basis for the Z coordinate of the point -// bernstein_basis_1D(elem_val_1d, point(2)); - -// // Save the basis value at the point to a temp array and zero out the temp array -// for(int i = 0; i < num_dg_dofs_1d; i++){ -// elem_val_3d(i,2) = elem_val_1d(i); -// elem_val_1d(i) = 0.0; -// } - -// // Multiply the i, j, k components of the basis from each node -// // to get the tensor product basis for the node -// for(int k = 0; k < num_dg_dofs_1d; k++){ -// for(int j = 0; j < num_dg_dofs_1d; j++){ -// for(int i = 0; i < num_dg_dofs_1d; i++){ - -// int dof_rlid = elem_dof_rid(i,j,k); -// elem_basis(dof_rlid) = elem_val_3d(i,0)*elem_val_3d(j,1)*elem_val_3d(k,2); -// } -// } -// } - -// for (int i =0; i< num_dg_dofs_1d; i++){ -// elem_val_1d(i) = 0.0; -// elem_val_3d(i,0) = 0.0; -// elem_val_3d(i,1) = 0.0; -// elem_val_3d(i,2) = 0.0; -// } -// } - ///////////////////////////////////////////////////////////////////////////// - /// - /// \fn lagrange_basis_1D - /// - /// \brief Computes the Lagrange basis functions in 1D at a given point. - /// - /// This function evaluates the values of the 1D Lagrange basis functions at a specified point - /// within the reference element, using the nodal positions. For each basis node, it computes - /// the interpolation value (the product over all other node positions) and stores - /// the result in the provided array. - /// - /// \param interp Output array to store the value of each basis function at the specified point. - /// \param x_point Point at which to evaluate the basis functions. - /// - /// \return void - /// - ///////////////////////////////////////////////////////////////////////////// + }; // end struct - KOKKOS_FUNCTION - void lagrange_basis_1D( - const CArrayKokkos& interp, // interpolant from each basis - const double x_point) const // point of interest in element - // calculate the basis value associated with each node_i + + // reference surfaces data structure + struct ReferenceSurface_t { - for (int vert_i = 0; vert_i < num_dofs_1d; vert_i++) { - double numerator = 1.0; // placeholder numerator - double denominator = 1.0; // placeholder denominator - double interpolant = 1.0; // placeholder value of numerator/denominator + size_t elem_dims = 0; + + // Basis evaluation at quadrature points on surface + CArrayKokkos qpt_basis; // access as (faces, surf_qpts, dofs) + CArrayKokkos qpt_grad_basis; // access as (faces, surf_qpts, dofs, dims) - for (int vert_j = 0; vert_j < num_dofs_1d; vert_j++) { // looping over the verts !=vert_i - if (vert_j != vert_i) { - // Calculate the numerator - numerator = numerator * (x_point - dof_positions_1d(vert_j)); + CArrayKokkos outward_sign; + CArrayKokkos outward_normal; - // Calculate the denominator - denominator = denominator * (dof_positions_1d(vert_i) - dof_positions_1d(vert_j)); - } // end if + + void initialize_ref_surf(const struct SurfaceQuadrature_t& SurfQuadrature, + const struct ReferenceElement_t& ReferenceElement) + { - interpolant = numerator / denominator; // storing a single value for interpolation for node vert_i - } // end looping over nodes != vert_i + elem_dims = SurfQuadrature.elem_dims; - // writing value to vectors for later use - interp(vert_i) = interpolant; // Interpolant value at given point - } // end loop over all nodes - } // end of Lagrange_1D function + // shorten the var names + const size_t num_ref_surfs = SurfQuadrature.num_ref_surfs; + const size_t num_qpts_in_surf = SurfQuadrature.num_qpts_in_surf; + const size_t num_dofs_in_elem = ReferenceElement.num_dofs_in_elem; - ///////////////////////////////////////////////////////////////////////////// - /// - /// \fn lagrange_elem_basis_1D - /// - /// \brief Computes the values of the element-based (e.g. DG) 1D Lagrange basis functions at a given point. - /// - /// This function evaluates the values of the 1D Lagrange basis functions associated with the element's - /// degrees of freedom (typically for a DG basis) at a specified point within the reference element. - /// For each basis node, it computes the basis value as the Lagrange interpolant using the nodal positions - /// specific to the DG basis, and stores the results in the provided array. - /// - /// \param interp Output array to store the value of each element-based 1D basis function at the specified point. - /// \param x_point Point at which to evaluate the element-based basis functions. - /// - /// \return void - /// - ///////////////////////////////////////////////////////////////////////////// - KOKKOS_FUNCTION - void lagrange_elem_basis_1D( - const CArrayKokkos& interp, // interpolant from each basis - const double x_point) const // point of interest in element - // calculate the basis value associated with each node_i - { - for (int vert_i = 0; vert_i < num_dg_dofs_1d; vert_i++) { - double numerator = 1.0; // placeholder numerator - double denominator = 1.0; // placeholder denominator - double interpolant = 1.0; // placeholder value of numerator/denominator + if(elem_dims==0) throw std::runtime_error("ERROR: quadrature not correctly specified \n"); + if(elem_dims>3) throw std::runtime_error("ERROR: only 1D, 2D, and 3D reference elements supported \n"); - for (int vert_j = 0; vert_j < num_dg_dofs_1d; vert_j++) { // looping over the verts !=vert_i - if (vert_j != vert_i) { - // Calculate the numerator - numerator = numerator * (x_point - dg_dof_positions_1d(vert_j)); + qpt_basis = CArrayKokkos(num_ref_surfs, num_qpts_in_surf, num_dofs_in_elem, "surf_qpt_basis"); + qpt_grad_basis = CArrayKokkos(num_ref_surfs, num_qpts_in_surf, num_dofs_in_elem, elem_dims, "surf_qpt_grad_basis"); - // Calculate the denominator - denominator = denominator * (dg_dof_positions_1d(vert_i) - dg_dof_positions_1d(vert_j)); - } // end if + // the sign for outward normal relative to the reference element + outward_sign = CArrayKokkos(num_ref_surfs, "surf_outward_sign"); + outward_normal = CArrayKokkos(num_ref_surfs, elem_dims, "surf_outward_normal"); - interpolant = numerator / denominator; // storing a single value for interpolation for node vert_i - } // end looping over nodes != vert_i + //face 0 (xi=-1): sign = -1 + //face 1 (xi=+1): sign = +1 + //face 2 (eta=-1): sign = -1 + //face 3 (eta=+1): sign = +1 + //face 4 (mu=-1): sign = -1 + //face 5 (mu=+1): sign = +1 + RUN_CLASS({ + outward_sign(0) = -1.; + outward_sign(1) = 1.; - // writing value to vectors for later use - interp(vert_i) = interpolant; // Interpolant value at given point - } // end loop over all nodes - } // end of Lagrange_1D function + if(elem_dims>1){ + outward_sign(2) = -1.; + outward_sign(3) = 1.; + } + if(elem_dims>2){ + outward_sign(4) = -1.; + outward_sign(5) = 1.; + } + + if(elem_dims==1){ + outward_normal(0,0) = -1.; // face 0 xi- + outward_normal(1,0) = 1.; // face 1 xi+ + } + if(elem_dims==2){ + outward_normal(0,0) = -1.; // face 0 xi- + outward_normal(0,1) = 0.; // face 0 xi- - ///////////////////////////////////////////////////////////////////////////// - /// - /// \fn lagrange_derivative_1D - /// - /// \brief Computes the values of the derivatives of the 1D Lagrange basis functions at a given point. - /// - /// This function evaluates the first derivatives of the 1D Lagrange basis functions associated - /// with the element's degrees of freedom at a specified point within the reference element. - /// For each basis node, it computes the derivative of the basis function using the nodal - /// positions and stores the results in the provided array. - /// - /// \param derivative Output array to store the value of each 1D basis function derivative at the given point. - /// \param x_point Point at which to evaluate the derivatives of the basis functions. - /// - /// \return void - /// - ///////////////////////////////////////////////////////////////////////////// - KOKKOS_INLINE_FUNCTION - void lagrange_derivative_1D( - const CArrayKokkos& derivative, // derivative - const double x_point) const // point of interest in element - { - for (int vert_i = 0; vert_i < num_dofs_1d; vert_i++) { // looping over the nodes - double denominator = 1.0; // placeholder denominator - double num_gradient = 0.0; // placeholder for numerator of the gradient - double gradient = 0.0; + outward_normal(1,0) = 1.; // face 1 xi+ + outward_normal(1,1) = 0.; // face 1 xi+ - for (int vert_j = 0; vert_j < num_dofs_1d; vert_j++) { // looping over the nodes !=vert_i - if (vert_j != vert_i) { - // Calculate the denominator that is the same for - // both the basis and the gradient of the basis - denominator = denominator * (dof_positions_1d(vert_i) - dof_positions_1d(vert_j)); + outward_normal(2,0) = 0.; // face 2 eta- + outward_normal(2,1) = -1.; // face 2 eta- - double product_gradient = 1.0; + outward_normal(3,0) = 0.; // face 3 eta+ + outward_normal(3,1) = 1.; // face 3 eta+ + } + if(elem_dims==3){ + outward_normal(0,0) = -1.; // face 0 xi- + outward_normal(0,1) = 0.; // face 0 xi- + outward_normal(0,2) = 0.; // face 0 xi- + + outward_normal(1,0) = 1.; // face 1 xi+ + outward_normal(1,1) = 0.; // face 1 xi+ + outward_normal(1,2) = 0.; // face 1 xi+ + + outward_normal(2,0) = 0.; // face 2 eta- + outward_normal(2,1) = -1.; // face 2 eta- + outward_normal(2,2) = 0.; // face 2 eta- + + outward_normal(3,0) = 0.; // face 3 eta+ + outward_normal(3,1) = 1.; // face 3 eta+ + outward_normal(3,2) = 0.; // face 3 eta+ + + outward_normal(4,0) = 0.; // face 4 mu- + outward_normal(4,1) = 0.; // face 4 mu- + outward_normal(4,2) = -1.; // face 4 mu- + + outward_normal(5,0) = 0.; // face 5 mu+ + outward_normal(5,1) = 0.; // face 5 mu+ + outward_normal(5,2) = 1.; // face 5 mu+ + } - // Calculate the numerator of the gradient - for (int N = 0; N < num_dofs_1d; N++) { // looping over the nodes !=vert_i - if (N != vert_j && N != vert_i) { - product_gradient = product_gradient * (x_point - dof_positions_1d(N)); - } // end if - } // end for + }); // end serial RUN on GPU - // Sum over the product of the numerator - // contributions from each node - num_gradient += product_gradient; - } // end if + // Notes on arrays + // RS.qpt_basis = CArrayKokkos (num_ref_surfs, num_qpts_in_surf, num_dofs_in_elem); + // RS.qpt_grad_basis = CArrayKokkos (num_ref_surfs, num_qpts_in_surf, num_dofs_in_elem, elem_dims); + // SQ.qpt_positions = CArrayKokkos (num_ref_surfs, num_qpts_in_surf, elem_dims); - gradient = (num_gradient / denominator); // storing the derivative of the interpolating function - } // end looping over nodes != vert_i + // The face qpt values + CArrayKokkos face_qpt_basis(num_qpts_in_surf, num_dofs_in_elem); + CArrayKokkos face_qpt_grad_basis(num_qpts_in_surf, num_dofs_in_elem, elem_dims); + CArrayKokkos face_qpt_positions(num_qpts_in_surf, elem_dims); - // writing value to vectors for later use - derivative(vert_i) = gradient; // derivative of each function - } // end loop over all nodes - } // end of Lagrange_1D function + // get the basis and grad basis functions for each surfaces of the element + for(size_t face=0; face &interp, -// const double X) const { + // build basis and grad basis in the reference element + FOR_ALL_CLASS(qpt, 0, num_qpts_in_surf, { + for(size_t dim=0; dim& lob_nodes_1D, - const int& num) + const size_t& num) { if (num == 1) { lob_nodes_1D(0) = 0.0; @@ -320,7 +320,7 @@ static void get_lobatto_nodes_1D(const CArrayKokkos& lob_nodes_1D, KOKKOS_FUNCTION static void get_lobatto_weights_1D( const CArrayKokkos& lob_weights_1D, // Lobbatto weights - const int& num) // Interpolation order + const size_t& num) // Interpolation order { if (num == 1) { lob_weights_1D(0) = 2.0; @@ -593,7 +593,7 @@ static void get_lobatto_weights_1D( KOKKOS_FUNCTION static void get_legendre_nodes_1D( const CArrayKokkos& leg_nodes_1D, - const int& num) + const size_t& num) { if (num == 1) { leg_nodes_1D(0) = 0.0; @@ -844,7 +844,7 @@ static void get_legendre_nodes_1D( KOKKOS_FUNCTION static void get_legendre_weights_1D( const CArrayKokkos& leg_weights_1D, // Legendre weights - const int& num) // Interpolation order + const size_t& num) // Interpolation order { if (num == 1) { leg_weights_1D(0) = 2.0; diff --git a/src/elements/ref_surf_elem.h b/src/elements/ref_surf_elem.h deleted file mode 100644 index 9b159f92..00000000 --- a/src/elements/ref_surf_elem.h +++ /dev/null @@ -1,2124 +0,0 @@ -#ifndef REFSURFELEM_H -#define REFSURFELEM_H - -#include -#include "matar.h" - - -namespace elements -{ - -using namespace mtr; - -// Constructs kinematic and thermodynamic basis functions in the element. -// Kinematic basis will be referenced as basis -// Thermodynamic basis is referenced as elem_basis, since the thermodynamic quantities are internal to the elements - -struct fe_ref_surf_t{ - -#if 0 - size_t num_dim; - - // Kinematic Dofs - size_t num_dofs_1d; - size_t num_dofs_in_surf; - - // Thermodynamic Dofs - size_t num_elem_dofs_1d; - size_t num_elem_dofs_in_surf; - - // // Gauss Points - size_t num_gauss_lob_1d; - size_t num_gauss_lob_in_surf; - - size_t num_gauss_leg_1d; - size_t num_gauss_leg_in_surf; - - //_patch - size_t num_patch_1d; - size_t num_patch_in_elem; - - // Num basis functions - // kinematic - size_t num_basis; - // thermodynamic - size_t num_elem_basis; - - // Kinematic basis evaluation at nodes - CArrayKokkos gauss_lob_basis; - CArrayKokkos gauss_leg_basis; - - // Thermodynamic basis evaluation at nodes - CArrayKokkos gauss_lob_elem_basis; - CArrayKokkos gauss_leg_elem_basis; - - // Gradient of basis - // CArrayKokkos gauss_lob_grad_basis; - CArrayKokkos gauss_leg_grad_basis; - - // Gauss and DOF positions - CArrayKokkos lob_nodes_1D; - CArrayKokkos leg_nodes_1D; - - CArrayKokkos gauss_lob_positions; - CArrayKokkos gauss_leg_positions; - CArrayKokkos gauss_leg_surf_positions; - - CArrayKokkos dof_positions; - CArrayKokkos dof_positions_1d; - - CArrayKokkos elem_dof_positions; - CArrayKokkos elem_dof_positions_1d; - - // Quadrature Weights - CArrayKokkos lob_weights_1D; - CArrayKokkos leg_weights_1D; - - CArrayKokkos gauss_lob_weights; - CArrayKokkos gauss_leg_weights; - CArrayKokkos gauss_leg_surf_weights; - - - void init(int p_order, int num_dim_inp){ - - num_dim = num_dim_inp-1; - - if(p_order == 0){ - - num_gauss_lob_1d = 2; // num gauss lobatto points in 1d - num_gauss_leg_1d = 1; - num_dofs_1d = 2; - num_elem_dofs_1d = 1;// This is for the e.g. thermodynamic basis - num_patch_1d = 1; - num_patch_in_surf = num_patch_1d*num_patch_1d; - - } - else{ - - num_gauss_lob_1d = 2 * p_order + 1; // num gauss lobatto points in 1d - num_gauss_leg_1d = 2*p_order; - - num_dofs_1d = p_order+1; - num_elem_dofs_1d = p_order; - - num_patch_1d = (num_dofs_1d - 1) / 2; - num_patch_in_elem = num_patch_1d*num_patch_1d; - - } - - num_gauss_lob_in_surf = 1; - - num_gauss_leg_in_surf = 1; - - //num_dofs_in_surf = 1; - - num_dofs_in_surf = 1; - - // WARNING WARNING: go back and change thermo basis names to patch or zones - num_elem_dofs_in_surf = 1; - - for (int dim = 0; dim < num_dim; dim++){ - - num_gauss_lob_in_surf *= num_gauss_lob_1d; - num_gauss_leg_in_surf *= num_gauss_leg_1d; - - num_dofs_in_surf *= num_dofs_1d; - num_elem_dofs_in_surf *= num_elem_dofs_1d; - - } - - // keeping both for now. being able to call ref_elem.num_basis is convenient for computations. // - num_basis = num_dofs_in_surf; - num_elem_basis = num_elem_dofs_in_surf; - - // allocate memory - dof_positions = CArrayKokkos (num_dofs_in_surf, num_dim, "dof_positions"); - dof_positions_1d = CArrayKokkos (num_dofs_1d, "dof_positions_1d"); - - elem_dof_positions = CArrayKokkos (num_elem_dofs_in_surf, num_dim, "elem_dof_positions"); - elem_dof_positions_1d = CArrayKokkos (num_elem_dofs_1d, "elem_dof_positions_1d"); - - gauss_lob_weights = CArrayKokkos (num_gauss_lob_in_surf, "gauss_lob_weights"); - gauss_leg_weights = CArrayKokkos (num_gauss_leg_in_surf, "gauss_leg_weights"); - - // Memory for gradients - gauss_lob_grad_basis = CArrayKokkos (num_gauss_lob_in_surf, num_basis, num_dim, "gauss_lob_grad_basis"); - gauss_leg_grad_basis = CArrayKokkos (num_gauss_leg_in_surf, num_basis, num_dim, "gauss_leg_grad_basis"); - - // Basis evaluation at the nodes - gauss_lob_basis = CArrayKokkos (num_gauss_lob_in_surf, num_basis, "gauss_lob_basis"); - gauss_leg_basis = CArrayKokkos (num_gauss_leg_in_surf, num_basis, "gauss_leg_basis"); - - gauss_lob_elem_basis = CArrayKokkos (num_gauss_lob_in_surf, num_elem_basis, "gauss_lob_elem_basis"); - gauss_leg_elem_basis = CArrayKokkos (num_gauss_leg_in_surf, num_elem_basis, "gauss_leg_elem_basis"); - - gauss_lob_positions = CArrayKokkos (num_gauss_lob_in_surf, num_dim,"gauss_lob_positions"); - gauss_leg_positions = CArrayKokkos (num_gauss_leg_in_surf, num_dim,"gauss_leg_positions"); - - // gauss_leg_surf_positions = CArrayKokkos (num_surfs, num_gauss_leg_in_surfs, num_dim-1, "gauss_leg_surf_positions"); - // gauss_leg_surf_weights = CArrayKokkos (num_surfs, num_gauss_leg_in_surfs, "gauss_leg_surf_weights"); - - // --- build gauss nodal positions and weights --- - - lob_nodes_1D = CArrayKokkos (num_gauss_lob_1d,"lob_nodes_1d"); - - RUN_CLASS({ - lobatto_nodes_1D( lob_nodes_1D, num_gauss_lob_1d); - }); - - lob_weights_1D = CArrayKokkos (num_gauss_lob_1d,"lob_weights_1d"); - RUN_CLASS({ - lobatto_weights_1D(lob_weights_1D, num_gauss_lob_1d); - }); - - leg_nodes_1D = CArrayKokkos (num_gauss_leg_1d,"leg_nodes_1d"); - RUN_CLASS({ - legendre_nodes_1D(leg_nodes_1D, num_gauss_leg_1d); - }); - - leg_weights_1D = CArrayKokkos (num_gauss_leg_1d,"leg_weights_1d"); - RUN_CLASS({ - legendre_weights_1D(leg_weights_1D, num_gauss_leg_1d); - }); - - FOR_ALL_CLASS( j, 0, num_gauss_lob_1d, - i, 0, num_gauss_lob_1d, { - - int lob_rid = lobatto_rid_2D(i,j,k); - - gauss_lob_positions(lob_rid,0) = lob_nodes_1D(i); - gauss_lob_positions(lob_rid,1) = lob_nodes_1D(j); - - gauss_lob_weights(lob_rid) = lob_weights_1D(i)*lob_weights_1D(j); - }); - Kokkos::fence(); - - - FOR_ALL_CLASS( j, 0, num_gauss_leg_1d, - i, 0, num_gauss_leg_1d, { - - int leg_rid = legendre_rid_2D(i,j,k); - - //printf(" leg_node_1D value = %f \n", leg_nodes_1D(i) ); - gauss_leg_positions(leg_rid,0) = leg_nodes_1D(i); - gauss_leg_positions(leg_rid,1) = leg_nodes_1D(j); - //printf(" leg_weight: %f \n", leg_weights_1D(i)); - gauss_leg_weights(leg_rid) = leg_weights_1D(i)*leg_weights_1D(j); - }); - Kokkos::fence(); - - // surface quadrature i faces - // FOR_ALL_CLASS( k, 0, num_gauss_leg_1d, - // j, 0, num_gauss_leg_1d, { - - // int leg_rid = legendre_rid_2D(j,k); - - // // i min - // gauss_leg_surf_positions(0, leg_rid,0) = leg_nodes_1D(j); - // gauss_leg_surf_positions(0, leg_rid,1) = leg_nodes_1D(k); - // gauss_leg_weights(0,leg_rid) = leg_weights_1D(j)*leg_weights_1D(k); - - // // i max - // gauss_leg_surf_positions(1, leg_rid,0) = leg_nodes_1D(j); - // gauss_leg_surf_positions(1, leg_rid,1) = leg_nodes_1D(k); - // gauss_leg_weights(1,leg_rid) = leg_weights_1D(j)*leg_weights_1D(k); - // }); - // Kokkos::fence(); - - // // surface quadrature j faces - // FOR_ALL_CLASS( i, 0, num_gauss_leg_1d, - // k, 0, num_gauss_leg_1d, { - - // int leg_rid = legendre_rid_2D(j,k); - - // // j min - // gauss_leg_surf_positions(2, leg_rid,0) = leg_nodes_1D(i); - // gauss_leg_surf_positions(2, leg_rid,1) = leg_nodes_1D(k); - // gauss_leg_weights(2,leg_rid) = leg_weights_1D(j)*leg_weights_1D(k); - - // // j max - // gauss_leg_surf_positions(3, leg_rid,0) = leg_nodes_1D(i); - // gauss_leg_surf_positions(3, leg_rid,1) = leg_nodes_1D(k); - // gauss_leg_weights(3,leg_rid) = leg_weights_1D(i)*leg_weights_1D(k); - // }); - // Kokkos::fence(); - - // // surface quadrature k faces - // FOR_ALL_CLASS(i , 0, num_gauss_leg_1d, - // j, 0, num_gauss_leg_1d, { - - // int leg_rid = legendre_rid_2D(i,j); - - // // k min - // gauss_leg_surf_positions(4, leg_rid,0) = leg_nodes_1D(i); - // gauss_leg_surf_positions(4, leg_rid,1) = leg_nodes_1D(j); - // gauss_leg_weights(4,leg_rid) = leg_weights_1D(i)*leg_weights_1D(j); - - // // k max - // gauss_leg_surf_positions(5, leg_rid,0) = leg_nodes_1D(i); - // gauss_leg_surf_positions(5, leg_rid,1) = leg_nodes_1D(j); - // gauss_leg_weights(5,leg_rid) = leg_weights_1D(i)*leg_weights_1D(j); - // }); - // Kokkos::fence(); - - // Saving vertex positions in 1D - if( p_order == 0){ - // dofs same as lobatto quadrature points - FOR_ALL_CLASS(i, 0, num_gauss_lob_1d,{ - dof_positions_1d(i) = lob_nodes_1D(i); - }); - } - - else{ - - RUN_CLASS({ - int dof_id = 0; - - for(int i = 0; i < num_gauss_lob_1d; i=i+2){ - - dof_positions_1d(dof_id) = lob_nodes_1D(i); - - dof_id++; - } - }); - } - Kokkos::fence(); - - FOR_ALL_CLASS(num_j, 0, num_dofs_1d, - num_i, 0, num_dofs_1d, { - - int dof_rlid = dof_rid_2D(num_i, num_j); - - dof_positions(dof_rlid, 0) = dof_positions_1d(num_i); - dof_positions(dof_rlid, 1) = dof_positions_1d(num_j); - }); - Kokkos::fence(); - - // basis and grad basis evaluations done at points // - - // temp variables hold evaluations at a single point for each dof // - CArrayKokkos temp_nodal_basis(num_dofs_in_surf); - CArrayKokkos temp_elem_basis(num_elem_dofs_in_surf); - - CArrayKokkos val_1d(num_dofs_1d); - CArrayKokkos val_2d(num_dofs_1d, 2); - - CArrayKokkos elem_val_1d(num_elem_dofs_1d); - CArrayKokkos elem_val_2d(num_elem_dofs_1d, 2); - - CArrayKokkos point(2); - - //printf(" num_dofs = %d \n", num_dofs_in_surf ); - - // //--- evaluate the basis at the lobatto positions - // FOR_ALL_CLASS(gauss_lob_rid, 0, num_gauss_lob_in_surf, { - - // // Get the nodal coordinates - // for(int dim = 0; dim < 3; dim++){ - // point(dim) = gauss_lob_positions(gauss_lob_rid, dim); - // //printf(" point value = %f \n", point(dim) ); - - // } - - // get_basis(temp_nodal_basis, val_1d, val_2d, point); - // double check_basis = 0.0; - - // for(int basis_id = 0; basis_id < num_dofs_in_surf; basis_id++){ - // //printf(" computed basis value = %f \n", temp_nodal_basis(basis_id) ); - // gauss_lob_basis(gauss_lob_rid, basis_id) = temp_nodal_basis(basis_id); - // //check_basis += temp_nodal_basis(basis_id); - // temp_nodal_basis(basis_id) = 0.0; - // } - // //printf(" basis tally = %f \n", check_basis ); - - // }); - // Kokkos::fence(); - - // WARNING WARNING WARNING:: works well for modest Pn orders, especially since this and the - // following loops to build the basis structures are only called once. Should consider making - // these computations parallel if we need to run with very large Pn orders. - RUN_CLASS({ - for (int gauss_lob_rid = 0; gauss_lob_rid < num_gauss_lob_in_surf; gauss_lob_rid++){ - - // Get the nodal coordinates - for(int dim = 0; dim < 2; dim++){ - point(dim) = gauss_lob_positions(gauss_lob_rid, dim); - //printf(" point value = %f \n", point(dim) ); - - } - - get_basis(temp_nodal_basis, val_1d, val_2d, point); - //double check_basis = 0.0; - - for(int basis_id = 0; basis_id < num_dofs_in_surf; basis_id++){ - //printf(" computed basis value = %f \n", temp_nodal_basis(basis_id) ); - gauss_lob_basis(gauss_lob_rid, basis_id) = temp_nodal_basis(basis_id); - //check_basis += temp_nodal_basis(basis_id); - temp_nodal_basis(basis_id) = 0.0; - } - //printf(" basis tally = %f \n", check_basis ); - } - }); - Kokkos::fence(); - - // --- evaluate the basis at the legendre points - // FOR_ALL_CLASS(gauss_leg_rid, 0, num_gauss_leg_in_surf, { - - // // Get the nodal coordinates - // for(int dim = 0; dim < 3; dim++){ - // point(dim) = gauss_leg_positions(gauss_leg_rid, dim); - // //printf(" point value = %f \n", point(dim) ); - // } - - // get_basis(temp_nodal_basis, val_1d, val_2d, point); - - // double check_basis = 0.0; - - // for(int basis_id = 0; basis_id < num_dofs_in_surf; basis_id++){ - // //printf(" computed basis value = %f \n", temp_nodal_basis(basis_id) ); - // gauss_leg_basis(gauss_leg_rid, basis_id) = temp_nodal_basis(basis_id); - // check_basis += temp_nodal_basis(basis_id); - // temp_nodal_basis(basis_id) = 0.0; - // } - - // printf(" basis tally = %f \n", check_basis ); - // }); - // Kokkos::fence(); - - RUN_CLASS({ - - for (int gauss_leg_rid = 0; gauss_leg_rid < num_gauss_leg_in_surf; gauss_leg_rid++){ - // Get the nodal coordinates - for(int dim = 0; dim < 2; dim++){ - point(dim) = gauss_leg_positions(gauss_leg_rid, dim); - //printf(" point value = %f \n", point(dim) ); - } - - get_basis(temp_nodal_basis, val_1d, val_2d, point); - - //double check_basis = 0.0; - - for(int basis_id = 0; basis_id < num_dofs_in_surf; basis_id++){ - //printf(" computed basis value = %f \n", temp_nodal_basis(basis_id) ); - gauss_leg_basis(gauss_leg_rid, basis_id) = temp_nodal_basis(basis_id); - //check_basis += temp_nodal_basis(basis_id); - temp_nodal_basis(basis_id) = 0.0; - } - - //printf(" basis tally = %f \n", check_basis ); - - } - - }); - Kokkos::fence(); - - - // --- evaluate the thermodynamic basis at the lobatto positions - // FOR_ALL_CLASS(gauss_lob_rid, 0, num_gauss_lob_in_surf, { - - // // Get the nodal coordinates - // for(int dim = 0; dim < 3; dim++){ - // point(dim) = gauss_lob_positions(gauss_lob_rid, dim); - // } - - // get_bernstein_basis(temp_elem_basis, elem_val_1d, elem_val_2d, point); - - // //double check_basis = 0.0; - - // for(int basis_id = 0; basis_id < num_elem_dofs_in_surf; basis_id++){ - - // gauss_lob_elem_basis(gauss_lob_rid, basis_id) = temp_elem_basis(basis_id); - // //check_basis += temp_elem_basis(basis_id); - // temp_elem_basis(basis_id) = 0.0; - // } - // //printf(" basis tally = %f \n", check_basis ); - - // }); - // Kokkos::fence(); - - RUN_CLASS({ - for (int gauss_lob_rid = 0; gauss_lob_rid < num_gauss_lob_in_surf; gauss_lob_rid++){ - // Get the nodal coordinates - for(int dim = 0; dim < 2; dim++){ - point(dim) = gauss_lob_positions(gauss_lob_rid, dim); - } - - get_bernstein_basis(temp_elem_basis, elem_val_1d, elem_val_2d, point); - - //double check_basis = 0.0; - - for(int basis_id = 0; basis_id < num_elem_dofs_in_surf; basis_id++){ - - gauss_lob_elem_basis(gauss_lob_rid, basis_id) = temp_elem_basis(basis_id); - //check_basis += temp_elem_basis(basis_id); - temp_elem_basis(basis_id) = 0.0; - } - //printf(" basis tally = %f \n", check_basis ); - - } - - }); - Kokkos::fence(); - - // --- evaluate the thermodynamic basis at the legendre points - // FOR_ALL_CLASS(gauss_leg_rid, 0, num_gauss_leg_in_surf, { - - // // Get the nodal coordinates - // for(int dim = 0; dim < 3; dim++){ - // point(dim) = gauss_leg_positions(gauss_leg_rid, dim); - // } - - // get_bernstein_basis(temp_elem_basis, elem_val_1d, elem_val_2d, point); - - // //double check_basis = 0.0; - - // for(int basis_id = 0; basis_id < num_elem_dofs_in_surf; basis_id++){ - - // gauss_leg_elem_basis(gauss_leg_rid, basis_id) = temp_elem_basis(basis_id); - // //check_basis += temp_elem_basis(basis_id); - // temp_elem_basis(basis_id) = 0.0; - // } - - // //printf(" basis tally = %f \n", check_basis ); - // }); - // Kokkos::fence(); - - RUN_CLASS({ - for (int gauss_leg_rid = 0; gauss_leg_rid < num_gauss_leg_in_surf; gauss_leg_rid++ ){ - // Get the nodal coordinates - for(int dim = 0; dim < 2; dim++){ - point(dim) = gauss_leg_positions(gauss_leg_rid, dim); - } - - get_bernstein_basis(temp_elem_basis, elem_val_1d, elem_val_2d, point); - - //double check_basis = 0.0; - - for(int basis_id = 0; basis_id < num_elem_dofs_in_surf; basis_id++){ - - gauss_leg_elem_basis(gauss_leg_rid, basis_id) = temp_elem_basis(basis_id); - //check_basis += temp_elem_basis(basis_id); - temp_elem_basis(basis_id) = 0.0; - } - - //printf(" basis tally = %f \n", check_basis ); - - } - - }); - Kokkos::fence(); - - - - // --- evaluate grad_basis functions at the lobatto points --- - - CArrayKokkos temp_partial_xi(num_dofs_in_surf); - CArrayKokkos temp_partial_eta(num_dofs_in_surf); - - CArrayKokkos Dval_1d(num_dofs_1d); - CArrayKokkos Dval_2d(num_dofs_1d,2); - - // FOR_ALL_CLASS(gauss_lob_rid, 0, num_gauss_lob_in_surf,{ - - // // Get the lobatto coordinates - // for(int dim = 0; dim < 3; dim++){ - // point(dim) = gauss_lob_positions(gauss_lob_rid, dim); - // } - - // //double check[3]; - // //for (int i = 0; i < 3; i++) check[i] = 0.0; - - // partial_xi_basis(temp_partial_xi, val_1d, val_2d, Dval_1d, Dval_2d, point); - // partial_eta_basis(temp_partial_eta, val_1d, val_2d, Dval_1d, Dval_2d, point); - // partial_mu_basis(temp_partial_mu, val_1d, val_2d, Dval_1d, Dval_2d, point); - - // for(int basis_id = 0; basis_id < num_dofs_in_surf; basis_id++){ - - - // gauss_lob_grad_basis(gauss_lob_rid, basis_id, 0) = temp_partial_xi(basis_id); - // gauss_lob_grad_basis(gauss_lob_rid, basis_id, 1) = temp_partial_eta(basis_id); - // gauss_lob_grad_basis(gauss_lob_rid, basis_id, 2) = temp_partial_mu(basis_id); - - // // check[0] += temp_partial_xi(basis_id); - // // check[1] += temp_partial_eta(basis_id); - // // check[2] += temp_partial_mu(basis_id); - - // temp_partial_xi(basis_id) = 0.0; - // temp_partial_eta(basis_id) = 0.0; - // temp_partial_mu(basis_id) = 0.0; - // } - - // //printf(" grad_basis tally = %f, %f, %f \n", check[0], check[1], check[2]); - // }); - // Kokkos::fence(); - - RUN_CLASS({ - for (int gauss_lob_rid = 0; gauss_lob_rid < num_gauss_lob_in_surf; gauss_lob_rid++){ - // Get the lobatto coordinates - for(int dim = 0; dim < 2; dim++){ - point(dim) = gauss_lob_positions(gauss_lob_rid, dim); - } - - //double check[3]; - //for (int i = 0; i < 3; i++) check[i] = 0.0; - - partial_xi_basis(temp_partial_xi, val_1d, val_2d, Dval_1d, Dval_2d, point); - partial_eta_basis(temp_partial_eta, val_1d, val_2d, Dval_1d, Dval_2d, point); - - for(int basis_id = 0; basis_id < num_dofs_in_surf; basis_id++){ - - - gauss_lob_grad_basis(gauss_lob_rid, basis_id, 0) = temp_partial_xi(basis_id); - gauss_lob_grad_basis(gauss_lob_rid, basis_id, 1) = temp_partial_eta(basis_id); - - // check[0] += temp_partial_xi(basis_id); - // check[1] += temp_partial_eta(basis_id); - // check[2] += temp_partial_mu(basis_id); - - temp_partial_xi(basis_id) = 0.0; - temp_partial_eta(basis_id) = 0.0; - } - - //printf(" grad_basis tally = %f, %f, %f \n", check[0], check[1], check[2]); - } - - }); - Kokkos::fence(); - - - // FOR_ALL_CLASS(gauss_leg_rid, 0, num_gauss_leg_in_surf, { - - // // Get the nodal coordinates - // for(int dim = 0; dim < 3; dim++){ - // point(dim) = gauss_leg_positions(gauss_leg_rid, dim); - // } - - // partial_xi_basis(temp_partial_xi, val_1d, val_2d, Dval_1d, Dval_2d, point); - // partial_eta_basis(temp_partial_eta, val_1d, val_2d, Dval_1d, Dval_2d, point); - // partial_mu_basis(temp_partial_mu, val_1d, val_2d, Dval_1d, Dval_2d, point); - - // double check[3]; - // for (int i = 0; i < 3; i++) check[i] = 0.0; - - // for(int basis_id = 0; basis_id < num_dofs_in_surf; basis_id++){ - - - // gauss_leg_grad_basis(gauss_leg_rid, basis_id, 0) = temp_partial_xi(basis_id); - // //printf(" grad basis value : %f \n ", gauss_leg_grad_basis(gauss_leg_rid, basis_id, 0) ); - // gauss_leg_grad_basis(gauss_leg_rid, basis_id, 1) = temp_partial_eta(basis_id); - // //printf(" grad basis value : %f \n ", gauss_leg_grad_basis(gauss_leg_rid, basis_id, 1) ); - // gauss_leg_grad_basis(gauss_leg_rid, basis_id, 2) = temp_partial_mu(basis_id); - // //printf(" grad basis value : %f \n ", gauss_leg_grad_basis(gauss_leg_rid, basis_id, 2) ); - - // check[0] += temp_partial_xi(basis_id); - // check[1] += temp_partial_eta(basis_id); - // check[2] += temp_partial_mu(basis_id); - - // temp_partial_xi(basis_id) = 0.0; - // temp_partial_eta(basis_id) = 0.0; - // temp_partial_mu(basis_id) = 0.0; - // } - - // //printf(" grad_basis tally = %f, %f, %f \n", check[0], check[1], check[2]); - // }); - // Kokkos::fence(); - - RUN_CLASS({ - for (int gauss_leg_rid = 0; gauss_leg_rid < num_gauss_leg_in_surf; gauss_leg_rid++){ - // Get the nodal coordinates - for(int dim = 0; dim < 2; dim++){ - point(dim) = gauss_leg_positions(gauss_leg_rid, dim); - } - - partial_xi_basis(temp_partial_xi, val_1d, val_2d, Dval_1d, Dval_2d, point); - partial_eta_basis(temp_partial_eta, val_1d, val_2d, Dval_1d, Dval_2d, point); - - // double check[2]; - // for (int i = 0; i < 2; i++) check[i] = 0.0; - - for(int basis_id = 0; basis_id < num_dofs_in_surf; basis_id++){ - - - gauss_leg_grad_basis(gauss_leg_rid, basis_id, 0) = temp_partial_xi(basis_id); - //printf(" grad basis value : %f \n ", gauss_leg_grad_basis(gauss_leg_rid, basis_id, 0) ); - gauss_leg_grad_basis(gauss_leg_rid, basis_id, 1) = temp_partial_eta(basis_id); - //printf(" grad basis value : %f \n ", gauss_leg_grad_basis(gauss_leg_rid, basis_id, 1) ); - - // check[0] += temp_partial_xi(basis_id); - // check[1] += temp_partial_eta(basis_id); - - temp_partial_xi(basis_id) = 0.0; - temp_partial_eta(basis_id) = 0.0; - temp_partial_mu(basis_id) = 0.0; - } - - //printf(" grad_basis tally = %f, %f, %f \n", check[0], check[1], check[2]); - } - - }); - Kokkos::fence(); - - - } // end of member function - -KOKKOS_FUNCTION -void lobatto_nodes_1D ( const CArrayKokkos &lob_nodes_1D, - const int &num) const{ - if (num == 1){ - lob_nodes_1D(0) = 0.0; - } - else if (num == 2){ - lob_nodes_1D(0) = -1.0; - lob_nodes_1D(1) = 1.0; - } - else if (num == 3){ - lob_nodes_1D(0) = -1.0; - lob_nodes_1D(1) = 0.0; - lob_nodes_1D(2) = 1.0; - } - else if (num == 4){ - lob_nodes_1D(0) = -1.0; - lob_nodes_1D(1) = -1.0/5.0*sqrt(5.0); - lob_nodes_1D(2) = 1.0/5.0*sqrt(5.0); - lob_nodes_1D(3) = 1.0; - } - else if (num == 5){ - lob_nodes_1D(0) = -1.0; - lob_nodes_1D(1) = -1.0/7.0*sqrt(21.0); - lob_nodes_1D(2) = 0.0; - lob_nodes_1D(3) = 1.0/7.0*sqrt(21.0); - lob_nodes_1D(4) = 1.0; - } - else if (num == 6){ - lob_nodes_1D(0) = -1.0; - lob_nodes_1D(1) = -sqrt(1.0/21.0*(7.0 + 2.0*sqrt(7.0))); - lob_nodes_1D(2) = -sqrt(1.0/21.0*(7.0 - 2.0*sqrt(7.0))); - lob_nodes_1D(3) = sqrt(1.0/21.0*(7.0 - 2.0*sqrt(7.0))); - lob_nodes_1D(4) = sqrt(1.0/21.0*(7.0 +2.0*sqrt(7.0))); - lob_nodes_1D(5) = 1.0; - } - else if (num == 7){ - lob_nodes_1D(0) = - 1.0E+00; - lob_nodes_1D(1) = - 0.830223896278566929872032213967E+00; - lob_nodes_1D(2) = - 0.468848793470714213803771881909E+00; - lob_nodes_1D(3) = 0.0E+00; - lob_nodes_1D(4) = 0.468848793470714213803771881909E+00; - lob_nodes_1D(5) = 0.830223896278566929872032213967E+00; - lob_nodes_1D(6) = 1.0E+00; - } - else if (num == 8){ - lob_nodes_1D(0) = - 1.0E+00; - lob_nodes_1D(1) = - 0.871740148509606615337445761221E+00; - lob_nodes_1D(2) = - 0.591700181433142302144510731398E+00; - lob_nodes_1D(3) = - 0.209299217902478868768657260345E+00; - lob_nodes_1D(4) = 0.209299217902478868768657260345E+00; - lob_nodes_1D(5) = 0.591700181433142302144510731398E+00; - lob_nodes_1D(6) = 0.871740148509606615337445761221E+00; - lob_nodes_1D(7) = 1.0E+00; - } - else if (num == 9){ - lob_nodes_1D(0) = - 1.0E+00; - lob_nodes_1D(1) = - 0.899757995411460157312345244418E+00; - lob_nodes_1D(2) = - 0.677186279510737753445885427091E+00; - lob_nodes_1D(3) = - 0.363117463826178158710752068709E+00; - lob_nodes_1D(4) = 0.0E+00; - lob_nodes_1D(5) = 0.363117463826178158710752068709E+00; - lob_nodes_1D(6) = 0.677186279510737753445885427091E+00; - lob_nodes_1D(7) = 0.899757995411460157312345244418E+00; - lob_nodes_1D(8) = 1.0E+00; - } - else if (num == 10){ - lob_nodes_1D(0) = - 1.0E+00; - lob_nodes_1D(1) = - 0.919533908166458813828932660822E+00; - lob_nodes_1D(2) = - 0.738773865105505075003106174860E+00; - lob_nodes_1D(3) = - 0.477924949810444495661175092731E+00; - lob_nodes_1D(4) = - 0.165278957666387024626219765958E+00; - lob_nodes_1D(5) = 0.165278957666387024626219765958E+00; - lob_nodes_1D(6) = 0.477924949810444495661175092731E+00; - lob_nodes_1D(7) = 0.738773865105505075003106174860E+00; - lob_nodes_1D(8) = 0.919533908166458813828932660822E+00; - lob_nodes_1D(9) = 1.0E+00; - - } - else if (num == 11){ - lob_nodes_1D(0) = - 1.0E+00; - lob_nodes_1D(1) = - 0.934001430408059134332274136099E+00; - lob_nodes_1D(2) = - 0.784483473663144418622417816108E+00; - lob_nodes_1D(3) = - 0.565235326996205006470963969478E+00; - lob_nodes_1D(4) = - 0.295758135586939391431911515559E+00; - lob_nodes_1D(5) = 0.0E+00; - lob_nodes_1D(6) = 0.295758135586939391431911515559E+00; - lob_nodes_1D(7) = 0.565235326996205006470963969478E+00; - lob_nodes_1D(8) = 0.784483473663144418622417816108E+00; - lob_nodes_1D(9) = 0.934001430408059134332274136099E+00; - lob_nodes_1D(10) = 1.0E+00; - } - - else if (num == 12){ - lob_nodes_1D(0) = - 1.0E+00; - lob_nodes_1D(1) = - 0.944899272222882223407580138303E+00; - lob_nodes_1D(2) = - 0.819279321644006678348641581717E+00; - lob_nodes_1D(3) = - 0.632876153031869677662404854444E+00; - lob_nodes_1D(4) = - 0.399530940965348932264349791567E+00; - lob_nodes_1D(5) = - 0.136552932854927554864061855740E+00; - lob_nodes_1D(6) = 0.136552932854927554864061855740E+00; - lob_nodes_1D(7) = 0.399530940965348932264349791567E+00; - lob_nodes_1D(8) = 0.632876153031869677662404854444E+00; - lob_nodes_1D(9) = 0.819279321644006678348641581717E+00; - lob_nodes_1D(10) = 0.944899272222882223407580138303E+00; - lob_nodes_1D(11) = 1.0E+00; - } - - else if (num == 13){ - lob_nodes_1D(0) = - 1.0E+00; - lob_nodes_1D(1) = - 0.953309846642163911896905464755E+00; - lob_nodes_1D(2) = - 0.846347564651872316865925607099E+00; - lob_nodes_1D(3) = - 0.686188469081757426072759039566E+00; - lob_nodes_1D(4) = - 0.482909821091336201746937233637E+00; - lob_nodes_1D(5) = - 0.249286930106239992568673700374E+00; - lob_nodes_1D(6) = 0.0E+00; - lob_nodes_1D(7) = 0.249286930106239992568673700374E+00; - lob_nodes_1D(8) = 0.482909821091336201746937233637E+00; - lob_nodes_1D(9) = 0.686188469081757426072759039566E+00; - lob_nodes_1D(10) = 0.846347564651872316865925607099E+00; - lob_nodes_1D(11) = 0.953309846642163911896905464755E+00; - lob_nodes_1D(12) = 1.0E+00; - } - - else if (num == 14){ - lob_nodes_1D(0) = - 1.0E+00; - lob_nodes_1D(1) = - 0.959935045267260901355100162015E+00; - lob_nodes_1D(2) = - 0.867801053830347251000220202908E+00; - lob_nodes_1D(3) = - 0.728868599091326140584672400521E+00; - lob_nodes_1D(4) = - 0.550639402928647055316622705859E+00; - lob_nodes_1D(5) = - 0.342724013342712845043903403642E+00; - lob_nodes_1D(6) = - 0.116331868883703867658776709736E+00; - lob_nodes_1D(7) = 0.116331868883703867658776709736E+00; - lob_nodes_1D(8) = 0.342724013342712845043903403642E+00; - lob_nodes_1D(9) = 0.550639402928647055316622705859E+00; - lob_nodes_1D(10) = 0.728868599091326140584672400521E+00; - lob_nodes_1D(11) = 0.867801053830347251000220202908E+00; - lob_nodes_1D(12) = 0.959935045267260901355100162015E+00; - lob_nodes_1D(13) = 1.0E+00; - } - - else if (num == 15){ - lob_nodes_1D(0) = - 1.0E+00; - lob_nodes_1D(1) = - 0.965245926503838572795851392070E+00; - lob_nodes_1D(2) = - 0.885082044222976298825401631482E+00; - lob_nodes_1D(3) = - 0.763519689951815200704118475976E+00; - lob_nodes_1D(4) = - 0.606253205469845711123529938637E+00; - lob_nodes_1D(5) = - 0.420638054713672480921896938739E+00; - lob_nodes_1D(6) = - 0.215353955363794238225679446273E+00; - lob_nodes_1D(7) = 0.0E+00; - lob_nodes_1D(8) = 0.215353955363794238225679446273E+00; - lob_nodes_1D(9) = 0.420638054713672480921896938739E+00; - lob_nodes_1D(10) = 0.606253205469845711123529938637E+00; - lob_nodes_1D(11) = 0.763519689951815200704118475976E+00; - lob_nodes_1D(12) = 0.885082044222976298825401631482E+00; - lob_nodes_1D(13) = 0.965245926503838572795851392070E+00; - lob_nodes_1D(14) = 1.0E+00; - } - - else if (num == 16){ - lob_nodes_1D(0) = - 1.0E+00; - lob_nodes_1D(1) = - 0.969568046270217932952242738367E+00; - lob_nodes_1D(2) = - 0.899200533093472092994628261520E+00; - lob_nodes_1D(3) = - 0.792008291861815063931088270963E+00; - lob_nodes_1D(4) = - 0.652388702882493089467883219641E+00; - lob_nodes_1D(5) = - 0.486059421887137611781890785847E+00; - lob_nodes_1D(6) = - 0.299830468900763208098353454722E+00; - lob_nodes_1D(7) = - 0.101326273521949447843033005046E+00; - lob_nodes_1D(8) = 0.101326273521949447843033005046E+00; - lob_nodes_1D(9) = 0.299830468900763208098353454722E+00; - lob_nodes_1D(10) = 0.486059421887137611781890785847E+00; - lob_nodes_1D(11) = 0.652388702882493089467883219641E+00; - lob_nodes_1D(12) = 0.792008291861815063931088270963E+00; - lob_nodes_1D(13) = 0.899200533093472092994628261520E+00; - lob_nodes_1D(14) = 0.969568046270217932952242738367E+00; - lob_nodes_1D(15) = 1.0E+00; - } - - else if (num == 17){ - lob_nodes_1D(0) = - 1.0E+00; - lob_nodes_1D(1) = - 0.973132176631418314156979501874E+00; - lob_nodes_1D(2) = - 0.910879995915573595623802506398E+00; - lob_nodes_1D(3) = - 0.815696251221770307106750553238E+00; - lob_nodes_1D(4) = - 0.691028980627684705394919357372E+00; - lob_nodes_1D(5) = - 0.541385399330101539123733407504E+00; - lob_nodes_1D(6) = - 0.372174433565477041907234680735E+00; - lob_nodes_1D(7) = - 0.189511973518317388304263014753E+00; - lob_nodes_1D(8) = 0.0E+00; - lob_nodes_1D(9) = 0.189511973518317388304263014753E+00; - lob_nodes_1D(10) = 0.372174433565477041907234680735E+00; - lob_nodes_1D(11) = 0.541385399330101539123733407504E+00; - lob_nodes_1D(12) = 0.691028980627684705394919357372E+00; - lob_nodes_1D(13) = 0.815696251221770307106750553238E+00; - lob_nodes_1D(14) = 0.910879995915573595623802506398E+00; - lob_nodes_1D(15) = 0.973132176631418314156979501874E+00; - lob_nodes_1D(16) = 1.0E+00; - } - - else if (num == 18){ - lob_nodes_1D(0) = - 1.0E+00; - lob_nodes_1D(1) = - 0.976105557412198542864518924342E+00; - lob_nodes_1D(2) = - 0.920649185347533873837854625431E+00; - lob_nodes_1D(3) = - 0.835593535218090213713646362328E+00; - lob_nodes_1D(4) = - 0.723679329283242681306210365302E+00; - lob_nodes_1D(5) = - 0.588504834318661761173535893194E+00; - lob_nodes_1D(6) = - 0.434415036912123975342287136741E+00; - lob_nodes_1D(7) = - 0.266362652878280984167665332026E+00; - lob_nodes_1D(8) = - 0.897490934846521110226450100886E-01; - lob_nodes_1D(9) = 0.897490934846521110226450100886E-01; - lob_nodes_1D(10) = 0.266362652878280984167665332026E+00; - lob_nodes_1D(11) = 0.434415036912123975342287136741E+00; - lob_nodes_1D(12) = 0.588504834318661761173535893194E+00; - lob_nodes_1D(13) = 0.723679329283242681306210365302E+00; - lob_nodes_1D(14) = 0.835593535218090213713646362328E+00; - lob_nodes_1D(15) = 0.920649185347533873837854625431E+00; - lob_nodes_1D(16) = 0.976105557412198542864518924342E+00; - lob_nodes_1D(17) = 1.0E+00; - } - - else if (num == 19) { - lob_nodes_1D(0)= - 1.0E+00; - lob_nodes_1D(1)= - 0.978611766222080095152634063110E+00; - lob_nodes_1D(2)= - 0.928901528152586243717940258797E+00; - lob_nodes_1D(3)= - 0.852460577796646093085955970041E+00; - lob_nodes_1D(4)= - 0.751494202552613014163637489634E+00; - lob_nodes_1D(5)= - 0.628908137265220497766832306229E+00; - lob_nodes_1D(6)= - 0.488229285680713502777909637625E+00; - lob_nodes_1D(7)= - 0.333504847824498610298500103845E+00; - lob_nodes_1D(8)= - 0.169186023409281571375154153445E+00; - lob_nodes_1D(9)= 0.0E+00; - lob_nodes_1D(10) = 0.169186023409281571375154153445E+00; - lob_nodes_1D(11) = 0.333504847824498610298500103845E+00; - lob_nodes_1D(12) = 0.488229285680713502777909637625E+00; - lob_nodes_1D(13) = 0.628908137265220497766832306229E+00; - lob_nodes_1D(14) = 0.751494202552613014163637489634E+00; - lob_nodes_1D(15) = 0.852460577796646093085955970041E+00; - lob_nodes_1D(16) = 0.928901528152586243717940258797E+00; - lob_nodes_1D(17) = 0.978611766222080095152634063110E+00; - lob_nodes_1D(18) = 1.0E+00; - - } // end if - - else if (num == 20){ - lob_nodes_1D(0) = - 1.0E+00; - lob_nodes_1D(1) = - 0.980743704893914171925446438584E+00; - lob_nodes_1D(2) = - 0.935934498812665435716181584931E+00; - lob_nodes_1D(3) = - 0.866877978089950141309847214616E+00; - lob_nodes_1D(4) = - 0.775368260952055870414317527595E+00; - lob_nodes_1D(5) = - 0.663776402290311289846403322971E+00; - lob_nodes_1D(6) = - 0.534992864031886261648135961829E+00; - lob_nodes_1D(7) = - 0.392353183713909299386474703816E+00; - lob_nodes_1D(8) = - 0.239551705922986495182401356927E+00; - lob_nodes_1D(9) = - 0.805459372388218379759445181596E-01; - lob_nodes_1D(10) = 0.805459372388218379759445181596E-01; - lob_nodes_1D(11) = 0.239551705922986495182401356927E+00; - lob_nodes_1D(12) = 0.392353183713909299386474703816E+00; - lob_nodes_1D(13) = 0.534992864031886261648135961829E+00; - lob_nodes_1D(14) = 0.663776402290311289846403322971E+00; - lob_nodes_1D(15) = 0.775368260952055870414317527595E+00; - lob_nodes_1D(16) = 0.866877978089950141309847214616E+00; - lob_nodes_1D(17) = 0.935934498812665435716181584931E+00; - lob_nodes_1D(18) = 0.980743704893914171925446438584E+00; - lob_nodes_1D(19) = 1.0E+00; - } -} // end of lobbato_nodes_1D function - - -/**************************************************************************************//** -* lobatto_weights_1D creates quadrature weights corresponding the nodal positions defined -* on [-1,1] using the Gauss-Lobatto quadrature points. The CArray lob_weights_1D is passed -* in my reference and modified in place. The integer num is the numer of points being -* defined in 1D. -*****************************************************************************************/ - -//KOKKOS_FUNCTION -void lobatto_weights_1D( - const CArrayKokkos &lob_weights_1D, // Lobbatto weights - const int &num) const { // Interpolation order - if (num == 1){ - lob_weights_1D(0) = 2.0; - } - else if (num == 2){ - lob_weights_1D(0) = 1.0; - lob_weights_1D(1) = 1.0; - } - else if (num == 3){ - lob_weights_1D(0) = 1.0/3.0; - lob_weights_1D(1) = 4.0/3.0; - lob_weights_1D(2) = 1.0/3.0; - } - else if (num == 4){ - lob_weights_1D(0) = 1.0/6.0; - lob_weights_1D(1) = 5.0/6.0; - lob_weights_1D(2) = 5.0/6.0; - lob_weights_1D(3) = 1.0/6.0; - } - else if (num == 5){ - lob_weights_1D(0) = 1.0/10.0; - lob_weights_1D(1) = 49.0/90.0; - lob_weights_1D(2) = 32.0/45.0; - lob_weights_1D(3) = 49.0/90.0; - lob_weights_1D(4) = 1.0/10.0; - } - else if (num == 6){ - lob_weights_1D(0) = 1.0/15.0; - lob_weights_1D(1) = 1.0/30.0*(14.0 - sqrt(7.0)); - lob_weights_1D(2) = 1.0/30.0*(14.0 + sqrt(7.0)); - lob_weights_1D(3) = 1.0/30.0*(14.0 + sqrt(7.0)); - lob_weights_1D(4) = 1.0/30.0*(14.0 - sqrt(7.0)); - lob_weights_1D(5) = 1.0/15.0; - } - else if (num == 7){ - lob_weights_1D(0) = 0.476190476190476190476190476190E-01; - lob_weights_1D(1) = 0.276826047361565948010700406290E+00; - lob_weights_1D(2) = 0.431745381209862623417871022281E+00; - lob_weights_1D(3) = 0.487619047619047619047619047619E+00; - lob_weights_1D(4) = 0.431745381209862623417871022281E+00; - lob_weights_1D(5) = 0.276826047361565948010700406290E+00; - lob_weights_1D(6) = 0.476190476190476190476190476190E-01; - } - else if (num == 8){ - lob_weights_1D(0) = 0.357142857142857142857142857143E-01; - lob_weights_1D(1) = 0.210704227143506039382991065776E+00; - lob_weights_1D(2) = 0.341122692483504364764240677108E+00; - lob_weights_1D(3) = 0.412458794658703881567052971402E+00; - lob_weights_1D(4) = 0.412458794658703881567052971402E+00; - lob_weights_1D(5) = 0.341122692483504364764240677108E+00; - lob_weights_1D(6) = 0.210704227143506039382991065776E+00; - lob_weights_1D(7) = 0.357142857142857142857142857143E-01; - } - else if (num == 9){ - lob_weights_1D(0) = 0.277777777777777777777777777778E-01; - lob_weights_1D(1) = 0.165495361560805525046339720029E+00; - lob_weights_1D(2) = 0.274538712500161735280705618579E+00; - lob_weights_1D(3) = 0.346428510973046345115131532140E+00; - lob_weights_1D(4) = 0.371519274376417233560090702948E+00; - lob_weights_1D(5) = 0.346428510973046345115131532140E+00; - lob_weights_1D(6) = 0.274538712500161735280705618579E+00; - lob_weights_1D(7) = 0.165495361560805525046339720029E+00; - lob_weights_1D(8) = 0.277777777777777777777777777778E-01; - } - else if (num == 10){ - lob_weights_1D(0) = 0.222222222222222222222222222222E-01; - lob_weights_1D(1) = 0.133305990851070111126227170755E+00; - lob_weights_1D(2) = 0.224889342063126452119457821731E+00; - lob_weights_1D(3) = 0.292042683679683757875582257374E+00; - lob_weights_1D(4) = 0.327539761183897456656510527917E+00; - lob_weights_1D(5) = 0.327539761183897456656510527917E+00; - lob_weights_1D(6) = 0.292042683679683757875582257374E+00; - lob_weights_1D(7) = 0.224889342063126452119457821731E+00; - lob_weights_1D(8) = 0.133305990851070111126227170755E+00; - lob_weights_1D(9) = 0.222222222222222222222222222222E-01; - } - else if (num == 11){ - lob_weights_1D(0) = 0.181818181818181818181818181818E-01; - lob_weights_1D(1) = 0.109612273266994864461403449580E+00; - lob_weights_1D(2) = 0.187169881780305204108141521899E+00; - lob_weights_1D(3) = 0.248048104264028314040084866422E+00; - lob_weights_1D(4) = 0.286879124779008088679222403332E+00; - lob_weights_1D(5) = 0.300217595455690693785931881170E+00; - lob_weights_1D(6) = 0.286879124779008088679222403332E+00; - lob_weights_1D(7) = 0.248048104264028314040084866422E+00; - lob_weights_1D(8) = 0.187169881780305204108141521899E+00; - lob_weights_1D(9) = 0.109612273266994864461403449580E+00; - lob_weights_1D(10)= 0.181818181818181818181818181818E-01; - } - - else if (num == 12){ - lob_weights_1D(0) = 0.151515151515151515151515151515E-01; - lob_weights_1D(1) = 0.916845174131961306683425941341E-01; - lob_weights_1D(2) = 0.157974705564370115164671062700E+00; - lob_weights_1D(3) = 0.212508417761021145358302077367E+00; - lob_weights_1D(4) = 0.251275603199201280293244412148E+00; - lob_weights_1D(5) = 0.271405240910696177000288338500E+00; - lob_weights_1D(6) = 0.271405240910696177000288338500E+00; - lob_weights_1D(7) = 0.251275603199201280293244412148E+00; - lob_weights_1D(8) = 0.212508417761021145358302077367E+00; - lob_weights_1D(9) = 0.157974705564370115164671062700E+00; - lob_weights_1D(10) = 0.916845174131961306683425941341E-01; - lob_weights_1D(11) = 0.151515151515151515151515151515E-01; - } - - else if (num == 13){ - lob_weights_1D(0) = 0.128205128205128205128205128205E-01; - lob_weights_1D(1) = 0.778016867468189277935889883331E-01; - lob_weights_1D(2) = 0.134981926689608349119914762589E+00; - lob_weights_1D(3) = 0.183646865203550092007494258747E+00; - lob_weights_1D(4) = 0.220767793566110086085534008379E+00; - lob_weights_1D(5) = 0.244015790306676356458578148360E+00; - lob_weights_1D(6) = 0.251930849333446736044138641541E+00; - lob_weights_1D(7) = 0.244015790306676356458578148360E+00; - lob_weights_1D(8) = 0.220767793566110086085534008379E+00; - lob_weights_1D(9) = 0.183646865203550092007494258747E+00; - lob_weights_1D(10) = 0.134981926689608349119914762589E+00; - lob_weights_1D(11) = 0.778016867468189277935889883331E-01; - lob_weights_1D(12) = 0.128205128205128205128205128205E-01; - } - - else if (num == 14){ - lob_weights_1D(0) = 0.109890109890109890109890109890E-01; - lob_weights_1D(1) = 0.668372844976812846340706607461E-01; - lob_weights_1D(2) = 0.116586655898711651540996670655E+00; - lob_weights_1D(3) = 0.160021851762952142412820997988E+00; - lob_weights_1D(4) = 0.194826149373416118640331778376E+00; - lob_weights_1D(5) = 0.219126253009770754871162523954E+00; - lob_weights_1D(6) = 0.231612794468457058889628357293E+00; - lob_weights_1D(7) = 0.231612794468457058889628357293E+00; - lob_weights_1D(8) = 0.219126253009770754871162523954E+00; - lob_weights_1D(9) = 0.194826149373416118640331778376E+00; - lob_weights_1D(10) = 0.160021851762952142412820997988E+00; - lob_weights_1D(11) = 0.116586655898711651540996670655E+00; - lob_weights_1D(12) = 0.668372844976812846340706607461E-01; - lob_weights_1D(13) = 0.109890109890109890109890109890E-01; - } - - - else if (num == 15){ - lob_weights_1D(0) = 0.952380952380952380952380952381E-02; - lob_weights_1D(1) = 0.580298930286012490968805840253E-01; - lob_weights_1D(2) = 0.101660070325718067603666170789E+00; - lob_weights_1D(3) = 0.140511699802428109460446805644E+00; - lob_weights_1D(4) = 0.172789647253600949052077099408E+00; - lob_weights_1D(5) = 0.196987235964613356092500346507E+00; - lob_weights_1D(6) = 0.211973585926820920127430076977E+00; - lob_weights_1D(7) = 0.217048116348815649514950214251E+00; - lob_weights_1D(8) = 0.211973585926820920127430076977E+00; - lob_weights_1D(9) = 0.196987235964613356092500346507E+00; - lob_weights_1D(10) = 0.172789647253600949052077099408E+00; - lob_weights_1D(11) = 0.140511699802428109460446805644E+00; - lob_weights_1D(12) = 0.101660070325718067603666170789E+00; - lob_weights_1D(13) = 0.580298930286012490968805840253E-01; - lob_weights_1D(14) = 0.952380952380952380952380952381E-02; - } - - - else if (num == 16){ - lob_weights_1D(0) = 0.833333333333333333333333333333E-02; - lob_weights_1D(1) = 0.508503610059199054032449195655E-01; - lob_weights_1D(2) = 0.893936973259308009910520801661E-01; - lob_weights_1D(3) = 0.124255382132514098349536332657E+00; - lob_weights_1D(4) = 0.154026980807164280815644940485E+00; - lob_weights_1D(5) = 0.177491913391704125301075669528E+00; - lob_weights_1D(6) = 0.193690023825203584316913598854E+00; - lob_weights_1D(7) = 0.201958308178229871489199125411E+00; - lob_weights_1D(8) = 0.201958308178229871489199125411E+00; - lob_weights_1D(9) = 0.193690023825203584316913598854E+00; - lob_weights_1D(10) = 0.177491913391704125301075669528E+00; - lob_weights_1D(11) = 0.154026980807164280815644940485E+00; - lob_weights_1D(12) = 0.124255382132514098349536332657E+00; - lob_weights_1D(13) = 0.893936973259308009910520801661E-01; - lob_weights_1D(14) = 0.508503610059199054032449195655E-01; - lob_weights_1D(15) = 0.833333333333333333333333333333E-02; - } - - - else if (num == 17){ - lob_weights_1D(0) = 0.735294117647058823529411764706E-02; - lob_weights_1D(1) = 0.449219405432542096474009546232E-01; - lob_weights_1D(2) = 0.791982705036871191902644299528E-01; - lob_weights_1D(3) = 0.110592909007028161375772705220E+00; - lob_weights_1D(4) = 0.137987746201926559056201574954E+00; - lob_weights_1D(5) = 0.160394661997621539516328365865E+00; - lob_weights_1D(6) = 0.177004253515657870436945745363E+00; - lob_weights_1D(7) = 0.187216339677619235892088482861E+00; - lob_weights_1D(8) = 0.190661874753469433299407247028E+00; - lob_weights_1D(9) = 0.187216339677619235892088482861E+00; - lob_weights_1D(10) = 0.177004253515657870436945745363E+00; - lob_weights_1D(11) = 0.160394661997621539516328365865E+00; - lob_weights_1D(12) = 0.137987746201926559056201574954E+00; - lob_weights_1D(13) = 0.110592909007028161375772705220E+00; - lob_weights_1D(14) = 0.791982705036871191902644299528E-01; - lob_weights_1D(15) = 0.449219405432542096474009546232E-01; - lob_weights_1D(16) = 0.735294117647058823529411764706E-02; - } - - else if (num == 18){ - lob_weights_1D(0) = 0.653594771241830065359477124183E-02; - lob_weights_1D(1) = 0.399706288109140661375991764101E-01; - lob_weights_1D(2) = 0.706371668856336649992229601678E-01; - lob_weights_1D(3) = 0.990162717175028023944236053187E-01; - lob_weights_1D(4) = 0.124210533132967100263396358897E+00; - lob_weights_1D(5) = 0.145411961573802267983003210494E+00; - lob_weights_1D(6) = 0.161939517237602489264326706700E+00; - lob_weights_1D(7) = 0.173262109489456226010614403827E+00; - lob_weights_1D(8) = 0.179015863439703082293818806944E+00; - lob_weights_1D(9) = 0.179015863439703082293818806944E+00; - lob_weights_1D(10) = 0.173262109489456226010614403827E+00; - lob_weights_1D(11) = 0.161939517237602489264326706700E+00; - lob_weights_1D(12) = 0.145411961573802267983003210494E+00; - lob_weights_1D(13) = 0.124210533132967100263396358897E+00; - lob_weights_1D(14) = 0.990162717175028023944236053187E-01; - lob_weights_1D(15) = 0.706371668856336649992229601678E-01; - lob_weights_1D(16) = 0.399706288109140661375991764101E-01; - lob_weights_1D(17) = 0.653594771241830065359477124183E-02; - } - - else if (num == 19) { - lob_weights_1D(0) = 0.584795321637426900584795321637E-02; - lob_weights_1D(1) = 0.357933651861764771154255690351E-01; - lob_weights_1D(2) = 0.633818917626297368516956904183E-01; - lob_weights_1D(3) = 0.891317570992070844480087905562E-01; - lob_weights_1D(4) = 0.112315341477305044070910015464E+00; - lob_weights_1D(5) = 0.132267280448750776926046733910E+00; - lob_weights_1D(6) = 0.148413942595938885009680643668E+00; - lob_weights_1D(7) = 0.160290924044061241979910968184E+00; - lob_weights_1D(8) = 0.167556584527142867270137277740E+00; - lob_weights_1D(9) = 0.170001919284827234644672715617E+00; - lob_weights_1D(10) = 0.167556584527142867270137277740E+00; - lob_weights_1D(11) = 0.160290924044061241979910968184E+00; - lob_weights_1D(12) = 0.148413942595938885009680643668E+00; - lob_weights_1D(13) = 0.132267280448750776926046733910E+00; - lob_weights_1D(14) = 0.112315341477305044070910015464E+00; - lob_weights_1D(15) = 0.891317570992070844480087905562E-01; - lob_weights_1D(16) = 0.633818917626297368516956904183E-01; - lob_weights_1D(17) = 0.357933651861764771154255690351E-01; - lob_weights_1D(18) = 0.584795321637426900584795321637E-02; - } // end if - - else if (num == 20) { - lob_weights_1D(0) = 0.526315789473684210526315789474E-02; - lob_weights_1D(1) = 0.322371231884889414916050281173E-01; - lob_weights_1D(2) = 0.571818021275668260047536271732E-01; - lob_weights_1D(3) = 0.806317639961196031447768461137E-01; - lob_weights_1D(4) = 0.101991499699450815683781205733E+00; - lob_weights_1D(5) = 0.120709227628674725099429705002E+00; - lob_weights_1D(6) = 0.136300482358724184489780792989E+00; - lob_weights_1D(7) = 0.148361554070916825814713013734E+00; - lob_weights_1D(8) = 0.156580102647475487158169896794E+00; - lob_weights_1D(9) = 0.160743286387845749007726726449E+00; - lob_weights_1D(10) = 0.160743286387845749007726726449E+00; - lob_weights_1D(11) = 0.156580102647475487158169896794E+00; - lob_weights_1D(12) = 0.148361554070916825814713013734E+00; - lob_weights_1D(13) = 0.136300482358724184489780792989E+00; - lob_weights_1D(14) = 0.120709227628674725099429705002E+00; - lob_weights_1D(15) = 0.101991499699450815683781205733E+00; - lob_weights_1D(16) = 0.806317639961196031447768461137E-01; - lob_weights_1D(17) = 0.571818021275668260047536271732E-01; - lob_weights_1D(18) = 0.322371231884889414916050281173E-01; - lob_weights_1D(19) = 0.526315789473684210526315789474E-02; - } // end if -} // end of lobatto_weights_1D function - - -KOKKOS_FUNCTION -void legendre_nodes_1D( - const CArrayKokkos &leg_nodes_1D, - const int &num) const { - - if (num == 1){ - leg_nodes_1D(0) = 0.0; - } - else if (num == 2){ - leg_nodes_1D(0) = -0.577350269189625764509148780501; - leg_nodes_1D(1) = 0.577350269189625764509148780501; - } - else if (num == 3){ - leg_nodes_1D(0) = -0.774596669241483377035853079956; - leg_nodes_1D(1) = 0.0; - leg_nodes_1D(2) = 0.774596669241483377035853079956; - } - else if (num == 4){ - leg_nodes_1D(0) = -0.861136311594052575223946488892; - leg_nodes_1D(1) = -0.339981043584856264802665759103; - leg_nodes_1D(2) = 0.339981043584856264802665759103; - leg_nodes_1D(3) = 0.861136311594052575223946488892; - } - else if (num == 5){ - leg_nodes_1D(0) = -0.906179845938663992797626878299; - leg_nodes_1D(1) = -0.538469310105683091036314420700; - leg_nodes_1D(2) = 0.0; - leg_nodes_1D(3) = 0.538469310105683091036314420700; - leg_nodes_1D(4) = 0.906179845938663992797626878299; - } - else if (num == 6){ - leg_nodes_1D(0) = -0.932469514203152027812301554493; - leg_nodes_1D(1) = -0.661209386466264513661399595019; - leg_nodes_1D(2) = -0.238619186083196908630501721680; - leg_nodes_1D(3) = 0.238619186083196908630501721680; - leg_nodes_1D(4) = 0.661209386466264513661399595019; - leg_nodes_1D(5) = 0.932469514203152027812301554493; - } - else if (num == 7){ - leg_nodes_1D(0) = -0.949107912342758524526189684047; - leg_nodes_1D(1) = -0.741531185599394439863864773280; - leg_nodes_1D(2) = -0.405845151377397166906606412076; - leg_nodes_1D(3) = 0.0E+00; - leg_nodes_1D(4) = 0.405845151377397166906606412076; - leg_nodes_1D(5) = 0.741531185599394439863864773280; - leg_nodes_1D(6) = 0.949107912342758524526189684047; - } - else if (num == 8){ - leg_nodes_1D(0) = -0.960289856497536231683560868569; - leg_nodes_1D(1) = -0.796666477413626739591553936475; - leg_nodes_1D(2) = -0.525532409916328985817739049189; - leg_nodes_1D(3) = -0.183434642495649804939476142360; - leg_nodes_1D(4) = 0.183434642495649804939476142360; - leg_nodes_1D(5) = 0.525532409916328985817739049189; - leg_nodes_1D(6) = 0.796666477413626739591553936475; - leg_nodes_1D(7) = 0.960289856497536231683560868569; - } - else if (num == 9){ - leg_nodes_1D(0) = -0.968160239507626089835576202903; - leg_nodes_1D(1) = -0.836031107326635794299429788069; - leg_nodes_1D(2) = -0.613371432700590397308702039341; - leg_nodes_1D(3) = -0.324253423403808929038538014643; - leg_nodes_1D(4) = 0.0E+00; - leg_nodes_1D(5) = 0.324253423403808929038538014643; - leg_nodes_1D(6) = 0.613371432700590397308702039341; - leg_nodes_1D(7) = 0.836031107326635794299429788069; - leg_nodes_1D(8) = 0.968160239507626089835576202903; - } - else if (num == 10){ - leg_nodes_1D(0) = -0.9739065285171717200779640120844; - leg_nodes_1D(1) = -0.8650633666889845107320966884234; - leg_nodes_1D(2) = -0.6794095682990244062343273651148; - leg_nodes_1D(3) = -0.4333953941292471907992659431657; - leg_nodes_1D(4) = -0.1488743389816312108848260011297; - leg_nodes_1D(5) = 0.1488743389816312108848260011297; - leg_nodes_1D(6) = 0.4333953941292471907992659431657; - leg_nodes_1D(7) = 0.6794095682990244062343273651148; - leg_nodes_1D(8) = 0.8650633666889845107320966884234; - leg_nodes_1D(9) = 0.9739065285171717200779640120844; - - } - else if (num == 11){ - leg_nodes_1D(0) = -0.9782286581460569928039380011228; - leg_nodes_1D(1) = -0.8870625997680952990751577693039; - leg_nodes_1D(2) = -0.7301520055740493240934162520311; - leg_nodes_1D(3) = -0.5190961292068118159257256694586; - leg_nodes_1D(4) = -0.2695431559523449723315319854008; - leg_nodes_1D(5) = 0.0E+00; - leg_nodes_1D(6) = 0.2695431559523449723315319854008; - leg_nodes_1D(7) = 0.5190961292068118159257256694586; - leg_nodes_1D(8) = 0.7301520055740493240934162520311; - leg_nodes_1D(9) = 0.8870625997680952990751577693039; - leg_nodes_1D(10) = 0.9782286581460569928039380011228; - } - - else if (num == 12){ - leg_nodes_1D(0) = -0.9815606342467192506905490901492; - leg_nodes_1D(1) = -0.9041172563704748566784658661190; - leg_nodes_1D(2) = -0.7699026741943046870368938332128; - leg_nodes_1D(3) = -0.5873179542866174472967024189405; - leg_nodes_1D(4) = -0.3678314989981801937526915366437; - leg_nodes_1D(5) = -0.1252334085114689154724413694638; - leg_nodes_1D(6) = 0.1252334085114689154724413694638; - leg_nodes_1D(7) = 0.3678314989981801937526915366437; - leg_nodes_1D(8) = 0.5873179542866174472967024189405; - leg_nodes_1D(9) = 0.7699026741943046870368938332128; - leg_nodes_1D(10) = 0.9041172563704748566784658661190; - leg_nodes_1D(11) = 0.9815606342467192506905490901492; - } - - else if (num == 13){ - leg_nodes_1D(0) = -0.98418305471858814947282944880710; - leg_nodes_1D(1) = -0.91759839922297796520654783650071; - leg_nodes_1D(2) = -0.80157809073330991279420648958285; - leg_nodes_1D(3) = -0.64234933944034022064398460699551; - leg_nodes_1D(4) = -0.44849275103644685287791285212763; - leg_nodes_1D(5) = -0.23045831595513479406552812109798; - leg_nodes_1D(6) = 0.0E+00; - leg_nodes_1D(7) = 0.23045831595513479406552812109798; - leg_nodes_1D(8) = 0.44849275103644685287791285212763; - leg_nodes_1D(9) = 0.64234933944034022064398460699551; - leg_nodes_1D(10) = 0.80157809073330991279420648958285; - leg_nodes_1D(11) = 0.91759839922297796520654783650071; - leg_nodes_1D(12) = 0.98418305471858814947282944880710; - } - - else if (num == 14){ - leg_nodes_1D(0) = -0.986283808696812338841597266704052; - leg_nodes_1D(1) = -0.928434883663573517336391139377874; - leg_nodes_1D(2) = -0.827201315069764993189794742650394; - leg_nodes_1D(3) = -0.687292904811685470148019803019334; - leg_nodes_1D(4) = -0.515248636358154091965290718551188; - leg_nodes_1D(5) = -0.319112368927889760435671824168475; - leg_nodes_1D(6) = -0.108054948707343662066244650219834; - leg_nodes_1D(7) = 0.108054948707343662066244650219834; - leg_nodes_1D(8) = 0.319112368927889760435671824168475; - leg_nodes_1D(9) = 0.515248636358154091965290718551188; - leg_nodes_1D(10) = 0.687292904811685470148019803019334; - leg_nodes_1D(11) = 0.827201315069764993189794742650394; - leg_nodes_1D(12) = 0.928434883663573517336391139377874; - leg_nodes_1D(13) = 0.986283808696812338841597266704052; - } - - else if (num == 15){ - leg_nodes_1D(0) = -0.987992518020485428489565718586612; - leg_nodes_1D(1) = -0.937273392400705904307758947710209; - leg_nodes_1D(2) = -0.848206583410427216200648320774216; - leg_nodes_1D(3) = -0.724417731360170047416186054613938; - leg_nodes_1D(4) = -0.570972172608538847537226737253910; - leg_nodes_1D(5) = -0.394151347077563369897207370981045; - leg_nodes_1D(6) = -0.201194093997434522300628303394596; - leg_nodes_1D(7) = 0.0E+00; - leg_nodes_1D(8) = 0.201194093997434522300628303394596; - leg_nodes_1D(9) = 0.394151347077563369897207370981045; - leg_nodes_1D(10) = 0.570972172608538847537226737253910; - leg_nodes_1D(11) = 0.724417731360170047416186054613938; - leg_nodes_1D(12) = 0.848206583410427216200648320774216; - leg_nodes_1D(13) = 0.937273392400705904307758947710209; - leg_nodes_1D(14) = 0.987992518020485428489565718586612; - } - - else if (num == 16){ - leg_nodes_1D(0) = -0.989400934991649932596154173450332; - leg_nodes_1D(1) = -0.944575023073232576077988415534608; - leg_nodes_1D(2) = -0.865631202387831743880467897712393; - leg_nodes_1D(3) = -0.755404408355003033895101194847442; - leg_nodes_1D(4) = -0.617876244402643748446671764048791; - leg_nodes_1D(5) = -0.458016777657227386342419442983577; - leg_nodes_1D(6) = -0.281603550779258913230460501460496; - leg_nodes_1D(7) = -0.095012509837637440185319335424958; - leg_nodes_1D(8) = 0.095012509837637440185319335424958; - leg_nodes_1D(9) = 0.281603550779258913230460501460496; - leg_nodes_1D(10) = 0.458016777657227386342419442983577; - leg_nodes_1D(11) = 0.617876244402643748446671764048791; - leg_nodes_1D(12) = 0.755404408355003033895101194847442; - leg_nodes_1D(13) = 0.865631202387831743880467897712393; - leg_nodes_1D(14) = 0.944575023073232576077988415534608; - leg_nodes_1D(15) = 0.989400934991649932596154173450332; - } - - else if (num == 17){ - leg_nodes_1D(0) = -0.990575475314417335675434019940665; - leg_nodes_1D(1) = -0.950675521768767761222716957895803; - leg_nodes_1D(2) = -0.880239153726985902122955694488155; - leg_nodes_1D(3) = -0.781514003896801406925230055520476; - leg_nodes_1D(4) = -0.657671159216690765850302216643002; - leg_nodes_1D(5) = -0.512690537086476967886246568629551; - leg_nodes_1D(6) = -0.351231763453876315297185517095346; - leg_nodes_1D(7) = -0.178484181495847855850677493654065; - leg_nodes_1D(8) = 0.0E+00; - leg_nodes_1D(9) = 0.178484181495847855850677493654065; - leg_nodes_1D(10) = 0.351231763453876315297185517095346; - leg_nodes_1D(11) = 0.512690537086476967886246568629551; - leg_nodes_1D(12) = 0.657671159216690765850302216643002; - leg_nodes_1D(13) = 0.781514003896801406925230055520476; - leg_nodes_1D(14) = 0.880239153726985902122955694488155; - leg_nodes_1D(15) = 0.950675521768767761222716957895803; - leg_nodes_1D(16) = 0.990575475314417335675434019940665; - } - - else if (num == 18){ - leg_nodes_1D(0) = -0.991565168420930946730016004706150; - leg_nodes_1D(1) = -0.955823949571397755181195892929776; - leg_nodes_1D(2) = -0.892602466497555739206060591127145; - leg_nodes_1D(3) = -0.803704958972523115682417455014590; - leg_nodes_1D(4) = -0.691687043060353207874891081288848; - leg_nodes_1D(5) = -0.559770831073947534607871548525329; - leg_nodes_1D(6) = -0.411751161462842646035931793833051; - leg_nodes_1D(7) = -0.251886225691505509588972854877911; - leg_nodes_1D(8) = -0.084775013041735301242261852935783; - leg_nodes_1D(9) = 0.084775013041735301242261852935783; - leg_nodes_1D(10) = 0.251886225691505509588972854877911; - leg_nodes_1D(11) = 0.411751161462842646035931793833051; - leg_nodes_1D(12) = 0.559770831073947534607871548525329; - leg_nodes_1D(13) = 0.691687043060353207874891081288848; - leg_nodes_1D(14) = 0.803704958972523115682417455014590; - leg_nodes_1D(15) = 0.892602466497555739206060591127145; - leg_nodes_1D(16) = 0.955823949571397755181195892929776; - leg_nodes_1D(17) = 0.991565168420930946730016004706150; - } - - else if (num == 19) { - leg_nodes_1D(0) = -0.992406843843584403189017670253260; - leg_nodes_1D(1) = -0.960208152134830030852778840687651; - leg_nodes_1D(2) = -0.903155903614817901642660928532312; - leg_nodes_1D(3) = -0.822714656537142824978922486712713; - leg_nodes_1D(4) = -0.720966177335229378617095860823781; - leg_nodes_1D(5) = -0.600545304661681023469638164946239; - leg_nodes_1D(6) = -0.464570741375960945717267148104102; - leg_nodes_1D(7) = -0.316564099963629831990117328849844; - leg_nodes_1D(8) = -0.160358645640225375868096115740743; - leg_nodes_1D(9) = 0.0E+00; - leg_nodes_1D(10) = 0.160358645640225375868096115740743; - leg_nodes_1D(11) = 0.316564099963629831990117328849844; - leg_nodes_1D(12) = 0.464570741375960945717267148104102; - leg_nodes_1D(13) = 0.600545304661681023469638164946239; - leg_nodes_1D(14) = 0.720966177335229378617095860823781; - leg_nodes_1D(15) = 0.822714656537142824978922486712713; - leg_nodes_1D(16) = 0.903155903614817901642660928532312; - leg_nodes_1D(17) = 0.960208152134830030852778840687651; - leg_nodes_1D(18) = 0.992406843843584403189017670253260; - - } // end if - - -} // end of legendre_nodes_1D function - -KOKKOS_FUNCTION -void legendre_weights_1D( - const CArrayKokkos &leg_weights_1D, // Legendre weights - const int &num) const{ // Interpolation order - if (num == 1){ - leg_weights_1D(0) = 2.0; - } - else if (num == 2){ - leg_weights_1D(0) = 1.0; - leg_weights_1D(1) = 1.0; - } - else if (num == 3){ - leg_weights_1D(0) = 0.555555555555555555555555555555555; - leg_weights_1D(1) = 0.888888888888888888888888888888888; - leg_weights_1D(2) = 0.555555555555555555555555555555555; - } - else if (num == 4){ - leg_weights_1D(0) = 0.347854845137453857373063949221999; - leg_weights_1D(1) = 0.652145154862546142626936050778000; - leg_weights_1D(2) = 0.652145154862546142626936050778000; - leg_weights_1D(3) = 0.347854845137453857373063949221999; - } - else if (num == 5){ - leg_weights_1D(0) = 0.236926885056189087514264040719917; - leg_weights_1D(1) = 0.478628670499366468041291514835638; - leg_weights_1D(2) = 0.568888888888888888888888888888888; - leg_weights_1D(3) = 0.478628670499366468041291514835638; - leg_weights_1D(4) = 0.236926885056189087514264040719917; - } - else if (num == 6){ - leg_weights_1D(0) = 0.171324492379170345040296142172732; - leg_weights_1D(1) = 0.360761573048138607569833513837716; - leg_weights_1D(2) = 0.467913934572691047389870343989550; - leg_weights_1D(3) = 0.467913934572691047389870343989550; - leg_weights_1D(4) = 0.360761573048138607569833513837716; - leg_weights_1D(5) = 0.171324492379170345040296142172732; - } - else if (num == 7){ - leg_weights_1D(0) = 0.129484966168869693270611432679082; - leg_weights_1D(1) = 0.279705391489276667901467771423779; - leg_weights_1D(2) = 0.381830050505118944950369775488975; - leg_weights_1D(3) = 0.417959183673469387755102040816326; - leg_weights_1D(4) = 0.381830050505118944950369775488975; - leg_weights_1D(5) = 0.279705391489276667901467771423779; - leg_weights_1D(6) = 0.129484966168869693270611432679082; - } - else if (num == 8){ - leg_weights_1D(0) = 0.101228536290376259152531354309962; - leg_weights_1D(1) = 0.222381034453374470544355994426240; - leg_weights_1D(2) = 0.313706645877887287337962201986601; - leg_weights_1D(3) = 0.362683783378361982965150449277195; - leg_weights_1D(4) = 0.362683783378361982965150449277195; - leg_weights_1D(5) = 0.313706645877887287337962201986601; - leg_weights_1D(6) = 0.222381034453374470544355994426240; - leg_weights_1D(7) = 0.101228536290376259152531354309962; - } - else if (num == 9){ - leg_weights_1D(0) = 0.081274388361574411971892158110523; - leg_weights_1D(1) = 0.180648160694857404058472031242912; - leg_weights_1D(2) = 0.260610696402935462318742869418632; - leg_weights_1D(3) = 0.312347077040002840068630406584443; - leg_weights_1D(4) = 0.330239355001259763164525069286974; - leg_weights_1D(5) = 0.312347077040002840068630406584443; - leg_weights_1D(6) = 0.260610696402935462318742869418632; - leg_weights_1D(7) = 0.180648160694857404058472031242912; - leg_weights_1D(8) = 0.081274388361574411971892158110523; - } - else if (num == 10){ - leg_weights_1D(0) = 0.066671344308688137593568809893331; - leg_weights_1D(1) = 0.149451349150580593145776339657697; - leg_weights_1D(2) = 0.219086362515982043995534934228163; - leg_weights_1D(3) = 0.269266719309996355091226921569469; - leg_weights_1D(4) = 0.295524224714752870173892994651338; - leg_weights_1D(5) = 0.295524224714752870173892994651338; - leg_weights_1D(6) = 0.269266719309996355091226921569469; - leg_weights_1D(7) = 0.219086362515982043995534934228163; - leg_weights_1D(8) = 0.149451349150580593145776339657697; - leg_weights_1D(9) = 0.066671344308688137593568809893331; - } - else if (num == 11){ - leg_weights_1D(0) = 0.055668567116173666482753720442548; - leg_weights_1D(1) = 0.125580369464904624634694299223940; - leg_weights_1D(2) = 0.186290210927734251426097641431655; - leg_weights_1D(3) = 0.233193764591990479918523704843175; - leg_weights_1D(4) = 0.262804544510246662180688869890509; - leg_weights_1D(5) = 0.272925086777900630714483528336342; - leg_weights_1D(6) = 0.262804544510246662180688869890509; - leg_weights_1D(7) = 0.233193764591990479918523704843175; - leg_weights_1D(8) = 0.186290210927734251426097641431655; - leg_weights_1D(9) = 0.125580369464904624634694299223940; - leg_weights_1D(10)= 0.055668567116173666482753720442548; - } - - else if (num == 12){ - leg_weights_1D(0) = 0.04717533638651182719461596148501; - leg_weights_1D(1) = 0.10693932599531843096025471819399; - leg_weights_1D(2) = 0.16007832854334622633465252954335; - leg_weights_1D(3) = 0.20316742672306592174906445580979; - leg_weights_1D(4) = 0.23349253653835480876084989892487; - leg_weights_1D(5) = 0.24914704581340278500056243604295; - leg_weights_1D(6) = 0.24914704581340278500056243604295; - leg_weights_1D(7) = 0.23349253653835480876084989892487; - leg_weights_1D(8) = 0.20316742672306592174906445580979; - leg_weights_1D(9) = 0.16007832854334622633465252954335; - leg_weights_1D(10) = 0.10693932599531843096025471819399; - leg_weights_1D(11) = 0.04717533638651182719461596148501; - } - - else if (num == 13){ - leg_weights_1D(0) = 0.04048400476531587952002159220098; - leg_weights_1D(1) = 0.09212149983772844791442177595379; - leg_weights_1D(2) = 0.13887351021978723846360177686887; - leg_weights_1D(3) = 0.17814598076194573828004669199609; - leg_weights_1D(4) = 0.20781604753688850231252321930605; - leg_weights_1D(5) = 0.22628318026289723841209018603977; - leg_weights_1D(6) = 0.23255155323087391019458951526883; - leg_weights_1D(7) = 0.22628318026289723841209018603977; - leg_weights_1D(8) = 0.20781604753688850231252321930605; - leg_weights_1D(9) = 0.17814598076194573828004669199609; - leg_weights_1D(10) = 0.13887351021978723846360177686887; - leg_weights_1D(11) = 0.09212149983772844791442177595379; - leg_weights_1D(12) = 0.04048400476531587952002159220098; - } - - else if (num == 14){ - leg_weights_1D(0) = 0.03511946033175186303183287613819; - leg_weights_1D(1) = 0.08015808715976020980563327706285; - leg_weights_1D(2) = 0.12151857068790318468941480907247; - leg_weights_1D(3) = 0.15720316715819353456960193862384; - leg_weights_1D(4) = 0.18553839747793781374171659012515; - leg_weights_1D(5) = 0.20519846372129560396592406566121; - leg_weights_1D(6) = 0.21526385346315779019587644331626; - leg_weights_1D(7) = 0.21526385346315779019587644331626; - leg_weights_1D(8) = 0.20519846372129560396592406566121; - leg_weights_1D(9) = 0.18553839747793781374171659012515; - leg_weights_1D(10) = 0.15720316715819353456960193862384; - leg_weights_1D(11) = 0.12151857068790318468941480907247; - leg_weights_1D(12) = 0.08015808715976020980563327706285; - leg_weights_1D(13) = 0.03511946033175186303183287613819; - } - - - else if (num == 15){ - leg_weights_1D(0) = 0.03075324199611726835462839357720; - leg_weights_1D(1) = 0.07036604748810812470926741645066; - leg_weights_1D(2) = 0.10715922046717193501186954668586; - leg_weights_1D(3) = 0.13957067792615431444780479451102; - leg_weights_1D(4) = 0.16626920581699393355320086048120; - leg_weights_1D(5) = 0.18616100001556221102680056186642; - leg_weights_1D(6) = 0.19843148532711157645611832644383; - leg_weights_1D(7) = 0.20257824192556127288062019996751; - leg_weights_1D(8) = 0.19843148532711157645611832644383; - leg_weights_1D(9) = 0.18616100001556221102680056186642; - leg_weights_1D(10) = 0.16626920581699393355320086048120; - leg_weights_1D(11) = 0.13957067792615431444780479451102; - leg_weights_1D(12) = 0.10715922046717193501186954668586; - leg_weights_1D(13) = 0.07036604748810812470926741645066; - leg_weights_1D(14) = 0.03075324199611726835462839357720; - } - - - else if (num == 16){ - leg_weights_1D(0) = 0.02715245941175409485178057245601; - leg_weights_1D(1) = 0.06225352393864789286284383699437; - leg_weights_1D(2) = 0.09515851168249278480992510760224; - leg_weights_1D(3) = 0.12462897125553387205247628219201; - leg_weights_1D(4) = 0.14959598881657673208150173054747; - leg_weights_1D(5) = 0.16915651939500253818931207903035; - leg_weights_1D(6) = 0.18260341504492358886676366796921; - leg_weights_1D(7) = 0.18945061045506849628539672320828; - leg_weights_1D(8) = 0.18945061045506849628539672320828; - leg_weights_1D(9) = 0.18260341504492358886676366796921; - leg_weights_1D(10) = 0.16915651939500253818931207903035; - leg_weights_1D(11) = 0.14959598881657673208150173054747; - leg_weights_1D(12) = 0.12462897125553387205247628219201; - leg_weights_1D(13) = 0.09515851168249278480992510760224; - leg_weights_1D(14) = 0.06225352393864789286284383699437; - leg_weights_1D(15) = 0.02715245941175409485178057245601; - } - - - else if (num == 17){ - leg_weights_1D(0) = 0.02414830286854793196011002628756; - leg_weights_1D(1) = 0.05545952937398720112944016535824; - leg_weights_1D(2) = 0.08503614831717918088353537019106; - leg_weights_1D(3) = 0.11188384719340397109478838562635; - leg_weights_1D(4) = 0.13513636846852547328631998170235; - leg_weights_1D(5) = 0.15404576107681028808143159480195; - leg_weights_1D(6) = 0.16800410215645004450997066378832; - leg_weights_1D(7) = 0.17656270536699264632527099011319; - leg_weights_1D(8) = 0.17944647035620652545826564426188; - leg_weights_1D(9) = 0.17656270536699264632527099011319; - leg_weights_1D(10) = 0.16800410215645004450997066378832; - leg_weights_1D(11) = 0.15404576107681028808143159480195; - leg_weights_1D(12) = 0.13513636846852547328631998170235; - leg_weights_1D(13) = 0.11188384719340397109478838562635; - leg_weights_1D(14) = 0.08503614831717918088353537019106; - leg_weights_1D(15) = 0.05545952937398720112944016535824; - leg_weights_1D(16) = 0.02414830286854793196011002628756; - } - - else if (num == 18){ - leg_weights_1D(0) = 0.02161601352648331031334271026645; - leg_weights_1D(1) = 0.04971454889496979645333494620263; - leg_weights_1D(2) = 0.07642573025488905652912967761663; - leg_weights_1D(3) = 0.10094204410628716556281398492483; - leg_weights_1D(4) = 0.12255520671147846018451912680020; - leg_weights_1D(5) = 0.14064291467065065120473130375194; - leg_weights_1D(6) = 0.15468467512626524492541800383637; - leg_weights_1D(7) = 0.16427648374583272298605377646592; - leg_weights_1D(8) = 0.16914238296314359184065647013498; - leg_weights_1D(9) = 0.16914238296314359184065647013498; - leg_weights_1D(10) = 0.16427648374583272298605377646592; - leg_weights_1D(11) = 0.15468467512626524492541800383637; - leg_weights_1D(12) = 0.14064291467065065120473130375194; - leg_weights_1D(13) = 0.12255520671147846018451912680020; - leg_weights_1D(14) = 0.10094204410628716556281398492483; - leg_weights_1D(15) = 0.07642573025488905652912967761663; - leg_weights_1D(16) = 0.04971454889496979645333494620263; - leg_weights_1D(17) = 0.02161601352648331031334271026645; - } - - else if (num == 19) { - leg_weights_1D(0) = 0.01946178822972647703631204146443; - leg_weights_1D(1) = 0.04481422676569960033283815740199; - leg_weights_1D(2) = 0.06904454273764122658070825800601; - leg_weights_1D(3) = 0.09149002162244999946446209412383; - leg_weights_1D(4) = 0.11156664554733399471602390168176; - leg_weights_1D(5) = 0.12875396253933622767551578485687; - leg_weights_1D(6) = 0.14260670217360661177574610944190; - leg_weights_1D(7) = 0.15276604206585966677885540089766; - leg_weights_1D(8) = 0.15896884339395434764995643946504; - leg_weights_1D(9) = 0.16105444984878369597916362532091; - leg_weights_1D(10) = 0.15896884339395434764995643946504; - leg_weights_1D(11) = 0.15276604206585966677885540089766; - leg_weights_1D(12) = 0.14260670217360661177574610944190; - leg_weights_1D(13) = 0.12875396253933622767551578485687; - leg_weights_1D(14) = 0.11156664554733399471602390168176; - leg_weights_1D(15) = 0.09149002162244999946446209412383; - leg_weights_1D(16) = 0.06904454273764122658070825800601; - leg_weights_1D(17) = 0.04481422676569960033283815740199; - leg_weights_1D(18) = 0.01946178822972647703631204146443; - } // end if - - -} // end of legendre_weights_1D function - - -// --- ref index access member functions --- - - -KOKKOS_INLINE_FUNCTION -int dof_rid_2D(int i, int j) const -{ - return i + j*num_dofs_1d; -} - - -KOKKOS_INLINE_FUNCTION -int elem_dof_rid_2D(int i, int j) const -{ - return i + j*num_elem_dofs_1d; -} - -KOKKOS_INLINE_FUNCTION -int lobatto_rid_2D(int i, int j) const -{ - return i + j*num_gauss_lob_1d; -} - -KOKKOS_INLINE_FUNCTION -int legendre_rid_2D(int i, int j) const -{ - return i + j*num_gauss_leg_1d; -} - -KOKKOS_INLINE_FUNCTION -int legendre_rid_2D(int i, int j) const -{ - return i + j*num_gauss_leg_1d; -} - -KOKKOS_FUNCTION -void get_basis(const CArrayKokkos &basis, - const CArrayKokkos &val_1d, - const CArrayKokkos &val_2d, - const CArrayKokkos &point) const{ - - - // initialize to zero // - for (int i =0; i< num_dofs_1d; i++){ - val_1d(i) = 0.0; - } - - // Calculate 1D basis for the X coordinate of the point - lagrange_basis_1D(val_1d, point(0)); - - // Save the basis value at the point to a temp array and zero out the temp array - for(int i = 0; i < num_dofs_1d; i++){ - val_2d(i,0) = val_1d(i); - val_1d(i) = 0.0; - } - - // Calculate 1D basis for the Y coordinate of the point - lagrange_basis_1D(val_1d, point(1)); - - // Save the basis value at the point to a temp array and zero out the temp array - for(int i = 0; i < num_dofs_1d; i++){ - val_2d(i,1) = val_1d(i); - val_1d(i) = 0.0; - } - - - // Multiply the i, j, k components of the basis from each node - // to get the tensor product basis for the node - - for(int j = 0; j < num_dofs_1d; j++){ - for(int i = 0; i < num_dofs_1d; i++){ - - int dof_rlid = dof_rid_2d(i,j); - basis(dof_rlid) = val_2d(i,0)*val_2d(j,1); - } - } - - - for (int i =0; i< num_dofs_1d; i++){ - val_1d(i) = 0.0; - val_2d(i,0) = 0.0; - val_2d(i,1) = 0.0; - } -} - -KOKKOS_FUNCTION -void partial_xi_basis(const CArrayKokkos &partial_xi, - const CArrayKokkos &val_1d, - const CArrayKokkos &val_2d, - const CArrayKokkos &Dval_1d, - const CArrayKokkos &Dval_2d, - const CArrayKokkos &point) const { - - //initialize// - for (int i = 0; i < num_dofs_1d; i++){ - val_1d(i) = 0.0; - Dval_1d(i) = 0.0; - } - - - // Calculate 1D partial w.r.t. xi for the X coordinate of the point - lagrange_derivative_1D(Dval_1d, point(0)); - - - // Save the basis value at the point to a temp array and zero out the temp array - for(int i = 0; i < num_dofs_1d; i++){ - - Dval_2d(i,0) = Dval_1d(i); - Dval_1d(i) = 0.0; - } - - - // Calculate 1D basis for the Y coordinate of the point - lagrange_basis_1D(val_1d, point(1)); - - // Save the basis value at the point to a temp array and zero out the temp array - for(int i = 0; i < num_dofs_1d; i++){ - - val_2d(i,1) = val_1d(i); - val_1d(i) = 0.0; - } - - // Multiply the i, j, k components of the basis and partial_xi from each node - // to get the tensor product partial derivatives of the basis at each node - - for(int j = 0; j < num_dofs_1d; j++){ - for(int i = 0; i < num_dofs_1d; i++){ - - int dof_rlid = dof_rid_2D(i,j); - - // Partial w.r.t xi - partial_xi(dof_rlid) = Dval_2d(i, 0)*val_2d(j, 1); - - } - } - - - for (int i =0; i< num_dofs_1d; i++){ - val_1d(i) = 0.0; - val_2d(i,0) = 0.0; - val_2d(i,1) = 0.0; - Dval_1d(i) = 0.0; - Dval_2d(i,0) = 0.0; - Dval_2d(i,1) = 0.0; - } -} - -KOKKOS_FUNCTION -void partial_eta_basis(const CArrayKokkos &partial_eta, - const CArrayKokkos &val_1d, - const CArrayKokkos &val_2d, - const CArrayKokkos &Dval_1d, - const CArrayKokkos &Dval_2d, - const CArrayKokkos &point) const { - - //initialize// - for (int i = 0; i < num_dofs_1d; i++){ - val_1d(i) = 0.0; - Dval_1d(i) = 0.0; - } - - // Calculate 1D basis for the Y coordinate of the point - lagrange_basis_1D(val_1d, point(0)); - - // Save the basis value at the point to a temp array and zero out the temp array - for(int i = 0; i < num_dofs_1d; i++){ - val_2d(i,0) = val_1d(i); - val_1d(i) = 0.0; - } - - // Calculate 1D partial w.r.t. eta for the Y coordinate of the point - lagrange_derivative_1D(Dval_1d, point(1)); - - // Save the basis value at the point to a temp array and zero out the temp array - for(int i = 0; i < num_dofs_1d; i++){ - - Dval_2d(i,1) = Dval_1d(i); - - Dval_1d(i) = 0.0; - } - - - // Multiply the i, j, k components of the basis and partial_eta from each node - // to get the tensor product partial derivatives of the basis at each node - for(int j = 0; j < num_dofs_1d; j++){ - for(int i = 0; i < num_dofs_1d; i++){ - - int dof_rlid = dof_rid_2D(i,j); - - // Partial w.r.t xi - partial_eta(dof_rlid) = val_2d(i, 0)*Dval_2d(j, 1); - - } - } - - for (int i =0; i< num_dofs_1d; i++){ - val_1d(i) = 0.0; - val_2d(i,0) = 0.0; - val_2d(i,1) = 0.0; - Dval_1d(i) = 0.0; - Dval_2d(i,0) = 0.0; - Dval_2d(i,1) = 0.0; - } -} - - -KOKKOS_FUNCTION -void partial_mu_basis(const CArrayKokkos &partial_mu, - const CArrayKokkos &val_1d, - const CArrayKokkos &val_2d, - const CArrayKokkos &Dval_1d, - const CArrayKokkos &Dval_2d, - const CArrayKokkos &point) const { - - //initialize// - for (int i = 0; i < num_dofs_1d; i++){ - val_1d(i) = 0.0; - Dval_1d(i) = 0.0; - } - - // Calculate 1D basis for the X coordinate of the point - lagrange_basis_1D(val_1d, point(0)); - - // Save the basis value at the point to a temp array and zero out the temp array - for(int i = 0; i < num_dofs_1d; i++){ - - val_2d(i,0) = val_1d(i); - val_1d(i) = 0.0; - } - - - // Calculate 1D basis for the Y coordinate of the point - lagrange_basis_1D(val_1d, point(1)); - - // Save the basis value at the point to a temp array and zero out the temp array - for(int i = 0; i < num_dofs_1d; i++){ - - val_2d(i,1) = val_1d(i); - val_1d(i) = 0.0; - } - - - // Multiply the i, j, k components of the basis and partial_xi from each node - // to get the tensor product partial derivatives of the basis at each node - for(int j = 0; j < num_dofs_1d; j++){ - for(int i = 0; i < num_dofs_1d; i++){ - - int dof_rlid = dof_rid_2D(i,j); - - // Partial w.r.t mu - partial_mu(dof_rlid) = val_2d(i, 0)*val_2d(j, 1); - - } - } - - for (int i =0; i< num_dofs_1d; i++){ - val_1d(i) = 0.0; - val_2d(i,0) = 0.0; - val_2d(i,1) = 0.0; - Dval_1d(i) = 0.0; - Dval_2d(i,0) = 0.0; - Dval_2d(i,1) = 0.0; - } -} - -KOKKOS_FUNCTION -void get_bernstein_basis(const CArrayKokkos &elem_basis, - const CArrayKokkos &elem_val_1d, - const CArrayKokkos &elem_val_2d, - const CArrayKokkos &point) const { - - // initialize to zero // - for (int i =0; i< num_elem_dofs_1d; i++){ - elem_val_1d(i) = 0.0; - } - - // Calculate 1D basis for the X coordinate of the point - bernstein_basis_1D(elem_val_1d, point(0)); - - // Save the basis value at the point to a temp array and zero out the temp array - for(int i = 0; i < num_elem_dofs_1d; i++){ - elem_val_2d(i,0) = elem_val_1d(i); - elem_val_1d(i) = 0.0; - } - - // Calculate 1D basis for the Y coordinate of the point - bernstein_basis_1D(elem_val_1d, point(1)); - - // Save the basis value at the point to a temp array and zero out the temp array - for(int i = 0; i < num_elem_dofs_1d; i++){ - elem_val_2d(i,1) = elem_val_1d(i); - elem_val_1d(i) = 0.0; - } - - - - // Multiply the i, j, k components of the basis from each node - // to get the tensor product basis for the node - for(int j = 0; j < num_elem_dofs_1d; j++){ - for(int i = 0; i < num_elem_dofs_1d; i++){ - - int dof_rlid = elem_dof_rid_2D(i,j); - elem_basis(dof_rlid) = elem_val_2d(i,0)*elem_val_2d(j,1); - } - } - - - for (int i =0; i< num_elem_dofs_1d; i++){ - elem_val_1d(i) = 0.0; - elem_val_2d(i,0) = 0.0; - elem_val_2d(i,1) = 0.0; - } -} - -KOKKOS_FUNCTION -void lagrange_basis_1D( - const CArrayKokkos &interp, // interpolant from each basis - const double x_point) const{ // point of interest in element - - - // calculate the basis value associated with each node_i - for(int vert_i = 0; vert_i < num_dofs_1d; vert_i++){ - - double numerator = 1.0; // placeholder numerator - double denominator = 1.0; // placeholder denominator - double interpolant = 1.0; // placeholder value of numerator/denominator - - - for(int vert_j = 0; vert_j < num_dofs_1d; vert_j++){ // looping over the verts !=vert_i - if (vert_j != vert_i ){ - - // Calculate the numerator - numerator = numerator*(x_point - dof_positions_1d(vert_j)); - - // Calculate the denominator - denominator = denominator*(dof_positions_1d(vert_i) - dof_positions_1d(vert_j)); - - }//end if - - interpolant = numerator/denominator; // storing a single value for interpolation for node vert_i - - } // end looping over nodes != vert_i - - // writing value to vectors for later use - interp(vert_i) = interpolant; // Interpolant value at given point - - } // end loop over all nodes -} // end of Lagrange_1D function - - -KOKKOS_INLINE_FUNCTION -void lagrange_derivative_1D( - const CArrayKokkos &derivative, // derivative - const double x_point) const { // point of interest in element - - for(int vert_i = 0; vert_i < num_dofs_1d; vert_i++){ // looping over the nodes - - - double denominator = 1.0; // placeholder denominator - double num_gradient = 0.0; // placeholder for numerator of the gradient - double gradient = 0.0; - - for(int vert_j = 0; vert_j < num_dofs_1d; vert_j++){ // looping over the nodes !=vert_i - if (vert_j != vert_i ){ - - // Calculate the denominator that is the same for - // both the basis and the gradient of the basis - denominator = denominator*(dof_positions_1d(vert_i) - dof_positions_1d(vert_j)); - - double product_gradient = 1.0; - - // Calculate the numerator of the gradient - for(int N = 0; N < num_dofs_1d; N++){ // looping over the nodes !=vert_i - - if (N != vert_j && N != vert_i ){ - product_gradient = product_gradient * (x_point - dof_positions_1d(N)); - }// end if - }//end for - - // Sum over the product of the numerator - // contributions from each node - num_gradient += product_gradient; - - }//end if - - gradient = (num_gradient/denominator); // storing the derivative of the interpolating function - - } // end looping over nodes != vert_i - - // writing value to vectors for later use - derivative(vert_i) = gradient; // derivative of each function - - } // end loop over all nodes -} // end of Lagrange_1D function - -KOKKOS_INLINE_FUNCTION -void bernstein_basis_1D( - const CArrayKokkos &interp, - const double X) const { - - for( int dof_i = 0; dof_i < num_elem_dofs_1d; dof_i++){ - interp(dof_i) = eval_bernstein(num_elem_dofs_1d-1, dof_i, X); - } -} - -// WARNING WARNING WARNING: Change to for loop? // -KOKKOS_INLINE_FUNCTION -double eval_bernstein ( - const size_t n,// polynomial order - const size_t v,// index - const double X) const { // point at which to evaluate polynomial - - if ( n == 0 && v != 0 ) return 0.0; - if ( n == 0 && v == 0 ) return 1.0; - if ( n < v ) return 0.0; - return 0.5*((1.0-X)*eval_bernstein(n-1, v, X) + (1.0+X)*eval_bernstein(n-1, v-1, X)); -} -#endif - -}; - -} // end namespace elements - -#endif diff --git a/src/geometry/geometry.h b/src/geometry/geometry.h index 1b8570d3..0dca3dea 100644 --- a/src/geometry/geometry.h +++ b/src/geometry/geometry.h @@ -1,13 +1,172 @@ #ifndef GEOMETRY_H #define GEOMETRY_H +// This pulls in kokkos, matar, mesh, ref_elem stuff, and PT-Scotch +#include "ELEMENTS.h" #include "matar.h" #include "shapes.h" +///////////////////////////////////////////////////////////////////////////// +/// +/// \fn jacobian +/// +/// \brief Calculates the jacobian matrix in 2D, 3D, 4D, ... +/// +/// \param jacobian The jacobian matrix calculated in the routine, J[dims,dims] +/// \param node_coords An array containing all node coords on the mesh +/// \param nodes_in_an_elem The node indices in a single elem, its a 1D array +/// \param a_grad_basis The gradient of the basis at a single point, Grad[DOFs,dims] +/// +///////////////////////////////////////////////////////////////////////////// +template +KOKKOS_INLINE_FUNCTION +void jacobian( + const T1 &jacobian, // e.g., ViewCArrayKokkos + const T2 &node_coords, // e.g., DCArrayKokkos + const T3 &nodes_in_an_elem, // e.g., ViewCArrayKokkos + const T4 &a_grad_basis){ // e.g., ViewCArrayKokkos + const size_t dims = a_grad_basis.dims(1); + const size_t num_dofs_in_elem = nodes_in_an_elem.size(); + // setting jacobian matrix to all zeros + for(size_t i = 0; i < dims; i++) // looping over dimension + for(size_t j = 0; j < dims; j++){ // looping over dimension + jacobian(i, j) = 0.0; + } // end for + // Calculate Jacobian: J[i,j] = partial x_i/partial \xi_j + for(size_t i = 0; i < dims; i++) // looping over dimension (partial) + for(size_t j = 0; j < dims; j++) // looping over dimension (node position) + for(size_t node_lid = 0; node_lid < num_dofs_in_elem; node_lid++){ + const size_t node_gid = nodes_in_an_elem(node_lid); + jacobian(i, j) += node_coords(node_gid, i)*a_grad_basis(node_lid, j); + } // end for + + +} // end of jacobian function + + +///////////////////////////////////////////////////////////////////////////// +/// +/// \fn build_quadrature_point_connectivity +/// +/// \brief Using mesh coordinates, mesh connectivity data structures, and +/// reference element to build connectivity between quadrature points +/// +/// This routine finds the corresponding local index for the face-adjacent +/// quadrature point on the surface. This map is critical to e.g. +/// discontinous Galerkin or cell-centered finite volume methods that solve a +/// Riemann problem on the surface at quadrature points. +/// nbr_qpt_lid = surf_qpt_qpt_map(surf_gid,face_lid,qpt_lid) +/// where surf_gid=[0:num_surfs], face_lid=[0,1], and qpt_lid=[0:num_surf_qpts] +/// so +/// nbr_qpt_lid = surf_qpt_qpt_map(surf_gid,0,qpt_lid) +/// qpt_lid = surf_qpt_qpt_map(surf_gid,1,nbr_qpt_lid) +/// +/// +/// \param Mesh The unstructured mesh object +/// \param RefSurface& The reference surface object +/// \param surf_qpt_qpt_map Array holding the quadrature point surface map +/// \param node_coords The mesh nodal coordinates +/// +/// \return void +/// +///////////////////////////////////////////////////////////////////////////// +void build_quadrature_point_connectivity(const swage::Mesh_t& Mesh, + const elements::ReferenceSurface_t& RefSurf, + CArrayKokkos& surf_qpt_qpt_map, + const DCArrayKokkos& node_coords){ + + const size_t elem_dims = RefSurf.elem_dims; + const size_t num_surfs = surf_qpt_qpt_map.dims(0); + const size_t num_nodes_in_elem = Mesh.num_nodes_in_elem; + const size_t num_qpts_in_surf = RefSurf.qpt_basis.dims(1); + + FOR_ALL(surf_gid, 0, num_surfs, { + + // get the first elem id and face in this surf + const size_t elem_gid = Mesh.elems_in_surf(surf_gid, 0); + const size_t face_lid = Mesh.faces_in_surf(surf_gid, 0); + + const size_t num_elems_in_surf = Mesh.num_elems_in_surf(surf_gid); + + // get the neighbor, where on the bdys, we use the first elem info + size_t nbr_elem_gid = elem_gid; + size_t nbr_face_lid = face_lid; + if(num_elems_in_surf==2){ + nbr_elem_gid = Mesh.elems_in_surf(surf_gid, 1); // second elem + nbr_face_lid = Mesh.faces_in_surf(surf_gid, 1); // second elem + } + + ViewCArrayKokkos nodes_in_elem(&Mesh.nodes_in_elem(elem_gid,0), num_nodes_in_elem); + ViewCArrayKokkos nodes_in_nbr_elem(&Mesh.nodes_in_elem(nbr_elem_gid,0), num_nodes_in_elem); + + // loop the quadrature points on side_lid 0 and match them to side_lid 1 + for(size_t qpt_lid=0; qpt_lid=0) continue; // this qpt was tagged + + ViewCArrayKokkos a_basis(&RefSurf.qpt_basis(face_lid,qpt_lid,0), num_nodes_in_elem); + + double qpt_x[3]; + + for (size_t dim=0; dim=0) continue; // this nbr_qpt was tagged + + ViewCArrayKokkos nbr_a_basis(&RefSurf.qpt_basis(nbr_face_lid,nbr_qpt_lid,0), num_nodes_in_elem); + + double nbr_qpt_x[3]; + + for(size_t dim=0; dim I + | | | | + | | | | + | | | | + | 2------------|-----3 + | / | / + | / | / + | / | / + | / | / + |/ |/ + 0------------------1 + + nodes are ordered for outward normal + patch 0: [0,4,6,2] xi-minus dir + patch 1: [1,3,7,5] xi-plus dir + patch 2: [0,1,5,4] eta-minus dir + patch 3: [3,2,6,7] eta-plus dir + patch 4: [0,2,3,1] zeta-minus dir + patch 6: [4,5,7,6] zeta-plus dir + + + 2D: + + J + ^ + | + 3---2 + | | --> I + 0---1 + + patch 0: [0, 3] xi-minus dir + patch 1: [1, 2] xi-plus dir + patch 2: [0, 1] eta-minus dir + patch 3: [3, 2] eta-plus dir + +*/ + +namespace swage +{ + +// sort in ascending order using bubble sort +KOKKOS_INLINE_FUNCTION +void bubble_sort(size_t arr[], const size_t num) +{ + for (size_t i = 0; i < (num - 1); i++) { + for (size_t j = 0; j < (num - i - 1); j++) { + if (arr[j] > arr[j + 1]) { + size_t temp = arr[j]; + arr[j] = arr[j + 1]; + arr[j + 1] = temp; + } // end if + } // end for j + } // end for i +} // end function + + + +template +KOKKOS_INLINE_FUNCTION +void bubble_sort(T arr) +{ + const size_t num = arr.dims(0); + for (size_t i = 0; i < (num - 1); i++) { + for (size_t j = 0; j < (num - i - 1); j++) { + if (arr(j) > arr(j + 1)) { + size_t temp = arr(j); + arr(j) = arr(j + 1); + arr(j + 1) = temp; + } // end if + } // end for j + } // end for i +} // end function + + + +struct zones_in_elem_t +{ + private: + size_t num_zones_in_elem_; + public: + zones_in_elem_t() { + }; + + zones_in_elem_t(const size_t num_zones_in_elem_inp) { + this->num_zones_in_elem_ = num_zones_in_elem_inp; + }; + + // return global zone index for given local zone index in an element + size_t host(const size_t elem_gid, const size_t zone_lid) const + { + return elem_gid * num_zones_in_elem_ + zone_lid; + }; + + // Return the global zone ID given an element gloabl ID and a local zone ID + KOKKOS_INLINE_FUNCTION + size_t operator()(const size_t elem_gid, const size_t zone_lid) const + { + return elem_gid * num_zones_in_elem_ + zone_lid; + }; +}; + +// if material points are defined strictly internal to the element. +struct gauss_in_elem_t +{ + private: + size_t num_gauss_in_elem_; + public: + gauss_in_elem_t() { + }; + + gauss_in_elem_t(const size_t num_gauss_in_elem_inp) { + this->num_gauss_in_elem_ = num_gauss_in_elem_inp; + }; + + // return global gauss index for given local gauss index in an element + size_t host(const size_t elem_gid, const size_t leg_gauss_lid) const + { + return elem_gid * num_gauss_in_elem_ + leg_gauss_lid; + }; + + // Return the global gauss ID given an element gloabl ID and a local gauss ID + KOKKOS_INLINE_FUNCTION + size_t operator()(const size_t elem_gid, const size_t leg_gauss_lid) const + { + return elem_gid * num_gauss_in_elem_ + leg_gauss_lid; + }; +}; + + +/// +struct corners_in_elem_t +{ + private: + size_t num_corners_in_elem_; + public: + corners_in_elem_t() { + }; + + corners_in_elem_t(const size_t num_corners_in_elem_inp) { + this->num_corners_in_elem_ = num_corners_in_elem_inp; + }; + + // return global gauss index for given local gauss index in an element + size_t host(const size_t elem_gid, const size_t corner_lid) const + { + return elem_gid * num_corners_in_elem_ + corner_lid; + }; + + // Return the global gauss ID given an element gloabl ID and a local gauss ID + KOKKOS_INLINE_FUNCTION + size_t operator()(const size_t elem_gid, const size_t corner_lid) const + { + return elem_gid * num_corners_in_elem_ + corner_lid; + }; +}; + +/// A functor to access the patches in a surface +struct patches_in_surf_t +{ + private: + size_t num_patches_in_surf_; + public: + patches_in_surf_t() { + }; + + patches_in_surf_t(const size_t num_patches_in_surf_inp) { + this->num_patches_in_surf_ = num_patches_in_surf_inp; + }; + + // return global patch index for given local patch index on a surface + size_t host(const size_t surf_gid, const size_t patch_lid) const + { + return surf_gid * num_patches_in_surf_ + patch_lid; + }; + + // return global patch index for given local patch index on a surface + KOKKOS_INLINE_FUNCTION + size_t operator()(const size_t surf_gid, const size_t patch_lid) const + { + return surf_gid * num_patches_in_surf_ + patch_lid; + }; +}; + +//////////////////////////////////////////////////////////////////////////////////// +/// +/// \fn get_surf_node_lids +/// +/// \brief builds the 1D local indexing to access the surface nodes in the element +/// +/// The element local indexing convention follows an i,j,k access pattern. This +/// function leverages the i,j,k convetion and saves the 1D index to access the +/// the surface nodes from the element. The populated array is 2D, being accessed +/// using the element face local index and then a local node index. The 2D array +/// is allocated outside the function based on mesh inputs -- number of 1D nodes +/// used to build a tensor product element and the number of dimensions. +/// +/// Important: the node ordering in this 2D array is based on the i,j,k pattern +/// of the element and not the surface. +/// +/// Important: the order of the faces in the element is iminus, iplus, jminus, +/// jplus, kminus, and then kplus. This order matches the patch +/// node lids. The convention used to build patches must match surfaces. +/// +/// The 2D array is accessed as: +/// surf_node_ordering_in_elem(face_lid, node_lid) +/// +/// \param surf_node_ordering_in_elem the array populated with local node indexing +/// \param num_1D The number of nodes (DOFs) in 1D used to build the element +/// \param num_dims The dimensions +/// +/// \return void +/// +/////////////////////////////////////////////////////////////////////////////////// +void get_surf_node_lids(CArrayKokkos& surf_node_ordering_in_elem, + const size_t num_1D, + const size_t num_dims){ + + if (num_dims == 3) { + // 3D arbitrary order elements + // num_1D = Pn+1 + // Nodes indices in elem = i + j*num_1D + k*num_1D*num_1D; + + // iminus-dir followed by iplus-dir + size_t face_lid = 0; + for (size_t i = 0; i &patch_node_ordering_in_elem, + const size_t num_1D, + const size_t num_dims){ + + size_t i_patch = 0; + size_t j_patch = 0; + size_t k_patch = 0; + size_t face_lid = 0; + + if (num_dims == 3) { + // node ordering in patches of an arbitrary-order element + + /* + + i,j,k layout + + k j + | / + |/ + o-->i + + i=0,imax surface + + o (j+1,k+1) + / | + (j,k+1) o o (j+1,k) + | / + (j,k) o + + */ + + // iminus-dir patches + i_patch = 0; + FOR_ALL(k, 0, num_1D-1, + j, 0, num_1D-1, { + + size_t node_lid = 0; + size_t surf_patch_lid = j+k*(num_1D-1); + + // node_lid 0 in patch + // index = i + j*num_1D + k*num_1D*num_1D; + patch_node_ordering_in_elem(face_lid, surf_patch_lid, node_lid) = + i_patch + j * num_1D + k * num_1D * num_1D; // node_rid(i_patch, j, k, num_1D); + node_lid++; + + // node_lid 1 in patch + // index = i + j*num_1D + (k+1)*num_1D*num_1D; + patch_node_ordering_in_elem(face_lid, surf_patch_lid, node_lid) = + i_patch + j * num_1D + (k + 1) * num_1D * num_1D; // node_rid(i_patch, j, k+1, num_1D); + node_lid++; + + // node_lid 2 in patch + // index = i + (j+1)*num_1D + (k+1)*num_1D*num_1D; + patch_node_ordering_in_elem(face_lid, surf_patch_lid, node_lid) = + i_patch + (j + 1) * num_1D + (k + 1) * num_1D * num_1D; // node_rid(i_patch, j+1, k+1, num_1D); + node_lid++; + + // node_lid 3 in patch + // index = i + (j+1)*num_1D + k*num_1D*num_1D; + patch_node_ordering_in_elem(face_lid, surf_patch_lid, node_lid) = + i_patch + (j + 1) * num_1D + k * num_1D * num_1D; // node_rid(i_patch, j+1, k, num_1D); + + }); // end parallel for + face_lid ++; + + // iplus-dir patches + i_patch = num_1D - 1; + FOR_ALL(k, 0, num_1D-1, + j, 0, num_1D-1, { + + size_t node_lid = 0; + size_t surf_patch_lid = j+k*(num_1D-1); + + // node_lid 0 in patch + // index = i + j*num_1D + k*num_1D*num_1D; + patch_node_ordering_in_elem(face_lid, surf_patch_lid, node_lid) = + i_patch + j * num_1D + k * num_1D * num_1D; // node_rid(i_patch, j, k, num_1D); + node_lid++; + + // node_lid 1 in patch + // index = i + (j+1)*num_1D + k*num_1D*num_1D; + patch_node_ordering_in_elem(face_lid, surf_patch_lid, node_lid) = + i_patch + (j + 1) * num_1D + k * num_1D * num_1D; // node_rid(i_patch, j+1, k, num_1D); + node_lid++; + + // node_lid 2 in patch + // index = i + (j+1)*num_1D + (k+1)*num_1D*num_1D; + patch_node_ordering_in_elem(face_lid, surf_patch_lid, node_lid) = + i_patch + (j + 1) * num_1D + (k + 1) * num_1D * num_1D; // node_rid(i_patch, j+1, k+1, num_1D); + node_lid++; + + // node_lid 3 in patch + // index = i + j*num_1D + (k+1)*num_1D*num_1D; + patch_node_ordering_in_elem(face_lid, surf_patch_lid, node_lid) = + i_patch + j * num_1D + (k + 1) * num_1D * num_1D; // node_rid(i_patch, j, k+1, num_1D); + + }); // end parallel for + face_lid ++; + + + /* + i,j,k layout + + k j + | / + |/ + o-->i + + + j=0,jmax + + (i,k+1) o--o (i+1,k+1) + | | + (i,,k) o--o (i+1,k) + + */ + + j_patch = 0; + FOR_ALL(k, 0, num_1D - 1, + i, 0, num_1D - 1, { + + size_t node_lid = 0; + size_t surf_patch_lid = i+k*(num_1D-1); + + // node_lid 0 in patch + patch_node_ordering_in_elem(face_lid, surf_patch_lid, node_lid) = + i + j_patch * num_1D + k * num_1D * num_1D; // node_rid(i, j_patch, k, num_1D); + node_lid++; + + // node_lid 1 in patch + patch_node_ordering_in_elem(face_lid, surf_patch_lid, node_lid) = + i + 1 + j_patch * num_1D + k * num_1D * num_1D; // node_rid(i+1, j_patch, k, num_1D); + node_lid++; + + // node_lid 2 in patch + patch_node_ordering_in_elem(face_lid, surf_patch_lid, node_lid) = + i + 1 + j_patch * num_1D + (k + 1) * num_1D * num_1D; // node_rid(i+1, j_patch, k+1, num_1D); + node_lid++; + + // node_lid 3 in patch + patch_node_ordering_in_elem(face_lid, surf_patch_lid, node_lid) = + i + j_patch * num_1D + (k + 1) * num_1D * num_1D; // node_rid(i, j_patch, k+1, num_1D); + + }); // end parallel for + face_lid ++; + + + j_patch = num_1D - 1; + FOR_ALL(k, 0, num_1D-1, + i, 0, num_1D-1, { + + size_t node_lid = 0; + size_t surf_patch_lid = i+k*(num_1D-1); + + // node_lid 0 in patch + patch_node_ordering_in_elem(face_lid, surf_patch_lid, node_lid) = + i + j_patch * num_1D + k * num_1D * num_1D; // node_rid(i, j_patch, k, num_1D); + node_lid++; + + // node_lid 1 in patch + patch_node_ordering_in_elem(face_lid, surf_patch_lid, node_lid) = + i + j_patch * num_1D + (k + 1) * num_1D * num_1D; // node_rid(i, j_patch, k+1, num_1D); + node_lid++; + + // node_lid 2 in patch + patch_node_ordering_in_elem(face_lid, surf_patch_lid, node_lid) = + i + 1 + j_patch * num_1D + (k + 1) * num_1D * num_1D; // node_rid(i+1, j_patch, k+1, num_1D); + node_lid++; + + // node_lid 3 in patch + patch_node_ordering_in_elem(face_lid, surf_patch_lid, node_lid) = + i + 1 + j_patch * num_1D + k * num_1D * num_1D; // node_rid(i+1, j_patch, k, num_1D); + + }); // end parallel for + face_lid ++; + + + /* + + i,j,k layout + + k j + | / + |/ + o-->i + + + k=0,kmax + + (i,j+1) o--o (i+1,j+1) + / / + (i,j) o--o (i+1,j) + + */ + + k_patch = 0; + FOR_ALL(j, 0, num_1D-1, + i, 0, num_1D-1, { + + size_t node_lid = 0; + size_t surf_patch_lid = i+j*(num_1D-1); + + // node_lid 0 in patch + patch_node_ordering_in_elem(face_lid, surf_patch_lid, node_lid) = + i + j * num_1D + k_patch * num_1D * num_1D; // node_rid(i, j, k_patch, num_1D); + node_lid++; + + // node_lid 1 in patch + patch_node_ordering_in_elem(face_lid, surf_patch_lid, node_lid) = + i + (j + 1) * num_1D + k_patch * num_1D * num_1D; // node_rid(i, j+1, k_patch, num_1D); + node_lid++; + + // node_lid 2 in patch + patch_node_ordering_in_elem(face_lid, surf_patch_lid, node_lid) = + i + 1 + (j + 1) * num_1D + k_patch * num_1D * num_1D; // node_rid(i+1, j+1, k_patch, num_1D); + node_lid++; + + // node_lid 3 in patch + patch_node_ordering_in_elem(face_lid, surf_patch_lid, node_lid) = + i + 1 + j * num_1D + k_patch * num_1D * num_1D; // node_rid(i+1, j, k_patch, num_1D); + + }); // end parallel for + face_lid ++; + + k_patch = num_1D - 1; + FOR_ALL(j, 0, num_1D-1, + i, 0, num_1D-1, { + + size_t node_lid = 0; + size_t surf_patch_lid = i+j*(num_1D-1); + + // node_lid 0 in patch + patch_node_ordering_in_elem(face_lid, surf_patch_lid, node_lid) = + i + j * num_1D + k_patch * num_1D * num_1D; // node_rid(i, j, k_patch, num_1D); + node_lid++; + + // node_lid 1 in patch + patch_node_ordering_in_elem(face_lid, surf_patch_lid, node_lid) = + i + 1 + j * num_1D + k_patch * num_1D * num_1D; // node_rid(i+1, j, k_patch, num_1D); + node_lid++; + + // node_lid 2 in patch + patch_node_ordering_in_elem(face_lid, surf_patch_lid, node_lid) = + i + 1 + (j + 1) * num_1D + k_patch * num_1D * num_1D; // node_rid(i+1, j+1, k_patch, num_1D); + node_lid++; + + // node_lid 3 in patch + patch_node_ordering_in_elem(face_lid, surf_patch_lid, node_lid) = + i + (j + 1) * num_1D + k_patch * num_1D * num_1D; // node_rid(i, j+1, k_patch, num_1D); + + }); // end parallel for + face_lid ++; + + if(face_lid!=6) Kokkos::abort("ERROR: wrong number of element faces in 3D when building patches.\n"); + + }// end if 3D element + else{ + // 2D arbitrary order elements + + // iminus-dir patches + i_patch = 0; + FOR_ALL(j, 0, num_1D-1, { + + size_t node_lid = 0; + size_t surf_patch_lid = j; + + patch_node_ordering_in_elem(face_lid, surf_patch_lid, node_lid) = + i_patch + j * num_1D; // node_rid(i_patch, j, num_1D; + node_lid++; + + patch_node_ordering_in_elem(face_lid, surf_patch_lid, node_lid) = + i_patch + (j + 1) * num_1D; // node_rid(i_patch, j+1, num_1D; + }); // end parallel for j + face_lid ++; + + // i-plus-dir patches + i_patch = num_1D - 1; + FOR_ALL(j, 0, num_1D-1, { + + size_t node_lid = 0; + size_t surf_patch_lid = j; + + patch_node_ordering_in_elem(face_lid, surf_patch_lid, node_lid) = + i_patch + j * num_1D; // node_rid(i_patch, j, num_1D; + node_lid++; + + patch_node_ordering_in_elem(face_lid, surf_patch_lid, node_lid) = + i_patch + (j + 1) * num_1D; // node_rid(i_patch, j+1, num_1D; + }); // end parallel for j + face_lid ++; + + j_patch = 0; + FOR_ALL(i, 0, num_1D-1, { + + size_t node_lid = 0; + size_t surf_patch_lid = i; + + patch_node_ordering_in_elem(face_lid, surf_patch_lid, node_lid) = + i + j_patch * num_1D; // node_rid(i, j_patch, num_1D); + node_lid++; + + patch_node_ordering_in_elem(face_lid, surf_patch_lid, node_lid) = + i + 1 + j_patch * num_1D; // node_rid(i+1, j_patch, num_1D); + }); // end parallel for i + face_lid ++; + + j_patch = num_1D - 1; + FOR_ALL(i, 0, num_1D-1, { + + size_t node_lid = 0; + size_t surf_patch_lid = i; + + patch_node_ordering_in_elem(face_lid, surf_patch_lid, node_lid) = + i + j_patch * num_1D; // node_rid(i, j_patch, num_1D); + node_lid++; + + patch_node_ordering_in_elem(face_lid, surf_patch_lid, node_lid) = + i + 1 + j_patch * num_1D; // node_rid(i+1, j_patch, num_1D); + }); // end parallel for i + face_lid ++; + + if(face_lid!=4) Kokkos::abort("ERROR: wrong number of element faces in 2D when building patches.\n"); + + } // end if 2D arbitrary-order element + Kokkos::fence(); + + +} // end function + + +} // end name space + +#endif \ No newline at end of file diff --git a/src/swage/unstructured_mesh.h b/src/swage/unstructured_mesh.h index b43c39c7..3cceb26f 100644 --- a/src/swage/unstructured_mesh.h +++ b/src/swage/unstructured_mesh.h @@ -35,6 +35,7 @@ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #define UNSTRUCTURED_MESH_H #include "matar.h" +#include "indexing_utils.h" #include #define PI 3.141592653589793 @@ -43,276 +44,138 @@ using namespace mtr; namespace mesh_init { -// element mesh types -enum elem_name_tag -{ - linear_simplex_element = 0, - linear_tensor_element = 1, - arbitrary_tensor_element = 2 -}; + // element mesh types + enum ElementNameType + { + linearTensorElement = 1, // single quadrature point element + arbitraryTensorElement = 2 // fully integrated arbitrary-order element + }; -// other enums could go here on the mesh + // other enums could go here on the mesh } // end namespace - -/* -========================== -Nodal indexing convention -========================== - - K - ^ J - | / - | / - | / - 6------------------7 - /| /| - / | / | - / | / | - / | / | - / | / | -4------------------5 | -| | | | ----> I -| | | | -| | | | -| | | | -| 2------------|-----3 -| / | / -| / | / -| / | / -| / | / -|/ |/ -0------------------1 - -nodes are ordered for outward normal -patch 0: [0,4,6,2] xi-minus dir -patch 1: [1,3,7,5] xi-plus dir -patch 2: [0,1,5,4] eta-minus dir -patch 3: [3,2,6,7] eta-plus dir -patch 4: [0,2,3,1] zeta-minus dir -patch 6: [4,5,7,6] zeta-plus dir -*/ - -// sort in ascending order using bubble sort -KOKKOS_INLINE_FUNCTION -void bubble_sort(size_t arr[], const size_t num) -{ - for (size_t i = 0; i < (num - 1); i++) { - for (size_t j = 0; j < (num - i - 1); j++) { - if (arr[j] > arr[j + 1]) { - size_t temp = arr[j]; - arr[j] = arr[j + 1]; - arr[j + 1] = temp; - } // end if - } // end for j - } // end for i -} // end function - - namespace swage { -struct zones_in_elem_t -{ - private: - size_t num_zones_in_elem_; - public: - zones_in_elem_t() { - }; - - zones_in_elem_t(const size_t num_zones_in_elem_inp) { - this->num_zones_in_elem_ = num_zones_in_elem_inp; - }; - - // return global zone index for given local zone index in an element - size_t host(const size_t elem_gid, const size_t zone_lid) const - { - return elem_gid * num_zones_in_elem_ + zone_lid; - }; - - // Return the global zone ID given an element gloabl ID and a local zone ID - KOKKOS_INLINE_FUNCTION - size_t operator()(const size_t elem_gid, const size_t zone_lid) const - { - return elem_gid * num_zones_in_elem_ + zone_lid; - }; -}; - -// if material points are defined strictly internal to the element. -struct gauss_in_elem_t -{ - private: - size_t num_gauss_in_elem_; - public: - gauss_in_elem_t() { - }; - - gauss_in_elem_t(const size_t num_gauss_in_elem_inp) { - this->num_gauss_in_elem_ = num_gauss_in_elem_inp; - }; - - // return global gauss index for given local gauss index in an element - size_t host(const size_t elem_gid, const size_t leg_gauss_lid) const - { - return elem_gid * num_gauss_in_elem_ + leg_gauss_lid; - }; - - // Return the global gauss ID given an element gloabl ID and a local gauss ID - KOKKOS_INLINE_FUNCTION - size_t operator()(const size_t elem_gid, const size_t leg_gauss_lid) const - { - return elem_gid * num_gauss_in_elem_ + leg_gauss_lid; - }; -}; - -/// if material points are defined at element interfaces -struct lobatto_in_elem_t -{ - private: - size_t num_lobatto_in_elem_; - public: - lobatto_in_elem_t() { - }; - - lobatto_in_elem_t(const size_t num_lobatto_in_elem_inp) { - this->num_lobatto_in_elem_ = num_lobatto_in_elem_inp; - }; - - // return global gauss index for given local gauss index in an element - size_t host(const size_t elem_gid, const size_t lob_gauss_lid) const - { - return elem_gid * num_lobatto_in_elem_ + lob_gauss_lid; - }; - // Return the global gauss ID given an element gloabl ID and a local gauss ID - KOKKOS_INLINE_FUNCTION - size_t operator()(const size_t elem_gid, const size_t lob_gauss_lid) const - { - return elem_gid * num_lobatto_in_elem_ + lob_gauss_lid; - }; -}; - -// struct nodes_in_zone_t { -// private: -// size_t num_nodes_in_zone_; -// public: -// nodes_in_zone_t(){}; - -// nodes_in_zone_t(const size_t num_nodes_in_zone_inp){ -// this->num_nodes_in_zone_ = num_nodes_in_zone_inp; -// }; - -// // return global zone index for given local zone index in an element -// size_t host(const size_t zone_gid, const size_t node_lid) const{ -// return zone_gid*num_nodes_in_zone_ + node_lid; -// }; - -// KOKKOS_INLINE_FUNCTION -// size_t operator()(const size_t zone_gid, const size_t node_lid) const{ -// return zone_gid*num_nodes_in_zone_ + node_lid; -// }; -// }; - -// mesh sizes and connectivity data structures -struct Mesh +//////////////////////////////////////////////////////////////////////////////////// +/// +/// \fn Mesh_t +/// +/// \brief Builds and stores mesh sizes and connectivity data structures for +/// arbitrary-order unstructured meshes in 2D/3D +/// +/// Mesh entity definitions: +/// Element: Aribtrary-order hexahedral or quadralateral volume +/// Zone: A discretization of an element by subdividing it using the nodes +/// The zone has 8 nodes (3D) or 4 nodes (2D) for any order mesh +/// Node: A kinematic degree of freedom +/// Corner: A element-node pair +/// Surface: The surface of the element, it is one dimension lower than the volume +/// Patch: A discretization of a surface by subdividing it using the nodes +/// Face: The local surface entity of the Element, equal to 6 (3D) or 4 (2D) +/// Side: A element-surface pair -- not in the mesh type at this time +/// +/////////////////////////////////////////////////////////////////////////////////// +struct Mesh_t { - // ******* Entity Definitions **********// - // Element: A hexahedral volume - // Zone: A discretization of an element base on subdividing the element using the nodes - // Node: A kinematic degree of freedom - // Surface: The 2D surface of the element - // Patch: A discretization of a surface by subdividing the surface using the nodes - // Corner: A element-node pair bool verbose = false; // ---- Global Mesh Definitions ---- // - mesh_init::elem_name_tag elem_kind = mesh_init::linear_tensor_element; ///< The type of elements used in the mesh + mesh_init::ElementNameType elem_kind = mesh_init::linearTensorElement; ///< The type of elements used in the mesh - size_t Pn = 1; ///< Polynomial order of kinematic space + size_t Pn = 1; ///< Polynomial order of kinematic space defining element size_t num_dims = 0; ///< Number of spatial dimension + // ---- Element Data Definitions ---- // - size_t num_elems = 0; ///< Number of elements in the mesh - size_t num_nodes_in_elem = 0; ///< Number of nodes in an element + size_t num_elems = 0; ///< Number of elements in the mesh + size_t num_nodes_in_elem = 0; ///< Number of nodes in an element size_t num_patches_in_elem = 0; ///< Number of patches in an element - size_t num_surfs_in_elem = 0; ///< Number of surfaces in an element - size_t num_zones_in_elem = 0; ///< Number of zones in an element + size_t num_surfs_in_elem = 0; ///< Number of surfaces in an element + size_t num_zones_in_elem = 0; ///< Number of zones in an element - size_t num_gauss_in_elem = 0; ///< Number of Gauss points in an element - size_t num_lobatto_in_elem = 0; ///< Number of Gauss Lobatto points in an element + size_t num_gauss_in_elem = 0; ///< Number of Gauss points in an element DCArrayKokkos nodes_in_elem; ///< Nodes in an element - CArrayKokkos corners_in_elem; ///< Corners in an element -- this can just be a functor + corners_in_elem_t corners_in_elem; RaggedRightArrayKokkos elems_in_elem; ///< Elements connected to an element - CArrayKokkos num_elems_in_elem; ///< Number of elements connected to an element + CArrayKokkos num_elems_in_elem; ///< Number of elements connected to an element CArrayKokkos patches_in_elem; ///< Patches in an element (including internal patches) - CArrayKokkos surfs_in_elem; ///< Surfaces on an element + CArrayKokkos surfs_in_elem; ///< Surfaces on an element + + zones_in_elem_t zones_in_elem; ///< Zones in an element + gauss_in_elem_t gauss_in_elem; ///< Gauss points in an element - // CArrayKokkos zones_in_elem; ///< Zones in an element - zones_in_elem_t zones_in_elem; ///< Zones in an element - lobatto_in_elem_t lobatto_in_elem; ///< Gauss Lobatto points in an element - gauss_in_elem_t gauss_in_elem; ///< Gauss points in an element // ---- Node Data Definitions ---- // size_t num_nodes = 0; ///< Number of nodes in the mesh RaggedRightArrayKokkos corners_in_node; ///< Corners connected to a node CArrayKokkos num_corners_in_node; ///< Number of corners connected to a node - RaggedRightArrayKokkos elems_in_node; ///< Elements connected to a given node - RaggedRightArrayKokkos nodes_in_node; ///< Nodes connected to a node along an edge - CArrayKokkos num_nodes_in_node; ///< Number of nodes connected to a node along an edge + RaggedRightArrayKokkos elems_in_node; ///< Elements connected to a given node + RaggedRightArrayKokkos nodes_in_node; ///< Nodes connected to a node along an edge + CArrayKokkos num_nodes_in_node; ///< Number of nodes connected to a node along an edge + // ---- Surface Data Definitions ---- // - size_t num_surfs = 0; ///< Number of surfaces in the mesh + size_t num_surfs = 0; ///< Number of surfaces in the mesh size_t num_nodes_in_surf = 0; ///< Number of nodes in a surface size_t num_patches_in_surf = 0; ///< Number of patches in a surface - CArrayKokkos patches_in_surf; ///< Patches in a surface - CArrayKokkos nodes_in_surf; ///< Nodes connected to a surface - CArrayKokkos elems_in_surf; ///< Elements connected to a surface + patches_in_surf_t patches_in_surf; ///< Patches in a surface + CArrayKokkos nodes_in_surf; ///< Nodes in a surface + CArrayKokkos elems_in_surf; ///< Elements connected to a surface + CArrayKokkos num_elems_in_surf; /// faces_in_surf; ///< Local face index of the element + // ---- Patch Data Definitions ---- // - size_t num_patches = 0; ///< Number of patches in the mesh + size_t num_patches = 0; ///< Number of patches in the mesh size_t num_nodes_in_patch = 0; ///< Number of nodes in a patch - // size_t num_lobatto_in_patch; ///< Number of Gauss Lobatto nodes in a patch - // size_t num_gauss_in_patch; ///< Number of Gauss nodes in a patch CArrayKokkos nodes_in_patch; ///< Nodes connected to a patch - CArrayKokkos elems_in_patch; ///< Elements connected to a patch - CArrayKokkos surf_in_patch; ///< Surfaces connected to a patch (co-planar) + CArrayKokkos elems_in_patch; ///< Elements connected to a patch + CArrayKokkos surf_in_patch; ///< the surface the patch belongs to + // ---- Corner Data Definitions ---- // size_t num_corners = 0; ///< Number of corners (define) in the mesh + // ---- Zone Data Definitions ---- // - size_t num_zones = 0; ///< Number of zones in the mesh - size_t num_nodes_in_zone = 0; ///< Number of nodes in a zone + size_t num_zones = 0; ///< Number of zones in the mesh + size_t num_nodes_in_zone = 0; ///< Number of nodes in a zone CArrayKokkos nodes_in_zone; ///< Nodes defining a zone - // nodes_in_zone_t nodes_in_zone; + // ---- Boundary Data Definitions ---- // - size_t num_bdy_sets = 0; ///< Number of boundary sets - size_t num_bdy_nodes = 0; ///< Number of boundary nodes - size_t num_bdy_patches = 0; ///< Number of boundary patches + size_t num_bdy_surfs = 0; ///< Number of boundary surfaces + size_t num_bdy_patches = 0; ///< Number of boundary patches + size_t num_bdy_nodes = 0; ///< Number of boundary nodes + size_t num_bdy_sets = 0; ///< Number of boundary sets + + CArrayKokkos bdy_surfs; ///< Boundary patches CArrayKokkos bdy_patches; ///< Boundary patches CArrayKokkos bdy_nodes; ///< Boundary nodes - RaggedRightArrayKokkos bdy_patches_in_set; ///< Boundary patches in a boundary set - DCArrayKokkos num_bdy_patches_in_set; ///< Number of boundary nodes in a set + RaggedRightArrayKokkos bdy_patches_in_set; ///< Boundary patches in a boundary set + DCArrayKokkos num_bdy_patches_in_set; ///< Number of boundary nodes in a set RaggedRightArrayKokkos bdy_nodes_in_set; ///< Boundary nodes in a boundary set DCArrayKokkos num_bdy_nodes_in_set; ///< Number of boundary nodes in a set + // ---- Internal Condition Data Definitions ---- // - size_t num_internal_sets = 0; ///< Number of internal sets + size_t num_internal_sets = 0; ///< Number of internal sets RaggedRightArrayKokkos internal_nodes_in_set; ///< Internal nodes in an internal set - DCArrayKokkos num_internal_nodes_in_set; ///< Number of internal nodes in a set + DCArrayKokkos num_internal_nodes_in_set; ///< Number of internal nodes in a set // MPI Decomposition Data Definitions ---- // @@ -320,19 +183,43 @@ struct Mesh DCArrayKokkos local_to_global_elem_mapping; ///< Local to global element mapping // Element communicaiton data definitions - size_t num_owned_elems = 0; ///< Number of owned elements on this rank + size_t num_owned_elems = 0; ///< Number of owned elements on this rank size_t num_boundary_elems = 0; ///< Number of boundary elements on this rank (send data to neighboring MPI ranks) DCArrayKokkos boundary_elem_local_ids; ///< Local IDs of boundary elements on this rank (send data to neighboring MPI ranks) size_t num_ghost_elems = 0; ///< Number of ghost elements on this rank (receive data from neighboring MPI ranks) // Node communicaiton data definitions - size_t num_owned_nodes = 0; ///< Number of owned nodes on this rank + size_t num_owned_nodes = 0; ///< Number of owned nodes on this rank size_t num_boundary_nodes = 0; ///< Number of boundary nodes on this rank (send data to neighboring MPI ranks) DCArrayKokkos shared_tally_owned_nodes; ///< Owned-node mask: true where this rank is the min MPI rank among ranks that own the global node (domain tally contributor); length num_owned_nodes // DCArrayKokkos boundary_node_local_ids; ///< Local IDs of boundary nodes on this rank (send data to neighboring MPI ranks) size_t num_ghost_nodes = 0; ///< Number of ghost nodes on this rank (receive data from neighboring MPI ranks) - // initialization methods + + ///////////////////////////////////////////////////////////////////////////// + /// + /// \fn initialize_dims + /// + /// \brief Set up mesh dimensions + /// + /// \param elem_dims_in The number dimensions + /// + ///////////////////////////////////////////////////////////////////////////// + void initialize_dims(const size_t num_dims_inp) + { + num_dims = num_dims_inp; + }; // end method + + + ///////////////////////////////////////////////////////////////////////////// + /// + /// \fn initialize_nodes + /// + /// \brief Set the number of nodes in the mesh + /// + /// \param num_nodes_inp The number dimensions + /// + ///////////////////////////////////////////////////////////////////////////// void initialize_nodes(const size_t num_nodes_inp) { if (num_dims == 0) { @@ -342,79 +229,196 @@ struct Mesh return; }; // end method - // initialization methods - void initialize_elems(const size_t num_elems_inp, const size_t num_dims_inp) + + ///////////////////////////////////////////////////////////////////////////// + /// + /// \fn initialize_elems + /// + /// \brief Setup the elemenets in a mesh comprised of linear edges and one + /// quadrature point per element + /// + /// \param num_elems_inp The number elems + /// + ///////////////////////////////////////////////////////////////////////////// + void initialize_elems(const size_t num_elems_inp) { + if (num_dims == 0) { Kokkos::abort("Error: mesh.num_dims is not set. Exiting at initialize_elems()."); } - num_dims = num_dims_inp; - num_nodes_in_elem = 1; - - for (int dim = 0; dim < num_dims; dim++) { - num_nodes_in_elem *= 2; - } - num_elems = num_elems_inp; - nodes_in_elem = DCArrayKokkos(num_elems, num_nodes_in_elem, "mesh.nodes_in_elem"); - corners_in_elem = CArrayKokkos(num_elems, num_nodes_in_elem, "mesh.corners_in_elem"); - // 1 Gauss point per element - num_gauss_in_elem = 1; + // --- Basic element bookkeeping --- + num_elems = num_elems_inp; + + // initializes a linear element with a single gauss point for saving results + Pn = 1; - // 1 zone per element - num_zones_in_elem = 1; + // --- Derived sizes --- + num_nodes_in_elem = (size_t)std::pow(2, num_dims); + num_nodes_in_zone = (size_t)std::pow(2, num_dims); // (4, or 8, always) + num_gauss_in_elem = 1; // 1 Gauss point per element + num_zones_in_elem = 1; // 1 zone per element + num_surfs_in_elem = num_dims == 2 ? 4 : 6; // 4 or 6 (always) + num_zones = num_zones_in_elem * num_elems; + + num_corners = num_nodes_in_elem*num_elems; - gauss_in_elem = gauss_in_elem_t(num_gauss_in_elem); + + // --- Allocations --- + nodes_in_elem = DCArrayKokkos(num_elems, num_nodes_in_elem, "mesh.nodes_in_elem"); + corners_in_elem = corners_in_elem_t(num_nodes_in_elem); + gauss_in_elem = gauss_in_elem_t(num_gauss_in_elem); + zones_in_elem = zones_in_elem_t(num_zones_in_elem); + surfs_in_elem = CArrayKokkos(num_elems, num_surfs_in_elem, "mesh.surfs_in_zone"); return; }; // end method - // initialization method + ///////////////////////////////////////////////////////////////////////////// + /// + /// \fn initialize_elems_Pn + /// + /// \brief Setup the elements in an arbitrary-order mesh + /// + /// \param num_elems_inp The number elems + /// \param elem_Pn_order The element order, where num_nodes_1D = Pn+1 + /// \param num_gauss_1D The number of gauss points in each direction + /// + ///////////////////////////////////////////////////////////////////////////// void initialize_elems_Pn( const size_t num_elems_inp, - const size_t num_dims_inp, - const size_t Pn_order) + const size_t elem_Pn_order, + const size_t num_gauss_1D) { + + // Note: num_gauss_1D creates an index space that can be used to register state on + if (num_dims == 0) { Kokkos::abort("Error: mesh.num_dims is not set. Exiting at initialize_elems_Pn()."); } - elem_kind = mesh_init::arbitrary_tensor_element; - - num_dims = num_dims_inp; + + // --- Set element details --- + Pn = elem_Pn_order; // Note: element Pn_order = dofs_1D-1, where dofs are the element nodes + if (Pn == 0) { + Kokkos::abort("Error: Pn must be greater than 0. Exiting at initialize_elems_Pn()."); + } + num_elems = num_elems_inp; + if (num_elems == 0) { + Kokkos::abort("Error: num_elems must be greater than 0. Exiting at initialize_elems_Pn()."); + } - Pn = Pn_order; + elem_kind = mesh_init::arbitraryTensorElement; - num_nodes_in_elem = std::pow(Pn_order + 1, num_dims); //(Pn_order + 1)**num_dims; // (Pn +1) - num_nodes_in_zone = std::pow(2, num_dims); // (4, or 8, always) - num_gauss_in_elem = std::pow(2*Pn_order, num_dims); // = 2*Pn - num_zones_in_elem = std::pow(Pn_order, num_dims); // Pn - num_surfs_in_elem = num_dims == 2 ? 4 : 6; // 4 or 6 (always) - num_zones = num_zones_in_elem * num_elems; + // --- Derived sizes --- + num_gauss_in_elem = (size_t)std::pow(num_gauss_1D, num_dims); // Note: 2*Pn with Legendre is needed for solids mechanics + num_nodes_in_elem = (size_t)std::pow(Pn + 1, num_dims); + num_nodes_in_zone = (size_t)std::pow(2, num_dims); // (4, or 8, always) + num_zones_in_elem = (size_t)std::pow(Pn, num_dims); // Pn^dim + num_surfs_in_elem = num_dims == 2 ? 4 : 6; // 4 or 6 (always) + num_zones = num_zones_in_elem * num_elems; + + num_corners = num_nodes_in_elem*num_elems; + // --- Allocations --- nodes_in_elem = DCArrayKokkos(num_elems, num_nodes_in_elem, "mesh.nodes_in_elem"); - corners_in_elem = CArrayKokkos(num_elems, num_nodes_in_elem, "mesh.corners_in_elem"); + corners_in_elem = corners_in_elem_t(num_nodes_in_elem); zones_in_elem = zones_in_elem_t(num_zones_in_elem); surfs_in_elem = CArrayKokkos(num_elems, num_surfs_in_elem, "mesh.surfs_in_zone"); nodes_in_zone = CArrayKokkos(num_zones, num_nodes_in_zone, "mesh.nodes_in_zone"); - gauss_in_elem = gauss_in_elem_t(num_gauss_in_elem); + gauss_in_elem = gauss_in_elem_t(num_gauss_in_elem); return; }; // end method - // initialization methods - void initialize_corners(const size_t num_corners_inp) - { - if (num_dims == 0) { - Kokkos::abort("Error: mesh.num_dims is not set. Exiting at initialize_corners()."); + + ///////////////////////////////////////////////////////////////////////////// + /// + /// \fn build_zones + /// + /// \brief Using the nodes in the element, decompose the element into zones. + /// + /// The zones have 8 nodes (3D) or 4 nodes (2D) for all element orders. The + /// zones are built after the mesh dims are set, and the nodes and elements + /// are initialized. + /// + ///////////////////////////////////////////////////////////////////////////// + void build_zones(){ + + const size_t num_1D = Pn + 1; + + if(num_zones==0) Kokkos::abort("ERROR: Elements must be initialized prior to creating zones."); + + if(num_dims==3){ + + FOR_ALL_CLASS(elem_gid, 0, num_elems, { + size_t node_lids[8]; // temp storage for local node ids + for (size_t k = 0; k < num_1D-1; k++) + for (size_t j = 0; j < num_1D-1; j++) + for (size_t i = 0; i < num_1D-1; i++) { + node_lids[0] = i + j * (num_1D) + k * (num_1D) * (num_1D); // i,j,k + node_lids[1] = i + 1 + j * (num_1D) + k * (num_1D) * (num_1D); // i+1, j, k + node_lids[2] = i + (j + 1) * (num_1D) + k * (num_1D) * (num_1D); // i,j+1,k + node_lids[3] = i + 1 + (j + 1) * (num_1D) + k * (num_1D) * (num_1D); // i+1, j+1, k + node_lids[4] = i + j * (num_1D) + (k + 1) * (num_1D) * (num_1D); // i, j , k+1 + node_lids[5] = i + 1 + j * (num_1D) + (k + 1) * (num_1D) * (num_1D); // i + 1, j , k+1 + node_lids[6] = i + (j + 1) * (num_1D) + (k + 1) * (num_1D) * (num_1D); // i,j+1,k+1 + node_lids[7] = i + 1 + (j + 1) * (num_1D) + (k + 1) * (num_1D) * (num_1D); // i+1, j+1, k+1 + + size_t zone_lid = i + j * (num_1D - 1) + k * (num_1D - 1) * (num_1D - 1); + size_t zone_gid = zones_in_elem(elem_gid, zone_lid); + + for (size_t node_lid = 0; node_lid < 8; node_lid++) { + // get global id for the node + size_t node_gid = nodes_in_elem(elem_gid, node_lids[node_lid]); + nodes_in_zone(zone_gid, node_lid) = node_gid; + } + } // end for + }); // end FOR_ALL elem_gid + + } + else if (num_dims==2){ + + FOR_ALL_CLASS(elem_gid, 0, num_elems, { + size_t node_lids[4]; // temp storage for local node ids + for (size_t j = 0; j < num_1D-1; j++) + for (size_t i = 0; i < num_1D-1; i++) { + node_lids[0] = i + j * (num_1D); // i, j + node_lids[1] = i + 1 + j * (num_1D); // i+1, j + node_lids[2] = i + (j + 1) * (num_1D); // i, j+1 + node_lids[3] = i + 1 + (j + 1) * (num_1D); // i+1, j+1 + + size_t zone_lid = i + j * (num_1D - 1); + size_t zone_gid = zones_in_elem(elem_gid, zone_lid); + + for (size_t node_lid = 0; node_lid < 4; node_lid++) { + // get global id for the node + size_t node_gid = nodes_in_elem(elem_gid, node_lids[node_lid]); + nodes_in_zone(zone_gid, node_lid) = node_gid; + } + } // end for + }); // end FOR_ALL elem_gid + } - num_corners = num_corners_inp; + else + { + Kokkos::abort("ERROR: incorrect mesh dimensions- only 2D and 3D are supported. Wow! How did this happen?"); + } // end if - return; - }; // end method + } // end build zones - // build the corner mesh connectivity arrays + + ///////////////////////////////////////////////////////////////////////////// + /// + /// \fn build_corner_connectivity + /// + /// \brief Build the corner mesh connectivity arrays + /// + /// The corner is defined as an element node pair. For high-order elements, + /// corners exist inside the element. + /// + ///////////////////////////////////////////////////////////////////////////// void build_corner_connectivity() { if (num_dims == 0) { @@ -465,10 +469,6 @@ struct Mesh elems_in_node(node_gid, j) = elem_gid; // save the elem_gid - // Save corner index to element - size_t corner_lid = node_lid; - corners_in_elem(elem_gid, corner_lid) = corner_gid; - // increment the number of corners saved to this node_gid count_saved_corners_in_node(node_gid) = count_saved_corners_in_node(node_gid) + 1; }); // end FOR_ALL over nodes in element @@ -477,12 +477,25 @@ struct Mesh return; } // end of build_corner_connectivity - // build elem connectivity arrays + + ///////////////////////////////////////////////////////////////////////////// + /// + /// \fn build_elem_elem_connectivity + /// + /// \brief Build the neighboring element to element connectivity + /// + /// The elements surrounding an element accounts for all adjacent elements + /// sharing a common node. + /// + ///////////////////////////////////////////////////////////////////////////// void build_elem_elem_connectivity() { if (num_dims == 0) { Kokkos::abort("Error: mesh.num_dims is not set. Exiting at build_elem_elem_connectivity()."); } + if (num_corners_in_node.size() == 0) { + Kokkos::abort("Error: build_corner_connectivity must be called first. Exiting at build_elem_elem_connectivity()."); + } // find the max number of elems around a node size_t max_num_elems_in_node; @@ -561,801 +574,420 @@ struct Mesh return; } // end of build_elem_elem_connectivity - // build the patches - void build_patch_connectivity() - { - if (num_dims == 0) { - Kokkos::abort("Error: mesh.num_dims is not set. Exiting at build_patch_connectivity()."); - } - // WARNING WARNING - // the mesh element kind should be in the input file and set when reading mesh - // mesh_elem_kind = mesh_init::linear_tensor_element; // MUST BE SET - - // building patches - - num_nodes_in_patch = 2 * (num_dims - 1); // 2 (2D) or 4 (3D) - num_surfs_in_elem = 2 * num_dims; // 4 (2D) or 6 (3D) - - // num_lobatto_in_patch = int(pow(3, num_dims-1)); - - // num_gauss_in_patch = 2*(num_dims-1); - size_t num_patches_in_surf; // = Pn_order or = Pn_order*Pn_order - - size_t num_1D = Pn + 1; // number of nodes in 1D - - // num quad points 1D // - // size_t num_lob_1D = 2*Pn + 1; - // size_t num_1D = 2*Pn; - - DCArrayKokkos node_ordering_in_elem; // dimensions will be (num_patches_in_elem, num_nodes_in_patch); - - // DCArrayKokkos lobatto_ordering_in_elem; // dimensions will be (num_patches_in_elem, num_lobatto_in_patch); - - // DCArrayKokkos gauss_ordering_in_elem; // dimensions will be (num_patches_in_elem, num_gauss_in_patch); - - if (verbose) printf("Number of dimensions = %zu \n", num_dims); - - if (num_dims == 3) { - // num_patches_in_surf = [1^2, 2^2, 3^2, 4^2, ... , Pn^2] - - num_patches_in_surf = Pn * Pn; - - num_patches_in_elem = num_patches_in_surf * num_surfs_in_elem; - - // nodes in a patch in the element - node_ordering_in_elem = DCArrayKokkos(num_patches_in_elem, num_nodes_in_patch, "node_ordering_in_elem"); - - // lobatto_ordering_in_elem = DCArrayKokkos (num_patches_in_elem, num_lobatto_in_patch); - - // gauss_ordering_in_elem = DCArrayKokkos (num_patches_in_elem, num_gauss_in_patch); - - // printf("num_patches_in_elem = %zu \n", num_patches_in_elem); - // printf("num_nodes_in_patch = %zu \n", num_nodes_in_patch); - // printf("num_lobatto_in_patch = %zu \n", num_lobatto_in_patch); - // printf("num_gauss_in_patch = %zu \n", num_gauss_in_patch); - // printf("Number of surfaces = %zu \n", num_surfs_in_elem); - } - else { - num_patches_in_surf = Pn; - - num_patches_in_elem = num_patches_in_surf * num_surfs_in_elem; - - // nodes in a patch in the element - node_ordering_in_elem = DCArrayKokkos(num_patches_in_elem, num_nodes_in_patch, "node_ordering_in_elem"); - // lobatto_ordering_in_elem = DCArrayKokkos (num_patches_in_elem, num_lobatto_in_patch); - // gauss_ordering_in_elem = DCArrayKokkos (num_patches_in_elem, num_gauss_in_patch); - } // end if dim - - // On the CPU, set the node order for the patches in an element - // classic linear elements - if (elem_kind == mesh_init::linear_tensor_element) { - if (num_dims == 3) { - - size_t temp_node_lids[24] = { 0, 4, 6, 2, - 1, 3, 7, 5, - 0, 1, 5, 4, - 3, 2, 6, 7, - 0, 2, 3, 1, - 4, 5, 7, 6 }; - - int count = 0; - int elem_patch_lid = 0; - for (size_t surf_lid = 0; surf_lid < num_surfs_in_elem; surf_lid++) { - for (size_t patch_lid = 0; patch_lid < num_patches_in_surf; patch_lid++) { - for (size_t node_lid = 0; node_lid < num_nodes_in_patch; node_lid++) { - node_ordering_in_elem.host(elem_patch_lid, node_lid) = temp_node_lids[count]; - // gauss_ordering_in_elem.host( elem_patch_lid, node_lid ) = temp_node_lids[count]; - count++; - } // end for node_lid - elem_patch_lid++; - } // end for patch_lid in a surface - } // end for i - - // count = 0; - // elem_patch_lid = 0; - // for ( size_t surf_lid=0; surf_lid < num_surfs_in_elem; surf_lid++ ){ - // for ( size_t patch_lid=0; patch_lid < num_patches_in_surf; patch_lid++ ){ - // for ( size_t lobatto_lid=0; lobatto_lid < num_lobatto_in_patch; lobatto_lid++ ){ - // lobatto_ordering_in_elem.host( elem_patch_lid, lobatto_lid ) = temp_node_lids[count]; - // count++; - // } // end for node_lid - // elem_patch_lid ++; - // } // end for patch_lid in a surface - // } // end for i - } - else { - // J - // | - // 3---2 - // | | -- I - // 0---1 - // - size_t temp_node_lids[8] = - { 0, 3, - 1, 2, - 0, 1, - 3, 2 }; - - int count = 0; - int elem_patch_lid = 0; - for (size_t surf_lid = 0; surf_lid < num_surfs_in_elem; surf_lid++) { - for (size_t patch_lid = 0; patch_lid < num_patches_in_surf; patch_lid++) { - for (size_t node_lid = 0; node_lid < num_nodes_in_patch; node_lid++) { - node_ordering_in_elem.host(elem_patch_lid, node_lid) = temp_node_lids[count]; - // gauss_ordering_in_elem.host( elem_patch_lid, node_lid ) = temp_node_lids[count]; - count++; - } // end for node_lid - elem_patch_lid++; - } // end for patch_lid in a surface - } // end for i - } // end if on dims - } // end of linear element iwth classic numbering - // ----- - // arbitrary-order element - // ----- - else if (elem_kind == mesh_init::arbitrary_tensor_element) { - size_t temp_node_lids[num_nodes_in_patch * num_patches_in_surf * num_surfs_in_elem]; - - if (verbose) printf("arbitrary order tensor element \n"); - - // arbitrary-order node ordering in patches of an element - if (num_dims == 3) { - /* - - i,j,k layout - - k j - | / - |/ - o-->i - - - i=0,imax - o (j+1,k+1) - /| - (j,k+1) o o (j+1,k) - |/ - (j,k) o - - */ - - int count = 0; - - int i_patch, j_patch, k_patch; - - // i-minus-dir patches - - i_patch = 0; - for (int k = 0; k < num_1D - 1; k++) { - for (int j = 0; j < num_1D - 1; j++) { - // node_lid 0 in patch - // index = i + j*num_1D + k*num_1D*num_1D; - temp_node_lids[count] = i_patch + j * num_1D + k * num_1D * num_1D; // node_rid(i_patch, j, k, num_1D); - count++; - - // node_lid 1 in patch - // index = i + j*num_1D + (k+1)*num_1D*num_1D; - temp_node_lids[count] = i_patch + j * num_1D + (k + 1) * num_1D * num_1D; // node_rid(i_patch, j, k+1, num_1D); - count++; - - // node_lid 2 in patch - // index = i + (j+1)*num_1D + (k+1)*num_1D*num_1D; - temp_node_lids[count] = i_patch + (j + 1) * num_1D + (k + 1) * num_1D * num_1D; // node_rid(i_patch, j+1, k+1, num_1D); - count++; - - // node_lid 3 in patch - // index = i + (j+1)*num_1D + k*num_1D*num_1D; - temp_node_lids[count] = i_patch + (j + 1) * num_1D + k * num_1D * num_1D; // node_rid(i_patch, j+1, k, num_1D); - count++; - } // end for k - } // end for j - - // printf("i-minus\n"); - - // i-plus-dir patches - i_patch = num_1D - 1; - // printf("num_1D = %zu \n", num_1D); - // printf("i_patch = %d \n", i_patch); - if (verbose) printf("num_nodes_in_elem %zu \n", num_nodes_in_elem); - for (int k = 0; k < num_1D - 1; k++) { - for (int j = 0; j < num_1D - 1; j++) { - // node_lid 0 in patch - // index = i + j*num_1D + k*num_1D*num_1D; - temp_node_lids[count] = i_patch + j * num_1D + k * num_1D * num_1D; // node_rid(i_patch, j, k, num_1D); - count++; - - // node_lid 1 in patch - // index = i + (j+1)*num_1D + k*num_1D*num_1D; - temp_node_lids[count] = i_patch + (j + 1) * num_1D + k * num_1D * num_1D; // node_rid(i_patch, j+1, k, num_1D); - count++; - - // node_lid 2 in patch - // index = i + (j+1)*num_1D + (k+1)*num_1D*num_1D; - temp_node_lids[count] = i_patch + (j + 1) * num_1D + (k + 1) * num_1D * num_1D; // node_rid(i_patch, j+1, k+1, num_1D); - count++; - - // node_lid 3 in patch - // index = i + j*num_1D + (k+1)*num_1D*num_1D; - temp_node_lids[count] = i_patch + j * num_1D + (k + 1) * num_1D * num_1D; // node_rid(i_patch, j, k+1, num_1D); - count++; - } // end for j - } // end for k - - // printf("i-plus\n"); - - /* - - i,j,k layout - - k j - | / - |/ - o-->i + ///////////////////////////////////////////////////////////////////////////// + /// + /// \fn build_surf_connectivity + /// + /// \brief Build the surfaces and decompose them into patches + /// + /// A surface separates two face-adjacent elements, where the shared nodes + /// define the surface. On mesh boundaries, there is no face-adjacent + /// element. This rountine calculates and populates boundary index spaces. + // Each surface is decomposed into patches using the nodes on the surface. + /// Patches have 4 nodes (3D) or 2 nodes (2D) for all element orders. + /// + ///////////////////////////////////////////////////////////////////////////// + void build_surf_connectivity() { + if(num_dims==0) Kokkos::abort("Error: mesh.num_dims is not set. Exiting at build_surf_connectivity \n"); + if(num_elems_in_elem.size()==0) Kokkos::abort("Error: build_elem_elem_connectivity must be called first. Existing build_surf_connectivity(). \n"); + if(num_corners_in_node.size() == 0) Kokkos::abort("Error: build_corner_connectivity must be called first. Exiting at build_surf_connectivity()."); + - j=0,jmax + //const bool mk_sides = false; // not built at this time to save memory, their need is unknown - (i,,k+1) o--o (i+1,,k+1) - | | - (i,,k) o--o (i+1,,k) + num_surfs_in_elem = 2*num_dims; // 4 (2D) or 6 (3D) + const size_t num_1D = Pn+1; + num_nodes_in_surf = pow(num_1D, num_dims - 1); - */ + // ----------------------------------------------------------------------- + // 1a. Get element face nodes and sort (small...biggest index) for building connectivity + // ----------------------------------------------------------------------- - j_patch = 0; - for (int k = 0; k < num_1D - 1; k++) { - for (int i = 0; i < num_1D - 1; i++) { - // node_lid 0 in patch - temp_node_lids[count] = i + j_patch * num_1D + k * num_1D * num_1D; // node_rid(i, j_patch, k, num_1D); - count++; - - // node_lid 1 in patch - temp_node_lids[count] = i + 1 + j_patch * num_1D + k * num_1D * num_1D; // node_rid(i+1, j_patch, k, num_1D); - count++; - - // node_lid 2 in patch - temp_node_lids[count] = i + 1 + j_patch * num_1D + (k + 1) * num_1D * num_1D; // node_rid(i+1, j_patch, k+1, num_1D); - count++; - - // node_lid 3 in patch - temp_node_lids[count] = i + j_patch * num_1D + (k + 1) * num_1D * num_1D; // node_rid(i, j_patch, k+1, num_1D); - count++; - } // end for i - } // end for k + // get the elem nodes on the surface + CArrayKokkos surf_node_ordering_in_elem(num_surfs_in_elem, num_nodes_in_surf,"surf_node_ordering_in_elem"); + get_surf_node_lids(surf_node_ordering_in_elem, num_1D, num_dims); + + // sort the nodes on each elem face from smallest to largest, these are the hash keys + CArrayKokkos face_hash_keys(num_elems,num_surfs_in_elem,num_nodes_in_surf,"face_hash_keys"); + + FOR_ALL_CLASS(elem_gid, 0, num_elems, + face_lid, 0, num_surfs_in_elem, + node_lid, 0, num_nodes_in_surf, { + + const size_t elem_node_lid = surf_node_ordering_in_elem(face_lid,node_lid); // elem nodes on face + const size_t node_gid = nodes_in_elem(elem_gid,elem_node_lid); // all nodes in element + face_hash_keys(elem_gid,face_lid,node_lid) = node_gid; // save the gid + }); + Kokkos::fence(); - // printf("j-minus\n"); + FOR_ALL_CLASS(elem_gid, 0, num_elems, + face_lid, 0, num_surfs_in_elem, { + ViewCArrayKokkos sorted_face_nodes(&face_hash_keys(elem_gid,face_lid,0),num_nodes_in_surf); + bubble_sort(sorted_face_nodes); // sort nodes from smallest to largest - j_patch = num_1D - 1; - for (int k = 0; k < num_1D - 1; k++) { - for (int i = 0; i < num_1D - 1; i++) { - // node_lid 0 in patch - temp_node_lids[count] = i + j_patch * num_1D + k * num_1D * num_1D; // node_rid(i, j_patch, k, num_1D); - count++; + // remember that sorted_face_nodes are in order of smallest to largest now + }); + Kokkos::fence(); - // node_lid 1 in patch - temp_node_lids[count] = i + j_patch * num_1D + (k + 1) * num_1D * num_1D; // node_rid(i, j_patch, k+1, num_1D); - count++; + DCArrayKokkos surf_counter(1); + surf_counter.set_values(0); + DCArrayKokkos bdy_surf_counter(1); + bdy_surf_counter.set_values(0); - // node_lid 2 in patch - temp_node_lids[count] = i + 1 + j_patch * num_1D + (k + 1) * num_1D * num_1D; // node_rid(i+1, j_patch, k+1, num_1D); - count++; + CArrayKokkos face_elems_in_elem(num_elems, num_surfs_in_elem,"face_elems_in_elem"); + face_elems_in_elem.set_values(-1); - // node_lid 3 in patch - temp_node_lids[count] = i + 1 + j_patch * num_1D + k * num_1D * num_1D; // node_rid(i+1, j_patch, k, num_1D); - count++; - } // end for i - } // end for k + surfs_in_elem = CArrayKokkos(num_elems, num_surfs_in_elem,"mesh.surfs_in_elem"); + //if(mk_sides) sides_in_elem = CArrayKokkos(num_elems*num_surfs_in_elem); - // printf("j-plus\n"); + // helper variables for temporary storage, its sized larger than num_surfs + CArrayKokkos elems_in_surf_helper(num_elems*num_surfs_in_elem,2,"elems_in_surf_helper"); + CArrayKokkos faces_in_surf_helper(num_elems*num_surfs_in_elem,2,"faces_in_surf_helper"); + CArrayKokkos num_elems_in_surf_helper(num_elems*num_surfs_in_elem, "num_elems_in_surf_helper"); + //if(mk_sides) CArrayKokkos sides_in_surf_helper(num_elems*num_surfs_in_elem,2); + CArrayKokkos bdy_surfs_helper(num_elems*num_surfs_in_elem,"bdy_surfs_helper"); - /* + // ----------------------------------------------------------------------- + // 1b. Build Surfaces + // ----------------------------------------------------------------------- - i,j,k layout + for (size_t elem_gid = 0; elem_gid < num_elems; elem_gid++) { - k j - | / - |/ - o-->i + + FOR_ALL_CLASS(face_lid, 0, num_surfs_in_elem, { - k=0,kmax + // only search for a matching surface if it wasn't already found + if(face_elems_in_elem(elem_gid,face_lid)<0){ - (i,j+1) o--o (i+1,j+1) - / / - (i,j) o--o (i+1,j) + bool found_nbr_surf = false; - */ + // loop over neighboring faces to see if they have the same nodes as this face + for(size_t nbr_elem_lid=0; nbr_elem_lid= 0) continue; - // node_lid 2 in patch - temp_node_lids[count] = i + 1 + (j + 1) * num_1D + k_patch * num_1D * num_1D; // node_rid(i+1, j+1, k_patch, num_1D); - count++; - // node_lid 3 in patch - temp_node_lids[count] = i + 1 + j * num_1D + k_patch * num_1D * num_1D; // node_rid(i+1, j, k_patch, num_1D); - count++; - } // end for i - } // end for j - // printf("k-minus\n"); - - k_patch = num_1D - 1; - for (int j = 0; j < num_1D - 1; j++) { - for (int i = 0; i < num_1D - 1; i++) { - // node_lid 0 in patch - temp_node_lids[count] = i + j * num_1D + k_patch * num_1D * num_1D; // node_rid(i, j, k_patch, num_1D); - count++; - - // node_lid 1 in patch - temp_node_lids[count] = i + 1 + j * num_1D + k_patch * num_1D * num_1D; // node_rid(i+1, j, k_patch, num_1D); - count++; - - // node_lid 2 in patch - temp_node_lids[count] = i + 1 + (j + 1) * num_1D + k_patch * num_1D * num_1D; // node_rid(i+1, j+1, k_patch, num_1D); - count++; - - // node_lid 3 in patch - temp_node_lids[count] = i + (j + 1) * num_1D + k_patch * num_1D * num_1D; // node_rid(i, j+1, k_patch, num_1D); - count++; - } // end for i - } // end for j - - // printf("k-plus\n"); - - count = 0; - int elem_patch_lid = 0; - for (size_t surf_lid = 0; surf_lid < 6; surf_lid++) { - for (size_t patch_lid = 0; patch_lid < num_patches_in_surf; patch_lid++) { - for (size_t node_lid = 0; node_lid < 4; node_lid++) { - node_ordering_in_elem.host(elem_patch_lid, node_lid) = temp_node_lids[count]; - count++; - } // end for node_lid - elem_patch_lid++; - } // end for patch_lid in a surface - } // end for i - } // end if 3D - // - else{ - // 2D arbitrary order elements - int count = 0; - int i_patch, j_patch; - - // i-minus-dir patches - - i_patch = 0; - for (int j = 0; j < num_1D - 1; j++) { - temp_node_lids[count] = i_patch + j * num_1D; // node_rid(i_patch, j, num_1D; - count++; - - temp_node_lids[count] = i_patch + (j + 1) * num_1D; // node_rid(i_patch, j+1, num_1D; - count++; - } // end for j - - // i-plus-dir patches - i_patch = num_1D - 1; - for (int j = 0; j < num_1D - 1; j++) { - temp_node_lids[count] = i_patch + j * num_1D; // node_rid(i_patch, j, num_1D; - count++; - - temp_node_lids[count] = i_patch + (j + 1) * num_1D; // node_rid(i_patch, j+1, num_1D; - count++; - } // end for j - - j_patch = 0; - for (int i = 0; i < num_1D - 1; i++) { - temp_node_lids[count] = i + j_patch * num_1D; // node_rid(i, j_patch, num_1D); - count++; - - temp_node_lids[count] = i + 1 + j_patch * num_1D; // node_rid(i+1, j_patch, num_1D); - count++; - } // end for i - - j_patch = num_1D - 1; - for (int i = 0; i < num_1D - 1; i++) { - temp_node_lids[count] = i + j_patch * num_1D; // node_rid(i, j_patch, num_1D); - count++; - - temp_node_lids[count] = i + 1 + j_patch * num_1D; // node_rid(i+1, j_patch, num_1D); - count++; - } // end for i - - count = 0; - int elem_patch_lid = 0; - for (size_t surf_lid = 0; surf_lid < num_surfs_in_elem; surf_lid++) { - for (size_t patch_lid = 0; patch_lid < num_patches_in_surf; patch_lid++) { - for (size_t node_lid = 0; node_lid < num_nodes_in_patch; node_lid++) { - node_ordering_in_elem.host(elem_patch_lid, node_lid) = temp_node_lids[count]; - count++; - } // end for node_lid - elem_patch_lid++; - } // end for patch_lid in a surface - } // end for i - } // end else on dim - - // build zones in high order element - FOR_ALL_CLASS(elem_gid, 0, num_elems, { - size_t node_lids[8]; // temp storage for local node ids - for (int k = 0; k < num_1D - 1; k++) { - for (int j = 0; j < num_1D - 1; j++) { - for (int i = 0; i < num_1D - 1; i++) { - node_lids[0] = i + j * (num_1D) + k * (num_1D) * (num_1D); // i,j,k - node_lids[1] = i + 1 + j * (num_1D) + k * (num_1D) * (num_1D); // i+1, j, k - node_lids[2] = i + (j + 1) * (num_1D) + k * (num_1D) * (num_1D); // i,j+1,k - node_lids[3] = i + 1 + (j + 1) * (num_1D) + k * (num_1D) * (num_1D); // i+1, j+1, k - node_lids[4] = i + j * (num_1D) + (k + 1) * (num_1D) * (num_1D); // i, j , k+1 - node_lids[5] = i + 1 + j * (num_1D) + (k + 1) * (num_1D) * (num_1D); // i + 1, j , k+1 - node_lids[6] = i + (j + 1) * (num_1D) + (k + 1) * (num_1D) * (num_1D); // i,j+1,k+1 - node_lids[7] = i + 1 + (j + 1) * (num_1D) + (k + 1) * (num_1D) * (num_1D); // i+1, j+1, k+1 - - size_t zone_lid = i + j * (num_1D - 1) + k * (num_1D - 1) * (num_1D - 1); - size_t zone_gid = zones_in_elem(elem_gid, zone_lid); - - for (int node_lid = 0; node_lid < 8; node_lid++) { - // get global id for the node - size_t node_gid = nodes_in_elem(elem_gid, node_lids[node_lid]); - nodes_in_zone(zone_gid, node_lid) = node_gid; + // loop over the nodes in the hash and compare + size_t tally = 0; // if tally = num_nodes_in_surf it is a match + for(size_t node_lid=0; node_lid hash_keys_in_elem(num_elems, num_patches_in_elem, num_nodes_in_patch, "hash_keys_in_elem"); // always 4 ids in 3D + if(tally==num_nodes_in_surf){ + + // its a match! Yah, this face has a soul mate! + const size_t surf_gid = Kokkos::atomic_fetch_add(&surf_counter(0), 1); + surfs_in_elem(elem_gid,face_lid) = surf_gid; + surfs_in_elem(nbr_elem_gid,nbr_face_lid) = surf_gid; + + // --- must compress these later to size num_surfs --- + elems_in_surf_helper(surf_gid,0) = elem_gid; + elems_in_surf_helper(surf_gid,1) = nbr_elem_gid; + faces_in_surf_helper(surf_gid,0) = face_lid; + faces_in_surf_helper(surf_gid,1) = nbr_face_lid; + + num_elems_in_surf_helper(surf_gid) = 2; + + //if(mk_sides){ + // const size_t side_gid = face_lid + elem_gid*num_surfs_in_elem; + // const size_t nbr_side_gid = nbr_face_lid + nbr_elem_gid*num_surfs_in_elem; + // sides_in_surf_helper(surf_gid,0) = side_gid; + // sides_in_surf_helper(surf_gid,1) = nbr_side_gid; + //} + + face_elems_in_elem(elem_gid,face_lid) = nbr_elem_gid; + face_elems_in_elem(nbr_elem_gid,nbr_face_lid) = elem_gid; + + found_nbr_surf = true; + + break; // exit + } // else not a match to this face_lid + + } // end for nbr faces in nbr elem + + if(found_nbr_surf==true) break; + + } // end for loop over nbr elems + + + // boundary surfaces don't have a match + if(found_nbr_surf==false){ + + // you didn't find a match, no soulmate for you, until then, you are on the bdy + const size_t surf_gid = Kokkos::atomic_fetch_add(&surf_counter(0), 1); + surfs_in_elem(elem_gid,face_lid) = surf_gid; + + // --- must compress these later to have size num_surfs --- + elems_in_surf_helper(surf_gid,0) = elem_gid; + elems_in_surf_helper(surf_gid,1) = -elem_gid-1; // negative because elem does not exist + faces_in_surf_helper(surf_gid,0) = face_lid; + faces_in_surf_helper(surf_gid,1) = -face_lid-1; // negative because elem does not exist + + num_elems_in_surf_helper(surf_gid) = 1; // no neighbor, it's a boundary + + // Important: + // By design, we use a negative index for surface elem and face connectivity ; + // on the boundary for the second accessor. A negative index will cause code to + // stop because element and face data structures are size_t. + + //if(mk_sides){ + // const size_t side_gid = face_lid + elem_gid*num_surfs_in_elem; + // sides_in_surf_helper(surf_gid,0) = side_gid; + //} + + // --- must compress these later to have size num_bdy_surfs --- + const size_t bdy_surf_gid = Kokkos::atomic_fetch_add(&bdy_surf_counter(0), 1); + bdy_surfs_helper(bdy_surf_gid) = surf_gid; + + } // end if bdy surface + + } // end if this surface was already saved + // remember if I found a face neighbor, the face_elems_in_elem has index >=0 + + }); // end parallel for over faces in elem + Kokkos::fence(); // don't go to the next elem until this one is finished + // jumping to the next element will break check on face_elems_in_elem(elem_gid,face_lid), thus a fence is needed + + } // end for elems + surf_counter.update_host(); + bdy_surf_counter.update_host(); + + // allocate memory for surface data structures + num_surfs = surf_counter.host(0); + num_bdy_surfs = bdy_surf_counter.host(0); + - // for saving the adjacent patch_lid, which is the slide_lid - // CArrayKokkos neighboring_side_lids (num_elems, num_patches_in_elem); + // ----------------------------------------------------------------------- + // 1c. Finish populating values in surface data structures + // ----------------------------------------------------------------------- - // allocate memory for the patches in the elem - patches_in_elem = CArrayKokkos(num_elems, num_patches_in_elem, "mesh.patches_in_elem"); + nodes_in_surf = CArrayKokkos(num_surfs,num_nodes_in_surf, "mesh.nodes_in_surf"); + elems_in_surf = CArrayKokkos(num_surfs, 2, "mesh.elems_in_surf"); + num_elems_in_surf = CArrayKokkos(num_surfs, "mesh.num_elems_in_surf"); + faces_in_surf = CArrayKokkos(num_surfs, 2, "mesh.elem_faces_in_surf"); + //if(mk_sides)sides_in_surf = CArrayKokkos(num_surfs, 2, "mesh.sides_in_surf"); + + FOR_ALL_CLASS(surf_gid, 0, num_surfs, { + elems_in_surf(surf_gid,0) = elems_in_surf_helper(surf_gid,0); // = elem_gid + elems_in_surf(surf_gid,1) = elems_in_surf_helper(surf_gid,1); // = nbr_elem_gid (on bdy = -elem_gid-1) + faces_in_surf(surf_gid,0) = faces_in_surf_helper(surf_gid,0); // = face_lid + faces_in_surf(surf_gid,1) = faces_in_surf_helper(surf_gid,1); // = nbr_face_lid (on bdy = -face_lid-1) + num_elems_in_surf(surf_gid) = num_elems_in_surf_helper(surf_gid); + + //if(mk_sides){ + // sides_in_surf(surf_gid,0) = sides_in_surf_helper(surf_gid,0); // = side_gid; + // if(num_elems_in_surf(surf_gid)==2) + // sides_in_surf(surf_gid,1) = sides_in_surf_helper(surf_gid,1); // = nbr_side_gid; + //} + + const size_t elem_gid = elems_in_surf(surf_gid,0); + const size_t face_lid = faces_in_surf(surf_gid,0); + surfs_in_elem(elem_gid, face_lid) = surf_gid; + + + // set the neighboring element, if it exists + if(num_elems_in_surf(surf_gid)==2){ + const size_t nbr_elem_gid = elems_in_surf(surf_gid,1); + const size_t nbr_face_lid = faces_in_surf(surf_gid,1); + surfs_in_elem(nbr_elem_gid, nbr_face_lid) = surf_gid; + } - // a temporary storage for the patch_gids that are on the mesh boundary - CArrayKokkos temp_bdy_patches(num_elems * num_patches_in_elem, "temp_bdy_patches"); + for(size_t node_lid=0; node_lid (num_bdy_surfs, "mesh.bdy_surfs"); + FOR_ALL_CLASS(bdy_surf_gid, 0, num_bdy_surfs,{ + bdy_surfs(bdy_surf_gid) = bdy_surfs_helper(bdy_surf_gid); + }); - // get and save the global index of the node - sorted_patch_nodes[patch_node_lid] = nodes_in_elem(elem_gid, node_lid); - } // end for node_lid + CArrayKokkos num_bdy_surfs_in_bdy_node(num_nodes, "mesh.num_bdy_surfs_in_bdy_node"); + num_bdy_surfs_in_bdy_node.set_values(0); - // sort nodes from smallest to largest - bubble_sort(sorted_patch_nodes, num_nodes_in_patch); + CArrayKokkos bdy_surf_node_storage_bin(num_bdy_surfs, num_nodes_in_surf, "mesh.bdy_surf_node_storage_bin"); + bdy_surf_node_storage_bin.set_values(-1); - // save hash_keys in the this elem - for (size_t key_lid = 0; key_lid < num_nodes_in_patch; key_lid++) { - hash_keys_in_elem(elem_gid, patch_lid, key_lid) = sorted_patch_nodes[key_lid]; // 4 node values are keys - } // for - } // end for patch_lid - }); // end FOR_ALL elem_gid + DCArrayKokkos bdy_node_counter(1, "mesh.bdy_node_counter"); + CArrayKokkos bdy_node_helper(num_bdy_surfs*num_nodes_in_surf, "mesh.bdy_node_helper"); - DCArrayKokkos num_values(2, "num_values"); + FOR_ALL_CLASS(bdy_surf_gid, 0, num_bdy_surfs,{ - // 8x8x8 mesh - // num_patches = 8*8*9*3 = 1728 - // bdy_patches = 8*8*6 = 384 - // + const size_t surf_gid = bdy_surfs(bdy_surf_gid); - // step 2: walk around the elements and save the elem pairs that have the same hash_key - RUN_CLASS({ - // serial execution on GPU - - size_t patch_gid = 0; - size_t bdy_patch_gid = 0; - - for (size_t elem_gid = 0; elem_gid < num_elems; elem_gid++) { - // loop over the patches in this elem - for (size_t patch_lid = 0; patch_lid < num_patches_in_elem; patch_lid++) { - size_t exit = 0; - - // negative values mean the patch has not been saved - if (hash_keys_in_elem(elem_gid, patch_lid, 0) >= 0) { - // find the nighboring patch with the same hash_key - - for (size_t neighbor_elem_lid = 0; neighbor_elem_lid < num_elems_in_elem(elem_gid); neighbor_elem_lid++) { - // get the neighboring element global index - size_t neighbor_elem_gid = elems_in_elem(elem_gid, neighbor_elem_lid); - - for (size_t neighbor_patch_lid = 0; neighbor_patch_lid < num_patches_in_elem; neighbor_patch_lid++) { - size_t save_it = 0; - for (size_t key_lid = 0; key_lid < num_nodes_in_patch; key_lid++) { - if (hash_keys_in_elem(neighbor_elem_gid, neighbor_patch_lid, key_lid) == hash_keys_in_elem(elem_gid, patch_lid, key_lid)) { - save_it++; // if save_it == num_nodes after this loop, then it is a match - } - } // end key loop - - // this hash is from the nodes on the patch - if (save_it == num_nodes_in_patch) { - // make it negative, because we saved it - hash_keys_in_elem(elem_gid, patch_lid, 0) = -1; - hash_keys_in_elem(neighbor_elem_gid, neighbor_patch_lid, 0) = -1; - - // save the patch_lids for the adjacent sides - // neighboring_side_lids(elem_gid, patch_lid) = neighbor_patch_lid; - // neighboring_side_lids(neighbor_elem_gid, neighbor_patch_lid) = patch_lid; - - // save the patch_gid - patches_in_elem(elem_gid, patch_lid) = patch_gid; - patches_in_elem(neighbor_elem_gid, neighbor_patch_lid) = patch_gid; - - patch_gid++; - - exit = 1; - break; - } // end if - } // end for loop over a neighbors patch set - - if (exit == 1) { - break; - } - } // end for loop over elem neighbors - } // end if hash<0 - } // end for patch_lid + const size_t elem_gid = elems_in_surf(surf_gid,0); + const size_t face_lid = faces_in_surf(surf_gid,0); - // loop over the patches in this element again - // remaining positive hash key values are the boundary patches - for (size_t patch_lid = 0; patch_lid < num_patches_in_elem; patch_lid++) { - if (hash_keys_in_elem(elem_gid, patch_lid, 0) >= 0) { - hash_keys_in_elem(elem_gid, patch_lid, 0) = -1; // make it negative, because we saved it + for(size_t node_lid=0; node_lid(num_patches, 2, "mesh.elems_in_patch"); - nodes_in_patch = CArrayKokkos(num_patches, num_nodes_in_patch, "mesh.nodes_in_patch"); + CArrayKokkos bdy_nodes(num_bdy_nodes, "mesh.bdy_nodes"); - // a temporary variable to help populate patch structures - CArrayKokkos num_elems_in_patch_saved(num_patches, "num_elems_in_patch_saved"); - - // initialize the number of elems in a patch saved to zero - FOR_ALL_CLASS(patch_gid, 0, num_patches, { - num_elems_in_patch_saved(patch_gid) = 0; + // compress the storage of boundary nodes + FOR_ALL_CLASS(bdy_node_gid, 0, num_bdy_nodes,{ + bdy_nodes(bdy_node_gid) = bdy_node_helper(bdy_node_gid); }); - for (size_t elem_gid = 0; elem_gid < num_elems; elem_gid++) { - FOR_ALL_CLASS(patch_lid, 0, num_patches_in_elem, { - size_t patch_gid = patches_in_elem(elem_gid, patch_lid); - - size_t num_saved = num_elems_in_patch_saved(patch_gid); - - elems_in_patch(patch_gid, num_saved) = elem_gid; - - // record that an elem_gid was saved - num_elems_in_patch_saved(patch_gid)++; - - // save the nodes on this patch - for (size_t patch_node_lid = 0; patch_node_lid < num_nodes_in_patch; patch_node_lid++) { - // get the local node index of the element for this patch and node in patch - size_t node_lid = node_ordering_in_elem(patch_lid, patch_node_lid); - - // get and save the global index of the node - nodes_in_patch(patch_gid, patch_node_lid) = nodes_in_elem(elem_gid, node_lid); - } // end for node_lid - }); // end FOR_ALL patch_lid - } // end for - - // Surfaces and patches in surface - if (elem_kind == mesh_init::arbitrary_tensor_element) { - // allocate memory for the surfaces in the elem - surfs_in_elem = CArrayKokkos(num_elems, num_surfs_in_elem); - // allocate memory for surface data structures - num_surfs = num_patches / num_patches_in_surf; - - patches_in_surf = CArrayKokkos(num_surfs, num_patches_in_surf, "mesh.patches_in_surf"); - elems_in_surf = CArrayKokkos(num_surfs, 2, "mesh.elems_in_surf"); - surf_in_patch = CArrayKokkos(num_patches, "mesh.surf_in_patch"); + // ----------------------------------------------------------------------- + // 2a. Build Patches, a decomposition of the surface + // ----------------------------------------------------------------------- + + num_patches_in_surf = pow(Pn,(num_dims-1)); // = Pn_order or = Pn_order*Pn_order + num_nodes_in_patch = 2*(num_dims-1); // 2 (2D) or 4 (3D) + num_patches_in_elem = num_surfs_in_elem*num_patches_in_surf; - FOR_ALL_CLASS(surf_gid, 0, num_surfs, { - // loop over the patches in this surface - for (size_t patch_lid = 0; patch_lid < num_patches_in_surf; patch_lid++) { - // get patch_gid - size_t patch_gid = patch_lid + surf_gid * num_patches_in_surf; + patches_in_surf = patches_in_surf_t(num_patches_in_surf); // initializing - // save the patch_gids - patches_in_surf(surf_gid, patch_lid) = patch_gid; + num_patches = num_surfs*num_patches_in_surf; + elems_in_patch = CArrayKokkos(num_patches, 2, "mesh.elems_in_patch"); + nodes_in_patch = CArrayKokkos(num_patches, num_nodes_in_patch, "mesh.nodes_in_patch"); + + surf_in_patch = CArrayKokkos(num_patches, "mesh.surf_in_patch"); + patches_in_elem = CArrayKokkos(num_elems, num_patches_in_elem,"patches_in_elem"); + - // save the surface this patch belongs to - surf_in_patch(patch_gid) = surf_gid; - } // end for + DCArrayKokkos patch_node_ordering_in_elem (num_surfs_in_elem, num_patches_in_surf, num_nodes_in_patch); + get_patch_node_lids(patch_node_ordering_in_elem, num_1D, num_dims); // R-hand rule node convention for patch nodes - // get first patch in the surface, and populate elem surface structures - size_t this_patch_gid = surf_gid * num_patches_in_surf; - elems_in_surf(surf_gid, 0) = elems_in_patch(this_patch_gid, 0); // elem_gid0 - elems_in_surf(surf_gid, 1) = elems_in_patch(this_patch_gid, 1); // elem_gid1 - }); // end FOR_ALL over surfaces + // now break up the surface into patches + FOR_ALL_CLASS(surf_gid, 0, num_surfs,{ - // save surfaces in elem - FOR_ALL_CLASS(elem_gid, 0, num_elems, { - for (size_t surf_lid = 0; surf_lid < num_surfs_in_elem; surf_lid++) { - // get the local patch_lid - size_t patch_lid = surf_lid * num_patches_in_surf; + const size_t elem_gid = elems_in_surf(surf_gid,0); + const size_t face_lid = faces_in_surf(surf_gid,0); - // get the patch_gids in this element - size_t patch_gid = patches_in_elem(elem_gid, patch_lid); + const int nbr_elem_gid = elems_in_surf(surf_gid,1); // num_nbrs (if negative, does not exist) + const int nbr_face_lid = faces_in_surf(surf_gid,1); // num_nbrs (if negative, does not exist) - // save the surface gid - // Grab the first patch on surf and return surface_gid from surf_in_patch // - surfs_in_elem(elem_gid, surf_lid) = surf_in_patch(patch_gid); - } // end surf_lid - }); + // loop patches on this surface + for(size_t patch_lid =0; patch_lid surf_node_ordering_in_elem; + elems_in_patch(patch_gid,0) = elem_gid; + elems_in_patch(patch_gid,1) = nbr_elem_gid; + + surf_in_patch(patch_gid) = surf_gid; - if (num_dims == 3) { - // num_1D = Pn+1 - int num_surface_nodes = num_surfs_in_elem * pow(num_1D, num_dims - 1); - size_t temp_surf_node_lids[num_surface_nodes]; - // 2D arbitrary order elements - int count = 0; - - for (int i_surf = 0; i_surf < 2; i_surf++) { - for (int k = 0; k < num_1D; k++) { - for (int j = 0; j < num_1D; j++) { - // node_lid 0 in patch - // index = i + j*num_1D + k*num_1D*num_1D; - temp_surf_node_lids[count] = i_surf + j * num_1D + k * num_1D * num_1D; - count++; - } // end for k - } // end for j - } + size_t elem_patch_lid = patch_lid + face_lid*num_patches_in_surf; + patches_in_elem(elem_gid, elem_patch_lid) = patch_gid; - for (int j_surf = 0; j_surf < 2; j_surf++) { - for (int k = 0; k < num_1D; k++) { - for (int i = 0; i < num_1D; i++) { - // node_lid 0 in patch - temp_surf_node_lids[count] = i + j_surf * num_1D + k * num_1D * num_1D; - count++; - } - } + + if(num_elems_in_surf(surf_gid)==2){ + size_t nbr_elem_patch_lid = patch_lid + nbr_face_lid*num_patches_in_surf; + patches_in_elem(nbr_elem_gid, nbr_elem_patch_lid) = patch_gid; } - - for (int k_surf = 0; k_surf < 2; k_surf++) { - for (int j = 0; j < num_1D; j++) { - for (int i = 0; i < num_1D; i++) { - // node_lid 0 in patch - temp_surf_node_lids[count] = i + j * num_1D + k_surf * num_1D * num_1D; - count++; - } - } + + // save the nodes in this patch using the first element to find the surface + for(size_t patch_node_lid=0; patch_node_lid(num_surfs, num_1D * num_1D, "mesh.nodes_in_surf"); - - num_nodes_in_surf = num_1D * num_1D; - surf_node_ordering_in_elem = DViewCArrayKokkos(&temp_surf_node_lids[0], num_surfs_in_elem, num_nodes_in_surf); - surf_node_ordering_in_elem.update_device(); - for (int elem_gid = 0; elem_gid < num_elems; elem_gid++) { - FOR_ALL_CLASS(surf_lid, 0, num_surfs_in_elem, { - int surf_gid = surfs_in_elem(elem_gid, surf_lid); - for (int surf_node_lid = 0; surf_node_lid < num_nodes_in_surf; surf_node_lid++) { - int node_lid = surf_node_ordering_in_elem(surf_lid, surf_node_lid); - int node_gid = nodes_in_elem(elem_gid, node_lid); - nodes_in_surf(surf_gid, surf_node_lid) = node_gid; - } // end loop over surf_node_lid - }); // end loop over FOR_ALL_CLASS - } // end loop over elem_gid - } // end 3D scope - } // end of high-order mesh objects - - // ---------------- - - // allocate memory for boundary patches - bdy_patches = CArrayKokkos(num_bdy_patches, "mesh.bdy_patches"); - - FOR_ALL_CLASS(bdy_patch_gid, 0, num_bdy_patches, { - bdy_patches(bdy_patch_gid) = temp_bdy_patches(bdy_patch_gid); - }); // end FOR_ALL bdy_patch_gid - - // find and store the boundary nodes - CArrayKokkos temp_bdy_nodes(num_nodes, "temp_bdy_nodes"); - CArrayKokkos hash_bdy_nodes(num_nodes, "hash_bdy_nodes"); - - FOR_ALL_CLASS(node_gid, 0, num_nodes, { - hash_bdy_nodes(node_gid) = -1; - }); // end for node_gid + }); - // Parallel loop over boundary patches - DCArrayKokkos num_bdy_nodes_saved(1, "num_bdy_nodes_saved"); - RUN_CLASS({ - num_bdy_nodes_saved(0) = 0; - for (size_t bdy_patch_gid = 0; bdy_patch_gid < num_bdy_patches; bdy_patch_gid++) { - // get the global index of the patch that is on the boundary - size_t patch_gid = bdy_patches(bdy_patch_gid); - - // tag the boundary nodes - for (size_t node_lid = 0; node_lid < num_nodes_in_patch; node_lid++) { - size_t node_gid = nodes_in_patch(patch_gid, node_lid); + // ----------------------------------------------------------------------- + // 2b. Build boundary patches + // ----------------------------------------------------------------------- + + num_bdy_patches = num_bdy_surfs*num_patches_in_surf; + bdy_patches = CArrayKokkos (num_bdy_patches, "mesh.bdy_patches"); - if (hash_bdy_nodes(node_gid) < 0) { - hash_bdy_nodes(node_gid) = node_gid; - temp_bdy_nodes(num_bdy_nodes_saved(0)) = node_gid; + FOR_ALL_CLASS(bdy_surf_gid, 0, num_bdy_surfs,{ - // printf("bdy_node = %lu \n", node_gid); - num_bdy_nodes_saved(0)++; - } // end if - } // end for node_lid - } // end for loop over bdy_patch_gid - }); // end RUN - Kokkos::fence(); + const size_t surf_gid = bdy_surfs(bdy_surf_gid); - // copy value to host (CPU) - num_bdy_nodes_saved.update_host(); + // all patches on this surface are on the boundary + for(size_t patch_lid =0; patch_lid(num_bdy_nodes, "mesh.bdy_nodes"); - - FOR_ALL_CLASS(node_gid, 0, num_bdy_nodes, { - bdy_nodes(node_gid) = temp_bdy_nodes(node_gid); - }); // end for boundary node_gid + // testing: + // 8x8x8 linear mesh + // num_patches = 8*8*9*3 = 1728 + // bdy_patches = 8*8*6 = 384 - // printf("Num boundary nodes = %lu \n", num_bdy_nodes); + // see indexing_utils for ASCII art of element + // size_t patch_node_ordering[24] = { 0, 4, 6, 2, + // 1, 3, 7, 5, + // 0, 1, 5, 4, + // 3, 2, 6, 7, + // 0, 2, 3, 1, + // 4, 5, 7, 6 }; + + // J + // | + // 3---2 + // | | -- I + // 0---1 + // + //size_t patch_node_ordering[8] = { 0, 3, + // 1, 2, + // 0, 1, + // 3, 2 }; - return; - } // end patch connectivity method + ///////////////////////////////////////////////////////////////////////////// + /// + /// \fn build_node_node_connectivity + /// + /// \brief Build the connectivity between a node and the nodes connected by edges + /// + /// + ///////////////////////////////////////////////////////////////////////////// // build the patches void build_node_node_connectivity() { @@ -1482,13 +1114,15 @@ struct Mesh num_saved++; } // end for node_lid }); // end parallel for over nodes + } // end of node node connectivity + ///////////////////////////////////////////////////////////////////////////// /// /// \fn build_connectivity /// - /// \brief Calls multiple build connectivity function + /// \brief Calls multiple build connectivity functions /// ///////////////////////////////////////////////////////////////////////////// void build_connectivity() @@ -1502,21 +1136,22 @@ struct Mesh build_elem_elem_connectivity(); if (verbose) printf("Built element-element connectivity \n"); - build_patch_connectivity(); - if (verbose) printf("Built patch connectivity \n"); + build_surf_connectivity(); + if (verbose) printf("Built surface and patch connectivity \n"); build_node_node_connectivity(); if (verbose) printf("Built node-node connectivity \n"); } + ///////////////////////////////////////////////////////////////////////////// /// - /// \fn init_bdy_sets + /// \fn initialize_bdy_sets /// /// \brief Initialize memory for boundary sets /// ///////////////////////////////////////////////////////////////////////////// - void init_bdy_sets(size_t num_bcs) + void initialize_bdy_sets(size_t num_bcs) { if (num_dims == 0) { Kokkos::abort("Error: mesh.num_dims is not set. Exiting at init_bdy_sets()."); @@ -1532,9 +1167,14 @@ struct Mesh // in tag_bdys fcn after the sparsity is known, see geometry_new.cpp return; - } // end of init_bdy_sets method + } // end of initialize_bdy_sets method + + + + // remember that build_quadrature_point_connectivity() is located in geometry.h + // the reason for this location is that it requires the mesh and reference + // spaces. - }; // end Mesh_t } // end namespace swage