-
Notifications
You must be signed in to change notification settings - Fork 30
Add Gauss-Legendre quadrature detached from MFEM include #1683
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 33 commits
Commits
Show all changes
35 commits
Select commit
Hold shift + click to select a range
6be497c
First commit of search algorithm
jcs15c 67fdfc6
Add test
jcs15c 3f0f03c
Merge branch 'develop' into feature/spainhour/mfemless_gauss_legendre
jcs15c a0b488f
Add cache methods, rename to quadrature.hpp
jcs15c fe23580
Update comments, test
jcs15c 651fe3f
Update integral methods to use MFEM if available
jcs15c d90bab0
Improve comments
jcs15c b5b199f
Re-add missing headers from CMakeLists.txt
jcs15c f0df642
Merge branch 'develop' into feature/spainhour/mfemless_gauss_legendre
jcs15c ac385b3
Temporarily revert test
jcs15c 0d23e1f
Merge branch 'develop' into feature/spainhour/mfemless_gauss_legendre
jcs15c 12ee22b
Merge branch 'feature/spainhour/mfemless_gauss_legendre' of https://g…
jcs15c e90dc6c
Move test to other suite
jcs15c 68002f3
More testing
jcs15c cc857c6
Another test check
jcs15c e300d22
Try to restore the test with necessary includes
jcs15c be3c09a
Fix some includes
jcs15c 49677e9
Remove the rest of MFEM from evaluate_integral
jcs15c 6c54654
Swap to axom::FlatMap
jcs15c 03b9ebe
Use new method in LinearizeCurves
jcs15c 6474280
Remove unnecessary member data
jcs15c 85f995e
Fix comments, asserts, numeric limits
jcs15c 540471f
Merge branch 'develop' into feature/spainhour/mfemless_gauss_legendre
jcs15c 55ad9f4
Make an ArrayView version
jcs15c 095f658
Remove unnecessary mfem include
jcs15c 5396af2
Finish removing mfem from LinearizeCurves.cpp
jcs15c cf06670
Update to use views and allocator IDs
jcs15c 1caf098
fix test
jcs15c aa242b1
Correct prints, add decorators
jcs15c 10eac22
Push debugging change
jcs15c cdda869
Fix a missing allocatorID pass
jcs15c 95dab9d
Remove debugging statement
jcs15c c03dab0
Merge branch 'develop' into feature/spainhour/mfemless_gauss_legendre
jcs15c 69620c6
Improve comment quality
jcs15c b474900
Merge branch 'develop' into feature/spainhour/mfemless_gauss_legendre
jcs15c File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,167 @@ | ||
| // Copyright (c) 2017-2025, Lawrence Livermore National Security, LLC and | ||
| // other Axom Project Developers. See the top-level LICENSE file for details. | ||
| // | ||
| // SPDX-License-Identifier: (BSD-3-Clause) | ||
|
|
||
| #include "axom/core/utilities/Utilities.hpp" | ||
| #include "axom/core/Array.hpp" | ||
| #include "axom/core/FlatMap.hpp" | ||
| #include "axom/core/NumericLimits.hpp" | ||
| #include "axom/core/numerics/quadrature.hpp" | ||
|
|
||
| // For math constants and includes | ||
| #include "axom/config.hpp" | ||
|
|
||
| #include <cmath> | ||
|
|
||
| namespace axom | ||
| { | ||
| namespace numerics | ||
| { | ||
|
|
||
| /*! | ||
| * \brief Computes a 1D quadrature rule of Gauss-Legendre points | ||
| * | ||
| * \param [in] npts The number of points in the rule | ||
| * \param [out] nodes The array of 1D nodes | ||
| * \param [out] weights The array of weights | ||
| * | ||
| * A Gauss-Legendre rule with \a npts points can exactly integrate | ||
| * polynomials of order 2 * npts - 1 | ||
| * | ||
| * Algorithm adapted from the MFEM implementation in `mfem/fem/intrules.cpp` | ||
| * | ||
| * \note This method constructs the points by scratch each time, without caching | ||
| * \sa get_gauss_legendre(int) | ||
| */ | ||
| void compute_gauss_legendre_data(int npts, | ||
| axom::Array<double>& nodes, | ||
| axom::Array<double>& weights, | ||
| int allocatorID) | ||
| { | ||
| assert("Quadrature rules must have >= 1 point" && (npts >= 1)); | ||
|
|
||
| nodes = axom::Array<double>(npts, npts, allocatorID); | ||
| weights = axom::Array<double>(npts, npts, allocatorID); | ||
|
|
||
| if(npts == 1) | ||
| { | ||
| nodes[0] = 0.5; | ||
| weights[0] = 1.0; | ||
| return; | ||
| } | ||
| if(npts == 2) | ||
| { | ||
| nodes[0] = 0.21132486540518711775; | ||
| nodes[1] = 0.78867513459481288225; | ||
|
|
||
| weights[0] = weights[1] = 0.5; | ||
| return; | ||
| } | ||
| if(npts == 3) | ||
| { | ||
| nodes[0] = 0.11270166537925831148207345; | ||
| nodes[1] = 0.5; | ||
| nodes[2] = 0.88729833462074168851792655; | ||
|
|
||
| weights[0] = 0.2777777777777777777777778; | ||
| weights[1] = 0.4444444444444444444444444; | ||
| weights[2] = 0.2777777777777777777777778; | ||
| return; | ||
| } | ||
|
|
||
| const int n = npts; | ||
| const int m = (npts + 1) / 2; | ||
|
|
||
| // Nodes are mirrored across x = 0.5, so only need to evaluate half | ||
| for(int i = 1; i <= m; ++i) | ||
| { | ||
| // Each node is the root of a Legendre polynomial, | ||
| // which are approximately uniformly distributed in arccos(xi). | ||
| // This makes cos a good initial guess for subsequent Newton iterations | ||
| double z = std::cos(M_PI * (i - 0.25) / (n + 0.5)); | ||
| double Pp_n, P_n, dz, xi = 0.0; | ||
|
|
||
| bool done = false; | ||
| while(true) | ||
| { | ||
| // Recursively evaluate P_n(z) through the recurrence relation | ||
| // n * P_n(z) = (2n - 1) * P_{n-1}(z) - (n - 1) * P_{n - 2}(z) | ||
| double P_nm1 = 1.0; // P_0(z) = 1 | ||
| P_n = z; // P_1(z) = z | ||
| for(int j = 2; j <= n; ++j) | ||
| { | ||
| double P_nm2 = P_nm1; | ||
| P_nm1 = P_n; | ||
| P_n = ((2 * j - 1) * z * P_nm1 - (j - 1) * P_nm2) / j; | ||
| } | ||
|
|
||
| // Evaluate P'_n(z) using another recurrence relation | ||
| // (z^2 - 1) * P'_n(z) = n * z * P_n(z) - n * P_{n-1}(z) | ||
| Pp_n = n * (z * P_n - P_nm1) / (z * z - 1); | ||
|
|
||
| if(done) | ||
| { | ||
| break; | ||
| } | ||
|
|
||
| // Compute the Newton method step size | ||
| dz = P_n / Pp_n; | ||
|
|
||
| if(std::fabs(dz) < axom::numeric_limits<double>::epsilon()) | ||
| { | ||
| done = true; | ||
|
|
||
| // Scale back to [0, 1] | ||
| xi = ((1 - z) + dz) / 2; | ||
| } | ||
|
|
||
| // Take the Newton step, repeat the process | ||
| z -= dz; | ||
| } | ||
|
|
||
| nodes[i - 1] = xi; | ||
| nodes[n - i] = 1.0 - xi; | ||
|
|
||
| // Evaluate the weight as w_i = 2 / (1 - z^2) / P'_n(z)^2, with z \in [-1, 1] | ||
| weights[i - 1] = weights[n - i] = 1.0 / (4.0 * xi * (1.0 - xi) * Pp_n * Pp_n); | ||
| } | ||
| } | ||
|
|
||
| /*! | ||
| * \brief Computes or accesses a precomputed 1D quadrature rule of Gauss-Legendre points | ||
| * | ||
| * \param [in] npts The number of points in the rule | ||
| * | ||
| * A Gauss-Legendre rule with \a npts points can exactly integrate | ||
| * polynomials of order 2 * npts - 1 | ||
| * | ||
| * \note If this method has already been called for a given order, it will reuse the same quadrature points | ||
| * without needing to recompute them | ||
| * | ||
| * \warning The use of a static variable to store cached nodes makes this method not threadsafe. | ||
| * | ||
| * \return The `QuadratureRule` object which contains axom::ArrayView<double>'s of stored nodes and weights | ||
| */ | ||
| QuadratureRule get_gauss_legendre(int npts, int allocatorID) | ||
| { | ||
| assert("Quadrature rules must have >= 1 point" && (npts >= 1)); | ||
|
|
||
| // Define a static map that stores the GL quadrature rule for a given order | ||
| static std::map<std::pair<int, int>, std::pair<axom::Array<double>, axom::Array<double>>> rule_library; | ||
|
|
||
| const std::pair<int, int> key = std::make_pair(npts, allocatorID); | ||
|
|
||
| auto value_it = rule_library.find(key); | ||
| if(value_it == rule_library.end()) | ||
| { | ||
| auto& vals = rule_library[key]; | ||
| compute_gauss_legendre_data(npts, vals.first, vals.second, allocatorID); | ||
| value_it = rule_library.find(key); | ||
| } | ||
|
|
||
| return QuadratureRule {value_it->second.first.view(), value_it->second.second.view()}; | ||
| } | ||
|
|
||
| } /* end namespace numerics */ | ||
| } /* end namespace axom */ | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,94 @@ | ||
| // Copyright (c) 2017-2025, Lawrence Livermore National Security, LLC and | ||
| // other Axom Project Developers. See the top-level LICENSE file for details. | ||
| // | ||
| // SPDX-License-Identifier: (BSD-3-Clause) | ||
|
|
||
| #ifndef AXOM_NUMERICS_QUADRATURE_HPP_ | ||
| #define AXOM_NUMERICS_QUADRATURE_HPP_ | ||
|
|
||
| #include "axom/core/Array.hpp" | ||
| #include "axom/core/memory_management.hpp" | ||
|
|
||
| /*! | ||
| * \file quadrature.hpp | ||
| * The functions declared in this header file find the nodes and weights of | ||
| * arbitrary order quadrature rules | ||
| */ | ||
|
|
||
| namespace axom | ||
| { | ||
| namespace numerics | ||
| { | ||
|
|
||
| /*! | ||
| * \class QuadratureRule | ||
| * | ||
| * \brief Stores a fixed array of 1D quadrature points and weights | ||
| */ | ||
| class QuadratureRule | ||
| { | ||
| // Define friend functions so rules can only be created via get_rule() methods | ||
| friend QuadratureRule get_gauss_legendre(int, int); | ||
|
|
||
| public: | ||
| //! \brief Accessor for quadrature nodes | ||
| AXOM_HOST_DEVICE | ||
| double node(size_t idx) const { return m_nodes[idx]; }; | ||
jcs15c marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| //! \brief Accessor for quadrature weights | ||
| AXOM_HOST_DEVICE | ||
| double weight(size_t idx) const { return m_weights[idx]; }; | ||
|
|
||
jcs15c marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| //! \brief Accessor for the size of the quadrature rule | ||
| AXOM_HOST_DEVICE | ||
| int getNumPoints() const { return m_nodes.size(); } | ||
|
|
||
| private: | ||
| //! \brief Use a private constructor to avoid creation of an invalid rule | ||
| QuadratureRule(axom::ArrayView<double> nodes, axom::ArrayView<double> weights) | ||
| : m_nodes(nodes) | ||
| , m_weights(weights) { }; | ||
|
|
||
| axom::ArrayView<double> m_nodes; | ||
| axom::ArrayView<double> m_weights; | ||
| }; | ||
|
|
||
| /*! | ||
| * \brief Computes a 1D quadrature rule of Gauss-Legendre points | ||
| * | ||
| * \param [in] npts The number of points in the rule | ||
| * \param [out] nodes The array of 1D nodes | ||
| * \param [out] weights The array of weights | ||
| * | ||
| * A Gauss-Legendre rule with \a npts points can exactly integrate | ||
| * polynomials of order 2 * npts - 1 | ||
| * | ||
| * Algorithm adapted from the MFEM implementation in `mfem/fem/intrules.cpp` | ||
| * | ||
| * \note This method constructs the points by scratch each time, without caching | ||
| * \sa get_gauss_legendre(int) | ||
| */ | ||
| void compute_gauss_legendre_data(int npts, | ||
| axom::Array<double>& nodes, | ||
| axom::Array<double>& weights, | ||
| int allocatorID = axom::getDefaultAllocatorID()); | ||
|
|
||
| /*! | ||
| * \brief Computes or accesses a precomputed 1D quadrature rule of Gauss-Legendre points | ||
| * | ||
| * \param [in] npts The number of points in the rule | ||
| * | ||
| * A Gauss-Legendre rule with \a npts points can exactly integrate | ||
| * polynomials of order 2 * npts - 1 | ||
| * | ||
| * \note If this method has already been called for a given order, it will reuse the same quadrature points | ||
| * without needing to recompute them | ||
| * | ||
| * \return The `QuadratureRule` object which contains axom::ArrayView<double>'s of stored nodes and weights | ||
| */ | ||
| QuadratureRule get_gauss_legendre(int npts, int allocatorID = axom::getDefaultAllocatorID()); | ||
|
|
||
| } /* end namespace numerics */ | ||
| } /* end namespace axom */ | ||
|
|
||
| #endif // AXOM_NUMERICS_QUADRATURE_HPP_ | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The comment doesn't (appear to) match the code -- presumably it's in how$z^2$ and $xi$ are defined
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fair point. The lines of code above say that
xi = (1 - z) / 2, but it was a bit confusing where the extra factor of 1/2 came from. I've updated the comment to be more clear.