Commit e1d92bb0 by Dave Moxey

### Working prism and interpolation routines, added demo to test routines

parent 1e607194
 ... ... @@ -11,6 +11,8 @@ SET(PartitionAnalyseSources SET(FoundationSources FoundationDemo.cpp) SET(NodalDemoSources NodalDemo.cpp) SET(NodalTriFeketeSources NodalTriFeketeDemo.cpp) ... ... @@ -41,6 +43,9 @@ TARGET_LINK_LIBRARIES(PartitionAnalyse LibUtilities) ADD_NEKTAR_EXECUTABLE(FoundationDemo demos FoundationSources ) TARGET_LINK_LIBRARIES(FoundationDemo LibUtilities) ADD_NEKTAR_EXECUTABLE(NodalDemo demos NodalDemoSources) TARGET_LINK_LIBRARIES(NodalDemo LibUtilities) ADD_NEKTAR_EXECUTABLE(NodalTriFeketeDemo demos NodalTriFeketeSources ) TARGET_LINK_LIBRARIES(NodalTriFeketeDemo LibUtilities) ... ...
 ... ... @@ -50,6 +50,17 @@ namespace Nektar namespace LibUtilities { /** * @brief Obtain the integration weights for the given nodal distribution. * * This routine constructs the integration weights for the given nodal * distribution inside NodalUtil::m_xi. To do this we solve the linear system * \f$\mathbf{V}^\top \mathbf{w} = \mathbf{g} \f$ where \f$\mathbf{g}_i = * \int_\Omega \psi_i(x)\, dx \f$, and we use the fact that under the definition * of the orthogonal basis for each element type, \f$\mathbf{g}_i = 0 \f$ for * \f$i > 0 \f$. We use NodalUtil::v_ModeZeroIntegral to return the analytic * value of \f$\mathbf{g}_0 \f$. */ NekVector NodalUtil::GetWeights() { // Get number of modes in orthogonal basis ... ... @@ -60,7 +71,7 @@ NekVector NodalUtil::GetWeights() if (numModes == m_numPoints) { NekVector g(m_numPoints, 0.0); g(0) = 2.0*sqrt(2.0); g(0) = v_ModeZeroIntegral(); SharedMatrix V = GetVandermonde(); ... ... @@ -76,6 +87,14 @@ NekVector NodalUtil::GetWeights() } } /** * @brief Return the Vandermonde matrix for the nodal distribution. * * This routine constructs and returns the Vandermonde matrix \f$\mathbf{V}\f$, * with each entry as \f$\mathbf{V}_{ij} = (\psi_i(\xi_j))\f$ where \f$\psi_i * \f$ is the orthogonal basis obtained through the abstract function * NodalUtil::v_OrthoBasis. */ SharedMatrix NodalUtil::GetVandermonde() { int rows = m_numPoints, cols = v_NumModes(); ... ... @@ -93,7 +112,19 @@ SharedMatrix NodalUtil::GetVandermonde() return matV; } /** * @brief Return the Vandermonde matrix of the derivative of the basis functions * for the nodal distribution. * * This routine constructs and returns the Vandermonde matrix for the derivative * of the basis functions \f$\mathbf{V}_d\f$ for coordinate directions \f$0 * \leq d \leq 2 \f$, with each entry as \f$\mathbf{V}_{ij} = (\partial_d * \psi_i(\xi_j))\f$ where \f$\partial_d\psi_i \f$ is the derivative of the * orthogonal basis obtained through the abstract function * NodalUtil::v_OrthoBasisDeriv. * * @param dir Direction of derivative in the standard element. */ SharedMatrix NodalUtil::GetVandermondeForDeriv(int dir) { int rows = m_numPoints, cols = v_NumModes(); ... ... @@ -112,6 +143,17 @@ SharedMatrix NodalUtil::GetVandermondeForDeriv(int dir) return matV; } /** * @brief Return the derivative matrix for the nodal distribution. * * This routine constructs and returns the derivative matrices * \f$\mathbf{D}_d\f$ for coordinate directions \f$0 \leq d \leq 2 \f$, which * can be used to evaluate the derivative of a nodal expansion at the points * defined by NodalUtil::m_xi. These are calculated as \f$\mathbf{D}_d = * \mathbf{V}_d \mathbf{V}^{-1} \f$, where \f$\mathbf{V}_d \f$ is the * derivative Vandermonde matrix and \f$\mathbf{V} \f$ is the Vandermonde * matrix. */ SharedMatrix NodalUtil::GetDerivMatrix(int dir) { SharedMatrix V = GetVandermonde(); ... ... @@ -126,15 +168,64 @@ SharedMatrix NodalUtil::GetDerivMatrix(int dir) return D; } NodalUtilTriangle::NodalUtilTriangle(int degree, Array &r, Array &s) /** * @brief Construct the interpolation matrix used to evaluate the basis at the * points @p xi inside the element. * * This routine returns a matrix \f$\mathbf{I} \f$ that can be used to evaluate * the nodal basis at the points defined by the parameter @p xi, In particular * if the array \f$\mathbf{u} \f$ with components \f$\mathbf{u}_i = * u^\delta(\xi_i) \f$ represents the function * * @param xi An array of first size number of spatial dimensions \f$d \f$ and * secondary size the number of points to */ SharedMatrix NodalUtil::GetInterpolationMatrix( Array > &xi) { boost::shared_ptr subUtil = v_CreateUtil(xi); SharedMatrix matS = GetVandermonde(); SharedMatrix matT = subUtil->GetVandermonde(); SharedMatrix D = MemoryManager >::AllocateSharedPtr( matT->GetRows(), matS->GetColumns(), 0.0); matS->Invert(); *D = (*matT) * (*matS); return D; } /** * @brief Construct the nodal utility class for a triangle. * * The constructor of this class sets up two important member variables: * * - NodalUtilTriangle::m_eta is used to construct the collapsed coordinate * locations of the nodal points \f$(\eta_1, \eta_2) \f$ inside the square * \f$[-1,1]\f$ * - NodalUtilTriangle::m_ordering constructs a mapping from the index set \f$I * = \{ (i,j)\ |\ 0\leq i,j \leq P, i+j \leq P \}\f$ to an ordering \f$0 \leq * m(ij) \leq (P+1)(P+2)/2 \f$ that defines the monomials \f$\xi_1^i \xi_2^j * \f$ that span the triangular space. This is then used to calculate which * \f$(i,j) \f$ pair corresponds to a column of the Vandermonde matrix when * calculating the orthogonal polynomials. * * @param degree Polynomial order of this nodal triangle. * @param r \f$r \f$-coordinates of nodal points in the standard element. * @param s \f$s \f$-coordinates of nodal points in the standard element. */ NodalUtilTriangle::NodalUtilTriangle(int degree, Array r, Array s) : NodalUtil(degree, 2), m_eta(2) { // Set up parent variables. m_numPoints = r.num_elements(); m_xi[0] = r; m_xi[1] = s; // Construct a mapping (i,j) -> m from the triangular tensor product space // (i,j) to a single ordering m. for (int i = 0; i <= m_degree; ++i) { for (int j = 0; j <= m_degree - i; ++j) ... ... @@ -162,6 +253,20 @@ NodalUtilTriangle::NodalUtilTriangle(int degree, } } /** * @brief Return the value of the modal functions for the triangular element at * the nodal points #m_xi for a given mode. * * In a triangle, we use the orthogonal basis * * \f[ * \psi_{m(ij)} = \sqrt{2} P^{(0,0)}_i(\xi_1) P_j^{(2i+1,0)}(\xi_2) (1-\xi_2)^i * \f] * * where \f$m(ij) \f$ is the mapping defined in NodalUtilTriangle::m_ordering. * * @param mode The mode of the orthogonal basis to evaluate. */ NekVector NodalUtilTriangle::v_OrthoBasis(const int mode) { std::vector jacobi_i(m_numPoints), jacobi_j(m_numPoints); ... ... @@ -186,6 +291,21 @@ NekVector NodalUtilTriangle::v_OrthoBasis(const int mode) return ret; } /** * @brief Return the value of the derivative of the modal functions for the * triangular element at the nodal points #m_xi for a given mode. * * In a triangle, we use the orthogonal basis * * \f[ * \psi_{m(ij)} = \sqrt{2} P^{(0,0)}_i(\xi_1) P_j^{(2i+1,0)}(\xi_2) (1-\xi_2)^i * \f] * * where \f$m(ij) \f$ is the mapping defined in * NodalUtilTriangle::m_ordering. The derivative is then evaluated * * @param mode The mode of the orthogonal basis to evaluate. */ NekVector NodalUtilTriangle::v_OrthoBasisDeriv( const int dir, const int mode) { ... ... @@ -246,10 +366,29 @@ NekVector NodalUtilTriangle::v_OrthoBasisDeriv( return ret; } NodalUtilTetrahedron::NodalUtilTetrahedron(int degree, Array &r, Array &s, Array &t) /** * @brief Construct the nodal utility class for a tetrahedron. * * The constructor of this class sets up two important member variables: * * - NodalUtilTriangle::m_eta is used to construct the collapsed coordinate * locations of the nodal points \f$(\eta_1, \eta_2) \f$ inside the square * \f$[-1,1]\f$ * - NodalUtilTriangle::m_ordering constructs a mapping from the index set \f$I * = \{ (i,j)\ |\ 0\leq i,j \leq P, i+j \leq P \}\f$ to an ordering \f$0 \leq * m(ij) \leq (P+1)(P+2)/2 \f$ that defines the monomials \f$\xi_1^i \xi_2^j * \f$ that span the triangular space. This is then used to calculate which * \f$(i,j) \f$ pair corresponds to a column of the Vandermonde matrix when * calculating the orthogonal polynomials. * * @param degree Polynomial order of this nodal triangle. * @param r \f$r \f$-coordinates of nodal points in the standard element. * @param s \f$s \f$-coordinates of nodal points in the standard element. */ NodalUtilTetrahedron::NodalUtilTetrahedron(int degree, Array r, Array s, Array t) : NodalUtil(degree, 3), m_eta(3) { m_numPoints = r.num_elements(); ... ... @@ -426,6 +565,152 @@ NekVector NodalUtilTetrahedron::v_OrthoBasisDeriv( return ret; } NodalUtilPrism::NodalUtilPrism(int degree, Array r, Array s, Array t) : NodalUtil(degree, 3), m_eta(3) { m_numPoints = r.num_elements(); m_xi[0] = r; m_xi[1] = s; m_xi[2] = t; for (int i = 0; i <= m_degree; ++i) { for (int j = 0; j <= m_degree; ++j) { for (int k = 0; k <= m_degree - i; ++k) { m_ordering.push_back(Mode(i, j, k)); } } } // Calculate collapsed coordinates from r/s values m_eta[0] = Array(m_numPoints); m_eta[1] = Array(m_numPoints); m_eta[2] = Array(m_numPoints); for (int i = 0; i < m_numPoints; ++i) { if (fabs(m_xi[2][i] - 1.0) < NekConstants::kNekZeroTol) { // Very top point of the prism m_eta[0][i] = -1.0; m_eta[1][i] = m_xi[1][i]; m_eta[2][i] = 1.0; } else { // Third basis function collapsed to "pr" direction instead of "qr" // direction m_eta[0][i] = 2.0*(1.0 + m_xi[0][i])/(1.0 - m_xi[2][i]) - 1.0; m_eta[1][i] = m_xi[1][i]; m_eta[2][i] = m_xi[2][i]; } } } NekVector NodalUtilPrism::v_OrthoBasis(const int mode) { std::vector jacA(m_numPoints), jacB(m_numPoints); std::vector jacC(m_numPoints); Mode modes = m_ordering[mode]; const int I = modes.get<0>(), J = modes.get<1>(), K = modes.get<2>(); // Calculate Jacobi polynomials Polylib::jacobfd( m_numPoints, &m_eta[0][0], &jacA[0], NULL, I, 0.0, 0.0); Polylib::jacobfd( m_numPoints, &m_eta[1][0], &jacB[0], NULL, J, 0.0, 0.0); Polylib::jacobfd( m_numPoints, &m_eta[2][0], &jacC[0], NULL, K, 2.0 * I + 1.0, 0.0); NekVector ret(m_numPoints); NekDouble sqrt2 = sqrt(2.0); for (int i = 0; i < m_numPoints; ++i) { ret[i] = sqrt2 * jacA[i] * jacB[i] * jacC[i] *