From a76520fd4f2d2a269a9335543fbab22cc1745d39 Mon Sep 17 00:00:00 2001 From: Nathaniel Morgan Date: Thu, 2 Jul 2026 08:49:56 -0600 Subject: [PATCH 01/59] starting refactor of ref_elem to support 1D, 2D, and 3D ref spaces --- src/elements/ref_elem_new.h | 996 ++++++++++++++++++++++++++++++++++ src/elements/ref_quadrature.h | 8 +- src/swage/unstructured_mesh.h | 5 +- 3 files changed, 1002 insertions(+), 7 deletions(-) create mode 100644 src/elements/ref_elem_new.h diff --git a/src/elements/ref_elem_new.h b/src/elements/ref_elem_new.h new file mode 100644 index 00000000..4e83746a --- /dev/null +++ b/src/elements/ref_elem_new.h @@ -0,0 +1,996 @@ +/********************************************************************************************** +� 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. +**********************************************************************************************/ +#ifndef REF_ELEM_NEW_H +#define REF_ELEM_NEW_H + +#include +#include "matar.h" +#include "ref_quadrature.h" + + +using namespace mtr; + + +namespace ref_space +{ + enum ElementType + { + linearElement = 0, // single quadrature point element + arbitraryOrderElement = 1 // fully integrated arbitrary-order element + }; + + // LagrangeGLL: continuous H1-like (kinematic). LagrangeGL: DG L2-like (thermo). + enum BasisType + { + LagrangeLobatto = 0, // GLL in Steven's solver (C0 kinematic space) + LagrangeLegendra = 1, // GL in Steven's solver (DG thermo space) + }; + + enum QuadType + { + GaussLobatto = 0, + GaussLegendre = 1 + }; +} // end ref_elem namespace + + +namespace elements +{ + + // Quadrature rules for surfaces and elems + struct Quadrature_t + { + ref_space::QuadType QuadType; + + size_t elem_dims = 0; + size_t num_qpts_in_elem = 0; + size_t num_qpts_in_1d = 0; + + CArrayKokkos qpt_positions; + CArrayKokkos qpt_weights; + + ///////////////////////////////////////////////////////////////////////////// + /// + /// \fn initialize_quadrature + /// + /// \brief Set up quadrature in a volume or surface element + /// + /// \param TypeInp The type of quadrature (e.g., Lobatto or Legendra) + /// \param num_qpts_in_1d_inp The number of quadrature in 1D, applied to each direction. + /// \param elem_dims_in The number dimensions + /// + ///////////////////////////////////////////////////////////////////////////// + void initialize_quadrature(const ref_space::QuadType TypeInp, + const size_t num_qpts_in_1d_inp, + const size_t elem_dims_in) + { + QuadType = TypeInp; + + elem_dims = elem_dims_in; + num_qpts_in_1d = num_qpts_in_1d_inp; + if(num_qpts_in_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"); + + // temporary 1D variables to build 3D element + CArrayKokkos qpt_positions_1d(num_qpts_in_1d, "qpt_positions_1d"); + CArrayKokkos qpt_weights_1d (num_qpts_in_1d, "qpt_weights_1d"); + + if(QuadType = QuadType::GaussLegendre){ + RUN_CLASS({ + get_legendre_nodes_1D(qpt_positions_1d, num_qpts_in_1d); + get_legendre_weights_1D(qpt_weights_1d, num_qpts_in_1d); + }); + } + else if(QuadType = QuadType::GaussLobatto){ + RUN_CLASS({ + get_lobatto_nodes_1D(qpt_positions_1d, num_qpts_in_1d); + get_lobatto_weights_1D(qpt_weights_1d, num_qpts_in_1d); + }); + } + else + { + throw std::runtime_error("ERROR: unsupported quadrature set specified \n"); + } + + // 3D volume element + if(elem_dims==3){ + FOR_ALL_CLASS(k, 0, num_qpts_in_1d, + j, 0, num_qpts_in_1d, + i, 0, num_qpts_in_1d, { + + const size_t qpt_rid = qpt_rid(i, j, k); + + qpt_positions(qpt_rid, 0) = qpt_positions_1d(i); + qpt_positions(qpt_rid, 1) = qpt_positions_1d(j); + qpt_positions(qpt_rid, 2) = qpt_positions_1d(k); + + qpt_weights(qpt_rid) = qpt_weights_1d(i) * qpt_weights_1d(j) * qpt_weights_1d(k); + }); + Kokkos::fence(); + } + // 2D volume or 2D surface element + else if (elem_dims==2){ + FOR_ALL_CLASS(j, 0, num_qpts_in_1d, + i, 0, num_qpts_in_1d, { + + const size_t qpt_rid = qpt_rid(i, j); + + qpt_positions(qpt_rid, 0) = qpt_positions_1d(i); + qpt_positions(qpt_rid, 1) = qpt_positions_1d(j); + + qpt_weights(qpt_rid) = qpt_weights_1d(i) * qpt_weights_1d(j); + }); + Kokkos::fence(); + } + // 1D volume, edge, or 1D surface element + else if (elem_dims==1) { + FOR_ALL_CLASS(i, 0, num_qpts_in_1d, { + + const size_t qpt_rid = i; + + qpt_positions(qpt_rid, 0) = qpt_positions_1d(i); + + qpt_weights(qpt_rid) = qpt_weights_1d(i); + }); + Kokkos::fence(); + } + else{ + throw std::runtime_error("ERROR: unsupported quadrature elem dims \n"); + } + + } // init fcn quadrature + + + ///////////////////////////////////////////////////////////////////////////// + /// + /// \fn 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 qpt_rid(size_t i, size_t j, size_t k) const + { + return i + (j + k * num_qpts_in_1d) * num_qpts_in_1d; + } // end function + + ///////////////////////////////////////////////////////////////////////////// + /// + /// \fn qpt_rid + /// + /// \brief Compute the 1D array index for a quadrature point in an 2D volume + /// or surface element. + /// + /// Calculates the row-major flat index corresponding to a quadrature point + /// at the position (i, j) within a 2D volume or surface 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 qpt_rid(size_t i, size_t j) const + { + return i + j * num_qpts_in_1d; + } // end function + + }; // end Quadrature_t + + + // reference element data structure + struct ref_elem_t + { + + ref_space::ElemType ElemType = ref_space::linearElement; ///< The type of element + ref_space::BasisType BasisType = ref_space::LagrangeLobatto; /// dof_positions; + + + // 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. This that case, user must create multiple reference elements, one + /// that defines the position and one for the discontinous fields. + /// + /// \param num_dims_inp The number of spatial dimensions (e.g., 1, 2, or 3). + /// \param p_order The element order e.g., constant = 0, linear = 1, quadratic = 2, ... + /// + /// \return void + /// + ///////////////////////////////////////////////////////////////////////////// + void initialize_ref_elem(const ref_space::ElemType ElemTypeInp, + const ref_space::BasisType BasisTypeInp, + const struct Quadrature_t Quadrature, + const size_t p_order) + { + // set element and basis type + ElemType = 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 elems supported \n"); + + // ----------------------------------------------------------------------- + // Step 1a: determine the number of DOFs in 3D + // ----------------------------------------------------------------------- + num_dofs_1d = p_order + 1; + + for (int dim = 0; dim < elem_dims; dim++) { + num_dofs_in_elem *= num_dofs_1d; + } // end for + + // ----------------------------------------------------------------------- + // Step 1b: get the positions in reference space for the DOFs + // ----------------------------------------------------------------------- + dof_positions = CArrayKokkos(num_dofs_in_elem, elem_dims, "dof_positions"); + CArrayKokkos dof_positions_1d(num_dofs_1d, "dof_positions_1d"); + + // dof positions can be at legendre or lobatto locations in elem + if(BasisTypeInp = ref_elem_init::legendre){ + RUN_CLASS({ + get_legendre_nodes_1D(dof_positions_1d, num_dofs_1d); + }); + } + else if(BasisTypeInp = ref_elem_init::lobatto){ + RUN_CLASS({ + get_lobatto_nodes_1D(dof_positions_1d, num_dofs_1d); + }); + } + else + { + throw std::runtime_error("ERROR: unsupported basis DOF locations specified \n"); + } + + // 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, { + + const size_t dof_rlid = dof_rid(i, j, k); + + dof_positions(dof_rlid, 0) = dof_positions_1d(i); + dof_positions(dof_rlid, 1) = dof_positions_1d(j); + dof_positions(dof_rlid, 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, { + + const size_t dof_rlid = dof_rid(i, j); + + dof_positions(dof_rlid, 0) = dof_positions_1d(i); + dof_positions(dof_rlid, 1) = dof_positions_1d(j); + }); + } // end if 1D + // 1D volume, edge, or 1D surface element + else { + FOR_ALL_CLASS(i, 0, num_dofs_1d, { + + const size_t dof_rlid = i; + + dof_positions(dof_rlid, 0) = dof_positions_1d(i); + }); + } // end if 1D + Kokkos::fence(); + + // ----------------------------------------------------------------------- + // Step 2: Calculate the basis values at quadrature points + // ----------------------------------------------------------------------- + qpt_basis = CArrayKokkos(Quadrature.num_qpts_in_elem, num_dofs_in_elem, "qpt_basis"); + + // 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); + + + RUN_CLASS({ + for (size_t qpt_rid = 0; qpt_rid < Quadrature.num_qpts_in_elem; qpt_rid++) { + + // Get the evaluation coordinates + for (size_t dim = 0; dim < elem_dims; dim++) { + point(dim) = Quadrature.positions(qpt_rid, dim); + } + + get_basis(temp_basis, dof_positions_1d, temp_val_1d, temp_val_3d, point); + + 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(); + + // ----------------------------------------------------------------------- + // Step 3: Calculate the grad basis values at quadrature points + // ----------------------------------------------------------------------- + qpt_grad_basis = CArrayKokkos(Quadrature.num_qpts_in_elem, num_dofs_in_elem, elem_dims, "qpt_grad_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 Dval_1d(num_dofs_1d); + CArrayKokkos Dval_Nd(num_dofs_1d, elem_dims); + + + RUN_CLASS({ + for (int qpt_rid = 0; qpt_rid < Quadrature.num_qpts_in_elem; qpt_rid++) { + + // Get the evaluation coordinates + for (size_t dim = 0; dim < elem_dims; dim++) { + point(dim) = Quadrature.positions(qpt_rid, dim); + } + + partial_xi_basis(temp_partial_xi, dof_positions_1d, val_1d, val_3d, Dval_1d, Dval_3d, point); + if(elem_dims>1)partial_eta_basis(temp_partial_eta, dof_positions_1d, val_1d, val_3d, Dval_1d, Dval_3d, point); + if(elem_dims>2)partial_mu_basis(temp_partial_mu, dof_positions_1d, val_1d, val_3d, Dval_1d, Dval_3d, 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(); + + } // end of member function + + + ///////////////////////////////////////////////////////////////////////////// + /// + /// \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 + k * num_dofs_1d) * num_dofs_1d; + } + + ///////////////////////////////////////////////////////////////////////////// + /// + /// \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. + /// + /// \return The row-major offset index for the DOF in this element. + /// + ///////////////////////////////////////////////////////////////////////////// + KOKKOS_INLINE_FUNCTION + int dof_rid(int i, int j) const + { + return i + j * num_dofs_1d; + } + + + ///////////////////////////////////////////////////////////////////////////// + /// + /// \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 + { + + for(size_t dim=0; dim& 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 + { + // get grad basis + for (size_t i = 0; i < num_dofs_1d; i++) { + Dval_1d(i) = 0.0; + } + + // Calculate 1D partial w.r.t. xi for the X coordinate of the point + lagrange_derivative_1D(Dval_1d, dof_positions_1d, point(0)); + + // 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); + } + + // get Y and Z basis, the latter only if elem_dims = 3 + for(size_t dim=1; dim& 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 + { + + + // get X and Z basis values, the latter only if elem_dims = 3D + for(size_t dim=0; dim& 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) const + { + // this routine is only valid for 3D ref elems + + // get X and Y basis + for(size_t dim=0; dim& interp, // interpolant from each basis + const CArrayKokkos& dof_positions_1d, // location of basis DOFs in ref elem + const double x_point) const // point of interest in element + // calculate the basis value associated with each node_i + { + 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 + + 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)); + + // 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 + + ///////////////////////////////////////////////////////////////////////////// + /// + /// \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 CArrayKokkos& dof_positions_1d, // location of basis DOFs in ref elem + const double x_point) const // point of interest in element + { + 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; + + 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; + + // 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 + + // 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 + + }; // end struct + +} // end namespace elements + +#endif \ No newline at end of file diff --git a/src/elements/ref_quadrature.h b/src/elements/ref_quadrature.h index 0a1e150b..93d94f81 100644 --- a/src/elements/ref_quadrature.h +++ b/src/elements/ref_quadrature.h @@ -56,7 +56,7 @@ namespace elements ///////////////////////////////////////////////////////////////////////////// KOKKOS_FUNCTION static void get_lobatto_nodes_1D(const CArrayKokkos& 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/swage/unstructured_mesh.h b/src/swage/unstructured_mesh.h index b43c39c7..f358b0e6 100644 --- a/src/swage/unstructured_mesh.h +++ b/src/swage/unstructured_mesh.h @@ -46,9 +46,8 @@ namespace mesh_init // element mesh types enum elem_name_tag { - linear_simplex_element = 0, - linear_tensor_element = 1, - arbitrary_tensor_element = 2 + linear_tensor_element = 1, // single quadrature point element + arbitrary_tensor_element = 2 // fully integrated arbitrary-order element }; // other enums could go here on the mesh From 851251835cd2d3f96a6ce143e693b8e198c1ab10 Mon Sep 17 00:00:00 2001 From: Nathaniel Morgan Date: Thu, 2 Jul 2026 10:42:06 -0600 Subject: [PATCH 02/59] WIP: cleaned up unstructured mesh, overwrote ref_elem, and fixed bugs in ref_elem.h --- src/ELEMENTS.h | 1 - src/elements/ref_elem.h | 2055 ++++++++++++++----------------- src/elements/ref_elem_new.h | 996 ---------------- src/elements/ref_surf_elem.h | 2124 --------------------------------- src/swage/unstructured_mesh.h | 75 +- 5 files changed, 916 insertions(+), 4335 deletions(-) delete mode 100644 src/elements/ref_elem_new.h delete mode 100644 src/elements/ref_surf_elem.h 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/elements/ref_elem.h b/src/elements/ref_elem.h index 1dcb1317..c93a36f0 100644 --- a/src/elements/ref_elem.h +++ b/src/elements/ref_elem.h @@ -31,1318 +31,991 @@ 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. **********************************************************************************************/ -#ifndef REF_ELEM_H -#define REF_ELEM_H +#ifndef REF_ELEM_NEW_H +#define REF_ELEM_NEW_H #include #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 ref_space { - size_t num_dim; - - // 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; - - // 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; - - size_t num_gauss_1d; - size_t num_gauss_in_elem; - - // Zones - size_t num_zones_1d; - size_t num_zones_in_elem; - - // 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; - - // Thermodynamic basis evaluation at nodes // evaluations at points? - CArrayKokkos lobotto_point_dg_basis; - CArrayKokkos gauss_point_dg_basis; - - // 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 - - CArrayKokkos lobotto_point_positions; - CArrayKokkos gauss_point_positions; - // CArrayKokkos gauss_surf_positions; - - CArrayKokkos dof_positions; - CArrayKokkos dof_positions_1d; - - CArrayKokkos dg_dof_positions; - CArrayKokkos dg_dof_positions_1d; - - // Quadrature Weights - CArrayKokkos lob_weights_1D; // lobatto weights in 1D - CArrayKokkos leg_weights_1D; // Gauss legendre weights in 1D - - CArrayKokkos lobotto_point_weights; - CArrayKokkos gauss_point_weights; - // CArrayKokkos gauss_surf_weights; - - CArrayKokkos dof_lobatto_map; - CArrayKokkos dual_dof_lobatto_map; - - ///////////////////////////////////////////////////////////////////////////// - /// - /// \fn init - /// - /// \brief Initialize the reference element with polynomial order and dimension. - /// - /// 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. - /// - /// \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). - /// - /// \return void - /// - ///////////////////////////////////////////////////////////////////////////// - void init(int p_order, int num_dim_inp) + enum ElementType { - // 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; - - num_dofs_1d = p_order + 1; - num_dg_dofs_1d = p_order; - - num_zones_1d = p_order; - num_zones_in_elem = num_zones_1d * num_zones_1d * num_zones_1d; - } - - num_lobotto_in_elem = 1; - - num_dual_lobotto_in_elem = 1; - - num_gauss_in_elem = 1; - - num_dofs_in_elem = 1; - - // num_dofs_in_surf = 1; + linearElement = 0, // single quadrature point element + arbitraryOrderElement = 1 // fully integrated arbitrary-order element + }; - num_dg_dofs_in_elem = 1; - - for (int dim = 0; dim < num_dim; dim++) { - num_lobotto_in_elem *= num_lobotto_1d; - num_dual_lobotto_in_elem *= num_dual_lobotto_1d; - num_gauss_in_elem *= num_gauss_1d; - - num_dofs_in_elem *= num_dofs_1d; - num_dg_dofs_in_elem *= num_dg_dofs_1d; - } - - // keeping both for now. being able to call ref_elem.num_basis is convenient for computations. // - num_basis = num_dofs_in_elem; - num_dg_basis = num_dg_dofs_in_elem; - - // allocate memory - dof_positions = CArrayKokkos(num_dofs_in_elem, num_dim, "dof_positions"); - dof_positions_1d = CArrayKokkos(num_dofs_1d, "dof_positions_1d"); + // 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) + }; - 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"); + enum QuadratureType + { + GaussLobatto = 0, + GaussLegendre = 1 + }; +} // end ref_elem namespace - lobotto_point_weights = CArrayKokkos(num_lobotto_in_elem, "lobotto_point_weights"); - gauss_point_weights = CArrayKokkos(num_gauss_in_elem, "gauss_point_weights"); - // 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"); +namespace elements +{ - // 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"); + // Quadrature rules for surfaces and elems + struct Quadrature_t + { + ref_space::QuadratureType QuadratureType; + + size_t elem_dims = 0; + size_t num_qpts_in_elem = 0; + size_t num_qpts_in_1d = 0; + + CArrayKokkos qpt_positions; + CArrayKokkos qpt_weights; + + ///////////////////////////////////////////////////////////////////////////// + /// + /// \fn initialize_quadrature + /// + /// \brief Set up quadrature in a volume or surface element + /// + /// \param TypeInp The type of quadrature (e.g., Lobatto or Legendra) + /// \param num_qpts_in_1d_inp The number of quadrature in 1D, applied to each direction. + /// \param elem_dims_in The number dimensions + /// + ///////////////////////////////////////////////////////////////////////////// + void initialize_quadrature(const ref_space::QuadratureType TypeInp, + const size_t num_qpts_in_1d_inp, + const size_t elem_dims_in) + { + QuadratureType = TypeInp; + + elem_dims = elem_dims_in; + num_qpts_in_1d = num_qpts_in_1d_inp; + if(num_qpts_in_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"); - 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"); + // temporary 1D variables to build 3D element + CArrayKokkos qpt_positions_1d(num_qpts_in_1d, "qpt_positions_1d"); + CArrayKokkos qpt_weights_1d (num_qpts_in_1d, "qpt_weights_1d"); - lobotto_point_positions = CArrayKokkos(num_lobotto_in_elem, num_dim, "lobotto_point_positions"); - gauss_point_positions = CArrayKokkos(num_gauss_in_elem, num_dim, "gauss_point_positions"); + if(QuadratureType == ref_space::GaussLegendre){ + RUN_CLASS({ + get_legendre_nodes_1D(qpt_positions_1d, num_qpts_in_1d); + get_legendre_weights_1D(qpt_weights_1d, num_qpts_in_1d); + }); + } + else if(QuadratureType == ref_space::GaussLobatto){ + RUN_CLASS({ + get_lobatto_nodes_1D(qpt_positions_1d, num_qpts_in_1d); + get_lobatto_weights_1D(qpt_weights_1d, num_qpts_in_1d); + }); + } + else + { + throw std::runtime_error("ERROR: unsupported quadrature set specified \n"); + } - 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"); + // 3D volume element + if(elem_dims==3){ + FOR_ALL_CLASS(k, 0, num_qpts_in_1d, + j, 0, num_qpts_in_1d, + i, 0, num_qpts_in_1d, { - // --- build gauss nodal positions and weights --- + const size_t rid = get_qpt_rid(i, j, k); - lob_points_1D = CArrayKokkos(num_lobotto_1d, "lob_points_1d"); + qpt_positions(rid, 0) = qpt_positions_1d(i); + qpt_positions(rid, 1) = qpt_positions_1d(j); + qpt_positions(rid, 2) = qpt_positions_1d(k); - dual_lob_points_1D = CArrayKokkos(num_dual_lobotto_1d, "dual_lob_points_1d"); + qpt_weights(rid) = qpt_weights_1d(i) * qpt_weights_1d(j) * qpt_weights_1d(k); + }); + Kokkos::fence(); + } + // 2D volume or 2D surface element + else if (elem_dims==2){ + FOR_ALL_CLASS(j, 0, num_qpts_in_1d, + i, 0, num_qpts_in_1d, { - RUN_CLASS({ - get_lobatto_nodes_1D(lob_points_1D, num_lobotto_1d); - }); + const size_t rid = get_qpt_rid(i, j); - RUN_CLASS({ - get_lobatto_nodes_1D(dual_lob_points_1D, num_dual_lobotto_1d); - }); + qpt_positions(rid, 0) = qpt_positions_1d(i); + qpt_positions(rid, 1) = qpt_positions_1d(j); - lob_weights_1D = CArrayKokkos(num_lobotto_1d, "lob_weights_1d"); - RUN_CLASS({ - get_lobatto_weights_1D(lob_weights_1D, num_lobotto_1d); - }); + qpt_weights(rid) = qpt_weights_1d(i) * qpt_weights_1d(j); + }); + Kokkos::fence(); + } + // 1D volume, edge, or 1D surface element + else if (elem_dims==1) { + FOR_ALL_CLASS(i, 0, num_qpts_in_1d, { - leg_nodes_1D = CArrayKokkos(num_gauss_1d, "leg_nodes_1d"); - RUN_CLASS({ - get_legendre_nodes_1D(leg_nodes_1D, num_gauss_1d); - }); + const size_t rid = i; - leg_weights_1D = CArrayKokkos(num_gauss_1d, "leg_weights_1d"); - RUN_CLASS({ - get_legendre_weights_1D(leg_weights_1D, num_gauss_1d); - }); + qpt_positions(rid, 0) = qpt_positions_1d(i); - // //WARNING WARNING WARNING may need to add lobotto_elem_positions etc for BV matrix to get control coefficients // + qpt_weights(rid) = qpt_weights_1d(i); + }); + Kokkos::fence(); + } + else{ + throw std::runtime_error("ERROR: unsupported quadrature elem dims \n"); + } - // // --- 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); + } // init fcn quadrature + + + ///////////////////////////////////////////////////////////////////////////// + /// + /// \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) const + { + return i + (j + k * num_qpts_in_1d) * num_qpts_in_1d; + } // end function + + ///////////////////////////////////////////////////////////////////////////// + /// + /// \fn get_qpt_rid + /// + /// \brief Compute the 1D array index for a quadrature point in an 2D volume + /// or surface element. + /// + /// Calculates the row-major flat index corresponding to a quadrature point + /// at the position (i, j) within a 2D volume or surface 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) const + { + return i + j * num_qpts_in_1d; + } // end function + + }; // end Quadrature_t + + + // reference element data structure + struct ref_elem_t + { - 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); + ref_space::ElementType ElementType = ref_space::linearElement; ///< The type of element + ref_space::BasisType BasisType = ref_space::LagrangeLobatto; /// dof_positions; + + + // 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 Legendra 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 ref_space::ElementType ElemTypeInp, + const ref_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 elems supported \n"); + + // ----------------------------------------------------------------------- + // Step 1a: determine the number of DOFs in 3D + // ----------------------------------------------------------------------- + num_dofs_1d = p_order + 1; - lobotto_point_weights(lob_rid) = lob_weights_1D(i) * lob_weights_1D(j) * lob_weights_1D(k); - }); - Kokkos::fence(); + for (int dim = 0; dim < elem_dims; dim++) { + num_dofs_in_elem *= num_dofs_1d; + } // end for - // WARNING WARNING WARNING: Assumes p > 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(); + // ----------------------------------------------------------------------- + // Step 1b: get the positions in reference space for the DOFs + // ----------------------------------------------------------------------- + dof_positions = CArrayKokkos(num_dofs_in_elem, elem_dims, "dof_positions"); + CArrayKokkos dof_positions_1d(num_dofs_1d, "dof_positions_1d"); - 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) { + // dof positions can be at legendre or lobatto locations in elem + if(BasisTypeInp == ref_space::LagrangeLegendre){ 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(); - - // 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); + get_legendre_nodes_1D(dof_positions_1d, num_dofs_1d); }); } - 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); - - dof_id++; - } - }); - + else if(BasisTypeInp == ref_space::LagrangeLobatto){ RUN_CLASS({ - int dof_id = 0; - - for (int i = 0; i < num_dual_lobotto_1d; i = i + 2) { - dg_dof_positions_1d(dof_id) = dual_lob_points_1D(i); - - dof_id++; - } + get_lobatto_nodes_1D(dof_positions_1d, num_dofs_1d); }); } - Kokkos::fence(); - - 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); - - 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 // - - // 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); - - CArrayKokkos val_1d(num_dofs_1d); - CArrayKokkos val_3d(num_dofs_1d, 3); - - CArrayKokkos elem_val_1d(num_dg_dofs_1d); - CArrayKokkos elem_val_3d(num_dg_dofs_1d, 3); - - CArrayKokkos point(3); - - 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) ); - } + else + { + throw std::runtime_error("ERROR: unsupported basis DOF locations specified \n"); + } - get_basis(temp_nodal_basis, val_1d, val_3d, point); - // double check_basis = 0.0; + // 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, { - 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(); + const size_t rid = get_dof_rid(i, j, k); - 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) ); - } + 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, { - get_basis(temp_nodal_basis, val_1d, val_3d, point); + const size_t rid = get_dof_rid(i, j); - // double check_basis = 0.0; + 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 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; - } + const size_t rid = i; - // printf(" basis tally = %f \n", check_basis ); - } - }); + dof_positions(rid, 0) = dof_positions_1d(i); + }); + } // end if 1D Kokkos::fence(); + + // ----------------------------------------------------------------------- + // Step 2: Calculate the basis values at quadrature points + // ----------------------------------------------------------------------- + qpt_basis = CArrayKokkos(Quadrature.num_qpts_in_elem, num_dofs_in_elem, "qpt_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; + // 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 - 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; - } + CArrayKokkos point(elem_dims); - // printf(" basis tally = %f \n", check_basis ); - } - }); - 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); + for (size_t qpt_rid = 0; qpt_rid < Quadrature.num_qpts_in_elem; qpt_rid++) { + + // Get the evaluation coordinates + for (size_t dim = 0; dim < elem_dims; dim++) { + point(dim) = Quadrature.qpt_positions(qpt_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; + get_basis(temp_basis, dof_positions_1d, temp_val_1d, temp_val_Nd, point); - 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; + 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; } - - // printf(" basis tally = %f \n", check_basis ); - } + } // end for over qpts in elem }); Kokkos::fence(); - // --- evaluate grad_basis functions at the lobatto points --- + // ----------------------------------------------------------------------- + // Step 3: Calculate the grad basis values at quadrature points + // ----------------------------------------------------------------------- + qpt_grad_basis = CArrayKokkos(Quadrature.num_qpts_in_elem, num_dofs_in_elem, elem_dims, "qpt_grad_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 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); - } - - // double check[3]; - // for (int i = 0; i < 3; i++) check[i] = 0.0; - - 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); - - 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); - - // 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; - } + CArrayKokkos temp_Dval_1d(num_dofs_1d); + CArrayKokkos temp_Dval_Nd(num_dofs_1d, elem_dims); - // printf(" grad_basis tally = %f, %f, %f \n", check[0], check[1], check[2]); - } - }); - 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); - } - - 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); - - double check[3]; - for (int i = 0; i < 3; i++) { - check[i] = 0.0; + for (int qpt_rid = 0; qpt_rid < Quadrature.num_qpts_in_elem; qpt_rid++) { + + // Get the evaluation coordinates + for (size_t dim = 0; dim < elem_dims; dim++) { + point(dim) = Quadrature.qpt_positions(qpt_rid, dim); } - 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); + 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; - temp_partial_eta(basis_id) = 0.0; - temp_partial_mu(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 - // printf(" grad_basis tally = %f, %f, %f \n", check[0], check[1], check[2]); - } + + } // end for qpts in elem }); Kokkos::fence(); - } // end 3d scope - } // end of member function + } // end of member function + + + ///////////////////////////////////////////////////////////////////////////// + /// + /// \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 + int get_dof_rid(int i, int j, int k) const + { + return i + (j + k * num_dofs_1d) * num_dofs_1d; + } + + ///////////////////////////////////////////////////////////////////////////// + /// + /// \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 + int get_dof_rid(int i, int j) const + { + return i + j * num_dofs_1d; + } + + + ///////////////////////////////////////////////////////////////////////////// + /// + /// \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 + { + for(size_t dim=0; dim 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; - } + for (size_t 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)); + // Calculate 1D basis for the this dim coordinate of the point + lagrange_basis_1D(val_1d, dof_positions_1d, point(dim)); - // 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; - } + // 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); + } - // Calculate 1D basis for the Y coordinate of the point - lagrange_basis_1D(val_1d, point(1)); + } // end loop over dims - // 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; - } - // Calculate 1D basis for the Z coordinate of the point - lagrange_basis_1D(val_1d, point(2)); + 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); + basis(rid) = val_Nd(i, 0) * val_Nd(j, 1) * val_Nd(k, 2); + + } + } // 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); + 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 - // 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; - } - // 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); + // 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& 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 + { + // get grad basis + for (size_t i = 0; i < num_dofs_1d; i++) { + Dval_1d(i) = 0.0; } - } - - 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; - } - } - - ///////////////////////////////////////////////////////////////////////////// - /// - /// \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; - } - - // 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; - } - - // Calculate 1D basis for the Y coordinate of the point - lagrange_elem_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_dg_dofs_1d; i++) { - val_3d(i, 1) = val_1d(i); - val_1d(i) = 0.0; - } - - // 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; - } + // Calculate 1D partial w.r.t. xi for the X coordinate of the point + lagrange_derivative_1D(Dval_1d, dof_positions_1d, point(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); - basis(dof_rlid) = val_3d(i, 0) * val_3d(j, 1) * val_3d(k, 2); - } + // 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); } - } - 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; - } + // get Y and Z basis, the latter only if elem_dims = 3 + for(size_t dim=1; dim& 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 + { + + + // get X and Z basis values, the latter only if elem_dims = 3D + for(size_t dim=0; dim& 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; - } - // Calculate 1D basis for the Y coordinate of the point - lagrange_basis_1D(val_1d, point(0)); + // Calculate 1D basis for the this dim coordinate of the point + lagrange_basis_1D(val_1d, dof_positions_1d, point(dim)); - // 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; - } + // 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); + } - // Calculate 1D partial w.r.t. eta for the Y coordinate of the point - lagrange_derivative_1D(Dval_1d, point(1)); + } // end loop over dims - // 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); + // get grad basis + for (size_t i = 0; i < num_dofs_1d; i++) { + Dval_1d(i) = 0.0; + } - Dval_1d(i) = 0.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)); - // Calculate 1D basis for the Z coordinate of the point - lagrange_basis_1D(val_1d, point(2)); + // 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); + } - // 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; - } - // 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); + 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); // Partial w.r.t xi - partial_eta(dof_rlid) = val_3d(i, 0) * Dval_3d(j, 1) * val_3d(k, 2); + 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; } - } - } - - 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; - } - } - - ///////////////////////////////////////////////////////////////////////////// - /// - /// \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; - } + } // 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); - // 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_3d(i, 0) = val_1d(i); - val_1d(i) = 0.0; - } + // 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 + + } // end partial_eta_basis + + ///////////////////////////////////////////////////////////////////////////// + /// + /// \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) const + { + // this routine is only valid for 3D ref elems + + // get X and Y basis + for(size_t dim=0; dim &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 - /// - ///////////////////////////////////////////////////////////////////////////// - - 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 - - ///////////////////////////////////////////////////////////////////////////// - /// - /// \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 - - 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)); - - // Calculate the denominator - denominator = denominator * (dg_dof_positions_1d(vert_i) - dg_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 - - ///////////////////////////////////////////////////////////////////////////// - /// - /// \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; - - 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_dg_dofs_1d; dof_i++){ -// interp(dof_i) = eval_bernstein(num_dg_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)); -// } - -}; // end struct + ///////////////////////////////////////////////////////////////////////////// + /// + /// \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 + /// + ///////////////////////////////////////////////////////////////////////////// + + 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) const // point of interest in element + // calculate the basis value associated with each node_i + { + 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 + + 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)); + + // 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 + + ///////////////////////////////////////////////////////////////////////////// + /// + /// \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 CArrayKokkos& dof_positions_1d, // location of basis DOFs in ref elem + const double x_point) const // point of interest in element + { + 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; + + 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; + + // 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 + + // 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 + + }; // end struct } // end namespace elements diff --git a/src/elements/ref_elem_new.h b/src/elements/ref_elem_new.h deleted file mode 100644 index 4e83746a..00000000 --- a/src/elements/ref_elem_new.h +++ /dev/null @@ -1,996 +0,0 @@ -/********************************************************************************************** -� 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. -**********************************************************************************************/ -#ifndef REF_ELEM_NEW_H -#define REF_ELEM_NEW_H - -#include -#include "matar.h" -#include "ref_quadrature.h" - - -using namespace mtr; - - -namespace ref_space -{ - enum ElementType - { - linearElement = 0, // single quadrature point element - arbitraryOrderElement = 1 // fully integrated arbitrary-order element - }; - - // LagrangeGLL: continuous H1-like (kinematic). LagrangeGL: DG L2-like (thermo). - enum BasisType - { - LagrangeLobatto = 0, // GLL in Steven's solver (C0 kinematic space) - LagrangeLegendra = 1, // GL in Steven's solver (DG thermo space) - }; - - enum QuadType - { - GaussLobatto = 0, - GaussLegendre = 1 - }; -} // end ref_elem namespace - - -namespace elements -{ - - // Quadrature rules for surfaces and elems - struct Quadrature_t - { - ref_space::QuadType QuadType; - - size_t elem_dims = 0; - size_t num_qpts_in_elem = 0; - size_t num_qpts_in_1d = 0; - - CArrayKokkos qpt_positions; - CArrayKokkos qpt_weights; - - ///////////////////////////////////////////////////////////////////////////// - /// - /// \fn initialize_quadrature - /// - /// \brief Set up quadrature in a volume or surface element - /// - /// \param TypeInp The type of quadrature (e.g., Lobatto or Legendra) - /// \param num_qpts_in_1d_inp The number of quadrature in 1D, applied to each direction. - /// \param elem_dims_in The number dimensions - /// - ///////////////////////////////////////////////////////////////////////////// - void initialize_quadrature(const ref_space::QuadType TypeInp, - const size_t num_qpts_in_1d_inp, - const size_t elem_dims_in) - { - QuadType = TypeInp; - - elem_dims = elem_dims_in; - num_qpts_in_1d = num_qpts_in_1d_inp; - if(num_qpts_in_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"); - - // temporary 1D variables to build 3D element - CArrayKokkos qpt_positions_1d(num_qpts_in_1d, "qpt_positions_1d"); - CArrayKokkos qpt_weights_1d (num_qpts_in_1d, "qpt_weights_1d"); - - if(QuadType = QuadType::GaussLegendre){ - RUN_CLASS({ - get_legendre_nodes_1D(qpt_positions_1d, num_qpts_in_1d); - get_legendre_weights_1D(qpt_weights_1d, num_qpts_in_1d); - }); - } - else if(QuadType = QuadType::GaussLobatto){ - RUN_CLASS({ - get_lobatto_nodes_1D(qpt_positions_1d, num_qpts_in_1d); - get_lobatto_weights_1D(qpt_weights_1d, num_qpts_in_1d); - }); - } - else - { - throw std::runtime_error("ERROR: unsupported quadrature set specified \n"); - } - - // 3D volume element - if(elem_dims==3){ - FOR_ALL_CLASS(k, 0, num_qpts_in_1d, - j, 0, num_qpts_in_1d, - i, 0, num_qpts_in_1d, { - - const size_t qpt_rid = qpt_rid(i, j, k); - - qpt_positions(qpt_rid, 0) = qpt_positions_1d(i); - qpt_positions(qpt_rid, 1) = qpt_positions_1d(j); - qpt_positions(qpt_rid, 2) = qpt_positions_1d(k); - - qpt_weights(qpt_rid) = qpt_weights_1d(i) * qpt_weights_1d(j) * qpt_weights_1d(k); - }); - Kokkos::fence(); - } - // 2D volume or 2D surface element - else if (elem_dims==2){ - FOR_ALL_CLASS(j, 0, num_qpts_in_1d, - i, 0, num_qpts_in_1d, { - - const size_t qpt_rid = qpt_rid(i, j); - - qpt_positions(qpt_rid, 0) = qpt_positions_1d(i); - qpt_positions(qpt_rid, 1) = qpt_positions_1d(j); - - qpt_weights(qpt_rid) = qpt_weights_1d(i) * qpt_weights_1d(j); - }); - Kokkos::fence(); - } - // 1D volume, edge, or 1D surface element - else if (elem_dims==1) { - FOR_ALL_CLASS(i, 0, num_qpts_in_1d, { - - const size_t qpt_rid = i; - - qpt_positions(qpt_rid, 0) = qpt_positions_1d(i); - - qpt_weights(qpt_rid) = qpt_weights_1d(i); - }); - Kokkos::fence(); - } - else{ - throw std::runtime_error("ERROR: unsupported quadrature elem dims \n"); - } - - } // init fcn quadrature - - - ///////////////////////////////////////////////////////////////////////////// - /// - /// \fn 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 qpt_rid(size_t i, size_t j, size_t k) const - { - return i + (j + k * num_qpts_in_1d) * num_qpts_in_1d; - } // end function - - ///////////////////////////////////////////////////////////////////////////// - /// - /// \fn qpt_rid - /// - /// \brief Compute the 1D array index for a quadrature point in an 2D volume - /// or surface element. - /// - /// Calculates the row-major flat index corresponding to a quadrature point - /// at the position (i, j) within a 2D volume or surface 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 qpt_rid(size_t i, size_t j) const - { - return i + j * num_qpts_in_1d; - } // end function - - }; // end Quadrature_t - - - // reference element data structure - struct ref_elem_t - { - - ref_space::ElemType ElemType = ref_space::linearElement; ///< The type of element - ref_space::BasisType BasisType = ref_space::LagrangeLobatto; /// dof_positions; - - - // 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. This that case, user must create multiple reference elements, one - /// that defines the position and one for the discontinous fields. - /// - /// \param num_dims_inp The number of spatial dimensions (e.g., 1, 2, or 3). - /// \param p_order The element order e.g., constant = 0, linear = 1, quadratic = 2, ... - /// - /// \return void - /// - ///////////////////////////////////////////////////////////////////////////// - void initialize_ref_elem(const ref_space::ElemType ElemTypeInp, - const ref_space::BasisType BasisTypeInp, - const struct Quadrature_t Quadrature, - const size_t p_order) - { - // set element and basis type - ElemType = 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 elems supported \n"); - - // ----------------------------------------------------------------------- - // Step 1a: determine the number of DOFs in 3D - // ----------------------------------------------------------------------- - num_dofs_1d = p_order + 1; - - for (int dim = 0; dim < elem_dims; dim++) { - num_dofs_in_elem *= num_dofs_1d; - } // end for - - // ----------------------------------------------------------------------- - // Step 1b: get the positions in reference space for the DOFs - // ----------------------------------------------------------------------- - dof_positions = CArrayKokkos(num_dofs_in_elem, elem_dims, "dof_positions"); - CArrayKokkos dof_positions_1d(num_dofs_1d, "dof_positions_1d"); - - // dof positions can be at legendre or lobatto locations in elem - if(BasisTypeInp = ref_elem_init::legendre){ - RUN_CLASS({ - get_legendre_nodes_1D(dof_positions_1d, num_dofs_1d); - }); - } - else if(BasisTypeInp = ref_elem_init::lobatto){ - RUN_CLASS({ - get_lobatto_nodes_1D(dof_positions_1d, num_dofs_1d); - }); - } - else - { - throw std::runtime_error("ERROR: unsupported basis DOF locations specified \n"); - } - - // 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, { - - const size_t dof_rlid = dof_rid(i, j, k); - - dof_positions(dof_rlid, 0) = dof_positions_1d(i); - dof_positions(dof_rlid, 1) = dof_positions_1d(j); - dof_positions(dof_rlid, 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, { - - const size_t dof_rlid = dof_rid(i, j); - - dof_positions(dof_rlid, 0) = dof_positions_1d(i); - dof_positions(dof_rlid, 1) = dof_positions_1d(j); - }); - } // end if 1D - // 1D volume, edge, or 1D surface element - else { - FOR_ALL_CLASS(i, 0, num_dofs_1d, { - - const size_t dof_rlid = i; - - dof_positions(dof_rlid, 0) = dof_positions_1d(i); - }); - } // end if 1D - Kokkos::fence(); - - // ----------------------------------------------------------------------- - // Step 2: Calculate the basis values at quadrature points - // ----------------------------------------------------------------------- - qpt_basis = CArrayKokkos(Quadrature.num_qpts_in_elem, num_dofs_in_elem, "qpt_basis"); - - // 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); - - - RUN_CLASS({ - for (size_t qpt_rid = 0; qpt_rid < Quadrature.num_qpts_in_elem; qpt_rid++) { - - // Get the evaluation coordinates - for (size_t dim = 0; dim < elem_dims; dim++) { - point(dim) = Quadrature.positions(qpt_rid, dim); - } - - get_basis(temp_basis, dof_positions_1d, temp_val_1d, temp_val_3d, point); - - 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(); - - // ----------------------------------------------------------------------- - // Step 3: Calculate the grad basis values at quadrature points - // ----------------------------------------------------------------------- - qpt_grad_basis = CArrayKokkos(Quadrature.num_qpts_in_elem, num_dofs_in_elem, elem_dims, "qpt_grad_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 Dval_1d(num_dofs_1d); - CArrayKokkos Dval_Nd(num_dofs_1d, elem_dims); - - - RUN_CLASS({ - for (int qpt_rid = 0; qpt_rid < Quadrature.num_qpts_in_elem; qpt_rid++) { - - // Get the evaluation coordinates - for (size_t dim = 0; dim < elem_dims; dim++) { - point(dim) = Quadrature.positions(qpt_rid, dim); - } - - partial_xi_basis(temp_partial_xi, dof_positions_1d, val_1d, val_3d, Dval_1d, Dval_3d, point); - if(elem_dims>1)partial_eta_basis(temp_partial_eta, dof_positions_1d, val_1d, val_3d, Dval_1d, Dval_3d, point); - if(elem_dims>2)partial_mu_basis(temp_partial_mu, dof_positions_1d, val_1d, val_3d, Dval_1d, Dval_3d, 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(); - - } // end of member function - - - ///////////////////////////////////////////////////////////////////////////// - /// - /// \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 + k * num_dofs_1d) * num_dofs_1d; - } - - ///////////////////////////////////////////////////////////////////////////// - /// - /// \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. - /// - /// \return The row-major offset index for the DOF in this element. - /// - ///////////////////////////////////////////////////////////////////////////// - KOKKOS_INLINE_FUNCTION - int dof_rid(int i, int j) const - { - return i + j * num_dofs_1d; - } - - - ///////////////////////////////////////////////////////////////////////////// - /// - /// \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 - { - - for(size_t dim=0; dim& 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 - { - // get grad basis - for (size_t i = 0; i < num_dofs_1d; i++) { - Dval_1d(i) = 0.0; - } - - // Calculate 1D partial w.r.t. xi for the X coordinate of the point - lagrange_derivative_1D(Dval_1d, dof_positions_1d, point(0)); - - // 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); - } - - // get Y and Z basis, the latter only if elem_dims = 3 - for(size_t dim=1; dim& 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 - { - - - // get X and Z basis values, the latter only if elem_dims = 3D - for(size_t dim=0; dim& 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) const - { - // this routine is only valid for 3D ref elems - - // get X and Y basis - for(size_t dim=0; dim& interp, // interpolant from each basis - const CArrayKokkos& dof_positions_1d, // location of basis DOFs in ref elem - const double x_point) const // point of interest in element - // calculate the basis value associated with each node_i - { - 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 - - 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)); - - // 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 - - ///////////////////////////////////////////////////////////////////////////// - /// - /// \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 CArrayKokkos& dof_positions_1d, // location of basis DOFs in ref elem - const double x_point) const // point of interest in element - { - 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; - - 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; - - // 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 - - // 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 - - }; // end struct - -} // end namespace elements - -#endif \ No newline at end of file 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/swage/unstructured_mesh.h b/src/swage/unstructured_mesh.h index f358b0e6..4b894fd5 100644 --- a/src/swage/unstructured_mesh.h +++ b/src/swage/unstructured_mesh.h @@ -191,6 +191,33 @@ struct lobatto_in_elem_t }; }; +/// if material points are defined at element interfaces +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; + }; +}; + // struct nodes_in_zone_t { // private: // size_t num_nodes_in_zone_; @@ -216,7 +243,7 @@ struct lobatto_in_elem_t struct Mesh { // ******* Entity Definitions **********// - // Element: A hexahedral volume + // Element: A hexahedral or Quadralateral 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 @@ -242,7 +269,7 @@ struct Mesh size_t num_lobatto_in_elem = 0; ///< Number of Gauss Lobatto 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 @@ -347,23 +374,25 @@ struct Mesh 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; + num_dims = num_dims_inp; + num_elems = num_elems_inp; + + Pn = 1; + + 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) - 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; + corners_in_elem = corners_in_elem_t(num_nodes_in_elem); + gauss_in_elem = gauss_in_elem_t(num_gauss_in_elem); - // 1 zone per element - num_zones_in_elem = 1; + num_zones = num_zones_in_elem * num_elems; - 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 @@ -384,16 +413,16 @@ struct Mesh Pn = Pn_order; - 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_nodes_in_elem = (size_t)std::pow(Pn_order + 1, num_dims); //(Pn_order + 1)**num_dims; // (Pn +1) + num_nodes_in_zone = (size_t)std::pow(2, num_dims); // (4, or 8, always) + num_gauss_in_elem = (size_t)std::pow(2*Pn_order, num_dims); // = 2*Pn + num_zones_in_elem = (size_t)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; 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"); @@ -465,8 +494,8 @@ 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; + //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; From 7ea0c1a86dca28a9243f081263a527f22d8a92d9 Mon Sep 17 00:00:00 2001 From: Nathaniel Morgan Date: Thu, 2 Jul 2026 10:54:01 -0600 Subject: [PATCH 03/59] WIP: fixed bug and cleaned up ref_elem coding --- src/elements/ref_elem.h | 11 ++++++----- src/swage/unstructured_mesh.h | 3 +++ 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/elements/ref_elem.h b/src/elements/ref_elem.h index c93a36f0..0f4ab86d 100644 --- a/src/elements/ref_elem.h +++ b/src/elements/ref_elem.h @@ -243,7 +243,7 @@ namespace elements size_t elem_dims = 0; // Dofs - size_t num_dofs_in_elem = 0; + size_t num_dofs_in_elem = 1; size_t num_dofs_1d = 0; @@ -301,7 +301,8 @@ namespace elements // ----------------------------------------------------------------------- num_dofs_1d = p_order + 1; - for (int dim = 0; dim < elem_dims; dim++) { + 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 @@ -409,7 +410,7 @@ namespace elements RUN_CLASS({ - for (int qpt_rid = 0; qpt_rid < Quadrature.num_qpts_in_elem; qpt_rid++) { + for (size_t qpt_rid = 0; qpt_rid < Quadrature.num_qpts_in_elem; qpt_rid++) { // Get the evaluation coordinates for (size_t dim = 0; dim < elem_dims; dim++) { @@ -476,7 +477,7 @@ namespace elements /// ///////////////////////////////////////////////////////////////////////////// KOKKOS_INLINE_FUNCTION - int get_dof_rid(int i, int j, int k) const + size_t get_dof_rid(size_t i, size_t j, size_t k) const { return i + (j + k * num_dofs_1d) * num_dofs_1d; } @@ -498,7 +499,7 @@ namespace elements /// ///////////////////////////////////////////////////////////////////////////// KOKKOS_INLINE_FUNCTION - int get_dof_rid(int i, int j) const + size_t get_dof_rid(size_t i, size_t j) const { return i + j * num_dofs_1d; } diff --git a/src/swage/unstructured_mesh.h b/src/swage/unstructured_mesh.h index 4b894fd5..dd672a34 100644 --- a/src/swage/unstructured_mesh.h +++ b/src/swage/unstructured_mesh.h @@ -412,6 +412,9 @@ struct Mesh num_elems = num_elems_inp; Pn = Pn_order; + if (Pn == 0) { + Kokkos::abort("Error: Pn must be greater than 0. Exiting at initialize_elems_Pn()."); + } num_nodes_in_elem = (size_t)std::pow(Pn_order + 1, num_dims); //(Pn_order + 1)**num_dims; // (Pn +1) num_nodes_in_zone = (size_t)std::pow(2, num_dims); // (4, or 8, always) From fd5d7c5618d9cf57ce992c73258e3020c4fba85d Mon Sep 17 00:00:00 2001 From: Nathaniel Morgan Date: Thu, 2 Jul 2026 13:09:03 -0600 Subject: [PATCH 04/59] WIP: added files for tests of ref elem --- examples/reference_element/CMakeLists.txt | 22 +++++++ .../reference_element/src/gradient_test.cpp | 60 ++++++++++++++++++ .../src/interpolation_test.cpp | 61 +++++++++++++++++++ .../src/kronecker_delta_test.cpp | 61 +++++++++++++++++++ .../src/partition_unity_test.cpp | 61 +++++++++++++++++++ src/elements/ref_elem.h | 35 ++++++----- 6 files changed, 284 insertions(+), 16 deletions(-) create mode 100644 examples/reference_element/CMakeLists.txt create mode 100644 examples/reference_element/src/gradient_test.cpp create mode 100644 examples/reference_element/src/interpolation_test.cpp create mode 100644 examples/reference_element/src/kronecker_delta_test.cpp create mode 100644 examples/reference_element/src/partition_unity_test.cpp diff --git a/examples/reference_element/CMakeLists.txt b/examples/reference_element/CMakeLists.txt new file mode 100644 index 00000000..a2bd0a5a --- /dev/null +++ b/examples/reference_element/CMakeLists.txt @@ -0,0 +1,22 @@ +# examples/average/CMakeLists.txt + +# 1. Define the executable +# Point directly to the source file inside 'src/' +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.cpp) + +# 2. Add this example's specific include path +# This allows main.cpp to find headers in examples/point_connectivity/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(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..18231ad1 --- /dev/null +++ b/examples/reference_element/src/gradient_test.cpp @@ -0,0 +1,60 @@ +/********************************************************************************************** +© 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" + + +using namespace mtr; +using namespace swage; // unstructured mesh and hash + +int main(int argc, char** argv) { + +MATAR_INITIALIZE(argc, argv); +{ // MATAR scope + + printf("\n--- gradient test ---\n"); + + + + printf("\nAll gradient checks passed.\n"); + +} +MATAR_FINALIZE(); + +} // end main + diff --git a/examples/reference_element/src/interpolation_test.cpp b/examples/reference_element/src/interpolation_test.cpp new file mode 100644 index 00000000..956b1405 --- /dev/null +++ b/examples/reference_element/src/interpolation_test.cpp @@ -0,0 +1,61 @@ +/********************************************************************************************** +© 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 + +int main(int argc, char** argv) { + +MATAR_INITIALIZE(argc, argv); +{ // MATAR scope + + printf("\n--- interpolation test ---\n"); + + + + printf("\nAll interpolation checks passed.\n"); + +} +MATAR_FINALIZE(); + +} // end main + diff --git a/examples/reference_element/src/kronecker_delta_test.cpp b/examples/reference_element/src/kronecker_delta_test.cpp new file mode 100644 index 00000000..49377d38 --- /dev/null +++ b/examples/reference_element/src/kronecker_delta_test.cpp @@ -0,0 +1,61 @@ +/********************************************************************************************** +© 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 + +int main(int argc, char** argv) { + +MATAR_INITIALIZE(argc, argv); +{ // MATAR scope + + printf("\n--- kronecker delta test ---\n"); + + + + 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..e6e9b754 --- /dev/null +++ b/examples/reference_element/src/partition_unity_test.cpp @@ -0,0 +1,61 @@ +/********************************************************************************************** +© 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 + +int main(int argc, char** argv) { + +MATAR_INITIALIZE(argc, argv); +{ // MATAR scope + + printf("\n--- partition unity test ---\n"); + + + + printf("\nAll partition unity checks passed.\n"); + +} +MATAR_FINALIZE(); + +} // end main + diff --git a/src/elements/ref_elem.h b/src/elements/ref_elem.h index 0f4ab86d..cb149592 100644 --- a/src/elements/ref_elem.h +++ b/src/elements/ref_elem.h @@ -42,7 +42,7 @@ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. using namespace mtr; -namespace ref_space +namespace reference_space { enum ElementType { @@ -71,7 +71,7 @@ namespace elements // Quadrature rules for surfaces and elems struct Quadrature_t { - ref_space::QuadratureType QuadratureType; + reference_space::QuadratureType QuadratureType; size_t elem_dims = 0; size_t num_qpts_in_elem = 0; @@ -91,7 +91,7 @@ namespace elements /// \param elem_dims_in The number dimensions /// ///////////////////////////////////////////////////////////////////////////// - void initialize_quadrature(const ref_space::QuadratureType TypeInp, + void initialize_quadrature(const reference_space::QuadratureType TypeInp, const size_t num_qpts_in_1d_inp, const size_t elem_dims_in) { @@ -112,13 +112,13 @@ namespace elements CArrayKokkos qpt_positions_1d(num_qpts_in_1d, "qpt_positions_1d"); CArrayKokkos qpt_weights_1d (num_qpts_in_1d, "qpt_weights_1d"); - if(QuadratureType == ref_space::GaussLegendre){ + if(QuadratureType == reference_space::GaussLegendre){ RUN_CLASS({ get_legendre_nodes_1D(qpt_positions_1d, num_qpts_in_1d); get_legendre_weights_1D(qpt_weights_1d, num_qpts_in_1d); }); } - else if(QuadratureType == ref_space::GaussLobatto){ + else if(QuadratureType == reference_space::GaussLobatto){ RUN_CLASS({ get_lobatto_nodes_1D(qpt_positions_1d, num_qpts_in_1d); get_lobatto_weights_1D(qpt_weights_1d, num_qpts_in_1d); @@ -234,11 +234,11 @@ namespace elements // reference element data structure - struct ref_elem_t + struct ReferenceElement_t { - ref_space::ElementType ElementType = ref_space::linearElement; ///< The type of element - ref_space::BasisType BasisType = ref_space::LagrangeLobatto; ///3) throw std::runtime_error("ERROR: only 1D, 2D, and 3D reference elems supported \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 @@ -313,12 +313,12 @@ namespace elements CArrayKokkos dof_positions_1d(num_dofs_1d, "dof_positions_1d"); // dof positions can be at legendre or lobatto locations in elem - if(BasisTypeInp == ref_space::LagrangeLegendre){ + if(BasisTypeInp == reference_space::LagrangeLegendre){ RUN_CLASS({ get_legendre_nodes_1D(dof_positions_1d, num_dofs_1d); }); } - else if(BasisTypeInp == ref_space::LagrangeLobatto){ + else if(BasisTypeInp == reference_space::LagrangeLobatto){ RUN_CLASS({ get_lobatto_nodes_1D(dof_positions_1d, num_dofs_1d); }); @@ -952,8 +952,9 @@ namespace elements 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 + interpolant = numerator / denominator; // storing a single value for interpolation for node vert_i // writing value to vectors for later use interp(vert_i) = interpolant; // Interpolant value at given point @@ -1008,9 +1009,11 @@ namespace elements num_gradient += product_gradient; } // end if - gradient = (num_gradient / denominator); // storing the derivative of the interpolating function + } // 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 From 5f22c934de8bb941941e957869b007e693b21b02 Mon Sep 17 00:00:00 2001 From: Nathaniel Morgan Date: Thu, 2 Jul 2026 13:13:12 -0600 Subject: [PATCH 05/59] WIP: fixed cmake build of ref elem tests --- examples/CMakeLists.txt | 1 + examples/reference_element/CMakeLists.txt | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) 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/reference_element/CMakeLists.txt b/examples/reference_element/CMakeLists.txt index a2bd0a5a..4df3d03b 100644 --- a/examples/reference_element/CMakeLists.txt +++ b/examples/reference_element/CMakeLists.txt @@ -5,7 +5,7 @@ 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.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/ From 02dfa948a4ec5fb6514fd334701e5019ec2cfa10 Mon Sep 17 00:00:00 2001 From: Nathaniel Morgan Date: Thu, 2 Jul 2026 13:41:59 -0600 Subject: [PATCH 06/59] WIP: partition of unity test passes 1D --- .../src/partition_unity_test.cpp | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/examples/reference_element/src/partition_unity_test.cpp b/examples/reference_element/src/partition_unity_test.cpp index e6e9b754..45bd1ce0 100644 --- a/examples/reference_element/src/partition_unity_test.cpp +++ b/examples/reference_element/src/partition_unity_test.cpp @@ -42,6 +42,23 @@ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. using namespace mtr; using namespace swage; // unstructured mesh and hash +using namespace elements; + +void verify_partition_of_unity(const Quadrature_t& Quad, + const ReferenceElement_t& RefElem) { + for (size_t qpt = 0; qpt < Quad.num_qpts_in_elem; qpt++) { + 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) > 1e-13) { + printf("Error: partion of unity failed, sum of basis = %f at rid = %zu \n", sum, qpt); + Kokkos::abort("Partition of unity failed at quadrature point "); + } + printf("sum = %f \n", sum); + } // end loop over qpts +} // end function int main(int argc, char** argv) { @@ -50,7 +67,30 @@ MATAR_INITIALIZE(argc, argv); printf("\n--- partition unity test ---\n"); + printf("\n--- 1D element ---\n"); + for(size_t num_qpts_1D = 1; num_qpts_1D<21; num_qpts_1D++){ + + printf("num quadrature points in 1D = %zu \n", num_qpts_1D); + + Quadrature_t Quad1D; + ReferenceElement_t FERefElem1D; + + const size_t elem_dims_test = 1; + Quad1D.initialize_quadrature(reference_space::GaussLegendre, + num_qpts_1D, + elem_dims_test); + + const size_t p_order = 1; // basis order for Lagrange polynomial + FERefElem1D.initialize_ref_elem(reference_space::arbitraryOrderElement, + reference_space::LagrangeLobatto, + Quad1D, + p_order); + + verify_partition_of_unity(Quad1D, + FERefElem1D); + printf("\n"); + } // end loop of num qpts printf("\nAll partition unity checks passed.\n"); From adc28422ed15e139dbf200575a617d475cf2509c Mon Sep 17 00:00:00 2001 From: Nathaniel Morgan Date: Thu, 2 Jul 2026 15:49:44 -0600 Subject: [PATCH 07/59] partion_unity_test is 1D,2D,3D, plus checking a large range of orders --- .../src/partition_unity_test.cpp | 80 ++++++++++++++----- 1 file changed, 59 insertions(+), 21 deletions(-) diff --git a/examples/reference_element/src/partition_unity_test.cpp b/examples/reference_element/src/partition_unity_test.cpp index 45bd1ce0..d1abc666 100644 --- a/examples/reference_element/src/partition_unity_test.cpp +++ b/examples/reference_element/src/partition_unity_test.cpp @@ -44,6 +44,10 @@ using namespace mtr; using namespace swage; // unstructured mesh and hash using namespace elements; +bool Verbose = true; +size_t max_num = 8; // max number of quadrature points to test +size_t max_order = 7; // max polynomial order to test + void verify_partition_of_unity(const Quadrature_t& Quad, const ReferenceElement_t& RefElem) { for (size_t qpt = 0; qpt < Quad.num_qpts_in_elem; qpt++) { @@ -56,7 +60,29 @@ void verify_partition_of_unity(const Quadrature_t& Quad, printf("Error: partion of unity failed, sum of basis = %f at rid = %zu \n", sum, qpt); Kokkos::abort("Partition of unity failed at quadrature point "); } - printf("sum = %f \n", sum); + if(Verbose)printf("sum = %f \n", sum); + } // end loop over qpts +} // end function + +void verify_gradient(const Quadrature_t& Quad, + const ReferenceElement_t& RefElem) { + for (size_t qpt = 0; qpt < Quad.num_qpts_in_elem; qpt++) { + double sum[3]; + sum[0] = 0.0; + sum[1] = 0.0; + sum[2] = 0.0; + + for(size_t dim=0; dim 1.e-13) { + printf("Error: gradient failed, sum of gradient basis = %f at rid = %zu \n", sum[dim], qpt); + Kokkos::abort("Gradient of basis failed at quadrature point "); + } + if(Verbose)printf("dim = %zu, sum = %f \n", dim, sum[dim]); + } // for dim + } // end loop over qpts } // end function @@ -68,28 +94,40 @@ MATAR_INITIALIZE(argc, argv); printf("\n--- partition unity test ---\n"); printf("\n--- 1D element ---\n"); - for(size_t num_qpts_1D = 1; num_qpts_1D<21; num_qpts_1D++){ - - printf("num quadrature points in 1D = %zu \n", num_qpts_1D); - - Quadrature_t Quad1D; - ReferenceElement_t FERefElem1D; + for(size_t num_qpts_1D = 1; num_qpts_1D Date: Thu, 2 Jul 2026 15:57:22 -0600 Subject: [PATCH 08/59] updated test to use 20 quadrature points --- .../reference_element/src/partition_unity_test.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/reference_element/src/partition_unity_test.cpp b/examples/reference_element/src/partition_unity_test.cpp index d1abc666..b7e7c170 100644 --- a/examples/reference_element/src/partition_unity_test.cpp +++ b/examples/reference_element/src/partition_unity_test.cpp @@ -44,9 +44,9 @@ using namespace mtr; using namespace swage; // unstructured mesh and hash using namespace elements; -bool Verbose = true; -size_t max_num = 8; // max number of quadrature points to test -size_t max_order = 7; // max polynomial order to test +bool Verbose = false; +size_t max_num = 20; // max number of quadrature points to test +size_t max_order = 20; // max polynomial order to test void verify_partition_of_unity(const Quadrature_t& Quad, const ReferenceElement_t& RefElem) { @@ -56,7 +56,7 @@ void verify_partition_of_unity(const Quadrature_t& Quad, for (size_t basis = 0; basis < RefElem.num_dofs_in_elem; basis++) { sum += RefElem.qpt_basis(qpt, basis); } - if (fabs(sum - 1.0) > 1e-13) { + if (fabs(sum - 1.0) > 1.e-12) { printf("Error: partion of unity failed, sum of basis = %f at rid = %zu \n", sum, qpt); Kokkos::abort("Partition of unity failed at quadrature point "); } @@ -76,8 +76,8 @@ void verify_gradient(const Quadrature_t& Quad, for (size_t basis = 0; basis < RefElem.num_dofs_in_elem; basis++) { sum[dim] += RefElem.qpt_grad_basis(qpt, basis, dim); } - if (fabs(sum[dim]) > 1.e-13) { - printf("Error: gradient failed, sum of gradient basis = %f at rid = %zu \n", sum[dim], qpt); + if (fabs(sum[dim]) > 1.e-12) { + printf("Error: gradient failed, sum of gradient basis = %f at rid = %zu, 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 = %f \n", dim, sum[dim]); From a7ca4f1cebba1381e20212cea03dfbe36120a9fc Mon Sep 17 00:00:00 2001 From: Nathaniel Morgan Date: Thu, 2 Jul 2026 16:22:42 -0600 Subject: [PATCH 09/59] partion of unity and gradient tests now include Legendre DOFs for thermal space --- .../src/partition_unity_test.cpp | 75 +++++++++++++++---- 1 file changed, 60 insertions(+), 15 deletions(-) diff --git a/examples/reference_element/src/partition_unity_test.cpp b/examples/reference_element/src/partition_unity_test.cpp index b7e7c170..a9250c3d 100644 --- a/examples/reference_element/src/partition_unity_test.cpp +++ b/examples/reference_element/src/partition_unity_test.cpp @@ -45,28 +45,34 @@ using namespace swage; // unstructured mesh and hash using namespace elements; bool Verbose = false; -size_t max_num = 20; // max number of quadrature points to test -size_t max_order = 20; // max polynomial order to test +size_t max_num = 19; // max number of quadrature points to test up to, the limit is 19 +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) { - for (size_t qpt = 0; qpt < Quad.num_qpts_in_elem; qpt++) { + + // 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 rid = %zu \n", sum, qpt); + printf("Error: partion of unity failed, sum of basis = %f at rid = %d with order = %zu \n", sum, qpt, RefElem.num_dofs_1d); Kokkos::abort("Partition of unity failed at quadrature point "); } if(Verbose)printf("sum = %f \n", sum); - } // end loop over qpts + }); // end loop over qpts + } // end function void verify_gradient(const Quadrature_t& Quad, const ReferenceElement_t& RefElem) { - for (size_t qpt = 0; qpt < Quad.num_qpts_in_elem; qpt++) { + + // device parallel loop + FOR_ALL(qpt, 0, Quad.num_qpts_in_elem, { double sum[3]; sum[0] = 0.0; sum[1] = 0.0; @@ -77,13 +83,14 @@ void verify_gradient(const Quadrature_t& Quad, sum[dim] += RefElem.qpt_grad_basis(qpt, basis, dim); } if (fabs(sum[dim]) > 1.e-12) { - printf("Error: gradient failed, sum of gradient basis = %f at rid = %zu, for dim = %zu, with order = %zu \n", sum[dim], qpt, dim, RefElem.num_dofs_1d); + printf("Error: gradient failed, sum of gradient basis = %f at rid = %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 = %f \n", dim, sum[dim]); } // for dim - } // end loop over qpts + }); // end loop over qpts + } // end function int main(int argc, char** argv) { @@ -91,10 +98,48 @@ int main(int argc, char** argv) { MATAR_INITIALIZE(argc, argv); { // MATAR scope - printf("\n--- partition unity test ---\n"); + printf("\n--- partition unity and gradient test ---\n"); + + printf("\n--- AO 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 = 1; p_order Date: Thu, 2 Jul 2026 16:29:11 -0600 Subject: [PATCH 10/59] added more combinations of DG and FE spaces --- .../src/partition_unity_test.cpp | 79 ++++++++++++++++++- 1 file changed, 77 insertions(+), 2 deletions(-) diff --git a/examples/reference_element/src/partition_unity_test.cpp b/examples/reference_element/src/partition_unity_test.cpp index a9250c3d..4650cf6d 100644 --- a/examples/reference_element/src/partition_unity_test.cpp +++ b/examples/reference_element/src/partition_unity_test.cpp @@ -100,7 +100,7 @@ MATAR_INITIALIZE(argc, argv); printf("\n--- partition unity and gradient test ---\n"); - printf("\n--- AO element with Legendre Quadrature & Legendre DOFs ---\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); @@ -138,7 +138,82 @@ MATAR_INITIALIZE(argc, argv); } // end loop of num qpts - printf("\n--- AO element with Legendre Quadrature & Lobatto DOFs ---\n"); + printf("\n--- DG element with Lobatto 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::GaussLobatto, + num_qpts_1D, + elem_dims_test); + + // build reference elements of varing orders + for (size_t p_order = 1; p_order Date: Mon, 6 Jul 2026 09:03:26 -0600 Subject: [PATCH 11/59] added collocated quadrature element test --- .../src/kronecker_delta_test.cpp | 54 +++++++++++++++++++ .../src/partition_unity_test.cpp | 4 +- 2 files changed, 56 insertions(+), 2 deletions(-) diff --git a/examples/reference_element/src/kronecker_delta_test.cpp b/examples/reference_element/src/kronecker_delta_test.cpp index 49377d38..4ab5bc1b 100644 --- a/examples/reference_element/src/kronecker_delta_test.cpp +++ b/examples/reference_element/src/kronecker_delta_test.cpp @@ -42,6 +42,11 @@ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. using namespace mtr; using namespace swage; // unstructured mesh and hash +using namespace elements; + +bool Verbose = false; +size_t max_num = 19; // max number of quadrature points to test up to, the limit is 19 + int main(int argc, char** argv) { @@ -50,7 +55,56 @@ MATAR_INITIALIZE(argc, argv); 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) { + 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"); diff --git a/examples/reference_element/src/partition_unity_test.cpp b/examples/reference_element/src/partition_unity_test.cpp index 4650cf6d..9451e9a8 100644 --- a/examples/reference_element/src/partition_unity_test.cpp +++ b/examples/reference_element/src/partition_unity_test.cpp @@ -35,13 +35,13 @@ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include -// This pulls in kokkos, matar, mesh, hash, ref_elem stuff, and PT-Scotch +// 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 hash +using namespace swage; // unstructured mesh and point cloud using namespace elements; bool Verbose = false; From f86fb455b354f9e93eb3aad062dc315d671abd60 Mon Sep 17 00:00:00 2001 From: Nathaniel Morgan Date: Mon, 6 Jul 2026 11:26:25 -0600 Subject: [PATCH 12/59] interpolation test partially added --- .../src/interpolation_test.cpp | 165 +++++++++++++++++- .../src/partition_unity_test.cpp | 3 +- 2 files changed, 166 insertions(+), 2 deletions(-) diff --git a/examples/reference_element/src/interpolation_test.cpp b/examples/reference_element/src/interpolation_test.cpp index 956b1405..21bb511e 100644 --- a/examples/reference_element/src/interpolation_test.cpp +++ b/examples/reference_element/src/interpolation_test.cpp @@ -42,14 +42,177 @@ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. using namespace mtr; using namespace swage; // unstructured mesh and hash +using namespace elements; + +bool Verbose = true; +size_t max_num = 3; // max number of quadrature points to test up to, the limit is 19 +size_t max_order = 3; // 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); + } + else if (RefElem.elem_dims==2){ + coeff = CArrayKokkos(p_order, p_order); + } + else { + coeff = CArrayKokkos(p_order, p_order, p_order); + } + 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); + } + + if (fabs(sum - exact_value) > 1.e-12) { + printf("Error: interpolation failed at qpt id = %d with order = %zu \n", qpt, p_order); + 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 test ---\n"); + 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 = 1; p_order 1.e-12) { - printf("Error: partion of unity failed, sum of basis = %f at rid = %d with order = %zu \n", sum, qpt, RefElem.num_dofs_1d); + 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); @@ -207,6 +207,7 @@ MATAR_INITIALIZE(argc, argv); if(Verbose)printf("gradient basis check: \n"); verify_gradient(Quad, FERefElem); if(Verbose)printf("\n"); + } // end p_order loop } // elem From ca58af9fc8687e284dde330af6d8809e04fd0cfa Mon Sep 17 00:00:00 2001 From: Nathaniel Morgan Date: Mon, 6 Jul 2026 11:50:46 -0600 Subject: [PATCH 13/59] WIP interpolation tests --- .../src/interpolation_test.cpp | 46 ++++++++++++++++--- .../src/partition_unity_test.cpp | 4 +- 2 files changed, 42 insertions(+), 8 deletions(-) diff --git a/examples/reference_element/src/interpolation_test.cpp b/examples/reference_element/src/interpolation_test.cpp index 21bb511e..74bd2233 100644 --- a/examples/reference_element/src/interpolation_test.cpp +++ b/examples/reference_element/src/interpolation_test.cpp @@ -46,7 +46,7 @@ using namespace elements; bool Verbose = true; size_t max_num = 3; // max number of quadrature points to test up to, the limit is 19 -size_t max_order = 3; // max polynomial order to test, limit is 19th-order with Legendre +size_t max_order = 2; // max polynomial order to test, limit is 19th-order with Legendre // polynomial with terms <= p_order @@ -100,13 +100,13 @@ void test_interpolation(const Quadrature_t& Quad, if(RefElem.elem_dims==1){ - coeff = CArrayKokkos(p_order); + coeff = CArrayKokkos(p_order+1); } else if (RefElem.elem_dims==2){ - coeff = CArrayKokkos(p_order, p_order); + coeff = CArrayKokkos(p_order+1, p_order+1); } else { - coeff = CArrayKokkos(p_order, p_order, p_order); + coeff = CArrayKokkos(p_order+1, p_order+1, p_order+1); } coeff.set_values(0.78914567); // a radom value @@ -193,8 +193,8 @@ MATAR_INITIALIZE(argc, argv); num_qpts_1D, elem_dims_test); - // build reference elements of varing orders - for (size_t p_order = 1; p_order Date: Mon, 6 Jul 2026 12:02:21 -0600 Subject: [PATCH 14/59] interpolation checks for all combinations of Quad and RefElem --- .../src/interpolation_test.cpp | 84 ++++++++++++++++++- 1 file changed, 80 insertions(+), 4 deletions(-) diff --git a/examples/reference_element/src/interpolation_test.cpp b/examples/reference_element/src/interpolation_test.cpp index 74bd2233..7ee7d7bd 100644 --- a/examples/reference_element/src/interpolation_test.cpp +++ b/examples/reference_element/src/interpolation_test.cpp @@ -44,9 +44,9 @@ using namespace mtr; using namespace swage; // unstructured mesh and hash using namespace elements; -bool Verbose = true; -size_t max_num = 3; // max number of quadrature points to test up to, the limit is 19 -size_t max_order = 2; // max polynomial order to test, limit is 19th-order with Legendre +bool Verbose = false; +size_t max_num = 19; // max number of quadrature points to test up to, the limit is 19 +size_t max_order = 19; // max polynomial order to test, limit is 19th-order with Legendre // polynomial with terms <= p_order @@ -163,8 +163,10 @@ void test_interpolation(const Quadrature_t& Quad, exact_value = polynomial(coeff, xi, eta, mu, p_order); } - if (fabs(sum - exact_value) > 1.e-12) { + // round off get bad at high p_order's due to polynomial sensativity + if (fabs(sum - exact_value) > 1.e-11*(double)p_order) { 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); @@ -249,6 +251,80 @@ MATAR_INITIALIZE(argc, argv); if(Verbose)printf("\n"); } // end loop of num qpts + + + printf("\n--- FE element with Lobatto Quadrature & Lobatto 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::GaussLobatto, + num_qpts_1D, + elem_dims_test); + + // build reference elements of varing orders, starting at 1, its Lobatto points + for (size_t p_order = 1; p_order Date: Mon, 6 Jul 2026 14:41:52 -0600 Subject: [PATCH 15/59] added gradient test and verified coding --- .../reference_element/src/gradient_test.cpp | 369 +++++++++++++++++- .../src/kronecker_delta_test.cpp | 1 + .../src/partition_unity_test.cpp | 2 +- 3 files changed, 369 insertions(+), 3 deletions(-) diff --git a/examples/reference_element/src/gradient_test.cpp b/examples/reference_element/src/gradient_test.cpp index 18231ad1..112e41c4 100644 --- a/examples/reference_element/src/gradient_test.cpp +++ b/examples/reference_element/src/gradient_test.cpp @@ -38,18 +38,381 @@ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // 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; + +bool Verbose = false; +size_t max_num = 19; // max number of quadrature points to test up to, the limit is 19 +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; + for (int i = 0; i <= p_order; ++i) { + result += (double)i*coeff(i) * pow(x, (double)i-1); + } + + return result; +} // end polynomial + +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; + for (int j = 0; j <= p_order; ++j) + for (int i = 0; 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; + for (int j = 0; 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 + +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; + 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 += (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; + 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 += (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; + 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 += (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.; + sum[1] = 0.; + sum[2] = 0.; + + for (size_t dof = 0; dof < RefElem.num_dofs_in_elem; dof++) { + for(size_t dim=0; dim 1.e-10*(double)p_order) { + 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 \n", 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 test ---\n"); + 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 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 diff --git a/examples/reference_element/src/partition_unity_test.cpp b/examples/reference_element/src/partition_unity_test.cpp index 96c23da6..0d58d6ff 100644 --- a/examples/reference_element/src/partition_unity_test.cpp +++ b/examples/reference_element/src/partition_unity_test.cpp @@ -83,7 +83,7 @@ void verify_gradient(const Quadrature_t& Quad, sum[dim] += RefElem.qpt_grad_basis(qpt, basis, dim); } if (fabs(sum[dim]) > 1.e-12) { - printf("Error: gradient failed, sum of gradient basis = %f at rid = %d, for dim = %zu, with order = %zu \n", sum[dim], qpt, dim, RefElem.num_dofs_1d); + printf("Error: gradient failed, sum of gradient basis = %f 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 = %f \n", dim, sum[dim]); From 9e3c78c44f9704d9dbb0becb1e6ea3b9081bbc99 Mon Sep 17 00:00:00 2001 From: Nathaniel Morgan Date: Mon, 6 Jul 2026 15:35:06 -0600 Subject: [PATCH 16/59] fixed gradient test problem --- .../reference_element/src/gradient_test.cpp | 34 +++++++++++++------ 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/examples/reference_element/src/gradient_test.cpp b/examples/reference_element/src/gradient_test.cpp index 112e41c4..4695536c 100644 --- a/examples/reference_element/src/gradient_test.cpp +++ b/examples/reference_element/src/gradient_test.cpp @@ -90,19 +90,24 @@ KOKKOS_INLINE_FUNCTION double grad_polynomial_x(const CArrayKokkos &coeff, const double x, const size_t p_order){ double result = 0.0; - for (int i = 0; i <= p_order; ++i) { + 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 = 0; i <= p_order-j; ++i) { + 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 @@ -113,7 +118,9 @@ 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; - for (int j = 0; j <= p_order; ++j) + 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 @@ -121,13 +128,16 @@ double grad_polynomial_y(const CArrayKokkos &coeff, const double x, cons 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 = 0; i <= p_order-j-k; ++i) { + 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 @@ -138,8 +148,10 @@ 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 = 0; j <= p_order-k; ++j) + 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 @@ -151,7 +163,9 @@ 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; - for (int k = 0; k <= p_order; ++k) + 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.); @@ -216,9 +230,9 @@ void test_gradient(const Quadrature_t& Quad, FOR_ALL(qpt, 0, Quad.num_qpts_in_elem, { double sum[3]; // gradient value - sum[0] = 0.; - sum[1] = 0.; - sum[2] = 0.; + 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 Date: Mon, 6 Jul 2026 17:12:27 -0600 Subject: [PATCH 17/59] Coding passes full suite of integration tests --- examples/reference_element/CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/examples/reference_element/CMakeLists.txt b/examples/reference_element/CMakeLists.txt index 4df3d03b..b2f4b935 100644 --- a/examples/reference_element/CMakeLists.txt +++ b/examples/reference_element/CMakeLists.txt @@ -2,6 +2,7 @@ # 1. Define the executable # Point directly to the source file inside 'src/' +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) @@ -9,6 +10,7 @@ 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(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) @@ -16,6 +18,7 @@ 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(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) From 86f39db6144d731f1277abff8192b9cfd6c62213 Mon Sep 17 00:00:00 2001 From: Nathaniel Morgan Date: Mon, 6 Jul 2026 17:22:39 -0600 Subject: [PATCH 18/59] minor cleaning up of coding --- examples/reference_element/src/gradient_test.cpp | 6 +++++- examples/reference_element/src/interpolation_test.cpp | 7 +++++-- examples/reference_element/src/partition_unity_test.cpp | 4 ++-- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/examples/reference_element/src/gradient_test.cpp b/examples/reference_element/src/gradient_test.cpp index 4695536c..37b59ba8 100644 --- a/examples/reference_element/src/gradient_test.cpp +++ b/examples/reference_element/src/gradient_test.cpp @@ -266,7 +266,11 @@ void test_gradient(const Quadrature_t& Quad, // round off get bad at high p_order's due to polynomial sensativity for(size_t dim=0; dim 1.e-10*(double)p_order) { + + const double error = fabs(sum[dim] - exact_value[dim]); + const double tolerance = fmax(1.e-10, 1.e-10 * (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[dim], exact_value[dim], sum[dim]-exact_value[dim]); Kokkos::abort("Interpolation failed at quadrature point "); diff --git a/examples/reference_element/src/interpolation_test.cpp b/examples/reference_element/src/interpolation_test.cpp index 7ee7d7bd..5e6766c7 100644 --- a/examples/reference_element/src/interpolation_test.cpp +++ b/examples/reference_element/src/interpolation_test.cpp @@ -163,8 +163,11 @@ void test_interpolation(const Quadrature_t& Quad, exact_value = polynomial(coeff, xi, eta, mu, p_order); } - // round off get bad at high p_order's due to polynomial sensativity - if (fabs(sum - exact_value) > 1.e-11*(double)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 "); diff --git a/examples/reference_element/src/partition_unity_test.cpp b/examples/reference_element/src/partition_unity_test.cpp index 0d58d6ff..d4134625 100644 --- a/examples/reference_element/src/partition_unity_test.cpp +++ b/examples/reference_element/src/partition_unity_test.cpp @@ -83,10 +83,10 @@ void verify_gradient(const Quadrature_t& Quad, sum[dim] += RefElem.qpt_grad_basis(qpt, basis, dim); } if (fabs(sum[dim]) > 1.e-12) { - printf("Error: gradient failed, sum of gradient basis = %f at qpt id = %d, for dim = %zu, with order = %zu \n", sum[dim], qpt, dim, RefElem.num_dofs_1d); + 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 = %f \n", dim, sum[dim]); + if(Verbose)printf("dim = %zu, sum = %.15e \n", dim, sum[dim]); } // for dim }); // end loop over qpts From 18881743d941a1b59284e5268b30786722b78082 Mon Sep 17 00:00:00 2001 From: Nathaniel Morgan Date: Tue, 7 Jul 2026 15:03:41 -0600 Subject: [PATCH 19/59] added integration test file and mesh test --- examples/average/include/mesh_io.h | 12 +- examples/average/src/average.cpp | 9 +- examples/decomp_example/include/mesh_io.h | 14 +- .../src/mesh_decomp_example.cpp | 4 +- examples/mesh_form/include/mesh_io.h | 16 +- examples/mesh_form/src/mesh_form.cpp | 2 +- examples/reference_element/CMakeLists.txt | 3 + .../src/integration_test.cpp | 464 ++++++++++++++++++ .../src/ref_plus_mesh_test.cpp | 151 ++++++ src/decomp_utilities/decomp_utils.h | 28 +- src/elements/ref_elem.h | 43 +- src/swage/unstructured_mesh.h | 69 ++- 12 files changed, 733 insertions(+), 82 deletions(-) create mode 100644 examples/reference_element/src/integration_test.cpp create mode 100644 examples/reference_element/src/ref_plus_mesh_test.cpp diff --git a/examples/average/include/mesh_io.h b/examples/average/include/mesh_io.h index f184a635..0800288e 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 diff --git a/examples/average/src/average.cpp b/examples/average/src/average.cpp index bfedc2a9..b1166ec1 100644 --- a/examples/average/src/average.cpp +++ b/examples/average/src/average.cpp @@ -44,14 +44,21 @@ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "state.h" +using namespace mtr; +using namespace swage; // unstructured mesh and point cloud +using namespace elements; // reference element space + 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++) { @@ -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..0ba010e7 100644 --- a/examples/decomp_example/src/mesh_decomp_example.cpp +++ b/examples/decomp_example/src/mesh_decomp_example.cpp @@ -59,11 +59,11 @@ 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; MPICArrayKokkos initial_node_coords; // Mesh partitioned by pt-scotch, including ghost - swage::Mesh final_mesh; + swage::Mesh_t final_mesh; 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..c81d961c 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 @@ -332,7 +332,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 +409,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++) { @@ -466,7 +466,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 +864,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 +964,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; } 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 index b2f4b935..53f03235 100644 --- a/examples/reference_element/CMakeLists.txt +++ b/examples/reference_element/CMakeLists.txt @@ -2,6 +2,7 @@ # 1. Define the executable # Point directly to the source file inside 'src/' +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) @@ -10,6 +11,7 @@ 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(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) @@ -18,6 +20,7 @@ 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(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) diff --git a/examples/reference_element/src/integration_test.cpp b/examples/reference_element/src/integration_test.cpp new file mode 100644 index 00000000..0ebd5824 --- /dev/null +++ b/examples/reference_element/src/integration_test.cpp @@ -0,0 +1,464 @@ +/********************************************************************************************** +© 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; + +bool Verbose = false; +size_t max_num = 19; // max number of quadrature points to test up to, the limit is 19 +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, ref_elem stuff, and PT-Scotch +#include "ELEMENTS.h" + + +using namespace mtr; +using namespace swage; // unstructured mesh and point cloud +using namespace elements; // reference element space + +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 + 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; + for(size_t ic=i; ic<=i+elem_order; ic++) + for(size_t jc=j; jc<=j+elem_order; jc++) + for(size_t kc=k; kc<=k+elem_order; kc++){ + 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++; + + node_coords(node_gid,0) = (double)ic*h; + node_coords(node_gid,1) = (double)jc*h; + node_coords(node_gid,2) = (double)kc*h; + } // end for + + }); // end parallel for + + Mesh.build_corner_connectivity(); + Mesh.build_elem_elem_connectivity(); + Mesh.build_patch_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"); + } + + // ========================================== + // Create state on the unstructured mesh structure + + DCArrayKokkos node_scalar(Mesh.num_nodes); + DCArrayKokkos gauss_scalar(Mesh.num_gauss_in_elem); + + + printf("\nReference plus mesh test finished.\n"); + + +} // end MATAR scope +MATAR_FINALIZE(); + +return 0; +} 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 cb149592..9e01f848 100644 --- a/src/elements/ref_elem.h +++ b/src/elements/ref_elem.h @@ -75,7 +75,7 @@ namespace elements size_t elem_dims = 0; size_t num_qpts_in_elem = 0; - size_t num_qpts_in_1d = 0; + size_t num_qpts_1d = 0; CArrayKokkos qpt_positions; CArrayKokkos qpt_weights; @@ -87,41 +87,41 @@ namespace elements /// \brief Set up quadrature in a volume or surface element /// /// \param TypeInp The type of quadrature (e.g., Lobatto or Legendra) - /// \param num_qpts_in_1d_inp The number of quadrature in 1D, applied to each direction. + /// \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_in_1d_inp, + const size_t num_qpts_1d_inp, const size_t elem_dims_in) { QuadratureType = TypeInp; elem_dims = elem_dims_in; - num_qpts_in_1d = num_qpts_in_1d_inp; - if(num_qpts_in_1d==0) throw std::runtime_error("ERROR: zero quadrature points specified \n"); + 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"); // temporary 1D variables to build 3D element - CArrayKokkos qpt_positions_1d(num_qpts_in_1d, "qpt_positions_1d"); - CArrayKokkos qpt_weights_1d (num_qpts_in_1d, "qpt_weights_1d"); + 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_in_1d); - get_legendre_weights_1D(qpt_weights_1d, num_qpts_in_1d); + 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_in_1d); - get_lobatto_weights_1D(qpt_weights_1d, num_qpts_in_1d); + get_lobatto_nodes_1D(qpt_positions_1d, num_qpts_1d); + get_lobatto_weights_1D(qpt_weights_1d, num_qpts_1d); }); } else @@ -131,9 +131,9 @@ namespace elements // 3D volume element if(elem_dims==3){ - FOR_ALL_CLASS(k, 0, num_qpts_in_1d, - j, 0, num_qpts_in_1d, - i, 0, num_qpts_in_1d, { + FOR_ALL_CLASS(k, 0, num_qpts_1d, + j, 0, num_qpts_1d, + i, 0, num_qpts_1d, { const size_t rid = get_qpt_rid(i, j, k); @@ -147,8 +147,8 @@ namespace elements } // 2D volume or 2D surface element else if (elem_dims==2){ - FOR_ALL_CLASS(j, 0, num_qpts_in_1d, - i, 0, num_qpts_in_1d, { + FOR_ALL_CLASS(j, 0, num_qpts_1d, + i, 0, num_qpts_1d, { const size_t rid = get_qpt_rid(i, j); @@ -161,7 +161,7 @@ namespace elements } // 1D volume, edge, or 1D surface element else if (elem_dims==1) { - FOR_ALL_CLASS(i, 0, num_qpts_in_1d, { + FOR_ALL_CLASS(i, 0, num_qpts_1d, { const size_t rid = i; @@ -201,7 +201,7 @@ namespace elements KOKKOS_INLINE_FUNCTION size_t get_qpt_rid(size_t i, size_t j, size_t k) const { - return i + (j + k * num_qpts_in_1d) * num_qpts_in_1d; + return i + (j + k * num_qpts_1d) * num_qpts_1d; } // end function ///////////////////////////////////////////////////////////////////////////// @@ -227,7 +227,7 @@ namespace elements KOKKOS_INLINE_FUNCTION size_t get_qpt_rid(size_t i, size_t j) const { - return i + j * num_qpts_in_1d; + return i + j * num_qpts_1d; } // end function }; // end Quadrature_t @@ -245,6 +245,8 @@ namespace elements // Dofs size_t num_dofs_in_elem = 1; size_t num_dofs_1d = 0; + + size_t Pn = 0; // DOF positions @@ -299,6 +301,7 @@ namespace elements // ----------------------------------------------------------------------- // Step 1a: determine the number of DOFs in 3D // ----------------------------------------------------------------------- + Pn = p_order; num_dofs_1d = p_order + 1; num_dofs_in_elem = 1; // Initialize to 1 diff --git a/src/swage/unstructured_mesh.h b/src/swage/unstructured_mesh.h index dd672a34..3cce500a 100644 --- a/src/swage/unstructured_mesh.h +++ b/src/swage/unstructured_mesh.h @@ -240,7 +240,7 @@ struct corners_in_elem_t // }; // mesh sizes and connectivity data structures -struct Mesh +struct Mesh_t { // ******* Entity Definitions **********// // Element: A hexahedral or Quadralateral volume @@ -358,6 +358,13 @@ struct Mesh // 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 + void initialize_dims(const size_t num_dims_inp) + { + num_dims = num_dims_inp; + }; // end method + // initialization methods void initialize_nodes(const size_t num_nodes_inp) { @@ -368,72 +375,88 @@ struct Mesh return; }; // end method + // initialization methods - void initialize_elems(const size_t num_elems_inp, const size_t num_dims_inp) + 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; + + // --- Basic element bookkeeping --- num_elems = num_elems_inp; + // initializes a linear element with a single gauss point for saving results Pn = 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) - - 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); - num_zones = num_zones_in_elem * num_elems; + + // --- 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 + // initialization method for an FE mesh 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; - num_elems = num_elems_inp; - Pn = Pn_order; + // --- 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_nodes_in_elem = (size_t)std::pow(Pn_order + 1, num_dims); //(Pn_order + 1)**num_dims; // (Pn +1) - num_nodes_in_zone = (size_t)std::pow(2, num_dims); // (4, or 8, always) - num_gauss_in_elem = (size_t)std::pow(2*Pn_order, num_dims); // = 2*Pn - num_zones_in_elem = (size_t)std::pow(Pn_order, num_dims); // Pn - num_surfs_in_elem = num_dims == 2 ? 4 : 6; // 4 or 6 (always) + num_elems = num_elems_inp; + if (num_elems == 0) { + Kokkos::abort("Error: num_elems must be greater than 0. Exiting at initialize_elems_Pn()."); + } + + elem_kind = mesh_init::arbitrary_tensor_element; - 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; + + + // --- 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); 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) { From 570220a25c4fa17a3a782fc635bf6ebf1d39c70a Mon Sep 17 00:00:00 2001 From: Nathaniel Morgan Date: Fri, 10 Jul 2026 08:36:09 -0600 Subject: [PATCH 20/59] Added Jacobian matrix --- src/geometry/geometry.h | 43 ++++++++++++++++++++++++++++++++--- src/swage/unstructured_mesh.h | 2 +- 2 files changed, 41 insertions(+), 4 deletions(-) diff --git a/src/geometry/geometry.h b/src/geometry/geometry.h index 1b8570d3..66235bdd 100644 --- a/src/geometry/geometry.h +++ b/src/geometry/geometry.h @@ -1,12 +1,49 @@ #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" - - - +#include "elements.h" // reference element space + +///////////////////////////////////////////////////////////////////////////// +/// +/// \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] +/// +///////////////////////////////////////////////////////////////////////////// +KOKKOS_INLINE_FUNCTION +void jacobian( + const ViewCArrayKokkos &jacobian, + const DCArrayKokkos &node_coords, + const ViewCArrayKokkos &nodes_in_an_elem, + const ViewCArrayKokkos &a_grad_basis){ + + const size_t dim = 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 j = 0; j < dim; j++) // looping over dimension + for(size_t k = 0; k < dim; k++){ // looping over dimension + jacobian(j, k) = 0.0; + } // end for + + // solving for the jacobian + for(size_t j = 0; j < dim; j++) // looping over dimension (partial) + for(size_t k = 0; k < dim; k++) // 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(j, k) += a_grad_basis(node_lid, j)*node_coords(node_gid, k); + } // end for +} // end of jacobian_2d function diff --git a/src/swage/unstructured_mesh.h b/src/swage/unstructured_mesh.h index 3cce500a..41ce28c5 100644 --- a/src/swage/unstructured_mesh.h +++ b/src/swage/unstructured_mesh.h @@ -438,7 +438,7 @@ struct Mesh_t // --- 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_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) From b661b61d5177d574600eeff6b8bccf25fcdc30c9 Mon Sep 17 00:00:00 2001 From: Nathaniel Morgan Date: Fri, 10 Jul 2026 11:02:56 -0600 Subject: [PATCH 21/59] fixed header in geometry.h --- src/geometry/geometry.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/geometry/geometry.h b/src/geometry/geometry.h index 66235bdd..6738848a 100644 --- a/src/geometry/geometry.h +++ b/src/geometry/geometry.h @@ -6,7 +6,10 @@ #include "matar.h" #include "shapes.h" -#include "elements.h" // reference element space + +using namespace mtr; +using namespace swage; // unstructured mesh and hash +using namespace elements; ///////////////////////////////////////////////////////////////////////////// /// From 8b9f497a5b9c25d9332c766b63bc3212f1e09536 Mon Sep 17 00:00:00 2001 From: Nathaniel Morgan Date: Fri, 10 Jul 2026 11:10:10 -0600 Subject: [PATCH 22/59] updated matar submodule --- .gitmodules | 2 +- matar | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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/matar b/matar index 8142fd6e..9926149c 160000 --- a/matar +++ b/matar @@ -1 +1 @@ -Subproject commit 8142fd6e47615be0d6b0401dbc4118eb8ce87dd8 +Subproject commit 9926149c7819e112420e51e30df3b62ea85cbe09 From e041cce311414f1a08fbe215a757af0e5e9fdd13 Mon Sep 17 00:00:00 2001 From: Nathaniel Morgan Date: Fri, 10 Jul 2026 13:16:14 -0600 Subject: [PATCH 23/59] called jacobian function for mesh elem --- .../src/ref_plus_mesh_test.cpp | 37 ++++++++++++++++++- src/geometry/geometry.h | 20 +++++----- 2 files changed, 46 insertions(+), 11 deletions(-) diff --git a/examples/reference_element/src/ref_plus_mesh_test.cpp b/examples/reference_element/src/ref_plus_mesh_test.cpp index baa2f3dd..39c90677 100644 --- a/examples/reference_element/src/ref_plus_mesh_test.cpp +++ b/examples/reference_element/src/ref_plus_mesh_test.cpp @@ -107,9 +107,11 @@ MATAR_INITIALIZE(argc, argv); size_t elem_gid = i + (j+k*num_elems_1D)*num_elems_1D; size_t node_lid = 0; - for(size_t ic=i; ic<=i+elem_order; ic++) + + + for(size_t kc=k; kc<=k+elem_order; kc++) for(size_t jc=j; jc<=j+elem_order; jc++) - for(size_t kc=k; kc<=k+elem_order; kc++){ + for(size_t ic=i; ic<=i+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++; @@ -141,6 +143,37 @@ MATAR_INITIALIZE(argc, argv); 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"); diff --git a/src/geometry/geometry.h b/src/geometry/geometry.h index 6738848a..f6687bf4 100644 --- a/src/geometry/geometry.h +++ b/src/geometry/geometry.h @@ -30,23 +30,25 @@ void jacobian( const ViewCArrayKokkos &nodes_in_an_elem, const ViewCArrayKokkos &a_grad_basis){ - const size_t dim = a_grad_basis.dims(1); + 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 j = 0; j < dim; j++) // looping over dimension - for(size_t k = 0; k < dim; k++){ // looping over dimension - jacobian(j, k) = 0.0; + 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 // solving for the jacobian - for(size_t j = 0; j < dim; j++) // looping over dimension (partial) - for(size_t k = 0; k < dim; k++) // looping over dimension (node position) + 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(j, k) += a_grad_basis(node_lid, j)*node_coords(node_gid, k); - } // end for -} // end of jacobian_2d function + jacobian(i, j) += node_coords(node_gid, i)*a_grad_basis(node_lid, j); + } // end for + + +} // end of jacobian function From 446bd8d40f4f055bcad5189efb7fd6089287157d Mon Sep 17 00:00:00 2001 From: Nathaniel Morgan Date: Mon, 13 Jul 2026 15:30:18 -0600 Subject: [PATCH 24/59] updated Jacobian routine and adding tests --- .../src/ref_plus_mesh_test.cpp | 149 +++++++++++++++++- src/geometry/geometry.h | 11 +- 2 files changed, 154 insertions(+), 6 deletions(-) diff --git a/examples/reference_element/src/ref_plus_mesh_test.cpp b/examples/reference_element/src/ref_plus_mesh_test.cpp index 39c90677..3f0b61db 100644 --- a/examples/reference_element/src/ref_plus_mesh_test.cpp +++ b/examples/reference_element/src/ref_plus_mesh_test.cpp @@ -37,12 +37,156 @@ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // 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 1: 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 here and compare] + 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 + 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("Jacobian calculation 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("\nTEST 1: Passes\n\n"); +} // end function + + int main(int argc, char** argv) { MATAR_INITIALIZE(argc, argv); @@ -176,6 +320,9 @@ MATAR_INITIALIZE(argc, argv); printf("\nReference plus mesh test finished.\n"); + // running unit tests of Jacobian + test_affine_transformation(); + } // end MATAR scope MATAR_FINALIZE(); diff --git a/src/geometry/geometry.h b/src/geometry/geometry.h index f6687bf4..7385b608 100644 --- a/src/geometry/geometry.h +++ b/src/geometry/geometry.h @@ -23,12 +23,13 @@ using namespace elements; /// \param a_grad_basis The gradient of the basis at a single point, Grad[DOFs,dims] /// ///////////////////////////////////////////////////////////////////////////// +template KOKKOS_INLINE_FUNCTION void jacobian( - const ViewCArrayKokkos &jacobian, - const DCArrayKokkos &node_coords, - const ViewCArrayKokkos &nodes_in_an_elem, - const ViewCArrayKokkos &a_grad_basis){ + 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(); @@ -39,7 +40,7 @@ void jacobian( jacobian(i, j) = 0.0; } // end for - // solving for the jacobian + // 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++){ From a2828a75879fc0d991245cff2ce967cf80f7fa17 Mon Sep 17 00:00:00 2001 From: Nathaniel Morgan Date: Mon, 13 Jul 2026 15:47:41 -0600 Subject: [PATCH 25/59] adding Jacobian tests --- .../src/ref_plus_mesh_test.cpp | 158 +++++++++++++++++- 1 file changed, 151 insertions(+), 7 deletions(-) diff --git a/examples/reference_element/src/ref_plus_mesh_test.cpp b/examples/reference_element/src/ref_plus_mesh_test.cpp index 3f0b61db..75955ca2 100644 --- a/examples/reference_element/src/ref_plus_mesh_test.cpp +++ b/examples/reference_element/src/ref_plus_mesh_test.cpp @@ -62,7 +62,7 @@ const size_t convert_ensight_to_ijk[8] = /// Maps reference cube [-1,1]³ to a skewed, rotated, stretched parallelepiped /////////////////////////////////////////////////////////////////////////////// void test_affine_transformation() { - printf("\n=== TEST 1: Affine Transformation ===\n"); + printf("\n=== TEST: Affine Transformation ===\n"); // Reference element: unit cube [-1,1]^3 // Physical element: defined by affine map x = A*xi + b @@ -144,7 +144,7 @@ void test_affine_transformation() { // Now compute Jacobian at element center (xi=0, eta=0, zeta=0) // and verify it matches A - // [Call jacobian function here and compare] + // Call jacobian function and then compare results DCArrayKokkos jac(3, 3); RUN({ // exact the basis and grad basis for this quadrature point @@ -167,15 +167,14 @@ void test_affine_transformation() { double det = det_3x3(jac); printf("Calculated determinant: %12.8f\n", det); - }); // end RUN + }); // end RUN on device jac.update_host(); - for(size_t i = 0; i < 3; i++){ // looping over dimension + 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("Jacobian calculation failed \n"); + Kokkos::abort("ERROR: Jacobian calculation in affine test failed\n"); } - } } // end for printf("\nTest criteria:\n"); @@ -183,7 +182,150 @@ void test_affine_transformation() { 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("\nTEST 1: Passes\n\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 affine test failed\n"); + } + } // end for + + printf("\nSkewed hex test: Passes\n\n"); + } // end function @@ -323,6 +465,8 @@ MATAR_INITIALIZE(argc, argv); // running unit tests of Jacobian test_affine_transformation(); + test_skewed_hexahedron(); + } // end MATAR scope MATAR_FINALIZE(); From f10a37cf7b884a1c76ed5c1e04aa339efe98871f Mon Sep 17 00:00:00 2001 From: Nathaniel Morgan Date: Mon, 13 Jul 2026 15:58:44 -0600 Subject: [PATCH 26/59] added jacobian test --- .../src/ref_plus_mesh_test.cpp | 155 +++++++++++++++++- 1 file changed, 153 insertions(+), 2 deletions(-) diff --git a/examples/reference_element/src/ref_plus_mesh_test.cpp b/examples/reference_element/src/ref_plus_mesh_test.cpp index 75955ca2..3a7e7888 100644 --- a/examples/reference_element/src/ref_plus_mesh_test.cpp +++ b/examples/reference_element/src/ref_plus_mesh_test.cpp @@ -320,7 +320,7 @@ void test_skewed_hexahedron() { 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 affine test failed\n"); + Kokkos::abort("ERROR: Jacobian calculation in skewed hex test failed\n"); } } // end for @@ -329,6 +329,157 @@ void test_skewed_hexahedron() { } // 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 + + + + int main(int argc, char** argv) { MATAR_INITIALIZE(argc, argv); @@ -464,8 +615,8 @@ MATAR_INITIALIZE(argc, argv); // running unit tests of Jacobian test_affine_transformation(); - test_skewed_hexahedron(); + test_rotated_scaled_cube(); } // end MATAR scope From ff654788133034bbc2da7f50eab1468a06b41727 Mon Sep 17 00:00:00 2001 From: Nathaniel Morgan Date: Wed, 22 Jul 2026 15:55:05 -0600 Subject: [PATCH 27/59] added surf quadrature and ref surf --- .../src/ref_plus_mesh_test.cpp | 322 ++++ src/elements/ref_elem.h | 1672 ++++++++++------- 2 files changed, 1352 insertions(+), 642 deletions(-) diff --git a/examples/reference_element/src/ref_plus_mesh_test.cpp b/examples/reference_element/src/ref_plus_mesh_test.cpp index 3a7e7888..9fba7169 100644 --- a/examples/reference_element/src/ref_plus_mesh_test.cpp +++ b/examples/reference_element/src/ref_plus_mesh_test.cpp @@ -478,6 +478,327 @@ void test_rotated_scaled_cube() { } // 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); + DCArrayKokkos node_in_elem_dual(8); + DCArrayKokkos jac(3, 3); + + for(size_t n = 0; n < 8; 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_in_elem_dual.host(n) = n; + } + } + 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); + 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); + 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]); + + double det_numerical = det_3x3(jac); + double det_error = fabs(det_analytical - det_numerical); + + printf("\n Determinant:\n"); + printf(" Analytical: %12.8f\n", det_analytical); + printf(" Numerical: %12.8f\n", det_numerical); + 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) all_passed = false; + + qpt_id += 13; + } // end for qpt loop + + // =============================== + // Now testing a surface element + // =============================== + + real_t surf_test_points[6][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 + }; + + 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<2; 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); + 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]); + + double det_numerical = det_3x3(jac); + double det_error = fabs(det_analytical - det_numerical); + + printf("\n Determinant:\n"); + printf(" Analytical: %12.8f\n", det_analytical); + printf(" Numerical: %12.8f\n", det_numerical); + 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) 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) { @@ -617,6 +938,7 @@ MATAR_INITIALIZE(argc, argv); test_affine_transformation(); test_skewed_hexahedron(); test_rotated_scaled_cube(); + test_manufactured_solution(); } // end MATAR scope diff --git a/src/elements/ref_elem.h b/src/elements/ref_elem.h index 9e01f848..99a9f22c 100644 --- a/src/elements/ref_elem.h +++ b/src/elements/ref_elem.h @@ -68,7 +68,769 @@ namespace reference_space namespace elements { - // Quadrature rules for surfaces and elems + ///////////////////////////////////////////////////////////////////////////// + /// + /// \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 + + + ///////////////////////////////////////////////////////////////////////////// + /// + /// \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 + + + ///////////////////////////////////////////////////////////////////////////// + /// + /// \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; + } + + + ///////////////////////////////////////////////////////////////////////////// + /// + /// \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; + } + + + ///////////////////////////////////////////////////////////////////////////// + /// + /// \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 + /// + ///////////////////////////////////////////////////////////////////////////// + + 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); + + 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 + + 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)); + + // Calculate the denominator + denominator = denominator * (dof_positions_1d(vert_i) - dof_positions_1d(vert_j)); + } // end if + + + } // end looping over nodes != vert_i + interpolant = numerator / denominator; // storing a single value for interpolation for node 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 + + ///////////////////////////////////////////////////////////////////////////// + /// + /// \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 CArrayKokkos& dof_positions_1d, // location of basis DOFs in ref elem + const double x_point) // point of interest in element + { + const size_t num_dofs_1d = dof_positions_1d.dims(0); + + 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; + + 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; + + // 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 + + // Sum over the product of the numerator + // contributions from each node + num_gradient += product_gradient; + } // end if + + + } // 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 + + + ///////////////////////////////////////////////////////////////////////////// + /// + /// \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); + + + for(size_t dim=0; dim& 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); + + // get grad basis + for (size_t i = 0; i < num_dofs_1d; i++) { + Dval_1d(i) = 0.0; + } + + // Calculate 1D partial w.r.t. xi for the X coordinate of the point + lagrange_derivative_1D(Dval_1d, dof_positions_1d, point(0)); + + // 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); + } + + // get Y and Z basis, the latter only if elem_dims = 3 + for(size_t dim=1; dim& 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); + + // get X and Z basis values, the latter only if elem_dims = 3D + for(size_t dim=0; dim& 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 + + const size_t num_dofs_1d = dof_positions_1d.dims(0); + const size_t elem_dims = point.dims(0); + + // get X and Y basis + for(size_t dim=0; dim + 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); + + 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); + + + 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); + } + + get_basis(temp_basis, dof_positions_1d, temp_val_1d, temp_val_Nd, point); + + 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(); + + // ----------------------------------------------------------------------- + // Step 3: Calculate the grad basis values at quadrature points + // ----------------------------------------------------------------------- + + + // 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); + } + + 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(); + + } // end get_basis_and_grad_basis function + + + // Quadrature rules for elems struct Quadrature_t { reference_space::QuadratureType QuadratureType; @@ -84,7 +846,7 @@ namespace elements /// /// \fn initialize_quadrature /// - /// \brief Set up quadrature in a volume or surface element + /// \brief Set up quadrature in a volume element /// /// \param TypeInp The type of quadrature (e.g., Lobatto or Legendra) /// \param num_qpts_1d_inp The number of quadrature in 1D, applied to each direction. @@ -135,7 +897,7 @@ namespace elements j, 0, num_qpts_1d, i, 0, num_qpts_1d, { - const size_t rid = get_qpt_rid(i, j, k); + const size_t rid = get_qpt_rid(i, j, k, num_qpts_1d); qpt_positions(rid, 0) = qpt_positions_1d(i); qpt_positions(rid, 1) = qpt_positions_1d(j); @@ -145,12 +907,12 @@ namespace elements }); Kokkos::fence(); } - // 2D volume or 2D surface element + // 2D volume else if (elem_dims==2){ FOR_ALL_CLASS(j, 0, num_qpts_1d, i, 0, num_qpts_1d, { - const size_t rid = get_qpt_rid(i, j); + const size_t rid = get_qpt_rid(i, j, num_qpts_1d); qpt_positions(rid, 0) = qpt_positions_1d(i); qpt_positions(rid, 1) = qpt_positions_1d(j); @@ -159,7 +921,7 @@ namespace elements }); Kokkos::fence(); } - // 1D volume, edge, or 1D surface element + // 1D volume else if (elem_dims==1) { FOR_ALL_CLASS(i, 0, num_qpts_1d, { @@ -177,62 +939,10 @@ namespace elements } // init fcn quadrature - - ///////////////////////////////////////////////////////////////////////////// - /// - /// \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) const - { - return i + (j + k * num_qpts_1d) * num_qpts_1d; - } // end function - - ///////////////////////////////////////////////////////////////////////////// - /// - /// \fn get_qpt_rid - /// - /// \brief Compute the 1D array index for a quadrature point in an 2D volume - /// or surface element. - /// - /// Calculates the row-major flat index corresponding to a quadrature point - /// at the position (i, j) within a 2D volume or surface 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) const - { - return i + j * num_qpts_1d; - } // end function - }; // end Quadrature_t + // reference element data structure struct ReferenceElement_t { @@ -251,6 +961,7 @@ namespace elements // DOF positions CArrayKokkos dof_positions; + CArrayKokkos dof_positions_1d; // Basis evaluation at quadrature points @@ -287,7 +998,7 @@ namespace elements ///////////////////////////////////////////////////////////////////////////// void initialize_ref_elem(const reference_space::ElementType ElemTypeInp, const reference_space::BasisType BasisTypeInp, - const struct Quadrature_t Quadrature, + const struct Quadrature_t& Quadrature, const size_t p_order) { // set element and basis type @@ -313,7 +1024,7 @@ namespace elements // Step 1b: get the positions in reference space for the DOFs // ----------------------------------------------------------------------- dof_positions = CArrayKokkos(num_dofs_in_elem, elem_dims, "dof_positions"); - CArrayKokkos dof_positions_1d(num_dofs_1d, "dof_positions_1d"); + dof_positions_1d = CArrayKokkos (num_dofs_1d, "dof_positions_1d"); // dof positions can be at legendre or lobatto locations in elem if(BasisTypeInp == reference_space::LagrangeLegendre){ @@ -337,7 +1048,7 @@ namespace elements j, 0, num_dofs_1d, i, 0, num_dofs_1d, { - const size_t rid = get_dof_rid(i, j, k); + const size_t rid = get_dof_rid(i, j, k, num_dofs_1d); dof_positions(rid, 0) = dof_positions_1d(i); dof_positions(rid, 1) = dof_positions_1d(j); @@ -349,7 +1060,7 @@ namespace elements FOR_ALL_CLASS(j, 0, num_dofs_1d, i, 0, num_dofs_1d, { - const size_t rid = get_dof_rid(i, j); + const size_t rid = get_dof_rid(i, j, num_dofs_1d); dof_positions(rid, 0) = dof_positions_1d(i); dof_positions(rid, 1) = dof_positions_1d(j); @@ -365,665 +1076,342 @@ namespace elements }); } // end if 1D Kokkos::fence(); - - // ----------------------------------------------------------------------- - // Step 2: Calculate the basis values at quadrature points - // ----------------------------------------------------------------------- - qpt_basis = CArrayKokkos(Quadrature.num_qpts_in_elem, num_dofs_in_elem, "qpt_basis"); - - // 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); + // 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); - RUN_CLASS({ - for (size_t qpt_rid = 0; qpt_rid < Quadrature.num_qpts_in_elem; qpt_rid++) { - - // Get the evaluation coordinates - for (size_t dim = 0; dim < elem_dims; dim++) { - point(dim) = Quadrature.qpt_positions(qpt_rid, dim); - } - - get_basis(temp_basis, dof_positions_1d, temp_val_1d, temp_val_Nd, point); - - 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(); + } // end of member function - // ----------------------------------------------------------------------- - // Step 3: Calculate the grad basis values at quadrature points - // ----------------------------------------------------------------------- - qpt_grad_basis = CArrayKokkos(Quadrature.num_qpts_in_elem, num_dofs_in_elem, elem_dims, "qpt_grad_basis"); - + }; // end struct - // 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); + // 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; - RUN_CLASS({ - for (size_t qpt_rid = 0; qpt_rid < Quadrature.num_qpts_in_elem; qpt_rid++) { - - // Get the evaluation coordinates - for (size_t dim = 0; dim < elem_dims; dim++) { - point(dim) = Quadrature.qpt_positions(qpt_rid, dim); - } - - 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 + size_t num_ref_surfs = 0; - - } // end for qpts in elem - }); - Kokkos::fence(); + CArrayKokkos qpt_positions; + CArrayKokkos qpt_weights; - } // end of member function - - ///////////////////////////////////////////////////////////////////////////// - /// - /// \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) const - { - return i + (j + k * num_dofs_1d) * num_dofs_1d; - } - ///////////////////////////////////////////////////////////////////////////// /// - /// \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. + /// \fn initialize_quadrature /// - /// \param i Local DOF index in the first (xi) coordinate direction. - /// \param j Local DOF index in the second (eta) coordinate direction. + /// \brief Set up quadrature for surface elements /// - /// \return The row-major offset index for the DOF in this element. + /// \param TypeInp The type of quadrature (e.g., Lobatto or Legendra) + /// \param num_qpts_1d_inp The number of quadrature in 1D, applied to each direction. + /// \param elem_dims_in The number dimensions /// ///////////////////////////////////////////////////////////////////////////// - KOKKOS_INLINE_FUNCTION - size_t get_dof_rid(size_t i, size_t j) const + void initialize_quadrature(const reference_space::QuadratureType TypeInp, + const size_t num_qpts_1d_inp, + const size_t elem_dims_in) { - return i + j * num_dofs_1d; - } + 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"); - ///////////////////////////////////////////////////////////////////////////// - /// - /// \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 - { - + 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"); - // 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); - } - } // end loop over dims + // 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"); + } - 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); - basis(rid) = val_Nd(i, 0) * val_Nd(j, 1) * val_Nd(k, 2); - - } - } // 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); - 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 + + // surface of 3D volume element + if(elem_dims==3){ + size_t side; - // 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& 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 - { - // get grad basis - for (size_t i = 0; i < num_dofs_1d; i++) { - Dval_1d(i) = 0.0; - } + qpt_weights(rid) = qpt_weights_1d(j) * qpt_weights_1d(k); - // Calculate 1D partial w.r.t. xi for the X coordinate of the point - lagrange_derivative_1D(Dval_1d, dof_positions_1d, point(0)); + }); // end for + + // xi-plus has coords (+1, eta, mu) + side = 1; + FOR_ALL_CLASS(k, 0, num_qpts_1d, + j, 0, num_qpts_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); - } + const size_t rid=get_qpt_rid(j, k, num_qpts_1d); - // get Y and Z basis, the latter only if elem_dims = 3 - for(size_t dim=1; dim& 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 - { + const size_t rid=get_qpt_rid(i, k, num_qpts_1d); + qpt_positions(side, rid, 0) = qpt_positions_1d(i); + qpt_positions(side, rid, 1) = 1.; + qpt_positions(side, rid, 2) = qpt_positions_1d(k); - // get X and Z basis values, the latter only if elem_dims = 3D - for(size_t dim=0; dim& 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) const - { - // this routine is only valid for 3D ref elems + qpt_weights(rid) = qpt_weights_1d(j); - // get X and Y basis - for(size_t dim=0; dim& interp, // interpolant from each basis - const CArrayKokkos& dof_positions_1d, // location of basis DOFs in ref elem - const double x_point) const // point of interest in element - // calculate the basis value associated with each node_i - { - 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 + } // init fcn quadrature - 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)); + }; // end struct - // Calculate the denominator - denominator = denominator * (dof_positions_1d(vert_i) - dof_positions_1d(vert_j)); - } // end if - - } // end looping over nodes != vert_i - interpolant = numerator / denominator; // storing a single value for interpolation for node vert_i + // reference surfaces data structure + struct ReferenceSurface_t + { + size_t elem_dims = 0; + + // Basis evaluation at quadrature points on surface + CArrayKokkos qpt_basis; // access as (sides, surf_qpts, dofs) + CArrayKokkos qpt_grad_basis; // access as (sides, surf_qpts, dofs, 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 + CArrayKokkos outward_sign; - ///////////////////////////////////////////////////////////////////////////// - /// - /// \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 CArrayKokkos& dof_positions_1d, // location of basis DOFs in ref elem - const double x_point) const // point of interest in element + + void initialize_ref_surf(const struct SurfaceQuadrature_t& SurfQuadrature, + const struct ReferenceElement_t& ReferenceElement) { - 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; - - 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; - - // 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 - - // Sum over the product of the numerator - // contributions from each node - num_gradient += product_gradient; - } // end if - - } // 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 + elem_dims = SurfQuadrature.elem_dims; + + // 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; + + 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"); + + 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"); + + // the sign for outward normal relative to the reference element + outward_sign = CArrayKokkos(num_ref_surfs, "surf_outward_sign"); + + //side 0 (xi=-1): sign = -1 + //side 1 (xi=+1): sign = +1 + //side 2 (eta=-1): sign = -1 + //side 3 (eta=+1): sign = +1 + //side 4 (mu=-1): sign = -1 + //side 5 (mu=+1): sign = +1 + outward_sign(0) = -1.; + outward_sign(1) = 1.; + if(elem_dims>1){ + outward_sign(2) = -1.; + outward_sign(3) = 1.; + } + if(elem_dims>2){ + outward_sign(4) = -1.; + outward_sign(5) = 1.; + } + + + // get the basis and grad basis functions for each surfaces of the element + for(size_t side=0; side (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); + ViewCArrayKokkos side_qpt_basis (&qpt_basis(side,0,0), num_qpts_in_surf, num_dofs_in_elem); + ViewCArrayKokkos side_qpt_grad_basis(&qpt_grad_basis(side,0,0,0), num_qpts_in_surf, num_dofs_in_elem, elem_dims); + ViewCArrayKokkos side_qpt_positions (&SurfQuadrature.qpt_positions(side,0,0), num_qpts_in_surf, elem_dims); + + get_basis_and_grad_basis(side_qpt_basis, + side_qpt_grad_basis, + side_qpt_positions, + ReferenceElement.dof_positions_1d); + + // the side qpt_basis were saved to member arrays using the views above here + + } // end for sides + + + } // end member function }; // end struct + } // end namespace elements -#endif \ No newline at end of file +#endif + From e953abcc2b25618c5b6d10c920a675ea824ff926 Mon Sep 17 00:00:00 2001 From: Nathaniel Morgan Date: Wed, 22 Jul 2026 16:06:34 -0600 Subject: [PATCH 28/59] created surf quad Jacobian tests --- .../src/ref_plus_mesh_test.cpp | 48 ++++++++++++++----- 1 file changed, 35 insertions(+), 13 deletions(-) diff --git a/examples/reference_element/src/ref_plus_mesh_test.cpp b/examples/reference_element/src/ref_plus_mesh_test.cpp index 9fba7169..1c6a3be7 100644 --- a/examples/reference_element/src/ref_plus_mesh_test.cpp +++ b/examples/reference_element/src/ref_plus_mesh_test.cpp @@ -674,6 +674,8 @@ void test_manufactured_solution() { 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; @@ -684,15 +686,31 @@ void test_manufactured_solution() { // Now testing a surface element // =============================== - real_t surf_test_points[6][3] = { - // side 0, xi = -1 + 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 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"); @@ -702,8 +720,9 @@ void test_manufactured_solution() { qpt_id = 0; all_passed = true; - for(int side = 0; side<2; side++){ + 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]; @@ -711,12 +730,13 @@ void test_manufactured_solution() { 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); - 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) - ); + // 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 @@ -790,6 +810,8 @@ void test_manufactured_solution() { 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; From b8d64edeeaf8d5c55abbe13d04f186381712e5ac Mon Sep 17 00:00:00 2001 From: Nathaniel Morgan Date: Wed, 29 Jul 2026 11:21:54 -0600 Subject: [PATCH 29/59] WIP, refactoring unstructured mesh object for surfaces --- examples/average/include/mesh_io.h | 6 +- examples/decomp_example/include/mesh_io.h | 6 +- examples/mesh_form/include/mesh_io.h | 15 +- .../src/ref_plus_mesh_test.cpp | 89 +- src/elements/ref_elem.h | 234 +-- src/swage/indexing_utils.h | 712 ++++++++ src/swage/unstructured_mesh.h | 1549 ++++++----------- 7 files changed, 1500 insertions(+), 1111 deletions(-) create mode 100644 src/swage/indexing_utils.h diff --git a/examples/average/include/mesh_io.h b/examples/average/include/mesh_io.h index 0800288e..7affdd1c 100644 --- a/examples/average/include/mesh_io.h +++ b/examples/average/include/mesh_io.h @@ -1001,9 +1001,9 @@ void write_vtu(swage::Mesh_t& 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/decomp_example/include/mesh_io.h b/examples/decomp_example/include/mesh_io.h index beebea9b..0e0444f5 100644 --- a/examples/decomp_example/include/mesh_io.h +++ b/examples/decomp_example/include/mesh_io.h @@ -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 diff --git a/examples/mesh_form/include/mesh_io.h b/examples/mesh_form/include/mesh_io.h index c81d961c..261092d2 100644 --- a/examples/mesh_form/include/mesh_io.h +++ b/examples/mesh_form/include/mesh_io.h @@ -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(); @@ -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 @@ -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/reference_element/src/ref_plus_mesh_test.cpp b/examples/reference_element/src/ref_plus_mesh_test.cpp index 1c6a3be7..c3700ebd 100644 --- a/examples/reference_element/src/ref_plus_mesh_test.cpp +++ b/examples/reference_element/src/ref_plus_mesh_test.cpp @@ -655,11 +655,11 @@ void test_manufactured_solution() { // 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[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[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]); + J_analytical[1][1]*J_analytical[2][0]); double det_numerical = det_3x3(jac); double det_error = fabs(det_analytical - det_numerical); @@ -790,11 +790,11 @@ void test_manufactured_solution() { // 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[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[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]); + J_analytical[1][1]*J_analytical[2][0]); double det_numerical = det_3x3(jac); double det_error = fabs(det_analytical - det_numerical); @@ -846,6 +846,8 @@ MATAR_INITIALIZE(argc, argv); // ============================================ // Create quadrature and reference element + std::cout<<"Building reference elements and quadrature \n"< 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). + /// 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 /// @@ -372,19 +382,26 @@ namespace elements /// /// \fn partial_xi_basis /// - /// \brief Compute the partial derivative of the basis function with respect to the xi coordinate. + /// \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. + /// 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 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 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. + /// \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 /// @@ -499,20 +516,29 @@ namespace elements /// /// \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. + /// \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 /// @@ -1165,63 +1191,63 @@ namespace elements // surface of 3D volume element if(elem_dims==3){ - size_t side; + size_t face; // xi-minus has coords (-1, eta, mu) - side = 0; + face = 0; FOR_ALL_CLASS(k, 0, num_qpts_1d, j, 0, num_qpts_1d, { const size_t rid=get_qpt_rid(j, k, num_qpts_1d); - qpt_positions(side, rid, 0) = -1.; - qpt_positions(side, rid, 1) = qpt_positions_1d(j); - qpt_positions(side, rid, 2) = qpt_positions_1d(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); qpt_weights(rid) = qpt_weights_1d(j) * qpt_weights_1d(k); }); // end for // xi-plus has coords (+1, eta, mu) - side = 1; + face = 1; FOR_ALL_CLASS(k, 0, num_qpts_1d, j, 0, num_qpts_1d, { const size_t rid=get_qpt_rid(j, k, num_qpts_1d); - qpt_positions(side, rid, 0) = 1.; - qpt_positions(side, rid, 1) = qpt_positions_1d(j); - qpt_positions(side, rid, 2) = qpt_positions_1d(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); qpt_weights(rid) = qpt_weights_1d(j) * qpt_weights_1d(k); }); // end for // eta-minus has coords (xi, -1, mu) - side = 2; + face = 2; FOR_ALL_CLASS(k, 0, num_qpts_1d, i, 0, num_qpts_1d, { const size_t rid=get_qpt_rid(i, k, num_qpts_1d); - qpt_positions(side, rid, 0) = qpt_positions_1d(i); - qpt_positions(side, rid, 1) = -1.; - qpt_positions(side, rid, 2) = qpt_positions_1d(k); + qpt_positions(face, rid, 0) = qpt_positions_1d(i); + qpt_positions(face, rid, 1) = -1.; + qpt_positions(face, rid, 2) = qpt_positions_1d(k); qpt_weights(rid) = qpt_weights_1d(i) * qpt_weights_1d(k); }); // end for // eta-plus has coords (xi,+1, mu) - side = 3; + face = 3; FOR_ALL_CLASS(k, 0, num_qpts_1d, i, 0, num_qpts_1d, { const size_t rid=get_qpt_rid(i, k, num_qpts_1d); - qpt_positions(side, rid, 0) = qpt_positions_1d(i); - qpt_positions(side, rid, 1) = 1.; - qpt_positions(side, rid, 2) = qpt_positions_1d(k); + qpt_positions(face, rid, 0) = qpt_positions_1d(i); + qpt_positions(face, rid, 1) = 1.; + qpt_positions(face, rid, 2) = qpt_positions_1d(k); qpt_weights(rid) = qpt_weights_1d(i) * qpt_weights_1d(k); @@ -1229,30 +1255,30 @@ namespace elements // mu-minus has coords (xi, eta, -1) - side = 4; + 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(side, rid, 0) = qpt_positions_1d(i); - qpt_positions(side, rid, 1) = qpt_positions_1d(j); - qpt_positions(side, rid, 2) = -1.; + 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(rid) = qpt_weights_1d(i) * qpt_weights_1d(j); }); // end for // mu-plus has coords (xi, eta, +1) - side = 5; + 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(side, rid, 0) = qpt_positions_1d(i); - qpt_positions(side, rid, 1) = qpt_positions_1d(j); - qpt_positions(side, rid, 2) = 1.; + 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(rid) = qpt_weights_1d(i) * qpt_weights_1d(j); @@ -1262,39 +1288,39 @@ namespace elements // surface of 2D element (line) else if (elem_dims==2){ - size_t side; + size_t face; // xi-minus has coords (-1, eta) - side = 0; + face = 0; FOR_ALL_CLASS(j, 0, num_qpts_1d, { const size_t rid=j; - qpt_positions(side, rid, 0) = -1.; - qpt_positions(side, rid, 1) = qpt_positions_1d(j); + qpt_positions(face, rid, 0) = -1.; + qpt_positions(face, rid, 1) = qpt_positions_1d(j); qpt_weights(rid) = qpt_weights_1d(j); }); // end for // xi-plus has coords (+1, eta) - side = 1; + face = 1; FOR_ALL_CLASS(j, 0, num_qpts_1d, { const size_t rid=j; - qpt_positions(side, rid, 0) = 1.; - qpt_positions(side, rid, 1) = qpt_positions_1d(j); + qpt_positions(face, rid, 0) = 1.; + qpt_positions(face, rid, 1) = qpt_positions_1d(j); qpt_weights(rid) = qpt_weights_1d(j); }); // end for // eta-minus has coords (xi, -1) - side = 2; + face = 2; FOR_ALL_CLASS(i, 0, num_qpts_1d, { const size_t rid=i; - qpt_positions(side, rid, 0) = qpt_positions_1d(i); - qpt_positions(side, rid, 1) = -1.; + qpt_positions(face, rid, 0) = qpt_positions_1d(i); + qpt_positions(face, rid, 1) = -1.; qpt_weights(rid) = qpt_weights_1d(i); @@ -1302,12 +1328,12 @@ namespace elements // eta-plus has coords (xi,+1) - side = 3; + face = 3; FOR_ALL_CLASS(i, 0, num_qpts_1d, { const size_t rid=i; - qpt_positions(side, rid, 0) = qpt_positions_1d(i); - qpt_positions(side, rid, 1) = 1.; + qpt_positions(face, rid, 0) = qpt_positions_1d(i); + qpt_positions(face, rid, 1) = 1.; qpt_weights(rid) = qpt_weights_1d(i); @@ -1316,13 +1342,13 @@ namespace elements } // surface of 1D element is a point else if (elem_dims==1) { - size_t side = 0; + size_t face = 0; const size_t rid = 0; - qpt_positions(side, rid, 0) = -1.; + qpt_positions(face, rid, 0) = -1.; qpt_weights(rid) = 1.0; - side = 1; - qpt_positions(side, rid, 0) = 1.; + face = 1; + qpt_positions(face, rid, 0) = 1.; qpt_weights(rid) = 1.0; } else{ @@ -1340,8 +1366,8 @@ namespace elements size_t elem_dims = 0; // Basis evaluation at quadrature points on surface - CArrayKokkos qpt_basis; // access as (sides, surf_qpts, dofs) - CArrayKokkos qpt_grad_basis; // access as (sides, surf_qpts, dofs, dims) + CArrayKokkos qpt_basis; // access as (faces, surf_qpts, dofs) + CArrayKokkos qpt_grad_basis; // access as (faces, surf_qpts, dofs, dims) CArrayKokkos outward_sign; @@ -1366,12 +1392,12 @@ namespace elements // the sign for outward normal relative to the reference element outward_sign = CArrayKokkos(num_ref_surfs, "surf_outward_sign"); - //side 0 (xi=-1): sign = -1 - //side 1 (xi=+1): sign = +1 - //side 2 (eta=-1): sign = -1 - //side 3 (eta=+1): sign = +1 - //side 4 (mu=-1): sign = -1 - //side 5 (mu=+1): sign = +1 + //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 outward_sign(0) = -1.; outward_sign(1) = 1.; if(elem_dims>1){ @@ -1385,25 +1411,25 @@ namespace elements // get the basis and grad basis functions for each surfaces of the element - for(size_t side=0; side (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); - ViewCArrayKokkos side_qpt_basis (&qpt_basis(side,0,0), num_qpts_in_surf, num_dofs_in_elem); - ViewCArrayKokkos side_qpt_grad_basis(&qpt_grad_basis(side,0,0,0), num_qpts_in_surf, num_dofs_in_elem, elem_dims); - ViewCArrayKokkos side_qpt_positions (&SurfQuadrature.qpt_positions(side,0,0), num_qpts_in_surf, elem_dims); + ViewCArrayKokkos face_qpt_basis (&qpt_basis(face,0,0), num_qpts_in_surf, num_dofs_in_elem); + ViewCArrayKokkos face_qpt_grad_basis(&qpt_grad_basis(face,0,0,0), num_qpts_in_surf, num_dofs_in_elem, elem_dims); + ViewCArrayKokkos face_qpt_positions (&SurfQuadrature.qpt_positions(face,0,0), num_qpts_in_surf, elem_dims); - get_basis_and_grad_basis(side_qpt_basis, - side_qpt_grad_basis, - side_qpt_positions, + get_basis_and_grad_basis(face_qpt_basis, + face_qpt_grad_basis, + face_qpt_positions, ReferenceElement.dof_positions_1d); - // the side qpt_basis were saved to member arrays using the views above here + // the face qpt_basis were saved to member arrays using the views above here - } // end for sides + } // end for faces } // end member function diff --git a/src/swage/indexing_utils.h b/src/swage/indexing_utils.h new file mode 100644 index 00000000..c47a996a --- /dev/null +++ b/src/swage/indexing_utils.h @@ -0,0 +1,712 @@ +/********************************************************************************************** +� 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. +**********************************************************************************************/ +#ifndef INDEXING_UTILS_H +#define INDEXING_UTILS_H + +#include "matar.h" + +using namespace mtr; + +/* + ========================== + Nodal indexing convention + ========================== + + 3D: + + 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 + + + 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; + }; +}; + + +/// if material points are defined at element interfaces +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; + }; +}; + +/// if material points are defined at element interfaces +struct patches_in_surf_t +{ + private: + size_t num_surfs_; + public: + patches_in_surf_t() { + }; + + patches_in_surf_t(const size_t num_surfs_inp) { + this->num_surfs_ = num_surfs_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_surfs_ + 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_surfs_ + 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(DCArrayKokkos& 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 + + +} // 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 41ce28c5..17049d12 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,302 +44,138 @@ using namespace mtr; namespace mesh_init { -// element mesh types -enum elem_name_tag -{ - linear_tensor_element = 1, // single quadrature point element - arbitrary_tensor_element = 2 // fully integrated arbitrary-order element -}; + // 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; - }; -}; - -/// if material points are defined at element interfaces -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; - }; -}; - -// 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 +//////////////////////////////////////////////////////////////////////////////////// +/// +/// \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 or Quadralateral 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 - corners_in_elem_t corners_in_elem; + 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 ---- // @@ -346,26 +183,43 @@ struct Mesh_t 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 - // initialization methods + + ///////////////////////////////////////////////////////////////////////////// + /// + /// \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) { @@ -376,7 +230,16 @@ struct Mesh_t }; // end method - // initialization methods + ///////////////////////////////////////////////////////////////////////////// + /// + /// \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) { @@ -398,6 +261,8 @@ struct Mesh_t 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"); @@ -409,7 +274,17 @@ struct Mesh_t return; }; // end method - // initialization method for an FE mesh + ///////////////////////////////////////////////////////////////////////////// + /// + /// \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 elem_Pn_order, @@ -433,7 +308,7 @@ struct Mesh_t Kokkos::abort("Error: num_elems must be greater than 0. Exiting at initialize_elems_Pn()."); } - elem_kind = mesh_init::arbitrary_tensor_element; + elem_kind = mesh_init::arbitraryTensorElement; // --- Derived sizes --- @@ -444,6 +319,7 @@ struct Mesh_t 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"); @@ -457,18 +333,92 @@ struct Mesh_t }; // 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 + + } // end build zones - return; - }; // end method - // 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) { @@ -519,10 +469,6 @@ struct Mesh_t 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 @@ -531,7 +477,17 @@ struct Mesh_t 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) { @@ -615,801 +571,417 @@ struct Mesh_t 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) + ///////////////////////////////////////////////////////////////////////////// + /// + /// \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() { - // num_lobatto_in_patch = int(pow(3, num_dims-1)); + //const bool mk_sides = false; // not built at this time to save memory, their need is unknown - // num_gauss_in_patch = 2*(num_dims-1); + 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); - size_t num_patches_in_surf; // = Pn_order or = Pn_order*Pn_order + // ----------------------------------------------------------------------- + // 1a. Get element face nodes and sort (small...biggest index) for building connectivity + // ----------------------------------------------------------------------- - size_t num_1D = Pn + 1; // number of nodes in 1D + // get the elem nodes on the surface + DCArrayKokkos surf_node_ordering_in_elem(num_surfs_in_elem, num_nodes_in_surf); + 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); + + 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 + }); - // num quad points 1D // - // size_t num_lob_1D = 2*Pn + 1; - // size_t num_1D = 2*Pn; + 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 - DCArrayKokkos node_ordering_in_elem; // dimensions will be (num_patches_in_elem, num_nodes_in_patch); + // remember that sorted_face_nodes are in order of smallest to largest now + }); - // DCArrayKokkos lobatto_ordering_in_elem; // dimensions will be (num_patches_in_elem, num_lobatto_in_patch); + DCArrayKokkos surf_counter(1); + surf_counter.set_values(0); + DCArrayKokkos bdy_surf_counter(1); + bdy_surf_counter.set_values(0); - // DCArrayKokkos gauss_ordering_in_elem; // dimensions will be (num_patches_in_elem, num_gauss_in_patch); + CArrayKokkos face_elems_in_elem(num_elems, num_surfs_in_elem); + face_elems_in_elem.set_values(-1); - if (verbose) printf("Number of dimensions = %zu \n", num_dims); + surfs_in_elem = CArrayKokkos(num_elems, num_surfs_in_elem); + //if(mk_sides) sides_in_elem = CArrayKokkos(num_elems*num_surfs_in_elem); - if (num_dims == 3) { - // num_patches_in_surf = [1^2, 2^2, 3^2, 4^2, ... , Pn^2] + // helper variables for temporary storage, its sized larger than num_surfs + CArrayKokkos elems_in_surf_helper(num_elems*num_surfs_in_elem,2); + CArrayKokkos faces_in_surf_helper(num_elems*num_surfs_in_elem,2); + CArrayKokkos num_elems_in_surf_helper(num_elems*num_surfs_in_elem); + //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); - num_patches_in_surf = Pn * Pn; + // ----------------------------------------------------------------------- + // 1b. Build Surfaces + // ----------------------------------------------------------------------- - num_patches_in_elem = num_patches_in_surf * num_surfs_in_elem; + for (size_t elem_gid = 0; elem_gid < num_elems; elem_gid++) { + + FOR_ALL_CLASS(face_lid, 0, 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"); + // only search for a matching surface if it wasn't already found + if(face_elems_in_elem(elem_gid,face_lid)<0){ - // lobatto_ordering_in_elem = DCArrayKokkos (num_patches_in_elem, num_lobatto_in_patch); + bool found_nbr_surf = false; - // gauss_ordering_in_elem = DCArrayKokkos (num_patches_in_elem, num_gauss_in_patch); + // 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(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 + 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; // negative because elem does not exist + faces_in_surf_helper(surf_gid,0) = face_lid; + faces_in_surf_helper(surf_gid,1) = -face_lid; // 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); + - // 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) { + // ----------------------------------------------------------------------- + // 1c. Finish populating values in surface data structures + // ----------------------------------------------------------------------- - 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 + nodes_in_surf = CArrayKokkos(num_surfs,num_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) + 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) + 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; } - 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 - - - j=0,jmax - - (i,,k+1) o--o (i+1,,k+1) - | | - (i,,k) o--o (i+1,,k) - - */ - 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 - - // printf("j-minus\n"); - - 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++; - - // 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++; - - // 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 + 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 - - // printf("j-plus\n"); + for(size_t node_lid=0; node_lidi - - - k=0,kmax + // ----------------------------------------------------------------------- + // 1b. Save boundary surfaces and build boundary nodes + // ----------------------------------------------------------------------- + bdy_surfs = CArrayKokkos (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); + }); - (i,j+1) o--o (i+1,j+1) - / / - (i,j) o--o (i+1,j) + 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); - */ + 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); - k_patch = 0; - 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++; + DCArrayKokkos bdy_node_counter(1, "mesh.bdy_node_counter"); + CArrayKokkos bdy_node_helper(num_bdy_surfs*num_nodes_in_surf, "mesh.bdy_node_helper"); - // node_lid 1 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++; + FOR_ALL_CLASS(bdy_surf_gid, 0, num_bdy_surfs,{ - // 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++; + const size_t surf_gid = bdy_surfs(bdy_surf_gid); - // 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; - } - } // i - } // j - } // k - }); // end FOR_ALL elem_gid - } // end if arbitrary-order element - else { - printf("\nERROR: mesh type is not known \n"); - } // end if + const size_t elem_gid = elems_in_surf(surf_gid,0); + const size_t face_lid = faces_in_surf(surf_gid,0); - // update the device - node_ordering_in_elem.update_device(); + 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 - - // for saving the adjacent patch_lid, which is the slide_lid - // CArrayKokkos neighboring_side_lids (num_elems, num_patches_in_elem); - - // allocate memory for the patches in the elem - patches_in_elem = CArrayKokkos(num_elems, num_patches_in_elem, "mesh.patches_in_elem"); - - // 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"); - - // step 1) calculate the hash values for each patch in the element - FOR_ALL_CLASS(elem_gid, 0, num_elems, { - for (size_t patch_lid = 0; patch_lid < num_patches_in_elem; patch_lid++) { - size_t sorted_patch_nodes[4]; // note: cannot be allocated with num_nodes_in_patch - - // first save the patch nodes - 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 - sorted_patch_nodes[patch_node_lid] = nodes_in_elem(elem_gid, node_lid); - } // end for node_lid - - // sort nodes from smallest to largest - bubble_sort(sorted_patch_nodes, num_nodes_in_patch); + FOR_ALL_CLASS(bdy_surf_gid, 0, num_bdy_surfs,{ + + const size_t surf_gid = bdy_surfs(bdy_surf_gid); - // 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 + const size_t elem_gid = elems_in_surf(surf_gid,0); + const size_t face_lid = faces_in_surf(surf_gid,0); - DCArrayKokkos num_values(2, "num_values"); + for(size_t node_lid=0; node_lid= 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 - - // 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 - - // neighboring_side_lids(elem_gid, patch_lid) = patch_lid; + // when the storage_bin==0, it is the first surface to have this node + if (bdy_surf_node_storage_bin(bdy_surf_gid,node_lid)==0){ + const size_t bdy_node_gid = Kokkos::atomic_fetch_add(&bdy_node_counter(0), 1); + const size_t node_gid = face_hash_keys(elem_gid,face_lid,node_lid); // sorted nodes on elem face small...big + bdy_node_helper(bdy_node_gid) = node_gid; + } - patches_in_elem(elem_gid, patch_lid) = patch_gid; - temp_bdy_patches(bdy_patch_gid) = patch_gid; - - patch_gid++; - bdy_patch_gid++; - } // end if - } // end for over patch_lid - } // end for over elem_gid - - // the num_values is because the values passed in are const, so a const pointer is needed - num_values(0) = patch_gid; // num_patches = patch_gid; - num_values(1) = bdy_patch_gid; // num_bdy_patches = bdy_patch_gid; - }); // end RUN - Kokkos::fence(); - - num_values.update_host(); + } // end for node_lid + }); // end parallel for Kokkos::fence(); + bdy_node_counter.update_host(); - num_patches = num_values.host(0); - // this lines assumes num_surfs == num_patches, only valid for 1st order elements - num_surfs = num_values.host(0); - num_bdy_patches = num_values.host(1); - - // size_t mesh_1D = 60; - // size_t exact_num_patches = (mesh_1D*mesh_1D)*(mesh_1D+1)*3; - // size_t exact_num_bdy_patches = (mesh_1D*mesh_1D)*6; - // printf("num_patches = %lu, exact = %lu \n", num_patches, exact_num_patches); - // printf("num_bdy_patches = %lu exact = %lu \n", num_bdy_patches, exact_num_bdy_patches); + num_bdy_nodes = bdy_node_counter.host(0); - // printf("Num patches = %lu \n", num_patches); - // printf("Num boundary patches = %lu \n", num_bdy_patches); + CArrayKokkos bdy_nodes(num_bdy_nodes, "mesh.bdy_nodes"); - 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"); - - // 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); + // ----------------------------------------------------------------------- + // 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; + + patches_in_surf = patches_in_surf_t(num_surfs); + + 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"); + patches_in_surf = patches_in_surf_t(num_surfs); + surf_in_patch = CArrayKokkos(num_patches, "mesh.surf_in_patch"); + patches_in_elem = CArrayKokkos(num_elems, num_patches_in_elem,"patches_in_elem"); + - // allocate memory for surface data structures - num_surfs = num_patches / num_patches_in_surf; + 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 - 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"); +printf("here0 num_surfs = %zu\n", num_surfs); +printf("here0 num_surfs_in_elem = %zu\n", num_surfs_in_elem); +printf("here0 num_patches_in_surf = %zu\n", num_patches_in_surf); +printf("here0 num_nodes_in_patch = %zu\n", num_nodes_in_patch); - 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; - // save the patch_gids - patches_in_surf(surf_gid, patch_lid) = patch_gid; + // now break up the surface into patches + FOR_ALL_CLASS(surf_gid, 0, num_surfs,{ - // save the surface this patch belongs to - surf_in_patch(patch_gid) = surf_gid; - } // end for +printf("here0 surf_gid = %d\n", surf_gid); + const size_t elem_gid = elems_in_surf(surf_gid,0); + const size_t face_lid = faces_in_surf(surf_gid,0); - // get first patch in the surface, and populate elem surface structures - size_t this_patch_gid = surf_gid * num_patches_in_surf; + 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) - 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 +printf("here0 elem_gid = %zu, face_lid = %zu, nbr_elem = %d, nbr_face = %d\n", + elem_gid, face_lid, nbr_elem_gid, nbr_face_lid); +printf("here0 looping patches\n"); - // 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; + // loop patches on this surface + for(size_t patch_lid =0; patch_lid surf_node_ordering_in_elem; - - 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++; - } - } +printf("here0 num_elems_in_surf = %zu \n", num_elems_in_surf(surf_gid)); + 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++; - } - } +printf("here0 saving nodes_in_patch\n"); + // 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); +printf("here0 done building patches\n"); - // 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); - 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() { @@ -1536,13 +1108,15 @@ struct Mesh_t 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() @@ -1556,21 +1130,22 @@ struct Mesh_t 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()."); @@ -1586,7 +1161,7 @@ struct Mesh_t // 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 }; // end Mesh_t From c00ab2fe87258e95ebcddd1220251a183cc0aa5a Mon Sep 17 00:00:00 2001 From: Nathaniel Morgan Date: Wed, 29 Jul 2026 13:01:07 -0600 Subject: [PATCH 30/59] fixed patches_in_surf index bug and fixed mesh ref_mesh_test.cpp --- .../src/ref_plus_mesh_test.cpp | 54 +++++-------------- src/swage/indexing_utils.h | 14 ++--- src/swage/unstructured_mesh.h | 35 +++++------- 3 files changed, 34 insertions(+), 69 deletions(-) diff --git a/examples/reference_element/src/ref_plus_mesh_test.cpp b/examples/reference_element/src/ref_plus_mesh_test.cpp index c3700ebd..2fe317ab 100644 --- a/examples/reference_element/src/ref_plus_mesh_test.cpp +++ b/examples/reference_element/src/ref_plus_mesh_test.cpp @@ -836,7 +836,7 @@ MATAR_INITIALIZE(argc, argv); Mesh_t Mesh; // unstructured mesh const size_t elem_dims = 3; - const size_t elem_order = 3; // cubic element + const size_t elem_order = 3; // cubic element will have 4 nodes in 1D const size_t num_elems_1D = 2; // the minimum quadrature for FE hydrodynamics based on elem order @@ -883,30 +883,6 @@ MATAR_INITIALIZE(argc, argv); const double h = 1.0/((double)num_nodes_1D); // create indexing for a Pn order mesh - /* - 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; - - - for(size_t kc=k; kc<=k+elem_order; kc++) - for(size_t jc=j; jc<=j+elem_order; jc++) - for(size_t ic=i; ic<=i+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++; - - node_coords(node_gid,0) = (double)ic*h; - node_coords(node_gid,1) = (double)jc*h; - node_coords(node_gid,2) = (double)kc*h; - } // end for - - }); // end parallel for - */ // Step 1: Initialize ALL node coordinates once (no race condition) FOR_ALL(kc, 0, num_nodes_1D, @@ -920,7 +896,7 @@ MATAR_INITIALIZE(argc, argv); node_coords(node_gid, 2) = (double)kc * h; }); - // Step 2: Build element connectivity (no coordinate writes) + // Step 2: Build element connectivity FOR_ALL(i, 0, num_elems_1D, j, 0, num_elems_1D, k, 0, num_elems_1D, { @@ -928,9 +904,10 @@ MATAR_INITIALIZE(argc, argv); size_t elem_gid = i + (j + k*num_elems_1D)*num_elems_1D; size_t node_lid = 0; - for(size_t kc=k; kc<=k+elem_order; kc++) - for(size_t jc=j; jc<=j+elem_order; jc++) - for(size_t ic=i; ic<=i+elem_order; ic++){ + // 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++; @@ -950,23 +927,20 @@ MATAR_INITIALIZE(argc, argv); Kokkos::abort("ERROR: wrong number of Gauss points in elem"); } - /* + // check surfaces FOR_ALL(elem_gid, 0, Mesh.num_elems, { - - printf("Num patches in elem = %zu \n", Mesh.num_patches_in_elem); - for(size_t patch_lid=0; patch_lidnum_surfs_ = num_surfs_inp; + 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_surfs_ + patch_lid; + 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_surfs_ + patch_lid; + return surf_gid * num_patches_in_surf_ + patch_lid; }; }; diff --git a/src/swage/unstructured_mesh.h b/src/swage/unstructured_mesh.h index 17049d12..49da95cf 100644 --- a/src/swage/unstructured_mesh.h +++ b/src/swage/unstructured_mesh.h @@ -645,8 +645,10 @@ struct Mesh_t for (size_t elem_gid = 0; elem_gid < num_elems; elem_gid++) { + FOR_ALL_CLASS(face_lid, 0, num_surfs_in_elem, { + // only search for a matching surface if it wasn't already found if(face_elems_in_elem(elem_gid,face_lid)<0){ @@ -713,9 +715,9 @@ struct Mesh_t // --- 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; // negative because elem does not exist + 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; // negative because elem does not exist + 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 @@ -763,9 +765,9 @@ struct Mesh_t 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) + 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) + 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){ @@ -863,12 +865,12 @@ struct Mesh_t 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; - patches_in_surf = patches_in_surf_t(num_surfs); + patches_in_surf = patches_in_surf_t(num_patches_in_surf); // initializing 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"); - patches_in_surf = patches_in_surf_t(num_surfs); + surf_in_patch = CArrayKokkos(num_patches, "mesh.surf_in_patch"); patches_in_elem = CArrayKokkos(num_elems, num_patches_in_elem,"patches_in_elem"); @@ -876,55 +878,44 @@ struct Mesh_t 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 -printf("here0 num_surfs = %zu\n", num_surfs); -printf("here0 num_surfs_in_elem = %zu\n", num_surfs_in_elem); -printf("here0 num_patches_in_surf = %zu\n", num_patches_in_surf); -printf("here0 num_nodes_in_patch = %zu\n", num_nodes_in_patch); - // now break up the surface into patches FOR_ALL_CLASS(surf_gid, 0, num_surfs,{ -printf("here0 surf_gid = %d\n", surf_gid); const size_t elem_gid = elems_in_surf(surf_gid,0); const size_t face_lid = faces_in_surf(surf_gid,0); 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) -printf("here0 elem_gid = %zu, face_lid = %zu, nbr_elem = %d, nbr_face = %d\n", - elem_gid, face_lid, nbr_elem_gid, nbr_face_lid); -printf("here0 looping patches\n"); - // loop patches on this surface for(size_t patch_lid =0; patch_lid Date: Wed, 29 Jul 2026 13:28:16 -0600 Subject: [PATCH 31/59] Surface pair test added and coding passes it --- .../src/ref_plus_mesh_test.cpp | 64 ++++++++++++++++--- 1 file changed, 55 insertions(+), 9 deletions(-) diff --git a/examples/reference_element/src/ref_plus_mesh_test.cpp b/examples/reference_element/src/ref_plus_mesh_test.cpp index 2fe317ab..94f66dd4 100644 --- a/examples/reference_element/src/ref_plus_mesh_test.cpp +++ b/examples/reference_element/src/ref_plus_mesh_test.cpp @@ -927,19 +927,65 @@ MATAR_INITIALIZE(argc, argv); Kokkos::abort("ERROR: wrong number of Gauss points in elem"); } + + printf("\n=== TEST: 2x2x2 Cubic Mesh Face Pair Test ===\n"); - // check surfaces - FOR_ALL(elem_gid, 0, Mesh.num_elems, { - - for(size_t patch_lid=0; patch_lid 5) { + 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 }); + + printf(" Face matching test PASSED!\n\n"); + + /* FOR_ALL(surf_gid, 0, Mesh.num_surfs, { From 206769c2d3c37f1e9ce14fc0999d974e2bed131f Mon Sep 17 00:00:00 2001 From: Nathaniel Morgan Date: Wed, 29 Jul 2026 13:34:34 -0600 Subject: [PATCH 32/59] Surface pair test added and coding passes it --- .../src/ref_plus_mesh_test.cpp | 26 +++++++------------ 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/examples/reference_element/src/ref_plus_mesh_test.cpp b/examples/reference_element/src/ref_plus_mesh_test.cpp index 94f66dd4..69071b8e 100644 --- a/examples/reference_element/src/ref_plus_mesh_test.cpp +++ b/examples/reference_element/src/ref_plus_mesh_test.cpp @@ -986,28 +986,20 @@ MATAR_INITIALIZE(argc, argv); printf(" Face matching test PASSED!\n\n"); - /* + FOR_ALL(surf_gid, 0, Mesh.num_surfs, { for(size_t side=0; side<1; side++){ - const size_t elem_gid = Mesh.elems_in_surf(surf_gid,side); - const size_t side_lid = Mesh.elem_sides_in_surf(surf_gid,side); - - //const size_t patch_lid = side_lid * Mesh.num_patches_in_surf; - //const size_t patch_gid = Mesh.patches_in_elem(elem_gid, patch_lid); // first patch on this surface - //printf("Patch_gid in elem on this side = %zu\n", patch_gid); - - - // verify reverse map in element, it must have surf gid using side_lid - printf("surf = %d on side = %zu has elem_gid = %zu, this surf is on elem_side = %zu, but the elem has the surf = %zu \n", - surf_gid, side, elem_gid, side_lid, Mesh.surfs_in_elem(elem_gid,side_lid)); - - - if(Mesh.surfs_in_elem(elem_gid,side_lid)!=surf_gid) Kokkos::abort("failed to match surf_gid using surf_lid \n"); - } // looping over the 2 sides of the surface () + const int elem_gid = Mesh.elems_in_surf(surf_gid,side); + const int face_lid = Mesh.faces_in_surf(surf_gid,side); + + // verifying the reverse map + if(Mesh.num_elems_in_surf(surf_gid)==2) + if(Mesh.surfs_in_elem(elem_gid,face_lid)!=surf_gid) Kokkos::abort("failed to match surf_gid using surf_lid \n"); + } // looping over the 2 sides of the surface }); - */ + // ========================================== // Create state on the unstructured mesh structure From 948c3a58956b9287c4c6431e975a8364f3d18f21 Mon Sep 17 00:00:00 2001 From: Nathaniel Morgan Date: Wed, 29 Jul 2026 13:58:27 -0600 Subject: [PATCH 33/59] added more surface checks --- .../src/ref_plus_mesh_test.cpp | 69 ++++++++++++++++--- 1 file changed, 58 insertions(+), 11 deletions(-) diff --git a/examples/reference_element/src/ref_plus_mesh_test.cpp b/examples/reference_element/src/ref_plus_mesh_test.cpp index 69071b8e..477e9c1c 100644 --- a/examples/reference_element/src/ref_plus_mesh_test.cpp +++ b/examples/reference_element/src/ref_plus_mesh_test.cpp @@ -983,22 +983,69 @@ MATAR_INITIALIZE(argc, argv); } // end face loop }); - printf(" Face matching test PASSED!\n\n"); + 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, { - for(size_t side=0; side<1; side++){ - const int elem_gid = Mesh.elems_in_surf(surf_gid,side); - const int face_lid = Mesh.faces_in_surf(surf_gid,side); - - // verifying the reverse map - if(Mesh.num_elems_in_surf(surf_gid)==2) - if(Mesh.surfs_in_elem(elem_gid,face_lid)!=surf_gid) Kokkos::abort("failed to match surf_gid using surf_lid \n"); - } // looping over the 2 sides of the surface - }); - + const size_t num_elems_in_surf = Mesh.num_elems_in_surf(surf_gid); + if(num_elems_in_surf==1) bdy_surf_counter(0)++; + + // 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"); // ========================================== From 696a1bbfaf1c2bff3a990ed65617c6f650d1c628 Mon Sep 17 00:00:00 2001 From: Nathaniel Morgan Date: Wed, 29 Jul 2026 15:09:06 -0600 Subject: [PATCH 34/59] fixed thread bug with test and fixed ref_elem surf quad bug --- .../src/ref_plus_mesh_test.cpp | 14 ++++++---- src/elements/ref_elem.h | 28 ++++++++++--------- 2 files changed, 24 insertions(+), 18 deletions(-) diff --git a/examples/reference_element/src/ref_plus_mesh_test.cpp b/examples/reference_element/src/ref_plus_mesh_test.cpp index 477e9c1c..466225b6 100644 --- a/examples/reference_element/src/ref_plus_mesh_test.cpp +++ b/examples/reference_element/src/ref_plus_mesh_test.cpp @@ -557,9 +557,9 @@ void test_manufactured_solution() { DCArrayKokkos jac(3, 3); 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_in_elem_dual.host(n) = n; } } node_coords_dual.update_device(); @@ -569,7 +569,8 @@ void test_manufactured_solution() { Quadrature_t Quad; ReferenceElement_t RefElem; - Quad.initialize_quadrature(reference_space::GaussLegendre, 3, 3); + 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, @@ -579,7 +580,8 @@ void test_manufactured_solution() { SurfaceQuadrature_t SurfQuad; ReferenceSurface_t RefSurf; - SurfQuad.initialize_quadrature(reference_space::GaussLegendre, 3, 3); + SurfQuad.initialize_quadrature(reference_space::GaussLegendre, 3, 3); //(type,num_qpts_1d,elem_dims) + RefSurf.initialize_ref_surf(SurfQuad, RefElem); @@ -994,7 +996,7 @@ MATAR_INITIALIZE(argc, argv); 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) bdy_surf_counter(0)++; + 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++) { @@ -1033,7 +1035,9 @@ MATAR_INITIALIZE(argc, argv); 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_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"); diff --git a/src/elements/ref_elem.h b/src/elements/ref_elem.h index c5f52798..43ea043d 100644 --- a/src/elements/ref_elem.h +++ b/src/elements/ref_elem.h @@ -1156,8 +1156,10 @@ namespace elements num_qpts_in_surf = 1; num_ref_surfs = 0; + for(size_t dim=0; dim Date: Wed, 29 Jul 2026 15:29:32 -0600 Subject: [PATCH 35/59] added const to global vars in tests for compiling on GPUs --- examples/reference_element/src/gradient_test.cpp | 6 +++--- examples/reference_element/src/integration_test.cpp | 6 +++--- examples/reference_element/src/interpolation_test.cpp | 6 +++--- examples/reference_element/src/kronecker_delta_test.cpp | 4 ++-- examples/reference_element/src/partition_unity_test.cpp | 6 +++--- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/examples/reference_element/src/gradient_test.cpp b/examples/reference_element/src/gradient_test.cpp index 37b59ba8..14d803d4 100644 --- a/examples/reference_element/src/gradient_test.cpp +++ b/examples/reference_element/src/gradient_test.cpp @@ -44,9 +44,9 @@ using namespace mtr; using namespace swage; // unstructured mesh and hash using namespace elements; -bool Verbose = false; -size_t max_num = 19; // max number of quadrature points to test up to, the limit is 19 -size_t max_order = 19; // max polynomial order to test, limit is 19th-order with Legendre +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 diff --git a/examples/reference_element/src/integration_test.cpp b/examples/reference_element/src/integration_test.cpp index 0ebd5824..26249f5e 100644 --- a/examples/reference_element/src/integration_test.cpp +++ b/examples/reference_element/src/integration_test.cpp @@ -44,9 +44,9 @@ using namespace mtr; using namespace swage; // unstructured mesh and hash using namespace elements; -bool Verbose = false; -size_t max_num = 19; // max number of quadrature points to test up to, the limit is 19 -size_t max_order = 19; // max polynomial order to test, limit is 19th-order with Legendre +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 diff --git a/examples/reference_element/src/interpolation_test.cpp b/examples/reference_element/src/interpolation_test.cpp index 5e6766c7..9025cbac 100644 --- a/examples/reference_element/src/interpolation_test.cpp +++ b/examples/reference_element/src/interpolation_test.cpp @@ -44,9 +44,9 @@ using namespace mtr; using namespace swage; // unstructured mesh and hash using namespace elements; -bool Verbose = false; -size_t max_num = 19; // max number of quadrature points to test up to, the limit is 19 -size_t max_order = 19; // max polynomial order to test, limit is 19th-order with Legendre +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 diff --git a/examples/reference_element/src/kronecker_delta_test.cpp b/examples/reference_element/src/kronecker_delta_test.cpp index cb684945..eeb57162 100644 --- a/examples/reference_element/src/kronecker_delta_test.cpp +++ b/examples/reference_element/src/kronecker_delta_test.cpp @@ -44,8 +44,8 @@ using namespace mtr; using namespace swage; // unstructured mesh and hash using namespace elements; -bool Verbose = false; -size_t max_num = 19; // max number of quadrature points to test up to, the limit is 19 +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) { diff --git a/examples/reference_element/src/partition_unity_test.cpp b/examples/reference_element/src/partition_unity_test.cpp index d4134625..1175d2c9 100644 --- a/examples/reference_element/src/partition_unity_test.cpp +++ b/examples/reference_element/src/partition_unity_test.cpp @@ -44,9 +44,9 @@ using namespace mtr; using namespace swage; // unstructured mesh and point cloud using namespace elements; -bool Verbose = false; -size_t max_num = 19; // max number of quadrature points to test up to, the limit is 19 -size_t max_order = 19; // max polynomial order to test, limit is 19th-order with Legendre +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, From d9e0ff6cfaf972398d2109456721a3f25aaee4e5 Mon Sep 17 00:00:00 2001 From: Nathaniel Morgan Date: Mon, 3 Aug 2026 11:50:55 -0600 Subject: [PATCH 36/59] fixed GPU bugs --- .../src/ref_plus_mesh_test.cpp | 1 + src/elements/ref_elem.h | 59 ++++++++++++------- src/swage/indexing_utils.h | 5 +- src/swage/unstructured_mesh.h | 13 +++- 4 files changed, 53 insertions(+), 25 deletions(-) diff --git a/examples/reference_element/src/ref_plus_mesh_test.cpp b/examples/reference_element/src/ref_plus_mesh_test.cpp index 466225b6..ccbe178a 100644 --- a/examples/reference_element/src/ref_plus_mesh_test.cpp +++ b/examples/reference_element/src/ref_plus_mesh_test.cpp @@ -957,6 +957,7 @@ MATAR_INITIALIZE(argc, argv); // 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"); } diff --git a/src/elements/ref_elem.h b/src/elements/ref_elem.h index 43ea043d..05eb4101 100644 --- a/src/elements/ref_elem.h +++ b/src/elements/ref_elem.h @@ -1400,39 +1400,58 @@ namespace elements //face 3 (eta=+1): sign = +1 //face 4 (mu=-1): sign = -1 //face 5 (mu=+1): sign = +1 - outward_sign(0) = -1.; - outward_sign(1) = 1.; - if(elem_dims>1){ - outward_sign(2) = -1.; - outward_sign(3) = 1.; - } - if(elem_dims>2){ - outward_sign(4) = -1.; - outward_sign(5) = 1.; - } + RUN({ + outward_sign(0) = -1.; + outward_sign(1) = 1.; + if(elem_dims>1){ + outward_sign(2) = -1.; + outward_sign(3) = 1.; + } + if(elem_dims>2){ + outward_sign(4) = -1.; + outward_sign(5) = 1.; + } + }); // end serial RUN on GPU + // 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); + + // 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); // get the basis and grad basis functions for each surfaces of the element for(size_t face=0; face (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); - ViewCArrayKokkos face_qpt_basis (&qpt_basis(face,0,0), num_qpts_in_surf, num_dofs_in_elem); - ViewCArrayKokkos face_qpt_grad_basis(&qpt_grad_basis(face,0,0,0), num_qpts_in_surf, num_dofs_in_elem, elem_dims); - ViewCArrayKokkos face_qpt_positions (&SurfQuadrature.qpt_positions(face,0,0), num_qpts_in_surf, elem_dims); + // build basis and grad basis in the reference element + FOR_ALL(qpt, 0, num_qpts_in_surf, { + for(size_t dim=0; dim& surf_node_ordering_in_elem, +void get_surf_node_lids(CArrayKokkos& surf_node_ordering_in_elem, const size_t num_1D, const size_t num_dims){ @@ -342,7 +342,7 @@ void get_surf_node_lids(DCArrayKokkos& surf_node_ordering_in_elem, { Kokkos::abort("Bad Bad Bad: Mesh class is only supported in 2D and 3D \n"); } - surf_node_ordering_in_elem.update_device(); + Kokkos::fence(); return; @@ -702,6 +702,7 @@ void get_patch_node_lids(DCArrayKokkos &patch_node_ordering_in_elem, 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 diff --git a/src/swage/unstructured_mesh.h b/src/swage/unstructured_mesh.h index 49da95cf..7249b0d9 100644 --- a/src/swage/unstructured_mesh.h +++ b/src/swage/unstructured_mesh.h @@ -598,7 +598,7 @@ struct Mesh_t // ----------------------------------------------------------------------- // get the elem nodes on the surface - DCArrayKokkos surf_node_ordering_in_elem(num_surfs_in_elem, num_nodes_in_surf); + CArrayKokkos surf_node_ordering_in_elem(num_surfs_in_elem, num_nodes_in_surf); 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 @@ -609,9 +609,10 @@ struct Mesh_t 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 + 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(); FOR_ALL_CLASS(elem_gid, 0, num_elems, face_lid, 0, num_surfs_in_elem, { @@ -620,6 +621,7 @@ struct Mesh_t // remember that sorted_face_nodes are in order of smallest to largest now }); + Kokkos::fence(); DCArrayKokkos surf_counter(1); surf_counter.set_values(0); @@ -644,7 +646,7 @@ struct Mesh_t // ----------------------------------------------------------------------- for (size_t elem_gid = 0; elem_gid < num_elems; elem_gid++) { - + FOR_ALL_CLASS(face_lid, 0, num_surfs_in_elem, { @@ -662,6 +664,10 @@ struct Mesh_t // loop faces of nbr elem for(size_t nbr_face_lid=0; nbr_face_lid= 0) continue; + + // 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 Date: Mon, 3 Aug 2026 13:56:21 -0600 Subject: [PATCH 37/59] fixed GPU bug in ref_plus_mesh_test.cpp --- .../src/ref_plus_mesh_test.cpp | 47 ++++++++++++------- src/elements/ref_elem.h | 15 +++--- src/swage/unstructured_mesh.h | 8 ++++ 3 files changed, 45 insertions(+), 25 deletions(-) diff --git a/examples/reference_element/src/ref_plus_mesh_test.cpp b/examples/reference_element/src/ref_plus_mesh_test.cpp index ccbe178a..fb603a4e 100644 --- a/examples/reference_element/src/ref_plus_mesh_test.cpp +++ b/examples/reference_element/src/ref_plus_mesh_test.cpp @@ -663,23 +663,30 @@ void test_manufactured_solution() { + J_analytical[0][2]*(J_analytical[1][0]*J_analytical[2][1] - J_analytical[1][1]*J_analytical[2][0]); - double det_numerical = det_3x3(jac); - double det_error = fabs(det_analytical - det_numerical); - - printf("\n Determinant:\n"); - printf(" Analytical: %12.8f\n", det_analytical); - printf(" Numerical: %12.8f\n", det_numerical); - 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"); + DCArrayKokkos passed_dual(1); + RUN({ + double det_numerical = det_3x3(jac); + double det_error = fabs(det_analytical - det_numerical); + + printf("\n Determinant:\n"); + printf(" Analytical: %12.8f\n", det_analytical); + printf(" Numerical: %12.8f\n", det_numerical); + printf(" Error: %12.2e\n", det_error); + + const double tol = 1e-10; + bool passed = (max_error < tol) && (det_error < tol); + passed_dual(0) = passed; + + 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"); + + }); + passed_dual.update_host(); + + if(!passed_dual.host(0)) all_passed = false; - if(passed==false)Kokkos::abort("test failed \n"); - - if(!passed) all_passed = false; qpt_id += 13; } // end for qpt loop @@ -848,7 +855,7 @@ MATAR_INITIALIZE(argc, argv); // ============================================ // Create quadrature and reference element - std::cout<<"Building reference elements and quadrature \n"<1){ @@ -1427,11 +1427,12 @@ namespace elements for(size_t face=0; face Date: Mon, 3 Aug 2026 14:23:34 -0600 Subject: [PATCH 38/59] Fixed GPU bug in test --- .../src/ref_plus_mesh_test.cpp | 45 ++++++++++--------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/examples/reference_element/src/ref_plus_mesh_test.cpp b/examples/reference_element/src/ref_plus_mesh_test.cpp index fb603a4e..016adede 100644 --- a/examples/reference_element/src/ref_plus_mesh_test.cpp +++ b/examples/reference_element/src/ref_plus_mesh_test.cpp @@ -663,29 +663,28 @@ void test_manufactured_solution() { + J_analytical[0][2]*(J_analytical[1][0]*J_analytical[2][1] - J_analytical[1][1]*J_analytical[2][0]); - DCArrayKokkos passed_dual(1); + DCArrayKokkos det_numerical(1); RUN({ - double det_numerical = det_3x3(jac); - double det_error = fabs(det_analytical - det_numerical); - - printf("\n Determinant:\n"); - printf(" Analytical: %12.8f\n", det_analytical); - printf(" Numerical: %12.8f\n", det_numerical); - printf(" Error: %12.2e\n", det_error); - - const double tol = 1e-10; - bool passed = (max_error < tol) && (det_error < tol); - passed_dual(0) = passed; + det_numerical(0) = det_3x3(jac); + }); + det_numerical.update_host(); - printf("\n Max Error: %12.2e\n", max_error); - printf(" Result: %s\n", passed ? " PASSED" : "X FAILED"); + double det_error = fabs(det_analytical - det_numerical(0)); + + printf("\n Determinant:\n"); + printf(" Analytical: %12.8f\n", det_analytical); + printf(" Numerical: %12.8f\n", det_numerical(0)); + printf(" Error: %12.2e\n", det_error); + + const double tol = 1e-10; + bool passed = (max_error < tol) && (det_error < tol); - if(passed==false)Kokkos::abort("test failed \n"); + printf("\n Max Error: %12.2e\n", max_error); + printf(" Result: %s\n", passed ? " PASSED" : "X FAILED"); - }); - passed_dual.update_host(); + if(passed==false)Kokkos::abort("test failed \n"); - if(!passed_dual.host(0)) all_passed = false; + if(!passed) all_passed = false; qpt_id += 13; @@ -805,12 +804,16 @@ void test_manufactured_solution() { + J_analytical[0][2]*(J_analytical[1][0]*J_analytical[2][1] - J_analytical[1][1]*J_analytical[2][0]); - double det_numerical = det_3x3(jac); - double det_error = fabs(det_analytical - det_numerical); + DCArrayKokkos det_numerical(1); + RUN({ + det_numerical(0) = det_3x3(jac); + }); + det_numerical.update_host(); + double det_error = fabs(det_analytical - det_numerical(0)); printf("\n Determinant:\n"); printf(" Analytical: %12.8f\n", det_analytical); - printf(" Numerical: %12.8f\n", det_numerical); + printf(" Numerical: %12.8f\n", det_numerical(0)); printf(" Error: %12.2e\n", det_error); const double tol = 1e-10; From 1a1cf2c00c19e1a704a280158683ca41d114d1af Mon Sep 17 00:00:00 2001 From: Nathaniel Morgan Date: Mon, 3 Aug 2026 14:29:31 -0600 Subject: [PATCH 39/59] fixed missing .host GPU bugs --- .../src/ref_plus_mesh_test.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/examples/reference_element/src/ref_plus_mesh_test.cpp b/examples/reference_element/src/ref_plus_mesh_test.cpp index 016adede..07324bf0 100644 --- a/examples/reference_element/src/ref_plus_mesh_test.cpp +++ b/examples/reference_element/src/ref_plus_mesh_test.cpp @@ -552,9 +552,9 @@ void test_manufactured_solution() { }; // Setup Kokkos arrays with IJK ordering - DCArrayKokkos node_coords_dual(8,3); - DCArrayKokkos node_in_elem_dual(8); - DCArrayKokkos jac(3, 3); + 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; @@ -663,17 +663,17 @@ void test_manufactured_solution() { + J_analytical[0][2]*(J_analytical[1][0]*J_analytical[2][1] - J_analytical[1][1]*J_analytical[2][0]); - DCArrayKokkos det_numerical(1); + 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(0)); + 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(0)); + printf(" Numerical: %12.8f\n", det_numerical.host(0)); printf(" Error: %12.2e\n", det_error); const double tol = 1e-10; @@ -804,16 +804,16 @@ void test_manufactured_solution() { + J_analytical[0][2]*(J_analytical[1][0]*J_analytical[2][1] - J_analytical[1][1]*J_analytical[2][0]); - DCArrayKokkos det_numerical(1); + 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(0)); + 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(0)); + printf(" Numerical: %12.8f\n", det_numerical.host(0)); printf(" Error: %12.2e\n", det_error); const double tol = 1e-10; From 50737c8bc2592de3f3d0d49586ce6eee4219c684 Mon Sep 17 00:00:00 2001 From: Nathaniel Morgan Date: Mon, 3 Aug 2026 14:53:31 -0600 Subject: [PATCH 40/59] named arrays in surf connectivity --- src/swage/unstructured_mesh.h | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/swage/unstructured_mesh.h b/src/swage/unstructured_mesh.h index 3b046792..2d44c8a0 100644 --- a/src/swage/unstructured_mesh.h +++ b/src/swage/unstructured_mesh.h @@ -606,11 +606,11 @@ struct Mesh_t // ----------------------------------------------------------------------- // get the elem nodes on the surface - CArrayKokkos surf_node_ordering_in_elem(num_surfs_in_elem, num_nodes_in_surf); + 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); + 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, @@ -636,18 +636,18 @@ struct Mesh_t DCArrayKokkos bdy_surf_counter(1); bdy_surf_counter.set_values(0); - CArrayKokkos face_elems_in_elem(num_elems, num_surfs_in_elem); + CArrayKokkos face_elems_in_elem(num_elems, num_surfs_in_elem,"face_elems_in_elem"); face_elems_in_elem.set_values(-1); - surfs_in_elem = CArrayKokkos(num_elems, num_surfs_in_elem); + 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); // helper variables for temporary storage, its sized larger than num_surfs - CArrayKokkos elems_in_surf_helper(num_elems*num_surfs_in_elem,2); - CArrayKokkos faces_in_surf_helper(num_elems*num_surfs_in_elem,2); - CArrayKokkos num_elems_in_surf_helper(num_elems*num_surfs_in_elem); + 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); + CArrayKokkos bdy_surfs_helper(num_elems*num_surfs_in_elem,"bdy_surfs_helper"); // ----------------------------------------------------------------------- // 1b. Build Surfaces @@ -772,7 +772,7 @@ struct Mesh_t // 1c. Finish populating values in surface data structures // ----------------------------------------------------------------------- - nodes_in_surf = CArrayKokkos(num_surfs,num_nodes_in_surf); + 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"); @@ -937,7 +937,7 @@ struct Mesh_t // ----------------------------------------------------------------------- num_bdy_patches = num_bdy_surfs*num_patches_in_surf; - bdy_patches = CArrayKokkos (num_bdy_patches); + bdy_patches = CArrayKokkos (num_bdy_patches, "mesh.bdy_patches"); FOR_ALL_CLASS(bdy_surf_gid, 0, num_bdy_surfs,{ From 9a89650e081527d25181ec08442b01451068ef99 Mon Sep 17 00:00:00 2001 From: Nathaniel Morgan Date: Mon, 3 Aug 2026 15:35:11 -0600 Subject: [PATCH 41/59] added ref_flux_test.cpp --- examples/reference_element/CMakeLists.txt | 3 + .../reference_element/src/ref_flux_test.cpp | 181 ++++++++++++++++++ 2 files changed, 184 insertions(+) create mode 100644 examples/reference_element/src/ref_flux_test.cpp diff --git a/examples/reference_element/CMakeLists.txt b/examples/reference_element/CMakeLists.txt index 53f03235..16f1df08 100644 --- a/examples/reference_element/CMakeLists.txt +++ b/examples/reference_element/CMakeLists.txt @@ -2,6 +2,7 @@ # 1. Define the executable # Point directly to the source file inside 'src/' +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) @@ -11,6 +12,7 @@ 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(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) @@ -20,6 +22,7 @@ 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(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) diff --git a/examples/reference_element/src/ref_flux_test.cpp b/examples/reference_element/src/ref_flux_test.cpp new file mode 100644 index 00000000..513cf200 --- /dev/null +++ b/examples/reference_element/src/ref_flux_test.cpp @@ -0,0 +1,181 @@ +/********************************************************************************************** +© 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 + + +// 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 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"); + } + + + printf("\n=== TEST: Advection in Taylor Green Vortex ===\n"); + + + + Kokkos::fence(); + + + + + printf("\n Surface flux test finished.\n"); + + +} // end MATAR scope +MATAR_FINALIZE(); + +return 0; +} From 1038f1dc1e1bba60c1bdd14279d59744d10edb17 Mon Sep 17 00:00:00 2001 From: Nathaniel Morgan Date: Tue, 4 Aug 2026 14:42:50 -0600 Subject: [PATCH 42/59] added ref surf normal and tests --- .../reference_element/src/ref_flux_test.cpp | 199 +++++++++++++++++- src/elements/ref_elem.h | 47 +++++ 2 files changed, 242 insertions(+), 4 deletions(-) diff --git a/examples/reference_element/src/ref_flux_test.cpp b/examples/reference_element/src/ref_flux_test.cpp index 513cf200..38093534 100644 --- a/examples/reference_element/src/ref_flux_test.cpp +++ b/examples/reference_element/src/ref_flux_test.cpp @@ -58,6 +58,9 @@ MATAR_INITIALIZE(argc, argv); ReferenceElement_t FERefElem; // kinematic space ReferenceElement_t DGRefElem; // thermal space, it is discontinous + SurfaceQuadrature_t SurfQuad; + ReferenceSurface_t RefSurf; + Mesh_t Mesh; // unstructured mesh const size_t elem_dims = 3; @@ -68,11 +71,13 @@ MATAR_INITIALIZE(argc, argv); const size_t num_DOFs_1d = elem_order + 1; // const size_t num_qpts_1d = 2*elem_order; // using Legendre, but if using Lobatto, it requires 2*Order+1 - // ============================================ - // Create quadrature and reference element + // ================================================================ + // Create quadrature along with the reference element and surface std::cout<<"Building reference elements and quadrature \n"; + // ---- reference element ---- + // create quadrature Quad.initialize_quadrature(reference_space::GaussLegendre, num_qpts_1d, @@ -91,6 +96,15 @@ MATAR_INITIALIZE(argc, argv); Quad, elem_order-1); + // ---- reference surface ---- + SurfQuad.initialize_quadrature(reference_space::GaussLegendre, + num_qpts_1d, + elem_dims); + + RefSurf.initialize_ref_surf(SurfQuad, + FERefElem); + + // ========================================== // Build the unstructured mesh structure @@ -143,7 +157,7 @@ MATAR_INITIALIZE(argc, argv); Mesh.build_corner_connectivity(); std::cout<<"Building element element connectivity \n"; Mesh.build_elem_elem_connectivity(); - std::cout<<"Building surface connectivity connectivity \n"; + std::cout<<"Building surface connectivity \n"; Mesh.build_surf_connectivity(); // check mesh index sizes @@ -161,11 +175,188 @@ MATAR_INITIALIZE(argc, argv); Kokkos::abort("ERROR: wrong number of boundary patches"); } + + // State arrays for this test + 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 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, num_qpts_in_surf, elem_dims, "surf_flux"); + DCArrayKokkos field(num_elems, "elem_field"); //elem_order-1 = 1 so it is a P0 element + DCArrayKokkos mesh_velocity(num_surfs, num_qpts_in_surf, elem_dims, "surf_mesh_velocity"); + mesh_velocity.set_values(0.0); - printf("\n=== TEST: Advection in Taylor Green Vortex ===\n"); + printf("\n=== TEST: Surface Integration ===\n"); + + + + FOR_ALL(elem_gid, 0, num_elems, { + + double tally_div=0; + double tally_flux[3][3]; + double tally_normal[3]; + + double eye[3][3]; + + for(size_t j=0; j nodes_in_elem(&Mesh.nodes_in_elem(elem_gid,0), num_nodes_in_elem); + + for(size_t face_lid=0; face_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 = 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 + + const double elem_vol = pow(h,elem_dims); + + + // 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-3.0); + Kokkos::abort("ERROR: divergence of Gradx should be equal to 3 \n"); + } + + }); // end parallel for elements + + FOR_ALL(surf_gid, 0, Mesh.num_surfs, { + + const size_t num_elems_in_surf = Mesh.num_elems_in_surf(surf_gid); + 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); + + + } // end side_lid loop + + }); // end surf loop Kokkos::fence(); diff --git a/src/elements/ref_elem.h b/src/elements/ref_elem.h index 091b26a2..50d0cec5 100644 --- a/src/elements/ref_elem.h +++ b/src/elements/ref_elem.h @@ -1372,6 +1372,7 @@ namespace elements CArrayKokkos qpt_grad_basis; // access as (faces, surf_qpts, dofs, dims) CArrayKokkos outward_sign; + CArrayKokkos outward_normal; void initialize_ref_surf(const struct SurfaceQuadrature_t& SurfQuadrature, @@ -1393,6 +1394,7 @@ namespace elements // 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"); //face 0 (xi=-1): sign = -1 //face 1 (xi=+1): sign = +1 @@ -1403,6 +1405,7 @@ namespace elements RUN_CLASS({ outward_sign(0) = -1.; outward_sign(1) = 1.; + if(elem_dims>1){ outward_sign(2) = -1.; outward_sign(3) = 1.; @@ -1411,6 +1414,50 @@ namespace elements 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- + + outward_normal(1,0) = 1.; // face 1 xi+ + outward_normal(1,1) = 0.; // face 1 xi+ + + outward_normal(2,0) = 0.; // face 2 eta- + outward_normal(2,1) = -1.; // face 2 eta- + + 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+ + } + }); // end serial RUN on GPU // Notes on arrays From eb9fe4d0d6568d5d372c85cf48612a438ca35ad6 Mon Sep 17 00:00:00 2001 From: Nathaniel Morgan Date: Tue, 4 Aug 2026 14:59:38 -0600 Subject: [PATCH 43/59] added volume calc via quadrature to flux test --- .../reference_element/src/ref_flux_test.cpp | 43 ++++++++++++++++--- 1 file changed, 38 insertions(+), 5 deletions(-) diff --git a/examples/reference_element/src/ref_flux_test.cpp b/examples/reference_element/src/ref_flux_test.cpp index 38093534..b1a5aca6 100644 --- a/examples/reference_element/src/ref_flux_test.cpp +++ b/examples/reference_element/src/ref_flux_test.cpp @@ -177,6 +177,8 @@ MATAR_INITIALIZE(argc, argv); // 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; @@ -185,6 +187,7 @@ MATAR_INITIALIZE(argc, argv); 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_qpts_in_elem, elem_dims, elem_dims, "elem_jacobian"); 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, num_qpts_in_surf, elem_dims, "surf_flux"); @@ -198,6 +201,40 @@ MATAR_INITIALIZE(argc, argv); FOR_ALL(elem_gid, 0, num_elems, { + double elem_vol = 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(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 += det_jac*Quad.qpt_weights(qpt_lid); + + } // end for + + if(fabs(elem_vol-pow(h,elem_dims))>1.0e-12){ + printf("wrong elem volume using gauss quadrature, error = %.15f \n", fabs(elem_vol-pow(h,elem_dims)) ); + Kokkos::abort("ERROR: wrong volume \n"); + } + + double tally_div=0; double tally_flux[3][3]; double tally_normal[3]; @@ -215,8 +252,6 @@ MATAR_INITIALIZE(argc, argv); eye[j][j] = 1.0; } - ViewCArrayKokkos nodes_in_elem(&Mesh.nodes_in_elem(elem_gid,0), num_nodes_in_elem); - for(size_t face_lid=0; face_lid= 1.e-12){ @@ -312,8 +347,6 @@ MATAR_INITIALIZE(argc, argv); Kokkos::abort("ERROR: no conservation of surface normals \n"); } // end check on tally_normal } // end for dim - - const double elem_vol = pow(h,elem_dims); // Test: check grad x = eye From 46f5aa2443b2004d6d9d8e3f07881c7532ee41cb Mon Sep 17 00:00:00 2001 From: Nathaniel Morgan Date: Wed, 5 Aug 2026 16:10:50 -0600 Subject: [PATCH 44/59] added remap test --- examples/reference_element/CMakeLists.txt | 3 + .../reference_element/src/ref_flux_test.cpp | 54 +- examples/reference_element/src/remap.cpp | 639 ++++++++++++++++++ 3 files changed, 661 insertions(+), 35 deletions(-) create mode 100644 examples/reference_element/src/remap.cpp diff --git a/examples/reference_element/CMakeLists.txt b/examples/reference_element/CMakeLists.txt index 16f1df08..5540371c 100644 --- a/examples/reference_element/CMakeLists.txt +++ b/examples/reference_element/CMakeLists.txt @@ -2,6 +2,7 @@ # 1. Define the executable # Point directly to the source file inside 'src/' +add_executable(remap src/remap.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) @@ -12,6 +13,7 @@ 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 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) @@ -22,6 +24,7 @@ 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 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) diff --git a/examples/reference_element/src/ref_flux_test.cpp b/examples/reference_element/src/ref_flux_test.cpp index b1a5aca6..95b1d946 100644 --- a/examples/reference_element/src/ref_flux_test.cpp +++ b/examples/reference_element/src/ref_flux_test.cpp @@ -187,21 +187,19 @@ MATAR_INITIALIZE(argc, argv); 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_qpts_in_elem, elem_dims, elem_dims, "elem_jacobian"); + 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"); - DCArrayKokkos surf_flux(num_surfs, num_qpts_in_surf, elem_dims, "surf_flux"); - DCArrayKokkos field(num_elems, "elem_field"); //elem_order-1 = 1 so it is a P0 element - DCArrayKokkos mesh_velocity(num_surfs, num_qpts_in_surf, elem_dims, "surf_mesh_velocity"); - mesh_velocity.set_values(0.0); - - printf("\n=== TEST: Surface Integration ===\n"); - + + printf("\n=== TEST: Surface Integration ===\n"); FOR_ALL(elem_gid, 0, num_elems, { - double elem_vol = 0.0; + elem_vol(elem_gid) = 0.0; ViewCArrayKokkos nodes_in_elem(&Mesh.nodes_in_elem(elem_gid,0), num_nodes_in_elem); @@ -216,7 +214,7 @@ MATAR_INITIALIZE(argc, argv); num_nodes_in_elem); // jacobian matrix - ViewCArrayKokkos jac(&elem_jac(qpt_lid,0,0),3,3); + ViewCArrayKokkos jac(&elem_jac(elem_gid,qpt_lid,0,0),3,3); jacobian(jac, node_coords, @@ -225,12 +223,16 @@ MATAR_INITIALIZE(argc, argv); const double det_jac = det_3x3(jac); - elem_vol += det_jac*Quad.qpt_weights(qpt_lid); + elem_vol(elem_gid) += det_jac*Quad.qpt_weights(qpt_lid); } // end for - if(fabs(elem_vol-pow(h,elem_dims))>1.0e-12){ - printf("wrong elem volume using gauss quadrature, error = %.15f \n", fabs(elem_vol-pow(h,elem_dims)) ); + 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"); } @@ -353,14 +355,14 @@ MATAR_INITIALIZE(argc, argv); bool passed = true; for(size_t i=0; i= 1.e-12) passed = false; + if(fabs((tally_flux[i][j]/elem_vol(elem_gid)) - eye[i][j]) >= 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-3.0); + if(fabs((tally_div/elem_vol(elem_gid)) - 3.0) >= 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 - - FOR_ALL(surf_gid, 0, Mesh.num_surfs, { - - const size_t num_elems_in_surf = Mesh.num_elems_in_surf(surf_gid); - 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); - - - } // end side_lid loop - - }); // end surf loop - Kokkos::fence(); - - - - printf("\n Surface flux test finished.\n"); diff --git a/examples/reference_element/src/remap.cpp b/examples/reference_element/src/remap.cpp new file mode 100644 index 00000000..6cd07d6f --- /dev/null +++ b/examples/reference_element/src/remap.cpp @@ -0,0 +1,639 @@ +/********************************************************************************************** +© 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 + + +inline int PointIndexFromIJK(int i, int j, int k, const int* order); +void write_vtk(const char* filename, + Mesh_t& Mesh, + DCArrayKokkos& node_coords, + DCArrayKokkos& elem_field, + int elem_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++; + } + }); + + 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"); //elem_order-1 = 1 so it is a P0 element + DCArrayKokkos elem_field_n(num_elems, "elem_field_n"); //elem_order-1 = 1 so it is a P0 element + DCArrayKokkos node_velocity(num_nodes, elem_dims, "node_velocity"); + + + const double max_vel = 1.0; + const size_t max_cycles = 10; + 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, { + + elem_field(elem_gid) = 1.0; // constant field + + 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(); + + + ////--------/// + + 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& node_coords, + DCArrayKokkos& elem_field, + int elem_order) +{ + // Update host side data + node_coords.update_host(); + elem_field.update_host(); + Mesh.nodes_in_elem.update_host(); + + + FILE* vtk_file = fopen(filename, "w"); + if (vtk_file == NULL) { + printf("Error: Could not open VTK file %s\n", filename); + return; + } + + // Write VTK header + fprintf(vtk_file, "# vtk DataFile Version 3.0\n"); + fprintf(vtk_file, "Remap Test Output\n"); + fprintf(vtk_file, "ASCII\n"); + fprintf(vtk_file, "DATASET UNSTRUCTURED_GRID\n"); + + // Write points (nodes) + fprintf(vtk_file, "POINTS %lu double\n", Mesh.num_nodes); + for (size_t node_gid = 0; node_gid < Mesh.num_nodes; node_gid++) { + fprintf(vtk_file, "%.8e %.8e %.8e\n", + node_coords.host(node_gid, 0), + node_coords.host(node_gid, 1), + node_coords.host(node_gid, 2)); + } + + // Write cells (elements) + size_t num_nodes_in_elem = Mesh.num_nodes_in_elem; + + // For VTK, we need to specify: number of cells, and total size + // size = num_elems * (1 + num_nodes_in_elem) where 1 is for the count + size_t total_size = Mesh.num_elems * (1 + num_nodes_in_elem); + fprintf(vtk_file, "CELLS %lu %lu\n", Mesh.num_elems, total_size); + + int order[3] = { elem_order, elem_order, elem_order }; + for (size_t elem_gid = 0; elem_gid < Mesh.num_elems; elem_gid++) { + fprintf(vtk_file, "%lu", num_nodes_in_elem); + //for (int k = 0; k <= elem_order; k++) { + // for (int j = 0; j <= elem_order; j++) { + // for (int i = 0; i <= elem_order; i++) { + // size_t node_lid = PointIndexFromIJK(i, j, k, order); + // fprintf(vtk_file, "%lu ", Mesh.nodes_in_elem.host(elem_gid, node_lid)); + // } + // } + //} + for (size_t node_lid = 0; node_lid < num_nodes_in_elem; node_lid++) { + fprintf(vtk_file, " %lu", Mesh.nodes_in_elem.host(elem_gid, node_lid)); + } + fprintf(vtk_file, "\n"); + } + + // Write cell types + fprintf(vtk_file, "CELL_TYPES %lu\n", Mesh.num_elems); + + // Determine VTK cell type based on element order and dimension + int vtk_cell_type; + //if (elem_order == 1 && Mesh.num_dims == 3) { + // vtk_cell_type = 12; // VTK_HEXAHEDRON (linear hex) + //} else if (elem_order == 2 && Mesh.num_dims == 3) { + // vtk_cell_type = 29; // VTK_TRIQUADRATIC_HEXAHEDRON + //} else { + // Default to higher order hex + vtk_cell_type = 72; // VTK_LAGRANGE_HEXAHEDRON + //} + vtk_cell_type = 11; + + for (size_t elem_gid = 0; elem_gid < Mesh.num_elems; elem_gid++) { + fprintf(vtk_file, "%d\n", vtk_cell_type); + } + + // Write cell data (element field) + fprintf(vtk_file, "CELL_DATA %lu\n", Mesh.num_elems); + fprintf(vtk_file, "SCALARS elem_field double 1\n"); + fprintf(vtk_file, "LOOKUP_TABLE default\n"); + for (size_t elem_gid = 0; elem_gid < Mesh.num_elems; elem_gid++) { + fprintf(vtk_file, "%.8e\n", elem_field.host(elem_gid)); + } + + /* + fprintf(vtk_file, "POINT_DATA %lu\n", Mesh.num_nodes); + fprintf(vtk_file, "VECTORS node_velocity double\n"); + + node_velocity.update_host(); + for (size_t node_gid = 0; node_gid < Mesh.num_nodes; node_gid++) { + fprintf(vtk_file, "%.8e %.8e %.8e\n", + node_velocity.host(node_gid, 0), + node_velocity.host(node_gid, 1), + node_velocity.host(node_gid, 2)); + } + */ + + fclose(vtk_file); + printf("VTK file written to: %s\n", filename); +} \ No newline at end of file From 6656f6178b8584f1d426d974160ccf8de4c7d0ad Mon Sep 17 00:00:00 2001 From: Nathaniel Morgan Date: Fri, 7 Aug 2026 09:30:49 -0600 Subject: [PATCH 45/59] updated remap for arbitrary-order tests --- examples/reference_element/src/remap.cpp | 427 +++++++++++++++-------- 1 file changed, 276 insertions(+), 151 deletions(-) diff --git a/examples/reference_element/src/remap.cpp b/examples/reference_element/src/remap.cpp index 6cd07d6f..aa2c9644 100644 --- a/examples/reference_element/src/remap.cpp +++ b/examples/reference_element/src/remap.cpp @@ -35,6 +35,13 @@ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #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 @@ -44,12 +51,42 @@ 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); -void write_vtk(const char* filename, - Mesh_t& Mesh, - DCArrayKokkos& node_coords, - DCArrayKokkos& elem_field, - int elem_order); int main(int argc, char** argv) { @@ -67,8 +104,12 @@ MATAR_INITIALIZE(argc, argv); Mesh_t Mesh; // unstructured mesh const size_t elem_dims = 3; - const size_t elem_order = 1; - const size_t num_elems_1D = 8; + const size_t elem_order = 3; + const size_t num_elems_1D = 16; + + const size_t max_cycles = 1000; + const double max_time = 0.5; + const double graphics_dt = 0.1; // the minimum quadrature for FE hydrodynamics based on elem order const size_t num_DOFs_1d = elem_order + 1; // @@ -200,7 +241,6 @@ MATAR_INITIALIZE(argc, argv); const double max_vel = 1.0; - const size_t max_cycles = 10; double h_cfl = h; double dt = 0.2*h_cfl/max_vel; // dt from CFL at start, this time is psuedo time @@ -262,8 +302,13 @@ MATAR_INITIALIZE(argc, argv); ////--------/// + double time = 0; + double time_output = graphics_dt; + size_t output_id = 0; for(size_t cycle=0; cycle= -1.e-8 ){ - } // end loop over cycle + 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); -/* - FOR_ALL(elem_gid, 0, num_elems,{ - printf("%.4f\n", elem_field(elem_gid)); - }); - Kokkos::fence(); + // 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; - FOR_ALL(node_gid, 0, num_nodes,{ - printf("%.4f, %.4f \n", node_coords(node_gid, 0), node_coords(node_gid, 1)); - }); -*/ + } // end if + + if (time >= max_time ) break; + + } // end loop over cycle + printf(" time = %.4f ", time); printf("\n Remap test finished.\n"); @@ -481,159 +537,228 @@ return 0; } // end function -///////////////////////////////////////////////////////////////////////////// -/// -/// \fn PointIndexFromIJK -/// -/// \brief Given (i,j,k) coordinates within the Lagrange hex, return an -/// offset into the local connectivity (PointIds) array. The order parameter -/// must point to an array of 3 integers specifying the order along each -/// axis of the hexahedron. -/// -///////////////////////////////////////////////////////////////////////////// -inline int PointIndexFromIJK(int i, int j, int k, const int* order) + +// +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) { - bool ibdy = (i == 0 || i == order[0]); - bool jbdy = (j == 0 || j == order[1]); - bool kbdy = (k == 0 || k == order[2]); - // How many boundaries do we lie on at once? - int nbdy = (ibdy ? 1 : 0) + (jbdy ? 1 : 0) + (kbdy ? 1 : 0); + std::ofstream vtu_file(filename); + if (!vtu_file.is_open()) { + std::cerr << "Error: Cannot open file " << filename << std::endl; + return; + } - if (nbdy == 3) { // Vertex DOF - // ijk is a corner node. Return the proper index (somewhere in [0,7]): - return (i ? (j ? 2 : 1) : (j ? 3 : 0)) + (k ? 4 : 0); + 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"; +} - int offset = 8; - if (nbdy == 2) { // Edge DOF - if (!ibdy) { // On i axis - return (i - 1) + (j ? order[0] - 1 + order[1] - 1 : 0) + (k ? 2 * (order[0] - 1 + order[1] - 1) : 0) + offset; - } - if (!jbdy) { // On j axis - return (j - 1) + (i ? order[0] - 1 : 2 * (order[0] - 1) + order[1] - 1) + (k ? 2 * (order[0] - 1 + order[1] - 1) : 0) + offset; +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) << " "; } - // !kbdy, On k axis - offset += 4 * (order[0] - 1) + 4 * (order[1] - 1); - return (k - 1) + (order[2] - 1) * (i ? (j ? 3 : 1) : (j ? 2 : 0)) + offset; + file << "\n"; } + + file << " \n"; - offset += 4 * (order[0] - 1 + order[1] - 1 + order[2] - 1); - if (nbdy == 1) { // Face DOF - if (ibdy) { // On i-normal face - 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) { // On j-normal face - 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); - // kbdy, On k-normal face - return (i - 1) + ((order[0] - 1) * (j - 1)) + (k ? (order[0] - 1) * (order[1] - 1) : 0) + offset; + // Offsets + file << " \n"; + file << " "; + for (size_t elem = 0; elem < num_elems; elem++) { + file << (elem + 1) * nodes_per_elem << " "; } + file << "\n \n"; - // nbdy == 0: Body 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))); -} + // Cell types + file << " \n"; + file << " "; + for (size_t elem = 0; elem < num_elems; elem++) { + file << VTK_LAGRANGE_HEXAHEDRON << " "; + } + file << "\n \n"; -void write_vtk(const char* filename, - Mesh_t& Mesh, - DCArrayKokkos& node_coords, - DCArrayKokkos& elem_field, - int elem_order) -{ - // Update host side data - node_coords.update_host(); - elem_field.update_host(); - Mesh.nodes_in_elem.update_host(); + 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"; - FILE* vtk_file = fopen(filename, "w"); - if (vtk_file == NULL) { - printf("Error: Could not open VTK file %s\n", filename); - return; + // 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"; - // Write VTK header - fprintf(vtk_file, "# vtk DataFile Version 3.0\n"); - fprintf(vtk_file, "Remap Test Output\n"); - fprintf(vtk_file, "ASCII\n"); - fprintf(vtk_file, "DATASET UNSTRUCTURED_GRID\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"; - // Write points (nodes) - fprintf(vtk_file, "POINTS %lu double\n", Mesh.num_nodes); - for (size_t node_gid = 0; node_gid < Mesh.num_nodes; node_gid++) { - fprintf(vtk_file, "%.8e %.8e %.8e\n", - node_coords.host(node_gid, 0), - node_coords.host(node_gid, 1), - node_coords.host(node_gid, 2)); + // writing X-component of node data + for (size_t i = 0; i < num_nodes; i++) { + file << " " << data.host(i, 0) << "\n"; } - // Write cells (elements) - size_t num_nodes_in_elem = Mesh.num_nodes_in_elem; + 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}; - // For VTK, we need to specify: number of cells, and total size - // size = num_elems * (1 + num_nodes_in_elem) where 1 is for the count - size_t total_size = Mesh.num_elems * (1 + num_nodes_in_elem); - fprintf(vtk_file, "CELLS %lu %lu\n", Mesh.num_elems, total_size); + std::vector> vtk_to_ijk; - int order[3] = { elem_order, elem_order, elem_order }; - for (size_t elem_gid = 0; elem_gid < Mesh.num_elems; elem_gid++) { - fprintf(vtk_file, "%lu", num_nodes_in_elem); - //for (int k = 0; k <= elem_order; k++) { - // for (int j = 0; j <= elem_order; j++) { - // for (int i = 0; i <= elem_order; i++) { - // size_t node_lid = PointIndexFromIJK(i, j, k, order); - // fprintf(vtk_file, "%lu ", Mesh.nodes_in_elem.host(elem_gid, node_lid)); - // } - // } - //} - for (size_t node_lid = 0; node_lid < num_nodes_in_elem; node_lid++) { - fprintf(vtk_file, " %lu", Mesh.nodes_in_elem.host(elem_gid, node_lid)); + 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}); + } } - fprintf(vtk_file, "\n"); } - // Write cell types - fprintf(vtk_file, "CELL_TYPES %lu\n", Mesh.num_elems); - - // Determine VTK cell type based on element order and dimension - int vtk_cell_type; - //if (elem_order == 1 && Mesh.num_dims == 3) { - // vtk_cell_type = 12; // VTK_HEXAHEDRON (linear hex) - //} else if (elem_order == 2 && Mesh.num_dims == 3) { - // vtk_cell_type = 29; // VTK_TRIQUADRATIC_HEXAHEDRON - //} else { - // Default to higher order hex - vtk_cell_type = 72; // VTK_LAGRANGE_HEXAHEDRON - //} - vtk_cell_type = 11; + std::sort(vtk_to_ijk.begin(), vtk_to_ijk.end()); - for (size_t elem_gid = 0; elem_gid < Mesh.num_elems; elem_gid++) { - fprintf(vtk_file, "%d\n", vtk_cell_type); + 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); } - - // Write cell data (element field) - fprintf(vtk_file, "CELL_DATA %lu\n", Mesh.num_elems); - fprintf(vtk_file, "SCALARS elem_field double 1\n"); - fprintf(vtk_file, "LOOKUP_TABLE default\n"); - for (size_t elem_gid = 0; elem_gid < Mesh.num_elems; elem_gid++) { - fprintf(vtk_file, "%.8e\n", elem_field.host(elem_gid)); +} + +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); } - /* - fprintf(vtk_file, "POINT_DATA %lu\n", Mesh.num_nodes); - fprintf(vtk_file, "VECTORS node_velocity double\n"); + 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; + } - node_velocity.update_host(); - for (size_t node_gid = 0; node_gid < Mesh.num_nodes; node_gid++) { - fprintf(vtk_file, "%.8e %.8e %.8e\n", - node_velocity.host(node_gid, 0), - node_velocity.host(node_gid, 1), - node_velocity.host(node_gid, 2)); + 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; } - */ - - fclose(vtk_file); - printf("VTK file written to: %s\n", filename); + + // 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 From 0f78590f5549523c0b7b25b605475b0e91ee1808 Mon Sep 17 00:00:00 2001 From: Nathaniel Morgan Date: Fri, 7 Aug 2026 09:42:11 -0600 Subject: [PATCH 46/59] updated output text --- examples/reference_element/src/remap.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/reference_element/src/remap.cpp b/examples/reference_element/src/remap.cpp index aa2c9644..f64754ff 100644 --- a/examples/reference_element/src/remap.cpp +++ b/examples/reference_element/src/remap.cpp @@ -498,7 +498,7 @@ MATAR_INITIALIZE(argc, argv); if( time-time_output >= -1.e-8 ){ - printf(" writing output at time = %.4f ", time); + printf(" Writing output at time = %.4f. ", time); // After your cycle loop ends, add: char filename[100]; From 63b8aafa0a17dbacdd5132ebba96e60910aba3bc Mon Sep 17 00:00:00 2001 From: Nathaniel Morgan Date: Fri, 7 Aug 2026 09:55:25 -0600 Subject: [PATCH 47/59] added update_host to vars being written to vtu --- examples/reference_element/src/remap.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/examples/reference_element/src/remap.cpp b/examples/reference_element/src/remap.cpp index f64754ff..b1219bf5 100644 --- a/examples/reference_element/src/remap.cpp +++ b/examples/reference_element/src/remap.cpp @@ -196,6 +196,9 @@ MATAR_INITIALIZE(argc, argv); node_lid++; } }); + Kokkos::fence(); + Mesh.nodes_in_elem.update_host(); + std::cout<<"Building corner connectivity \n"; Mesh.build_corner_connectivity(); @@ -497,6 +500,10 @@ MATAR_INITIALIZE(argc, argv); time += dt; if( time-time_output >= -1.e-8 ){ + node_coords.update_host(); + node_velocity.update_host(); + elem_field.update_host(); + printf(" Writing output at time = %.4f. ", time); From b25f9efbe58d673dc6a110acd3d85fe198be5216 Mon Sep 17 00:00:00 2001 From: Nathaniel Morgan Date: Fri, 7 Aug 2026 10:49:57 -0600 Subject: [PATCH 48/59] added graphics output at t=0 --- examples/reference_element/src/remap.cpp | 35 +++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/examples/reference_element/src/remap.cpp b/examples/reference_element/src/remap.cpp index b1219bf5..55e2b21c 100644 --- a/examples/reference_element/src/remap.cpp +++ b/examples/reference_element/src/remap.cpp @@ -309,6 +309,37 @@ MATAR_INITIALIZE(argc, argv); 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= -1.e-8 ){ + node_coords.update_host(); node_velocity.update_host(); elem_field.update_host(); From 239cb22ad3581cffcdd194b069f926f018c6f656 Mon Sep 17 00:00:00 2001 From: Nathaniel Morgan Date: Fri, 7 Aug 2026 11:32:22 -0600 Subject: [PATCH 49/59] updated comment --- examples/reference_element/src/remap.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/reference_element/src/remap.cpp b/examples/reference_element/src/remap.cpp index 55e2b21c..50f8a658 100644 --- a/examples/reference_element/src/remap.cpp +++ b/examples/reference_element/src/remap.cpp @@ -449,7 +449,7 @@ MATAR_INITIALIZE(argc, argv); nbr_face_lid = Mesh.faces_in_surf(surf_gid, 1); // second elem } - // testing + // Rusanov flux with cell averages gives upwind flux surf_flux(surf_gid) += -0.5*(elem_field(elem_gid)+elem_field(nbr_elem_gid))*normal_dot_vel +0.5*fabs(normal_dot_vel)*(elem_field(elem_gid)-elem_field(nbr_elem_gid)); From c2783a2d6f5eb6713454c09a2511f628d3fe66f2 Mon Sep 17 00:00:00 2001 From: Nathaniel Morgan Date: Tue, 11 Aug 2026 08:15:23 -0600 Subject: [PATCH 50/59] WIP: AO DG remap scheme --- examples/reference_element/CMakeLists.txt | 9 +- .../reference_element/src/remap_dg_test.cpp | 1111 +++++++++++++++++ .../src/{remap.cpp => remap_fv_test.cpp} | 6 +- 3 files changed, 1119 insertions(+), 7 deletions(-) create mode 100644 examples/reference_element/src/remap_dg_test.cpp rename examples/reference_element/src/{remap.cpp => remap_fv_test.cpp} (99%) diff --git a/examples/reference_element/CMakeLists.txt b/examples/reference_element/CMakeLists.txt index 5540371c..464116fc 100644 --- a/examples/reference_element/CMakeLists.txt +++ b/examples/reference_element/CMakeLists.txt @@ -2,7 +2,8 @@ # 1. Define the executable # Point directly to the source file inside 'src/' -add_executable(remap src/remap.cpp) +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) @@ -13,7 +14,8 @@ 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 PRIVATE 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) @@ -24,7 +26,8 @@ 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 PRIVATE ELEMENTS) +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) 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..b9ef97dd --- /dev/null +++ b/examples/reference_element/src/remap_dg_test.cpp @@ -0,0 +1,1111 @@ +/********************************************************************************************** +© 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_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 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_flux(num_elems, num_nodes_in_elem, "RHS_elem_flux"); // 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); + + 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 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; + double h_cfl = h/(double)num_nodes_1D; + double dt = 0.2*h_cfl/max_vel; // dt from CFL at start, this time is psuedo time + + + // ----------------------------------------------------- + double time = 0; + double time_output = graphics_dt; + size_t output_id = 0; + + + // ================================================================ + // 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 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); + + // 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 (FIXED syntax) + 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); + } + } + + // Compute (\nabal phi_q)*(v*U) + double grad_dot_flux = 0.0; + for(size_t dim = 0; dim < elem_dims; dim++){ + grad_dot_flux += a_grad_basis(dof_lid, dim) * qpt_vel[dim] * qpt_field; + } + + const double vol_qpt = elem_det_jac(elem_gid, qpt_lid) * Quad.qpt_weights(qpt_lid); + RHS_elem_flux(elem_gid, dof_lid) -= dt * grad_dot_flux * vol_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_flux(elem_gid, dof_lid) += + dt * RHS_surf_flux(elem_gid, face_lid, qpt_lid) * a_basis(dof_lid); + } + + // ----------------------------------------------------- + // 4d. Solve M * u^{n+1} = RHS using LU decomposition + + int singular = 0; + int parity = 0; + + ViewCArrayKokkos 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_flux(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; i 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(); + + + // ================================================================ + // Step 7: update time + time += dt; + + + // ================================================================ + // 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 ) 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.cpp b/examples/reference_element/src/remap_fv_test.cpp similarity index 99% rename from examples/reference_element/src/remap.cpp rename to examples/reference_element/src/remap_fv_test.cpp index 50f8a658..4350e16d 100644 --- a/examples/reference_element/src/remap.cpp +++ b/examples/reference_element/src/remap_fv_test.cpp @@ -238,8 +238,8 @@ MATAR_INITIALIZE(argc, argv); 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"); //elem_order-1 = 1 so it is a P0 element - DCArrayKokkos elem_field_n(num_elems, "elem_field_n"); //elem_order-1 = 1 so it is a P0 element + 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"); @@ -249,8 +249,6 @@ MATAR_INITIALIZE(argc, argv); FOR_ALL(elem_gid, 0, num_elems, { - elem_field(elem_gid) = 1.0; // constant field - ViewCArrayKokkos nodes_in_elem(&Mesh.nodes_in_elem(elem_gid,0), num_nodes_in_elem); double elem_coords[3]; From af9daa91433855604d378d10dab344ba6f024f76 Mon Sep 17 00:00:00 2001 From: Nathaniel Morgan Date: Tue, 11 Aug 2026 10:57:02 -0600 Subject: [PATCH 51/59] added rk time integration --- .../reference_element/src/remap_dg_test.cpp | 547 +++++++++--------- 1 file changed, 282 insertions(+), 265 deletions(-) diff --git a/examples/reference_element/src/remap_dg_test.cpp b/examples/reference_element/src/remap_dg_test.cpp index b9ef97dd..ea7d98b4 100644 --- a/examples/reference_element/src/remap_dg_test.cpp +++ b/examples/reference_element/src/remap_dg_test.cpp @@ -242,6 +242,7 @@ MATAR_INITIALIZE(argc, argv); 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_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"); @@ -390,15 +391,16 @@ MATAR_INITIALIZE(argc, argv); // ----------------------------------------------------- - const double max_vel = 1.0; - double h_cfl = h/(double)num_nodes_1D; - double dt = 0.2*h_cfl/max_vel; // dt from CFL at start, this time is psuedo time + 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; - double time_output = graphics_dt; - size_t output_id = 0; + 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 = 2; // runge kutta time integration levels // ================================================================ @@ -492,324 +494,339 @@ MATAR_INITIALIZE(argc, argv); FOR_ALL(corner_gid, 0, num_corners, { corner_field_n(corner_gid) = corner_field(corner_gid); }); + + FOR_ALL(node_gid, 0, num_nodes, { + for(size_t dim=0; dim nodes_in_elem(&Mesh.nodes_in_elem(elem_gid,0), num_nodes_in_elem); + // ---------------------------------------------------------- + // Step 3: Calculate the surface fluxes at quadrature points - 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); + RHS_surf_flux.set_values(0.0); + RHS_elem_flux.set_values(0.0); + FOR_ALL(surf_gid, 0, num_surfs, { + + const size_t num_elems_in_surf = Mesh.num_elems_in_surf(surf_gid); + + // 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); + + ViewCArrayKokkos nodes_in_elem(&Mesh.nodes_in_elem(elem_gid,0), num_nodes_in_elem); - // 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); + for(size_t dof_lid=0; dof_lid 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); + // extract the grad_basis at a single quadrature point (surf,qpt,dof,3D) + ViewCArrayKokkos a_grad_basis(&RefSurf.qpt_grad_basis(face_lid,qpt_lid,0,0), + num_nodes_in_elem, 3); - jacobian(jac, - node_coords, - nodes_in_elem, - a_grad_basis); + // 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); - const double det_jac_qpt = det_3x3(jac); + jacobian(jac, + node_coords, + nodes_in_elem, + a_grad_basis); - invert_3x3(jac, inv_jac, det_jac_qpt); + const double det_jac_qpt = det_3x3(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 a_nbr_basis(&RefSurf.qpt_basis(nbr_face_lid,nbr_qpt_lid,0), - num_nodes_in_elem); + const size_t nbr_qpt_lid = surf_qpt_qpt_map(surf_gid,0,qpt_lid); // matching qpt - // reconstruct the fields - double qpt_field = 0.0; - double nbr_qpt_field = 0.0; + ViewCArrayKokkos a_nbr_basis(&RefSurf.qpt_basis(nbr_face_lid,nbr_qpt_lid,0), + num_nodes_in_elem); - for(size_t node_lid=0; node_lid 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); + // ---------------------------------------------- + // 4a. Initialize RHS = M*u^n - // 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); + for(size_t dof_lid = 0; dof_lid < num_nodes_in_elem; dof_lid++){ + RHS_elem_flux(elem_gid, dof_lid) = 0.0; + + // 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_flux(elem_gid, dof_lid) += + elem_vol_matrix_n(elem_gid, dof_lid, node_lid) * corner_field_n(corner_gid); + } } - // Reconstruct velocity at quadrature point (FIXED syntax) - double qpt_vel[3]; - qpt_vel[0] = 0.0; - qpt_vel[1] = 0.0; - qpt_vel[2] = 0.0; + // ---------------------------------------------- + // 4b. Add VOLUME integral: \int (\nabal phi_q)*(v*U) dV - 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 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); + + // 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 (FIXED syntax) + 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); + } + } + + // Compute (\nabal phi_q)*(v*U) + double grad_dot_flux = 0.0; for(size_t dim = 0; dim < elem_dims; dim++){ - qpt_vel[dim] += a_basis(node_lid) * node_velocity(node_gid, dim); + grad_dot_flux += a_grad_basis(dof_lid, dim) * qpt_vel[dim] * qpt_field; } + + const double vol_qpt = elem_det_jac(elem_gid, qpt_lid) * Quad.qpt_weights(qpt_lid); + RHS_elem_flux(elem_gid, dof_lid) -= rk_alpha * dt * grad_dot_flux * vol_qpt; } - // Compute (\nabal phi_q)*(v*U) - double grad_dot_flux = 0.0; - for(size_t dim = 0; dim < elem_dims; dim++){ - grad_dot_flux += a_grad_basis(dof_lid, dim) * qpt_vel[dim] * qpt_field; - } - - const double vol_qpt = elem_det_jac(elem_gid, qpt_lid) * Quad.qpt_weights(qpt_lid); - RHS_elem_flux(elem_gid, dof_lid) -= dt * grad_dot_flux * vol_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++){ + // ---------------------------------------------- + // 4c. Add SURFACE flux contribution - ViewCArrayKokkos a_basis(&RefSurf.qpt_basis(face_lid, qpt_lid, 0), - num_nodes_in_elem); + 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_flux(elem_gid, dof_lid) += + rk_alpha * dt * RHS_surf_flux(elem_gid, face_lid, qpt_lid) * a_basis(dof_lid); + } - // surface flux (note: RHS_surf_flux already has correct sign) - RHS_elem_flux(elem_gid, dof_lid) += - dt * RHS_surf_flux(elem_gid, face_lid, qpt_lid) * a_basis(dof_lid); - } - - // ----------------------------------------------------- - // 4d. Solve M * u^{n+1} = RHS using LU decomposition - - int singular = 0; - int parity = 0; - - ViewCArrayKokkos perm(&perm_elem(elem_gid,0), num_nodes_in_elem); - ViewCArrayKokkos vv(&vv_elem(elem_gid,0), num_nodes_in_elem); + // ----------------------------------------------------- + // 4d. Solve M * u^{n+1} = RHS using LU decomposition - ViewCArrayKokkos A(&elem_inv_vol_matrix(elem_gid, 0, 0), - num_nodes_in_elem, num_nodes_in_elem); - ViewCArrayKokkos b(&RHS_elem_flux(elem_gid,0), num_nodes_in_elem); + int singular = 0; + int parity = 0; - 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; i 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_flux(elem_gid,0), num_nodes_in_elem); + singular = LU_decompose(A, perm, vv, parity); + if(singular == 0){ + Kokkos::abort("ERROR: matrix is singular \n"); + } - // ================================================================ - // 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_velocity(node_gid, 0)*dt; - node_coords(node_gid, 1) += node_velocity(node_gid, 1)*dt; - // z-coords never change - }); - Kokkos::fence(); - + //printf("\nA = \n"); + //for(size_t i=0; i 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); + // ================================================================ + // 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) + node_velocity(node_gid, 0) * rk_alpha * dt; + node_coords(node_gid, 1) = node_coords_n(node_gid, 1) + node_velocity(node_gid, 1) * rk_alpha * dt; + // z-coords never change + }); + Kokkos::fence(); - // 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); + // ================================================================ + // Step 6: build the volume matrix for nodal DG + elem_vol.set_values(0.0); + FOR_ALL(elem_gid, 0, num_elems, { - 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 + ViewCArrayKokkos nodes_in_elem(&Mesh.nodes_in_elem(elem_gid,0), num_nodes_in_elem); - }); // end parallel for - Kokkos::fence(); + // 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(); + + } // end Runge Kutta time level loop // ================================================================ From 7c9d48e2551364079189347a03aec81cfb2fa489 Mon Sep 17 00:00:00 2001 From: Nathaniel Morgan Date: Tue, 11 Aug 2026 13:07:02 -0600 Subject: [PATCH 52/59] WIP: DG algorithm fixes --- .../reference_element/src/remap_dg_test.cpp | 137 +++++++++++------- 1 file changed, 82 insertions(+), 55 deletions(-) diff --git a/examples/reference_element/src/remap_dg_test.cpp b/examples/reference_element/src/remap_dg_test.cpp index ea7d98b4..8b495c2b 100644 --- a/examples/reference_element/src/remap_dg_test.cpp +++ b/examples/reference_element/src/remap_dg_test.cpp @@ -255,7 +255,7 @@ MATAR_INITIALIZE(argc, argv); // 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_flux(num_elems, num_nodes_in_elem, "RHS_elem_flux"); // RHS vector + CArrayKokkos RHS_elem(num_elems, num_nodes_in_elem, "RHS_elem"); // RHS vector // ================================================================ @@ -350,6 +350,7 @@ MATAR_INITIALIZE(argc, argv); // 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); @@ -487,7 +488,6 @@ MATAR_INITIALIZE(argc, argv); for(size_t dof_lid=0; dof_lid a_grad_basis(&FERefElem.qpt_grad_basis(qpt_lid,0,0), - num_nodes_in_elem, 3); + num_nodes_in_elem, 3); ViewCArrayKokkos a_basis(&FERefElem.qpt_basis(qpt_lid,0), - num_nodes_in_elem); + num_nodes_in_elem); // Reconstruct field at quadrature point double qpt_field = 0.0; @@ -707,7 +706,7 @@ MATAR_INITIALIZE(argc, argv); } const double vol_qpt = elem_det_jac(elem_gid, qpt_lid) * Quad.qpt_weights(qpt_lid); - RHS_elem_flux(elem_gid, dof_lid) -= rk_alpha * dt * grad_dot_flux * vol_qpt; + RHS_elem(elem_gid, dof_lid) -= rk_alpha * dt * grad_dot_flux * vol_qpt; } // ---------------------------------------------- @@ -721,55 +720,13 @@ MATAR_INITIALIZE(argc, argv); num_nodes_in_elem); // surface flux (note: RHS_surf_flux already has correct sign) - RHS_elem_flux(elem_gid, dof_lid) += + RHS_elem(elem_gid, dof_lid) += rk_alpha * dt * RHS_surf_flux(elem_gid, face_lid, qpt_lid) * a_basis(dof_lid); } - - // ----------------------------------------------------- - // 4d. Solve M * u^{n+1} = RHS using LU decomposition - - int singular = 0; - int parity = 0; - - ViewCArrayKokkos 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_flux(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; i nodes_in_elem(&Mesh.nodes_in_elem(elem_gid,0), num_nodes_in_elem); @@ -826,8 +784,77 @@ MATAR_INITIALIZE(argc, argv); }); // 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; i Date: Wed, 12 Aug 2026 09:09:44 -0600 Subject: [PATCH 53/59] WIP: fixed bug in DG vol integral, added inv Jacobian --- .../reference_element/src/remap_dg_test.cpp | 113 +++++++++++++----- src/elements/ref_elem.h | 8 +- 2 files changed, 86 insertions(+), 35 deletions(-) diff --git a/examples/reference_element/src/remap_dg_test.cpp b/examples/reference_element/src/remap_dg_test.cpp index 8b495c2b..e40dde69 100644 --- a/examples/reference_element/src/remap_dg_test.cpp +++ b/examples/reference_element/src/remap_dg_test.cpp @@ -134,13 +134,6 @@ MATAR_INITIALIZE(argc, argv); Quad, elem_order); - - // for thermal DOFs, we use p_order-1 for the basis order of the Lagrange polynomial, it is DG - DGRefElem.initialize_ref_elem(reference_space::arbitraryOrderElement, - reference_space::LagrangeLegendre, - Quad, - elem_order-1); - // ---- reference surface ---- SurfQuad.initialize_quadrature(reference_space::GaussLegendre, num_qpts_1d, @@ -232,6 +225,7 @@ MATAR_INITIALIZE(argc, argv); 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"); @@ -242,6 +236,7 @@ MATAR_INITIALIZE(argc, argv); 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"); @@ -401,7 +396,7 @@ MATAR_INITIALIZE(argc, argv); 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 = 2; // runge kutta time integration levels + size_t rk_num_stages = 1; // runge kutta time integration levels // ================================================================ @@ -413,18 +408,41 @@ MATAR_INITIALIZE(argc, argv); 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); // ---------------------------------------------- // 4a. Initialize RHS = M*u^n @@ -669,7 +692,7 @@ MATAR_INITIALIZE(argc, argv); } // ---------------------------------------------- - // 4b. Add VOLUME integral: \int (\nabal phi_q)*(v*U) dV + // 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++){ @@ -679,6 +702,19 @@ MATAR_INITIALIZE(argc, argv); 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++){ @@ -686,7 +722,7 @@ MATAR_INITIALIZE(argc, argv); qpt_field += a_basis(node_lid) * corner_field(corner_gid); } - // Reconstruct velocity at quadrature point (FIXED syntax) + // Reconstruct velocity at quadrature point double qpt_vel[3]; qpt_vel[0] = 0.0; qpt_vel[1] = 0.0; @@ -698,16 +734,27 @@ MATAR_INITIALIZE(argc, argv); 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)*(v*U) + // 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 += a_grad_basis(dof_lid, dim) * qpt_vel[dim] * qpt_field; + 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); + 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 @@ -717,14 +764,13 @@ MATAR_INITIALIZE(argc, argv); 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); + 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(); @@ -734,8 +780,8 @@ MATAR_INITIALIZE(argc, argv); // 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) + node_velocity(node_gid, 0) * rk_alpha * dt; - node_coords(node_gid, 1) = node_coords_n(node_gid, 1) + node_velocity(node_gid, 1) * rk_alpha * dt; + 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(); @@ -842,8 +888,14 @@ MATAR_INITIALIZE(argc, argv); } // end Runge Kutta time level loop + + // ================================================================ + // Step 7: update time + time += dt; + + // Conservation Check double sum_elem = 0.0; - double elem_conserve_check = 0.0; + double domain_mass_time = 0.0; FOR_REDUCE_SUM(elem_gid, 0, num_elems, sum_elem, { for(size_t dof_lid=0; dof_lid1.e-12) Kokkos::abort("ERROR: Mass is not conserved"); // ================================================================ @@ -913,7 +961,10 @@ MATAR_INITIALIZE(argc, argv); } // end if - if (time >= max_time ) break; + if (time >= max_time ){ + printf("Domain mass at time=%f: %f \n", time, domain_mass_time); + break; + } } // end loop over cycle printf(" time = %.4f ", time); diff --git a/src/elements/ref_elem.h b/src/elements/ref_elem.h index 50d0cec5..8334d71c 100644 --- a/src/elements/ref_elem.h +++ b/src/elements/ref_elem.h @@ -738,7 +738,7 @@ namespace elements /// reference element or reference surface. /// /// The DOF's are the volume element and can be the Lagrange basis can be at - /// Lobatto or Legendra points, those points are in most cases are spatially + /// 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) @@ -874,7 +874,7 @@ namespace elements /// /// \brief Set up quadrature in a volume element /// - /// \param TypeInp The type of quadrature (e.g., Lobatto or Legendra) + /// \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 /// @@ -1010,7 +1010,7 @@ namespace elements /// 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 Legendra points, + /// 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. /// @@ -1139,7 +1139,7 @@ namespace elements /// /// \brief Set up quadrature for surface elements /// - /// \param TypeInp The type of quadrature (e.g., Lobatto or Legendra) + /// \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 /// From 8d82c876dd0e70273fae00b03f5f2131e7fc42c9 Mon Sep 17 00:00:00 2001 From: Nathaniel Morgan Date: Wed, 12 Aug 2026 09:13:25 -0600 Subject: [PATCH 54/59] DG P1 on TG vortex works --- examples/reference_element/src/remap_dg_test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/reference_element/src/remap_dg_test.cpp b/examples/reference_element/src/remap_dg_test.cpp index e40dde69..34337f54 100644 --- a/examples/reference_element/src/remap_dg_test.cpp +++ b/examples/reference_element/src/remap_dg_test.cpp @@ -413,7 +413,7 @@ MATAR_INITIALIZE(argc, argv); const size_t dim = 0; // x-coord const size_t corner_gid = Mesh.corners_in_elem(elem_gid,corner_lid); - corner_field(corner_gid) = 1.0; // sin(PI*node_coords(node_gid,dim)); + corner_field(corner_gid) = sin(PI*node_coords(node_gid,dim)); } }); // end parallel for From 25e4d92d9c254a4c33395840803f3f9aa19b2025 Mon Sep 17 00:00:00 2001 From: Nathaniel Morgan Date: Wed, 12 Aug 2026 13:02:25 -0600 Subject: [PATCH 55/59] DG P3 remap works --- examples/reference_element/src/remap_dg_test.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/reference_element/src/remap_dg_test.cpp b/examples/reference_element/src/remap_dg_test.cpp index 34337f54..78ea0c1d 100644 --- a/examples/reference_element/src/remap_dg_test.cpp +++ b/examples/reference_element/src/remap_dg_test.cpp @@ -105,8 +105,8 @@ MATAR_INITIALIZE(argc, argv); Mesh_t Mesh; // unstructured mesh const size_t elem_dims = 3; - const size_t elem_order = 1; - const size_t num_elems_1D = 16; + const size_t elem_order = 3; + const size_t num_elems_1D = 4; const size_t max_cycles = 100000; const double max_time = 0.5; From 7d6dfad355de0c95736026f73dfa9fb9d30b2098 Mon Sep 17 00:00:00 2001 From: Nathaniel Morgan Date: Wed, 12 Aug 2026 13:36:29 -0600 Subject: [PATCH 56/59] moved build_quadrature_point_connectivty() to geometry.h --- .../reference_element/src/remap_dg_test.cpp | 84 +----------- src/geometry/geometry.h | 121 +++++++++++++++++- src/swage/unstructured_mesh.h | 7 +- 3 files changed, 130 insertions(+), 82 deletions(-) diff --git a/examples/reference_element/src/remap_dg_test.cpp b/examples/reference_element/src/remap_dg_test.cpp index 78ea0c1d..96410af6 100644 --- a/examples/reference_element/src/remap_dg_test.cpp +++ b/examples/reference_element/src/remap_dg_test.cpp @@ -259,86 +259,10 @@ MATAR_INITIALIZE(argc, argv); CArrayKokkos surf_qpt_qpt_map(num_surfs,2,num_qpts_in_surf); surf_qpt_qpt_map.set_values(-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& 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 Date: Thu, 13 Aug 2026 14:47:03 -0600 Subject: [PATCH 57/59] updated namespace and added qpt connectivity in geometry.h --- src/geometry/geometry.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/geometry/geometry.h b/src/geometry/geometry.h index b0bf9fa6..d0f254aa 100644 --- a/src/geometry/geometry.h +++ b/src/geometry/geometry.h @@ -7,9 +7,9 @@ #include "matar.h" #include "shapes.h" -using namespace mtr; -using namespace swage; // unstructured mesh and hash -using namespace elements; // reference element space +// using namespace mtr; +//using namespace swage; // unstructured mesh and hash +//using namespace elements; // reference element space ///////////////////////////////////////////////////////////////////////////// /// @@ -78,8 +78,8 @@ void jacobian( /// \return void /// ///////////////////////////////////////////////////////////////////////////// -void build_quadrature_point_connectivity(const Mesh_t& Mesh, - const ReferenceSurface_t& RefSurf, +void build_quadrature_point_connectivity(const swage::Mesh_t& Mesh, + const elements::ReferenceSurface_t& RefSurf, CArrayKokkos& surf_qpt_qpt_map, const DCArrayKokkos& node_coords){ From 65eb5239d1c41d3b45cfe27a64acda9fb376a998 Mon Sep 17 00:00:00 2001 From: Nathaniel Morgan Date: Thu, 13 Aug 2026 14:48:30 -0600 Subject: [PATCH 58/59] removed name spaces in average.cpp --- examples/average/src/average.cpp | 3 --- src/geometry/geometry.h | 3 --- 2 files changed, 6 deletions(-) diff --git a/examples/average/src/average.cpp b/examples/average/src/average.cpp index b1166ec1..70515622 100644 --- a/examples/average/src/average.cpp +++ b/examples/average/src/average.cpp @@ -44,9 +44,6 @@ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "state.h" -using namespace mtr; -using namespace swage; // unstructured mesh and point cloud -using namespace elements; // reference element space int main(int argc, char** argv) { diff --git a/src/geometry/geometry.h b/src/geometry/geometry.h index d0f254aa..0dca3dea 100644 --- a/src/geometry/geometry.h +++ b/src/geometry/geometry.h @@ -7,9 +7,6 @@ #include "matar.h" #include "shapes.h" -// using namespace mtr; -//using namespace swage; // unstructured mesh and hash -//using namespace elements; // reference element space ///////////////////////////////////////////////////////////////////////////// /// From 6dd8a6a98fbe952ec2c6eb9c8cee44d90a45aba5 Mon Sep 17 00:00:00 2001 From: Nathaniel Morgan Date: Thu, 13 Aug 2026 15:08:32 -0600 Subject: [PATCH 59/59] removed NEW from ifndef in ref elem --- examples/decomp_example/src/mesh_decomp_example.cpp | 2 ++ src/elements/ref_elem.h | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/examples/decomp_example/src/mesh_decomp_example.cpp b/examples/decomp_example/src/mesh_decomp_example.cpp index 0ba010e7..a10177c1 100644 --- a/examples/decomp_example/src/mesh_decomp_example.cpp +++ b/examples/decomp_example/src/mesh_decomp_example.cpp @@ -60,10 +60,12 @@ int main(int argc, char** argv) { // Initial mesh built on rank zero swage::Mesh_t initial_mesh; + initial_mesh.num_dims = num_dims; MPICArrayKokkos initial_node_coords; // Mesh partitioned by pt-scotch, including ghost swage::Mesh_t final_mesh; + final_mesh.num_dims = num_dims; node_t final_node; MPICArrayKokkos final_node_coords; diff --git a/src/elements/ref_elem.h b/src/elements/ref_elem.h index 8334d71c..2248f03f 100644 --- a/src/elements/ref_elem.h +++ b/src/elements/ref_elem.h @@ -31,8 +31,8 @@ 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. **********************************************************************************************/ -#ifndef REF_ELEM_NEW_H -#define REF_ELEM_NEW_H +#ifndef REF_ELEM_H +#define REF_ELEM_H #include #include "matar.h"