diff --git a/CMakeLists.txt b/CMakeLists.txt index e9255bff6ac523e8bfdf61fe42044e4366d3993f..4b302fe373681cdc862851e871c53d3842d0d842 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -573,6 +573,7 @@ target_link_libraries( pugs PugsMesh PugsAlgebra + PugsAnalysis PugsUtils PugsLanguage PugsLanguageAST @@ -606,7 +607,7 @@ if(${CMAKE_VERSION} VERSION_LESS "3.13.0") LIBRARY DESTINATION lib ARCHIVE DESTINATION lib) else() - install(TARGETS pugs PugsAlgebra PugsUtils PugsLanguage PugsLanguageAST PugsLanguageModules PugsLanguageAlgorithms PugsLanguageUtils PugsMesh PugsScheme PugsOutput + install(TARGETS pugs PugsAlgebra PugsAnalysis PugsUtils PugsLanguage PugsLanguageAST PugsLanguageModules PugsLanguageAlgorithms PugsLanguageUtils PugsMesh PugsScheme PugsOutput RUNTIME DESTINATION bin LIBRARY DESTINATION lib diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 16904a7be0cddc9aa54206638ab4b5c759cd0de8..54cae625bc7c899c433bfdf4021ef6242dd5aaf4 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -12,6 +12,9 @@ add_subdirectory(language) # Pugs algebra add_subdirectory(algebra) +# Pugs analysis +add_subdirectory(analysis) + # Pugs mesh add_subdirectory(mesh) diff --git a/src/algebra/IntegrationTools.hpp b/src/algebra/IntegrationTools.hpp index 756fe09b9b4a2428a5649f37f6007ae9404298ca..08642b794df8470eb1e4598c75d643ccdf1de364 100644 --- a/src/algebra/IntegrationTools.hpp +++ b/src/algebra/IntegrationTools.hpp @@ -7,6 +7,8 @@ #include <utils/PugsMacros.hpp> #include <utils/Types.hpp> +#include <analysis/GaussLegendreQuadrature.hpp> + #include <cmath> enum class QuadratureType : int8_t @@ -19,356 +21,6 @@ enum class QuadratureType : int8_t QT__end }; -template <size_t Dimension> -class QuadratureBase -{ - protected: - size_t m_order; - SmallArray<const TinyVector<Dimension>> m_point_list; - SmallArray<const double> m_weight_list; - - virtual void _buildPointAndWeightLists() = 0; - - public: - size_t - numberOfPoints() const - { - Assert(m_point_list.size() == m_weight_list.size()); - return m_point_list.size(); - } - - const SmallArray<const TinyVector<Dimension>>& - pointList() const - { - return m_point_list; - } - - const SmallArray<const double>& - weightList() const - { - return m_weight_list; - } - - QuadratureBase(QuadratureBase&&) = default; - QuadratureBase(const QuadratureBase&) = default; - - protected: - explicit QuadratureBase(const size_t order) : m_order{order} {} - - public: - QuadratureBase() = delete; - virtual ~QuadratureBase() = default; -}; - -template <size_t Dimension> -class TensorialGaussLegendre final : public QuadratureBase<Dimension> -{ - private: - void _buildPointAndWeightLists(); - - public: - TensorialGaussLegendre(TensorialGaussLegendre&&) = default; - TensorialGaussLegendre(const TensorialGaussLegendre&) = default; - - explicit TensorialGaussLegendre(const size_t order) : QuadratureBase<Dimension>(order) - { - this->_buildPointAndWeightLists(); - } - - TensorialGaussLegendre() = delete; - ~TensorialGaussLegendre() = default; -}; - -template <> -void -TensorialGaussLegendre<1>::_buildPointAndWeightLists() -{ - const size_t nb_points = m_order / 2 + 1; - - SmallArray<TinyVector<1>> point_list(nb_points); - SmallArray<double> weight_list(nb_points); - - switch (m_order) { - case 0: - case 1: { - point_list[0] = 0; - - weight_list[0] = 2; - break; - } - case 2: - case 3: { - point_list[0] = -0.5773502691896257645091488; - point_list[1] = +0.5773502691896257645091488; - - weight_list[0] = 1; - weight_list[1] = 1; - break; - } - case 4: - case 5: { - point_list[0] = -0.7745966692414833770358531; - point_list[1] = 0; - point_list[2] = +0.7745966692414833770358531; - - weight_list[0] = 0.5555555555555555555555556; - weight_list[1] = 0.8888888888888888888888889; - weight_list[2] = 0.5555555555555555555555556; - break; - } - case 6: - case 7: { - point_list[0] = -0.8611363115940525752239465; - point_list[1] = -0.3399810435848562648026658; - point_list[2] = +0.3399810435848562648026658; - point_list[3] = +0.8611363115940525752239465; - - weight_list[0] = 0.3478548451374538573730639; - weight_list[1] = 0.6521451548625461426269361; - weight_list[2] = 0.6521451548625461426269361; - weight_list[3] = 0.3478548451374538573730639; - break; - } - case 8: - case 9: { - point_list[0] = -0.9061798459386639927976269; - point_list[1] = -0.5384693101056830910363144; - point_list[2] = 0; - point_list[3] = +0.5384693101056830910363144; - point_list[4] = +0.9061798459386639927976269; - - weight_list[0] = 0.2369268850561890875142640; - weight_list[1] = 0.4786286704993664680412915; - weight_list[2] = 0.5688888888888888888888889; - weight_list[3] = 0.4786286704993664680412915; - weight_list[4] = 0.2369268850561890875142640; - break; - } - case 10: - case 11: { - point_list[0] = -0.9324695142031520278123016; - point_list[1] = -0.6612093864662645136613996; - point_list[2] = -0.2386191860831969086305017; - point_list[3] = +0.2386191860831969086305017; - point_list[4] = +0.6612093864662645136613996; - point_list[5] = +0.9324695142031520278123016; - - weight_list[0] = 0.1713244923791703450402961; - weight_list[1] = 0.3607615730481386075698335; - weight_list[2] = 0.4679139345726910473898703; - weight_list[3] = 0.4679139345726910473898703; - weight_list[4] = 0.3607615730481386075698335; - weight_list[5] = 0.1713244923791703450402961; - break; - } - case 12: - case 13: { - point_list[0] = -0.9491079123427585245261897; - point_list[1] = -0.7415311855993944398638648; - point_list[2] = -0.4058451513773971669066064; - point_list[3] = 0; - point_list[4] = +0.4058451513773971669066064; - point_list[5] = +0.7415311855993944398638648; - point_list[6] = +0.9491079123427585245261897; - - weight_list[0] = 0.1294849661688696932706114; - weight_list[1] = 0.2797053914892766679014678; - weight_list[2] = 0.3818300505051189449503698; - weight_list[3] = 0.4179591836734693877551020; - weight_list[4] = 0.3818300505051189449503698; - weight_list[5] = 0.2797053914892766679014678; - weight_list[6] = 0.1294849661688696932706114; - break; - } - case 14: - case 15: { - point_list[0] = -0.9602898564975362316835609; - point_list[1] = -0.7966664774136267395915539; - point_list[2] = -0.5255324099163289858177390; - point_list[3] = -0.1834346424956498049394761; - point_list[4] = +0.1834346424956498049394761; - point_list[5] = +0.5255324099163289858177390; - point_list[6] = +0.7966664774136267395915539; - point_list[7] = +0.9602898564975362316835609; - - weight_list[0] = 0.1012285362903762591525314; - weight_list[1] = 0.2223810344533744705443560; - weight_list[2] = 0.3137066458778872873379622; - weight_list[3] = 0.3626837833783619829651504; - weight_list[4] = 0.3626837833783619829651504; - weight_list[5] = 0.3137066458778872873379622; - weight_list[6] = 0.2223810344533744705443560; - weight_list[7] = 0.1012285362903762591525314; - break; - } - case 16: - case 17: { - point_list[0] = -0.9681602395076260898355762; - point_list[1] = -0.8360311073266357942994298; - point_list[2] = -0.6133714327005903973087020; - point_list[3] = -0.3242534234038089290385380; - point_list[4] = 0; - point_list[5] = +0.3242534234038089290385380; - point_list[6] = +0.6133714327005903973087020; - point_list[7] = +0.8360311073266357942994298; - point_list[8] = +0.9681602395076260898355762; - - weight_list[0] = 0.0812743883615744119718922; - weight_list[1] = 0.1806481606948574040584720; - weight_list[2] = 0.2606106964029354623187429; - weight_list[3] = 0.3123470770400028400686304; - weight_list[4] = 0.3302393550012597631645251; - weight_list[5] = 0.3123470770400028400686304; - weight_list[6] = 0.2606106964029354623187429; - weight_list[7] = 0.1806481606948574040584720; - weight_list[8] = 0.0812743883615744119718922; - break; - } - case 18: - case 19: { - point_list[0] = -0.9739065285171717200779640; - point_list[1] = -0.8650633666889845107320967; - point_list[2] = -0.6794095682990244062343274; - point_list[3] = -0.4333953941292471907992659; - point_list[4] = -0.1488743389816312108848260; - point_list[5] = +0.1488743389816312108848260; - point_list[6] = +0.4333953941292471907992659; - point_list[7] = +0.6794095682990244062343274; - point_list[8] = +0.8650633666889845107320967; - point_list[9] = +0.9739065285171717200779640; - - weight_list[0] = 0.0666713443086881375935688; - weight_list[1] = 0.1494513491505805931457763; - weight_list[2] = 0.2190863625159820439955349; - weight_list[3] = 0.2692667193099963550912269; - weight_list[4] = 0.2955242247147528701738930; - weight_list[5] = 0.2955242247147528701738930; - weight_list[6] = 0.2692667193099963550912269; - weight_list[7] = 0.2190863625159820439955349; - weight_list[8] = 0.1494513491505805931457763; - weight_list[9] = 0.0666713443086881375935688; - break; - } - case 20: - case 21: { - point_list[0] = -0.9782286581460569928039380; - point_list[1] = -0.8870625997680952990751578; - point_list[2] = -0.7301520055740493240934163; - point_list[3] = -0.5190961292068118159257257; - point_list[4] = -0.2695431559523449723315320; - point_list[5] = 0; - point_list[6] = +0.2695431559523449723315320; - point_list[7] = +0.5190961292068118159257257; - point_list[8] = +0.7301520055740493240934163; - point_list[9] = +0.8870625997680952990751578; - point_list[10] = +0.9782286581460569928039380; - - weight_list[0] = 0.0556685671161736664827537; - weight_list[1] = 0.1255803694649046246346940; - weight_list[2] = 0.1862902109277342514260980; - weight_list[3] = 0.2331937645919904799185237; - weight_list[4] = 0.2628045445102466621806889; - weight_list[5] = 0.2729250867779006307144835; - weight_list[6] = 0.2628045445102466621806889; - weight_list[7] = 0.2331937645919904799185237; - weight_list[8] = 0.1862902109277342514260980; - weight_list[9] = 0.1255803694649046246346940; - weight_list[10] = 0.0556685671161736664827537; - break; - } - case 22: - case 23: { - point_list[0] = -0.9815606342467192506905491; - point_list[1] = -0.9041172563704748566784659; - point_list[2] = -0.7699026741943046870368938; - point_list[3] = -0.5873179542866174472967024; - point_list[4] = -0.3678314989981801937526915; - point_list[5] = -0.1252334085114689154724414; - point_list[6] = +0.1252334085114689154724414; - point_list[7] = +0.3678314989981801937526915; - point_list[8] = +0.5873179542866174472967024; - point_list[9] = +0.7699026741943046870368938; - point_list[10] = +0.9041172563704748566784659; - point_list[11] = +0.9815606342467192506905491; - - weight_list[0] = 0.0471753363865118271946160; - weight_list[1] = 0.1069393259953184309602547; - weight_list[2] = 0.1600783285433462263346525; - weight_list[3] = 0.2031674267230659217490645; - weight_list[4] = 0.2334925365383548087608499; - weight_list[5] = 0.2491470458134027850005624; - weight_list[6] = 0.2491470458134027850005624; - weight_list[7] = 0.2334925365383548087608499; - weight_list[8] = 0.2031674267230659217490645; - weight_list[9] = 0.1600783285433462263346525; - weight_list[10] = 0.1069393259953184309602547; - weight_list[11] = 0.0471753363865118271946160; - break; - } - default: { - throw UnexpectedError("Gauss-Legendre quadratures handle orders up to 23"); - break; - } - } - - m_point_list = point_list; - m_weight_list = weight_list; -} - -template <> -void -TensorialGaussLegendre<2>::_buildPointAndWeightLists() -{ - const size_t nb_points_1d = this->m_order / 2 + 1; - const size_t nb_points = nb_points_1d * nb_points_1d; - - SmallArray<TinyVector<2>> point_list(nb_points); - SmallArray<double> weight_list(nb_points); - - TensorialGaussLegendre<1> gauss_legendre_1d(m_order); - const auto& point_list_1d = gauss_legendre_1d.pointList(); - const auto& weight_list_1d = gauss_legendre_1d.weightList(); - - size_t l = 0; - for (size_t i = 0; i < nb_points_1d; ++i) { - for (size_t j = 0; j < nb_points_1d; ++j, ++l) { - point_list[l] = TinyVector<2>{point_list_1d[i][0], point_list_1d[j][0]}; - weight_list[l] = weight_list_1d[i] * weight_list_1d[j]; - } - } - - this->m_point_list = point_list; - this->m_weight_list = weight_list; -} - -template <> -void -TensorialGaussLegendre<3>::_buildPointAndWeightLists() -{ - const size_t nb_points_1d = this->m_order / 2 + 1; - const size_t nb_points = nb_points_1d * nb_points_1d * nb_points_1d; - - SmallArray<TinyVector<3>> point_list(nb_points); - SmallArray<double> weight_list(nb_points); - - TensorialGaussLegendre<1> gauss_legendre_1d(m_order); - const auto& point_list_1d = gauss_legendre_1d.pointList(); - const auto& weight_list_1d = gauss_legendre_1d.weightList(); - - size_t l = 0; - for (size_t i = 0; i < nb_points_1d; ++i) { - for (size_t j = 0; j < nb_points_1d; ++j) { - for (size_t k = 0; k < nb_points_1d; ++k, ++l) { - point_list[l] = TinyVector<3>{point_list_1d[i][0], point_list_1d[j][0], point_list_1d[k][0]}; - weight_list[l] = weight_list_1d[i] * weight_list_1d[j] * weight_list_1d[k]; - } - } - } - - this->m_point_list = point_list; - this->m_weight_list = weight_list; -} - class IntegrationMethod { public: @@ -576,7 +228,7 @@ class IntegrationMethodLegendre : public IntegrationMethod void fillArrayLegendre() { - TensorialGaussLegendre<1> tensorial_gauss_legendre(Order); + GaussLegendreQuadrature<1> tensorial_gauss_legendre(Order); copy_to(tensorial_gauss_legendre.pointList(), m_quadrature_positions); copy_to(tensorial_gauss_legendre.weightList(), m_weights); // switch (Order) { diff --git a/src/analysis/CMakeLists.txt b/src/analysis/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..b2e2f4c2f472639c54e013de1495a33a7433d021 --- /dev/null +++ b/src/analysis/CMakeLists.txt @@ -0,0 +1,5 @@ +# ------------------- Source files -------------------- + +add_library( + PugsAnalysis + GaussLegendreQuadrature.cpp) diff --git a/src/analysis/GaussLegendreQuadrature.cpp b/src/analysis/GaussLegendreQuadrature.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e9cc2b9638ee95045444efad116dccad0f117fc7 --- /dev/null +++ b/src/analysis/GaussLegendreQuadrature.cpp @@ -0,0 +1,291 @@ +#include <analysis/GaussLegendreQuadrature.hpp> +#include <utils/Exceptions.hpp> + +template <> +void +GaussLegendreQuadrature<1>::_buildPointAndWeightLists() +{ + const size_t nb_points = m_order / 2 + 1; + + SmallArray<TinyVector<1>> point_list(nb_points); + SmallArray<double> weight_list(nb_points); + + switch (m_order) { + case 0: + case 1: { + point_list[0] = 0; + + weight_list[0] = 2; + break; + } + case 2: + case 3: { + point_list[0] = -0.5773502691896257645091488; + point_list[1] = +0.5773502691896257645091488; + + weight_list[0] = 1; + weight_list[1] = 1; + break; + } + case 4: + case 5: { + point_list[0] = -0.7745966692414833770358531; + point_list[1] = 0; + point_list[2] = +0.7745966692414833770358531; + + weight_list[0] = 0.5555555555555555555555556; + weight_list[1] = 0.8888888888888888888888889; + weight_list[2] = 0.5555555555555555555555556; + break; + } + case 6: + case 7: { + point_list[0] = -0.8611363115940525752239465; + point_list[1] = -0.3399810435848562648026658; + point_list[2] = +0.3399810435848562648026658; + point_list[3] = +0.8611363115940525752239465; + + weight_list[0] = 0.3478548451374538573730639; + weight_list[1] = 0.6521451548625461426269361; + weight_list[2] = 0.6521451548625461426269361; + weight_list[3] = 0.3478548451374538573730639; + break; + } + case 8: + case 9: { + point_list[0] = -0.9061798459386639927976269; + point_list[1] = -0.5384693101056830910363144; + point_list[2] = 0; + point_list[3] = +0.5384693101056830910363144; + point_list[4] = +0.9061798459386639927976269; + + weight_list[0] = 0.2369268850561890875142640; + weight_list[1] = 0.4786286704993664680412915; + weight_list[2] = 0.5688888888888888888888889; + weight_list[3] = 0.4786286704993664680412915; + weight_list[4] = 0.2369268850561890875142640; + break; + } + case 10: + case 11: { + point_list[0] = -0.9324695142031520278123016; + point_list[1] = -0.6612093864662645136613996; + point_list[2] = -0.2386191860831969086305017; + point_list[3] = +0.2386191860831969086305017; + point_list[4] = +0.6612093864662645136613996; + point_list[5] = +0.9324695142031520278123016; + + weight_list[0] = 0.1713244923791703450402961; + weight_list[1] = 0.3607615730481386075698335; + weight_list[2] = 0.4679139345726910473898703; + weight_list[3] = 0.4679139345726910473898703; + weight_list[4] = 0.3607615730481386075698335; + weight_list[5] = 0.1713244923791703450402961; + break; + } + case 12: + case 13: { + point_list[0] = -0.9491079123427585245261897; + point_list[1] = -0.7415311855993944398638648; + point_list[2] = -0.4058451513773971669066064; + point_list[3] = 0; + point_list[4] = +0.4058451513773971669066064; + point_list[5] = +0.7415311855993944398638648; + point_list[6] = +0.9491079123427585245261897; + + weight_list[0] = 0.1294849661688696932706114; + weight_list[1] = 0.2797053914892766679014678; + weight_list[2] = 0.3818300505051189449503698; + weight_list[3] = 0.4179591836734693877551020; + weight_list[4] = 0.3818300505051189449503698; + weight_list[5] = 0.2797053914892766679014678; + weight_list[6] = 0.1294849661688696932706114; + break; + } + case 14: + case 15: { + point_list[0] = -0.9602898564975362316835609; + point_list[1] = -0.7966664774136267395915539; + point_list[2] = -0.5255324099163289858177390; + point_list[3] = -0.1834346424956498049394761; + point_list[4] = +0.1834346424956498049394761; + point_list[5] = +0.5255324099163289858177390; + point_list[6] = +0.7966664774136267395915539; + point_list[7] = +0.9602898564975362316835609; + + weight_list[0] = 0.1012285362903762591525314; + weight_list[1] = 0.2223810344533744705443560; + weight_list[2] = 0.3137066458778872873379622; + weight_list[3] = 0.3626837833783619829651504; + weight_list[4] = 0.3626837833783619829651504; + weight_list[5] = 0.3137066458778872873379622; + weight_list[6] = 0.2223810344533744705443560; + weight_list[7] = 0.1012285362903762591525314; + break; + } + case 16: + case 17: { + point_list[0] = -0.9681602395076260898355762; + point_list[1] = -0.8360311073266357942994298; + point_list[2] = -0.6133714327005903973087020; + point_list[3] = -0.3242534234038089290385380; + point_list[4] = 0; + point_list[5] = +0.3242534234038089290385380; + point_list[6] = +0.6133714327005903973087020; + point_list[7] = +0.8360311073266357942994298; + point_list[8] = +0.9681602395076260898355762; + + weight_list[0] = 0.0812743883615744119718922; + weight_list[1] = 0.1806481606948574040584720; + weight_list[2] = 0.2606106964029354623187429; + weight_list[3] = 0.3123470770400028400686304; + weight_list[4] = 0.3302393550012597631645251; + weight_list[5] = 0.3123470770400028400686304; + weight_list[6] = 0.2606106964029354623187429; + weight_list[7] = 0.1806481606948574040584720; + weight_list[8] = 0.0812743883615744119718922; + break; + } + case 18: + case 19: { + point_list[0] = -0.9739065285171717200779640; + point_list[1] = -0.8650633666889845107320967; + point_list[2] = -0.6794095682990244062343274; + point_list[3] = -0.4333953941292471907992659; + point_list[4] = -0.1488743389816312108848260; + point_list[5] = +0.1488743389816312108848260; + point_list[6] = +0.4333953941292471907992659; + point_list[7] = +0.6794095682990244062343274; + point_list[8] = +0.8650633666889845107320967; + point_list[9] = +0.9739065285171717200779640; + + weight_list[0] = 0.0666713443086881375935688; + weight_list[1] = 0.1494513491505805931457763; + weight_list[2] = 0.2190863625159820439955349; + weight_list[3] = 0.2692667193099963550912269; + weight_list[4] = 0.2955242247147528701738930; + weight_list[5] = 0.2955242247147528701738930; + weight_list[6] = 0.2692667193099963550912269; + weight_list[7] = 0.2190863625159820439955349; + weight_list[8] = 0.1494513491505805931457763; + weight_list[9] = 0.0666713443086881375935688; + break; + } + case 20: + case 21: { + point_list[0] = -0.9782286581460569928039380; + point_list[1] = -0.8870625997680952990751578; + point_list[2] = -0.7301520055740493240934163; + point_list[3] = -0.5190961292068118159257257; + point_list[4] = -0.2695431559523449723315320; + point_list[5] = 0; + point_list[6] = +0.2695431559523449723315320; + point_list[7] = +0.5190961292068118159257257; + point_list[8] = +0.7301520055740493240934163; + point_list[9] = +0.8870625997680952990751578; + point_list[10] = +0.9782286581460569928039380; + + weight_list[0] = 0.0556685671161736664827537; + weight_list[1] = 0.1255803694649046246346940; + weight_list[2] = 0.1862902109277342514260980; + weight_list[3] = 0.2331937645919904799185237; + weight_list[4] = 0.2628045445102466621806889; + weight_list[5] = 0.2729250867779006307144835; + weight_list[6] = 0.2628045445102466621806889; + weight_list[7] = 0.2331937645919904799185237; + weight_list[8] = 0.1862902109277342514260980; + weight_list[9] = 0.1255803694649046246346940; + weight_list[10] = 0.0556685671161736664827537; + break; + } + case 22: + case 23: { + point_list[0] = -0.9815606342467192506905491; + point_list[1] = -0.9041172563704748566784659; + point_list[2] = -0.7699026741943046870368938; + point_list[3] = -0.5873179542866174472967024; + point_list[4] = -0.3678314989981801937526915; + point_list[5] = -0.1252334085114689154724414; + point_list[6] = +0.1252334085114689154724414; + point_list[7] = +0.3678314989981801937526915; + point_list[8] = +0.5873179542866174472967024; + point_list[9] = +0.7699026741943046870368938; + point_list[10] = +0.9041172563704748566784659; + point_list[11] = +0.9815606342467192506905491; + + weight_list[0] = 0.0471753363865118271946160; + weight_list[1] = 0.1069393259953184309602547; + weight_list[2] = 0.1600783285433462263346525; + weight_list[3] = 0.2031674267230659217490645; + weight_list[4] = 0.2334925365383548087608499; + weight_list[5] = 0.2491470458134027850005624; + weight_list[6] = 0.2491470458134027850005624; + weight_list[7] = 0.2334925365383548087608499; + weight_list[8] = 0.2031674267230659217490645; + weight_list[9] = 0.1600783285433462263346525; + weight_list[10] = 0.1069393259953184309602547; + weight_list[11] = 0.0471753363865118271946160; + break; + } + default: { + throw NormalError("Gauss-Legendre quadrature formulae handle orders up to 23"); + } + } + + m_point_list = point_list; + m_weight_list = weight_list; +} + +template <> +void +GaussLegendreQuadrature<2>::_buildPointAndWeightLists() +{ + const size_t nb_points_1d = this->m_order / 2 + 1; + const size_t nb_points = nb_points_1d * nb_points_1d; + + SmallArray<TinyVector<2>> point_list(nb_points); + SmallArray<double> weight_list(nb_points); + + GaussLegendreQuadrature<1> gauss_legendre_1d(m_order); + const auto& point_list_1d = gauss_legendre_1d.pointList(); + const auto& weight_list_1d = gauss_legendre_1d.weightList(); + + size_t l = 0; + for (size_t i = 0; i < nb_points_1d; ++i) { + for (size_t j = 0; j < nb_points_1d; ++j, ++l) { + point_list[l] = TinyVector<2>{point_list_1d[i][0], point_list_1d[j][0]}; + weight_list[l] = weight_list_1d[i] * weight_list_1d[j]; + } + } + + this->m_point_list = point_list; + this->m_weight_list = weight_list; +} + +template <> +void +GaussLegendreQuadrature<3>::_buildPointAndWeightLists() +{ + const size_t nb_points_1d = this->m_order / 2 + 1; + const size_t nb_points = nb_points_1d * nb_points_1d * nb_points_1d; + + SmallArray<TinyVector<3>> point_list(nb_points); + SmallArray<double> weight_list(nb_points); + + GaussLegendreQuadrature<1> gauss_legendre_1d(m_order); + const auto& point_list_1d = gauss_legendre_1d.pointList(); + const auto& weight_list_1d = gauss_legendre_1d.weightList(); + + size_t l = 0; + for (size_t i = 0; i < nb_points_1d; ++i) { + for (size_t j = 0; j < nb_points_1d; ++j) { + for (size_t k = 0; k < nb_points_1d; ++k, ++l) { + point_list[l] = TinyVector<3>{point_list_1d[i][0], point_list_1d[j][0], point_list_1d[k][0]}; + weight_list[l] = weight_list_1d[i] * weight_list_1d[j] * weight_list_1d[k]; + } + } + } + + this->m_point_list = point_list; + this->m_weight_list = weight_list; +} diff --git a/src/analysis/GaussLegendreQuadrature.hpp b/src/analysis/GaussLegendreQuadrature.hpp new file mode 100644 index 0000000000000000000000000000000000000000..3e4a8b0cc3538f1539464dc2d81d478c1e6b1463 --- /dev/null +++ b/src/analysis/GaussLegendreQuadrature.hpp @@ -0,0 +1,25 @@ +#ifndef GAUSS_LEGENDRE_QUADRATURE_HPP +#define GAUSS_LEGENDRE_QUADRATURE_HPP + +#include <analysis/QuadratureFormula.hpp> + +template <size_t Dimension> +class GaussLegendreQuadrature final : public QuadratureForumla<Dimension> +{ + private: + void _buildPointAndWeightLists(); + + public: + GaussLegendreQuadrature(GaussLegendreQuadrature&&) = default; + GaussLegendreQuadrature(const GaussLegendreQuadrature&) = default; + + explicit GaussLegendreQuadrature(const size_t order) : QuadratureForumla<Dimension>(order) + { + this->_buildPointAndWeightLists(); + } + + GaussLegendreQuadrature() = delete; + ~GaussLegendreQuadrature() = default; +}; + +#endif // GAUSS_LEGENDRE_QUADRATURE_HPP diff --git a/src/analysis/QuadratureFormula.hpp b/src/analysis/QuadratureFormula.hpp new file mode 100644 index 0000000000000000000000000000000000000000..c92d3187246268ec61cf15356d57f7ea39111df8 --- /dev/null +++ b/src/analysis/QuadratureFormula.hpp @@ -0,0 +1,52 @@ +#ifndef QUADRATURE_FORMULA_HPP +#define QUADRATURE_FORMULA_HPP + +#include <algebra/TinyVector.hpp> +#include <utils/PugsAssert.hpp> +#include <utils/PugsMacros.hpp> +#include <utils/SmallArray.hpp> + +template <size_t Dimension> +class QuadratureForumla +{ + protected: + size_t m_order; + SmallArray<const TinyVector<Dimension>> m_point_list; + SmallArray<const double> m_weight_list; + + virtual void _buildPointAndWeightLists() = 0; + + public: + PUGS_INLINE size_t + numberOfPoints() const + { + Assert(m_point_list.size() == m_weight_list.size()); + return m_point_list.size(); + } + + PUGS_INLINE + const SmallArray<const TinyVector<Dimension>>& + pointList() const + { + return m_point_list; + } + + PUGS_INLINE + const SmallArray<const double>& + weightList() const + { + return m_weight_list; + } + + QuadratureForumla(QuadratureForumla&&) = default; + QuadratureForumla(const QuadratureForumla&) = default; + + protected: + explicit QuadratureForumla(const size_t order) : m_order{order} {} + + public: + QuadratureForumla() = delete; + virtual ~QuadratureForumla() = default; +}; + +#endif // QUADRATURE_FORMULA_HPP diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 5cc4fee50ca3e8d71899a58083f1c251fa30068c..b0b2b73281d3c7473cdd0636bd2c1490fd21329c 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -80,6 +80,7 @@ add_executable (unit_tests test_FunctionProcessor.cpp test_FunctionSymbolId.cpp test_FunctionTable.cpp + test_GaussLegendreQuadrature.cpp test_IfProcessor.cpp test_IncDecExpressionProcessor.cpp test_INodeProcessor.cpp @@ -145,6 +146,7 @@ target_link_libraries (unit_tests PugsLanguage PugsMesh PugsAlgebra + PugsAnalysis PugsScheme PugsOutput PugsUtils @@ -160,6 +162,7 @@ target_link_libraries (unit_tests target_link_libraries (mpi_unit_tests test_Pugs_MeshDataBase PugsAlgebra + PugsAnalysis PugsUtils PugsLanguage PugsLanguageAST diff --git a/tests/test_FunctionEvaluation.cpp b/tests/test_FunctionEvaluation.cpp index 7893ba07eec45c4b33c842454172dde6446e46ac..2e290556eac3a8d46d76cd9fd114cfa025e6ca2f 100644 --- a/tests/test_FunctionEvaluation.cpp +++ b/tests/test_FunctionEvaluation.cpp @@ -7,114 +7,9 @@ #include <language/utils/FunctionSymbolId.hpp> #include <language/utils/SymbolTable.hpp> #include <utils/PugsAssert.hpp> -// Instantiate to ensure full coverage is performed -// template class EvaluateAtPoints<double(TinyVector<1>)>; -template class TinyVector<1>; -template class EvaluateAtPoints<double(TinyVector<1>)>; -template class IntegrationTools<2>; // clazy:excludeall=non-pod-global-static -TEST_CASE("EvaluateAtPoints", "[integration]") -{ - std::shared_ptr<SymbolTable> s = std::make_shared<SymbolTable>(); - const FunctionSymbolId f{2, s}; - const Array<TinyVector<1>> positions(1); - positions[0] = 1; - SECTION("Instantiation") - { - // positions[0] = 1; - SmallArray<double> result(1); - // result = EvaluateAtPoints<double(TinyVector<1>)>::evaluate(f, positions); - // REQUIRE(result[0] == 1); - } -} - -TEST_CASE("QuadratureFormula", "[analysis]") -{ - SECTION("TensorialGaussLegendre") - { - auto integrate = [](auto f, auto quadrature_formula, const double a, const double b) { - auto point_list = quadrature_formula.pointList(); - auto weight_list = quadrature_formula.weightList(); - - double alpha = 0.5 * (b - a); - double beta = 0.5 * (a + b); - - auto x = [&alpha, &beta](auto x_hat) { return alpha * x_hat + beta; }; - - auto value = weight_list[0] * f(x(point_list[0])); - for (size_t i = 1; i < weight_list.size(); ++i) { - value += weight_list[i] * f(x(point_list[i])); - } - - return alpha * value; - }; - - auto get_order = [&integrate](auto f, auto quadrature_formula, const double a, const double b, - const double exact_value) { - double int_ab = integrate(f, quadrature_formula, a, b); - double int_first_half = integrate(f, quadrature_formula, a, 0.5 * (a + b)); - double int_second_half = integrate(f, quadrature_formula, 0.5 * (a + b), b); - - return -std::log((int_first_half + int_second_half - exact_value) / (int_ab - exact_value)) / std::log(2); - }; - - auto p0 = [](const TinyVector<1>&) { return 1; }; - auto p1 = [](const TinyVector<1>& X) { - const double x = X[0]; - return 2 * x + 1; - }; - auto p2 = [](const TinyVector<1>& X) { - const double x = X[0]; - return 3 * x * x + 2 * x + 1; - }; - auto p3 = [](const TinyVector<1>& X) { - const double x = X[0]; - return 4 * std::pow(x, 3) + 3 * x * x + 2 * x + 1; - }; - auto p4 = [](const TinyVector<1>& X) { - const double x = X[0]; - return 5 * std::pow(x, 4) + 4 * std::pow(x, 3) + 3 * x * x + 2 * x + 1; - }; - - SECTION("exact integration") - { - SECTION("order 0 and 1") - { - TensorialGaussLegendre<1> l0(0); - TensorialGaussLegendre<1> l1(1); - - REQUIRE(l0.numberOfPoints() == 1); - REQUIRE(l1.numberOfPoints() == 1); - - REQUIRE(integrate(p0, l1, 0.5, 1) == Catch::Approx(0.5)); - REQUIRE(integrate(p1, l1, 0, 1) == Catch::Approx(2)); - REQUIRE(integrate(p2, l1, 0, 1) != Catch::Approx(3)); - - REQUIRE(get_order(p2, l1, 0, 1, 3) == Catch::Approx(2)); - } - - SECTION("order 2 and 3") - { - TensorialGaussLegendre<1> l2(2); - TensorialGaussLegendre<1> l3(3); - - REQUIRE(l2.numberOfPoints() == 2); - REQUIRE(l3.numberOfPoints() == 2); - - REQUIRE(integrate(p0, l3, 0.5, 1) == Catch::Approx(0.5)); - REQUIRE(integrate(p1, l3, 0, 1) == Catch::Approx(2)); - REQUIRE(integrate(p2, l3, 0, 1) == Catch::Approx(3)); - REQUIRE(integrate(p3, l3, 0, 1) == Catch::Approx(4)); - REQUIRE(integrate(p4, l3, 0, 1) != Catch::Approx(5)); - - REQUIRE(get_order(p4, l3, 0, 1, 5) == Catch::Approx(4)); - } - } - } -} - TEST_CASE("IntegrationTools", "[integration]") { IntegrationTools<2> integrationtools(QuadratureType::gausslobatto); diff --git a/tests/test_GaussLegendreQuadrature.cpp b/tests/test_GaussLegendreQuadrature.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f2001356081242c33c0412c7d56c174f296f71aa --- /dev/null +++ b/tests/test_GaussLegendreQuadrature.cpp @@ -0,0 +1,503 @@ +#include <catch2/catch_approx.hpp> +#include <catch2/catch_test_macros.hpp> +#include <catch2/matchers/catch_matchers_all.hpp> + +#include <analysis/GaussLegendreQuadrature.hpp> +#include <utils/Exceptions.hpp> + +// clazy:excludeall=non-pod-global-static + +TEST_CASE("GaussLegendreQuadrature", "[analysis]") +{ + auto same_formulas = [](auto quadrature_formula0, auto quadrature_formula1) { + auto point_list0 = quadrature_formula0.pointList(); + auto weight_list0 = quadrature_formula0.weightList(); + + auto point_list1 = quadrature_formula1.pointList(); + auto weight_list1 = quadrature_formula1.weightList(); + + bool is_same = true; + + for (size_t i = 0; i < point_list0.size(); ++i) { + if (point_list0[i] != point_list1[i]) { + return false; + } + } + for (size_t i = 0; i < weight_list0.size(); ++i) { + if (weight_list0[i] != weight_list1[i]) { + return false; + } + } + + return is_same; + }; + + SECTION("1D") + { + auto integrate = [](auto f, auto quadrature_formula, const double a, const double b) { + auto point_list = quadrature_formula.pointList(); + auto weight_list = quadrature_formula.weightList(); + + double alpha = 0.5 * (b - a); + double beta = 0.5 * (a + b); + + auto x = [&alpha, &beta](auto x_hat) { return alpha * x_hat + beta; }; + + auto value = weight_list[0] * f(x(point_list[0])); + for (size_t i = 1; i < weight_list.size(); ++i) { + value += weight_list[i] * f(x(point_list[i])); + } + + return alpha * value; + }; + + auto get_order = [&integrate](auto f, auto quadrature_formula, const double a, const double b, + const double exact_value) { + double int_ab = integrate(f, quadrature_formula, a, b); + double int_first_half = integrate(f, quadrature_formula, a, 0.5 * (a + b)); + double int_second_half = integrate(f, quadrature_formula, 0.5 * (a + b), b); + + return -std::log((int_first_half + int_second_half - exact_value) / (int_ab - exact_value)) / std::log(2); + }; + + auto p0 = [](const TinyVector<1>&) { return 1; }; + auto p1 = [&p0](const TinyVector<1>& X) { + const double x = X[0]; + return 2 * x + p0(X); + }; + auto p2 = [&p1](const TinyVector<1>& X) { + const double x = X[0]; + return 3 * x * x + p1(X); + }; + auto p3 = [&p2](const TinyVector<1>& X) { + const double x = X[0]; + return 4 * std::pow(x, 3) + p2(X); + }; + auto p4 = [&p3](const TinyVector<1>& X) { + const double x = X[0]; + return 5 * std::pow(x, 4) + p3(X); + }; + auto p5 = [&p4](const TinyVector<1>& X) { + const double x = X[0]; + return 6 * std::pow(x, 5) + p4(X); + }; + auto p6 = [&p5](const TinyVector<1>& X) { + const double x = X[0]; + return 7 * std::pow(x, 6) + p5(X); + }; + auto p7 = [&p6](const TinyVector<1>& X) { + const double x = X[0]; + return 8 * std::pow(x, 7) + p6(X); + }; + auto p8 = [&p7](const TinyVector<1>& X) { + const double x = X[0]; + return 9 * std::pow(x, 8) + p7(X); + }; + auto p9 = [&p8](const TinyVector<1>& X) { + const double x = X[0]; + return 10 * std::pow(x, 9) + p8(X); + }; + auto p10 = [&p9](const TinyVector<1>& X) { + const double x = X[0]; + return 11 * std::pow(x, 10) + p9(X); + }; + auto p11 = [&p10](const TinyVector<1>& X) { + const double x = X[0]; + return 12 * std::pow(x, 11) + p10(X); + }; + auto p12 = [&p11](const TinyVector<1>& X) { + const double x = X[0]; + return 13 * std::pow(x, 12) + p11(X); + }; + auto p13 = [&p12](const TinyVector<1>& X) { + const double x = X[0]; + return 14 * std::pow(x, 13) + p12(X); + }; + auto p14 = [&p13](const TinyVector<1>& X) { + const double x = X[0]; + return 15 * std::pow(x, 14) + p13(X); + }; + auto p15 = [&p14](const TinyVector<1>& X) { + const double x = X[0]; + return 16 * std::pow(x, 15) + p14(X); + }; + auto p16 = [&p15](const TinyVector<1>& X) { + const double x = X[0]; + return 17 * std::pow(x, 16) + p15(X); + }; + auto p17 = [&p16](const TinyVector<1>& X) { + const double x = X[0]; + return 18 * std::pow(x, 17) + p16(X); + }; + auto p18 = [&p17](const TinyVector<1>& X) { + const double x = X[0]; + return 19 * std::pow(x, 18) + p17(X); + }; + auto p19 = [&p18](const TinyVector<1>& X) { + const double x = X[0]; + return 20 * std::pow(x, 19) + p18(X); + }; + auto p20 = [&p19](const TinyVector<1>& X) { + const double x = X[0]; + return 21 * std::pow(x, 20) + p19(X); + }; + auto p21 = [&p20](const TinyVector<1>& X) { + const double x = X[0]; + return 22 * std::pow(x, 21) + p20(X); + }; + auto p22 = [&p21](const TinyVector<1>& X) { + const double x = X[0]; + return 23 * std::pow(x, 22) + p21(X); + }; + auto p23 = [&p22](const TinyVector<1>& X) { + const double x = X[0]; + return 24 * std::pow(x, 23) + p22(X); + }; + auto p24 = [&p23](const TinyVector<1>& X) { + const double x = X[0]; + return 25 * std::pow(x, 24) + p23(X); + }; + + SECTION("order 0 and 1") + { + GaussLegendreQuadrature<1> l0(0); + GaussLegendreQuadrature<1> l1(1); + + REQUIRE(same_formulas(l0, l1)); + + REQUIRE(l0.numberOfPoints() == 1); + REQUIRE(l1.numberOfPoints() == 1); + + REQUIRE(integrate(p0, l1, 0.5, 1) == Catch::Approx(0.5)); + REQUIRE(integrate(p1, l1, 0, 1) == Catch::Approx(2)); + REQUIRE(integrate(p2, l1, 0, 1) != Catch::Approx(3)); + + REQUIRE(get_order(p2, l1, -1, 1, 4) == Catch::Approx(2)); + } + + SECTION("order 2 and 3") + { + GaussLegendreQuadrature<1> l2(2); + GaussLegendreQuadrature<1> l3(3); + + REQUIRE(same_formulas(l2, l3)); + + REQUIRE(l2.numberOfPoints() == 2); + REQUIRE(l3.numberOfPoints() == 2); + + REQUIRE(integrate(p0, l3, 0.5, 1) == Catch::Approx(0.5)); + REQUIRE(integrate(p1, l3, 0, 1) == Catch::Approx(2)); + REQUIRE(integrate(p2, l3, 0, 1) == Catch::Approx(3)); + REQUIRE(integrate(p3, l3, 0, 1) == Catch::Approx(4)); + REQUIRE(integrate(p4, l3, 0, 1) != Catch::Approx(5)); + + REQUIRE(get_order(p4, l3, -1, 1, 6) == Catch::Approx(4)); + } + + SECTION("order 4 and 5") + { + GaussLegendreQuadrature<1> l4(4); + GaussLegendreQuadrature<1> l5(5); + + REQUIRE(same_formulas(l4, l5)); + + REQUIRE(l4.numberOfPoints() == 3); + REQUIRE(l5.numberOfPoints() == 3); + + REQUIRE(integrate(p0, l5, 0.5, 1) == Catch::Approx(0.5)); + REQUIRE(integrate(p1, l5, 0, 1) == Catch::Approx(2)); + REQUIRE(integrate(p2, l5, 0, 1) == Catch::Approx(3)); + REQUIRE(integrate(p3, l5, 0, 1) == Catch::Approx(4)); + REQUIRE(integrate(p4, l5, 0, 1) == Catch::Approx(5)); + REQUIRE(integrate(p5, l5, 0, 1) == Catch::Approx(6)); + REQUIRE(integrate(p6, l5, 0, 1) != Catch::Approx(7)); + + REQUIRE(get_order(p6, l5, -1, 1, 8) == Catch::Approx(6)); + } + + SECTION("order 6 and 7") + { + GaussLegendreQuadrature<1> l6(6); + GaussLegendreQuadrature<1> l7(7); + + REQUIRE(same_formulas(l6, l7)); + + REQUIRE(l6.numberOfPoints() == 4); + REQUIRE(l7.numberOfPoints() == 4); + + REQUIRE(integrate(p0, l7, 0.5, 1) == Catch::Approx(0.5)); + REQUIRE(integrate(p1, l7, 0, 1) == Catch::Approx(2)); + REQUIRE(integrate(p2, l7, 0, 1) == Catch::Approx(3)); + REQUIRE(integrate(p3, l7, 0, 1) == Catch::Approx(4)); + REQUIRE(integrate(p4, l7, 0, 1) == Catch::Approx(5)); + REQUIRE(integrate(p5, l7, 0, 1) == Catch::Approx(6)); + REQUIRE(integrate(p6, l7, 0, 1) == Catch::Approx(7)); + REQUIRE(integrate(p7, l7, 0, 1) == Catch::Approx(8)); + REQUIRE(integrate(p8, l7, 0, 1) != Catch::Approx(9)); + + REQUIRE(get_order(p8, l7, -1, 1, 10) == Catch::Approx(8)); + } + + SECTION("order 8 and 9") + { + GaussLegendreQuadrature<1> l8(8); + GaussLegendreQuadrature<1> l9(9); + + REQUIRE(same_formulas(l8, l9)); + + REQUIRE(l8.numberOfPoints() == 5); + REQUIRE(l9.numberOfPoints() == 5); + + REQUIRE(integrate(p0, l9, 0.5, 1) == Catch::Approx(0.5)); + REQUIRE(integrate(p1, l9, 0, 1) == Catch::Approx(2)); + REQUIRE(integrate(p2, l9, 0, 1) == Catch::Approx(3)); + REQUIRE(integrate(p3, l9, 0, 1) == Catch::Approx(4)); + REQUIRE(integrate(p4, l9, 0, 1) == Catch::Approx(5)); + REQUIRE(integrate(p5, l9, 0, 1) == Catch::Approx(6)); + REQUIRE(integrate(p6, l9, 0, 1) == Catch::Approx(7)); + REQUIRE(integrate(p7, l9, 0, 1) == Catch::Approx(8)); + REQUIRE(integrate(p8, l9, 0, 1) == Catch::Approx(9)); + REQUIRE(integrate(p9, l9, 0, 1) == Catch::Approx(10)); + REQUIRE(integrate(p10, l9, 0, 1) != Catch::Approx(11).epsilon(1E-13)); + + REQUIRE(get_order(p10, l9, -1, 1, 12) == Catch::Approx(10)); + } + + SECTION("order 10 and 11") + { + GaussLegendreQuadrature<1> l10(10); + GaussLegendreQuadrature<1> l11(11); + + REQUIRE(same_formulas(l10, l11)); + + REQUIRE(l10.numberOfPoints() == 6); + REQUIRE(l11.numberOfPoints() == 6); + + REQUIRE(integrate(p0, l11, 0.5, 1) == Catch::Approx(0.5)); + REQUIRE(integrate(p1, l11, 0, 1) == Catch::Approx(2)); + REQUIRE(integrate(p2, l11, 0, 1) == Catch::Approx(3)); + REQUIRE(integrate(p3, l11, 0, 1) == Catch::Approx(4)); + REQUIRE(integrate(p4, l11, 0, 1) == Catch::Approx(5)); + REQUIRE(integrate(p5, l11, 0, 1) == Catch::Approx(6)); + REQUIRE(integrate(p6, l11, 0, 1) == Catch::Approx(7)); + REQUIRE(integrate(p7, l11, 0, 1) == Catch::Approx(8)); + REQUIRE(integrate(p8, l11, 0, 1) == Catch::Approx(9)); + REQUIRE(integrate(p9, l11, 0, 1) == Catch::Approx(10)); + REQUIRE(integrate(p10, l11, 0, 1) == Catch::Approx(11)); + REQUIRE(integrate(p11, l11, 0, 1) == Catch::Approx(12)); + REQUIRE(integrate(p12, l11, 0, 1) != Catch::Approx(13).epsilon(1E-13)); + + REQUIRE(get_order(p12, l11, -1, 1, 14) == Catch::Approx(12)); + } + + SECTION("order 12 and 13") + { + GaussLegendreQuadrature<1> l12(12); + GaussLegendreQuadrature<1> l13(13); + + REQUIRE(same_formulas(l12, l13)); + + REQUIRE(l12.numberOfPoints() == 7); + REQUIRE(l13.numberOfPoints() == 7); + + REQUIRE(integrate(p0, l13, 0.5, 1) == Catch::Approx(0.5)); + REQUIRE(integrate(p1, l13, 0, 1) == Catch::Approx(2)); + REQUIRE(integrate(p2, l13, 0, 1) == Catch::Approx(3)); + REQUIRE(integrate(p3, l13, 0, 1) == Catch::Approx(4)); + REQUIRE(integrate(p4, l13, 0, 1) == Catch::Approx(5)); + REQUIRE(integrate(p5, l13, 0, 1) == Catch::Approx(6)); + REQUIRE(integrate(p6, l13, 0, 1) == Catch::Approx(7)); + REQUIRE(integrate(p7, l13, 0, 1) == Catch::Approx(8)); + REQUIRE(integrate(p8, l13, 0, 1) == Catch::Approx(9)); + REQUIRE(integrate(p9, l13, 0, 1) == Catch::Approx(10)); + REQUIRE(integrate(p10, l13, 0, 1) == Catch::Approx(11)); + REQUIRE(integrate(p11, l13, 0, 1) == Catch::Approx(12)); + REQUIRE(integrate(p12, l13, 0, 1) == Catch::Approx(13)); + REQUIRE(integrate(p13, l13, 0, 1) == Catch::Approx(14)); + REQUIRE(integrate(p14, l13, 0, 1) != Catch::Approx(15).epsilon(1E-13)); + + REQUIRE(get_order(p14, l13, -1, 1, 16) == Catch::Approx(14)); + } + + SECTION("order 14 and 15") + { + GaussLegendreQuadrature<1> l14(14); + GaussLegendreQuadrature<1> l15(15); + + REQUIRE(same_formulas(l14, l15)); + + REQUIRE(l14.numberOfPoints() == 8); + REQUIRE(l15.numberOfPoints() == 8); + + REQUIRE(integrate(p0, l15, 0.5, 1) == Catch::Approx(0.5)); + REQUIRE(integrate(p1, l15, 0, 1) == Catch::Approx(2)); + REQUIRE(integrate(p2, l15, 0, 1) == Catch::Approx(3)); + REQUIRE(integrate(p3, l15, 0, 1) == Catch::Approx(4)); + REQUIRE(integrate(p4, l15, 0, 1) == Catch::Approx(5)); + REQUIRE(integrate(p5, l15, 0, 1) == Catch::Approx(6)); + REQUIRE(integrate(p6, l15, 0, 1) == Catch::Approx(7)); + REQUIRE(integrate(p7, l15, 0, 1) == Catch::Approx(8)); + REQUIRE(integrate(p8, l15, 0, 1) == Catch::Approx(9)); + REQUIRE(integrate(p9, l15, 0, 1) == Catch::Approx(10)); + REQUIRE(integrate(p10, l15, 0, 1) == Catch::Approx(11)); + REQUIRE(integrate(p11, l15, 0, 1) == Catch::Approx(12)); + REQUIRE(integrate(p12, l15, 0, 1) == Catch::Approx(13)); + REQUIRE(integrate(p13, l15, 0, 1) == Catch::Approx(14)); + REQUIRE(integrate(p14, l15, 0, 1) == Catch::Approx(15)); + REQUIRE(integrate(p15, l15, 0, 1) == Catch::Approx(16)); + REQUIRE(integrate(p16, l15, 0, 1) != Catch::Approx(17).epsilon(1E-13)); + + REQUIRE(get_order(p16, l15, -1, 1, 18) == Catch::Approx(16)); + } + + SECTION("order 16 and 17") + { + GaussLegendreQuadrature<1> l16(16); + GaussLegendreQuadrature<1> l17(17); + + REQUIRE(same_formulas(l16, l17)); + + REQUIRE(l16.numberOfPoints() == 9); + REQUIRE(l17.numberOfPoints() == 9); + + REQUIRE(integrate(p0, l17, 0.5, 1) == Catch::Approx(0.5)); + REQUIRE(integrate(p1, l17, 0, 1) == Catch::Approx(2)); + REQUIRE(integrate(p2, l17, 0, 1) == Catch::Approx(3)); + REQUIRE(integrate(p3, l17, 0, 1) == Catch::Approx(4)); + REQUIRE(integrate(p4, l17, 0, 1) == Catch::Approx(5)); + REQUIRE(integrate(p5, l17, 0, 1) == Catch::Approx(6)); + REQUIRE(integrate(p6, l17, 0, 1) == Catch::Approx(7)); + REQUIRE(integrate(p7, l17, 0, 1) == Catch::Approx(8)); + REQUIRE(integrate(p8, l17, 0, 1) == Catch::Approx(9)); + REQUIRE(integrate(p9, l17, 0, 1) == Catch::Approx(10)); + REQUIRE(integrate(p10, l17, 0, 1) == Catch::Approx(11)); + REQUIRE(integrate(p11, l17, 0, 1) == Catch::Approx(12)); + REQUIRE(integrate(p12, l17, 0, 1) == Catch::Approx(13)); + REQUIRE(integrate(p13, l17, 0, 1) == Catch::Approx(14)); + REQUIRE(integrate(p14, l17, 0, 1) == Catch::Approx(15)); + REQUIRE(integrate(p15, l17, 0, 1) == Catch::Approx(16)); + REQUIRE(integrate(p16, l17, 0, 1) == Catch::Approx(17)); + REQUIRE(integrate(p17, l17, 0, 1) == Catch::Approx(18)); + REQUIRE(integrate(p18, l17, 0, 1) != Catch::Approx(19).epsilon(1E-13)); + + REQUIRE(get_order(p18, l17, -1, 1, 20) == Catch::Approx(18)); + } + + SECTION("order 18 and 19") + { + GaussLegendreQuadrature<1> l18(18); + GaussLegendreQuadrature<1> l19(19); + + REQUIRE(same_formulas(l18, l19)); + + REQUIRE(l18.numberOfPoints() == 10); + REQUIRE(l19.numberOfPoints() == 10); + + REQUIRE(integrate(p0, l19, 0.5, 1) == Catch::Approx(0.5)); + REQUIRE(integrate(p1, l19, 0, 1) == Catch::Approx(2)); + REQUIRE(integrate(p2, l19, 0, 1) == Catch::Approx(3)); + REQUIRE(integrate(p3, l19, 0, 1) == Catch::Approx(4)); + REQUIRE(integrate(p4, l19, 0, 1) == Catch::Approx(5)); + REQUIRE(integrate(p5, l19, 0, 1) == Catch::Approx(6)); + REQUIRE(integrate(p6, l19, 0, 1) == Catch::Approx(7)); + REQUIRE(integrate(p7, l19, 0, 1) == Catch::Approx(8)); + REQUIRE(integrate(p8, l19, 0, 1) == Catch::Approx(9)); + REQUIRE(integrate(p9, l19, 0, 1) == Catch::Approx(10)); + REQUIRE(integrate(p10, l19, 0, 1) == Catch::Approx(11)); + REQUIRE(integrate(p11, l19, 0, 1) == Catch::Approx(12)); + REQUIRE(integrate(p12, l19, 0, 1) == Catch::Approx(13)); + REQUIRE(integrate(p13, l19, 0, 1) == Catch::Approx(14)); + REQUIRE(integrate(p14, l19, 0, 1) == Catch::Approx(15)); + REQUIRE(integrate(p15, l19, 0, 1) == Catch::Approx(16)); + REQUIRE(integrate(p16, l19, 0, 1) == Catch::Approx(17)); + REQUIRE(integrate(p17, l19, 0, 1) == Catch::Approx(18)); + REQUIRE(integrate(p18, l19, 0, 1) == Catch::Approx(19)); + REQUIRE(integrate(p19, l19, 0, 1) == Catch::Approx(20)); + REQUIRE(integrate(p20, l19, 0, 1) != Catch::Approx(21).epsilon(1E-13)); + + REQUIRE(get_order(p20, l19, -1, 1, 22) == Catch::Approx(20)); + } + + SECTION("order 20 and 21") + { + GaussLegendreQuadrature<1> l20(20); + GaussLegendreQuadrature<1> l21(21); + + REQUIRE(same_formulas(l20, l21)); + + REQUIRE(l20.numberOfPoints() == 11); + REQUIRE(l21.numberOfPoints() == 11); + + REQUIRE(integrate(p0, l21, 0.5, 1) == Catch::Approx(0.5)); + REQUIRE(integrate(p1, l21, 0, 1) == Catch::Approx(2)); + REQUIRE(integrate(p2, l21, 0, 1) == Catch::Approx(3)); + REQUIRE(integrate(p3, l21, 0, 1) == Catch::Approx(4)); + REQUIRE(integrate(p4, l21, 0, 1) == Catch::Approx(5)); + REQUIRE(integrate(p5, l21, 0, 1) == Catch::Approx(6)); + REQUIRE(integrate(p6, l21, 0, 1) == Catch::Approx(7)); + REQUIRE(integrate(p7, l21, 0, 1) == Catch::Approx(8)); + REQUIRE(integrate(p8, l21, 0, 1) == Catch::Approx(9)); + REQUIRE(integrate(p9, l21, 0, 1) == Catch::Approx(10)); + REQUIRE(integrate(p10, l21, 0, 1) == Catch::Approx(11)); + REQUIRE(integrate(p11, l21, 0, 1) == Catch::Approx(12)); + REQUIRE(integrate(p12, l21, 0, 1) == Catch::Approx(13)); + REQUIRE(integrate(p13, l21, 0, 1) == Catch::Approx(14)); + REQUIRE(integrate(p14, l21, 0, 1) == Catch::Approx(15)); + REQUIRE(integrate(p15, l21, 0, 1) == Catch::Approx(16)); + REQUIRE(integrate(p16, l21, 0, 1) == Catch::Approx(17)); + REQUIRE(integrate(p17, l21, 0, 1) == Catch::Approx(18)); + REQUIRE(integrate(p18, l21, 0, 1) == Catch::Approx(19)); + REQUIRE(integrate(p19, l21, 0, 1) == Catch::Approx(20)); + REQUIRE(integrate(p20, l21, 0, 1) == Catch::Approx(21)); + REQUIRE(integrate(p21, l21, 0, 1) == Catch::Approx(22)); + REQUIRE(integrate(p22, l21, 0, 1) != Catch::Approx(23).epsilon(1E-14)); + + REQUIRE(get_order(p22, l21, -1, 1, 24) == Catch::Approx(22).margin(0.02)); + } + + SECTION("order 22 and 23") + { + GaussLegendreQuadrature<1> l22(22); + GaussLegendreQuadrature<1> l23(23); + + REQUIRE(same_formulas(l22, l23)); + + REQUIRE(l22.numberOfPoints() == 12); + REQUIRE(l23.numberOfPoints() == 12); + + REQUIRE(integrate(p0, l23, 0.5, 1) == Catch::Approx(0.5)); + REQUIRE(integrate(p1, l23, 0, 1) == Catch::Approx(2)); + REQUIRE(integrate(p2, l23, 0, 1) == Catch::Approx(3)); + REQUIRE(integrate(p3, l23, 0, 1) == Catch::Approx(4)); + REQUIRE(integrate(p4, l23, 0, 1) == Catch::Approx(5)); + REQUIRE(integrate(p5, l23, 0, 1) == Catch::Approx(6)); + REQUIRE(integrate(p6, l23, 0, 1) == Catch::Approx(7)); + REQUIRE(integrate(p7, l23, 0, 1) == Catch::Approx(8)); + REQUIRE(integrate(p8, l23, 0, 1) == Catch::Approx(9)); + REQUIRE(integrate(p9, l23, 0, 1) == Catch::Approx(10)); + REQUIRE(integrate(p10, l23, 0, 1) == Catch::Approx(11)); + REQUIRE(integrate(p11, l23, 0, 1) == Catch::Approx(12)); + REQUIRE(integrate(p12, l23, 0, 1) == Catch::Approx(13)); + REQUIRE(integrate(p13, l23, 0, 1) == Catch::Approx(14)); + REQUIRE(integrate(p14, l23, 0, 1) == Catch::Approx(15)); + REQUIRE(integrate(p15, l23, 0, 1) == Catch::Approx(16)); + REQUIRE(integrate(p16, l23, 0, 1) == Catch::Approx(17)); + REQUIRE(integrate(p17, l23, 0, 1) == Catch::Approx(18)); + REQUIRE(integrate(p18, l23, 0, 1) == Catch::Approx(19)); + REQUIRE(integrate(p19, l23, 0, 1) == Catch::Approx(20)); + REQUIRE(integrate(p20, l23, 0, 1) == Catch::Approx(21)); + REQUIRE(integrate(p21, l23, 0, 1) == Catch::Approx(22)); + REQUIRE(integrate(p22, l23, 0, 1) == Catch::Approx(23)); + REQUIRE(integrate(p23, l23, 0, 1) == Catch::Approx(24)); + REQUIRE(integrate(p24, l23, 0, 1) != Catch::Approx(25).epsilon(1E-15)); + + REQUIRE(get_order(p24, l23, -1, 1, 26) == Catch::Approx(24).margin(0.05)); + } + + SECTION("not implemented formulae") + { + REQUIRE_THROWS_WITH(GaussLegendreQuadrature<1>(24), + "error: Gauss-Legendre quadrature formulae handle orders up to 23"); + } + } +}