diff --git a/.gitignore b/.gitignore index efec6b54f2..a9e4428e9d 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ build/ .deps/ .libs/ +.cache/ Makefile Makefile.in diff --git a/.gitmodules b/.gitmodules index 5281fdb8b3..8a55da5444 100644 --- a/.gitmodules +++ b/.gitmodules @@ -6,3 +6,6 @@ path = sc url = https://github.com/cburstedde/libsc.git ignore = dirty +[submodule "thirdparty/unordered_dense"] + path = thirdparty/unordered_dense + url = https://github.com/martinus/unordered_dense.git diff --git a/CMakeLists.txt b/CMakeLists.txt index a2396fe4e0..2b1b96a2fe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,6 +28,8 @@ project( LANGUAGES C CXX VERSION "${T8CODE_VERSION_MAJOR}.${T8CODE_VERSION_MINOR}.${T8CODE_VERSION_PATCH}" ) + +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) include( GNUInstallDirs) include( CTest ) @@ -52,6 +54,7 @@ option( T8CODE_BUILD_FORTRAN_INTERFACE "Build t8code's Fortran interface" OFF ) option( T8CODE_ENABLE_MPI "Enable t8code's features which rely on MPI" ON ) option( T8CODE_ENABLE_VTK "Enable t8code's features which rely on VTK" OFF ) +option( T8CODE_ENABLE_MRA "Enable t8code's features which rely on MRA" OFF ) option( T8CODE_ENABLE_OCC "Enable t8code's features which rely on OpenCASCADE" OFF ) option( T8CODE_ENABLE_NETCDF "Enable t8code's features which rely on netCDF" OFF ) @@ -106,6 +109,13 @@ if( T8CODE_ENABLE_VTK ) endif (VTK_FOUND) endif( T8CODE_ENABLE_VTK ) +if( T8CODE_ENABLE_MRA ) + find_package(GSL REQUIRED) + if(GSL_FOUND) + message("Found GSL") + endif(GSL_FOUND) +endif( T8CODE_ENABLE_MRA ) + if( T8CODE_ENABLE_OCC ) find_package( OpenCASCADE REQUIRED COMPONENTS TKBO TKPrim TKTopAlgo diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a6c24ce346..8f74dc0b9b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -122,6 +122,74 @@ if( T8CODE_ENABLE_OCC ) ) endif() +if( T8CODE_ENABLE_MRA ) + target_compile_definitions( T8 PUBLIC T8_ENABLE_MRA ) + + # Add GSL library + target_link_libraries( T8 PUBLIC GSL::gsl GSL::gslcblas ) + + # Add unordered_dense library https://github.com/martinus/unordered_dense + set( UNORDERED_DENSE_DIR "${CMAKE_SOURCE_DIR}/thirdparty/unordered_dense/include" ) + + target_include_directories(T8 PUBLIC + $ + $ + $ + ) + + target_sources( T8 PRIVATE + t8_mra/num/basis_functions.cxx + t8_mra/num/dunavant.cxx + ) + + install( FILES + t8_mra/t8_mra.hxx + DESTINATION include/t8_mra ) + + install( FILES + t8_mra/core/base.hxx + t8_mra/core/adaptation.hxx + t8_mra/core/mst.hxx + DESTINATION include/t8_mra/core ) + + install( FILES + t8_mra/io/vtk.hxx + DESTINATION include/t8_mra/io ) + + install( FILES + t8_mra/shapes/triangle.hxx + t8_mra/shapes/cartesian.hxx + DESTINATION include/t8_mra/shapes ) + + install( FILES + t8_mra/criteria/coarsening_criterion.hxx + t8_mra/criteria/refinement_criterion.hxx + DESTINATION include/t8_mra/criteria ) + + install( FILES + t8_mra/data/element_data.hxx + t8_mra/data/levelindex_map.hxx + t8_mra/data/levelindex_set.hxx + t8_mra/data/levelmultiindex.hxx + t8_mra/data/triangle_order.hxx + DESTINATION include/t8_mra/data ) + + install( FILES + t8_mra/num/basis_functions.hxx + t8_mra/num/dg_basis.hxx + t8_mra/num/dunavant.hxx + t8_mra/num/geometry.hxx + t8_mra/num/legendre_basis.hxx + t8_mra/num/mask_coefficients.hxx + t8_mra/num/mask_coefficients_compute.hxx + t8_mra/num/mask_coefficients_triangle.hxx + t8_mra/num/mat.hxx + t8_mra/num/multiindex.hxx + t8_mra/num/quadrature.hxx + t8_mra/num/vec.hxx + DESTINATION include/t8_mra/num ) +endif() + if( T8CODE_BUILD_PEDANTIC ) target_compile_options( T8 PUBLIC -pedantic ) set (T8_CXXFLAGS "${T8_CXXFLAGS} -Wpedantic") diff --git a/src/t8_mra/core/adaptation.hxx b/src/t8_mra/core/adaptation.hxx new file mode 100644 index 0000000000..9f6717c5d6 --- /dev/null +++ b/src/t8_mra/core/adaptation.hxx @@ -0,0 +1,854 @@ +#pragma once + +#ifdef T8_ENABLE_MRA + +#include "t8_mra/core/base.hxx" +#include "t8_mra/criteria/coarsening_criterion.hxx" +#include "t8_mra/criteria/refinement_criterion.hxx" +#include "t8_forest/t8_forest_adapt.h" +#include "t8_forest/t8_forest_iterate.h" + +#include +#include + +namespace t8_mra +{ + +/** + * @brief Adaptation mixin for multiscale classes + * + * Provides the t8code forest adaptation functionality: + * - coarsen(): remove non-significant detail information level by level + * - refine(): refine steep families and grade their neighbourhood + * - the adapt/iterate_replace machinery keeping lmi_idx and lmi_map + * in sync with the forest leaves + * + * What counts as "significant" or in need of refinement is decided by + * exchangeable criteria: a coarsening_criterion (default: hard_thresholding, + * see criteria/coarsening_criterion.hxx) and a refinement_criterion (default: + * harten_prediction, see criteria/refinement_criterion.hxx). + * + * @tparam Derived CRTP-derived class (multiscale implementation) + */ +template +class multiscale_adaptation { + protected: + Derived & + derived () + { + return static_cast (*this); + } + + const Derived & + derived () const + { + return static_cast (*this); + } + + /** + * @brief Reset all per-pass multiscale state + * + * Stale td_set/d_map entries would corrupt later thresholding, stale + * refinement/coarsening sets would adapt the wrong cells. + */ + void + clear_multiscale_state () + { + derived ().d_map.erase_all (); + derived ().td_set.erase_all (); + derived ().refinement_set.erase_all (); + derived ().coarsening_set.erase_all (); + } + + /** + * @brief Adapt the forest with the given callback and rebuild the lmi index + * + * Performs one t8_forest_new_adapt pass, moves the lmi_map (already + * updated by the MST operations) into fresh user data of the new forest, + * rebuilds the per-leaf lmi index via iterate_replace, releases the old + * forest and runs the element-specific post-adaptation hook. + */ + void + adapt_forest (t8_forest_adapt_t adapt_callback) + { + using element_t = typename Derived::element_t; + using levelmultiindex = typename Derived::levelmultiindex; + + t8_forest_ref (derived ().forest); + t8_forest_t new_forest = t8_forest_new_adapt (derived ().forest, adapt_callback, 0, 0, derived ().get_user_data ()); + + t8_mra::forest_data *new_user_data = T8_ALLOC (t8_mra::forest_data, 1); + new_user_data->lmi_map = new t8_mra::levelindex_map (derived ().maximum_level); + std::swap (new_user_data->lmi_map, derived ().get_user_data ()->lmi_map); + + const auto num_local_elements = t8_forest_get_local_num_leaf_elements (new_forest); + const auto num_ghost_elements = t8_forest_get_num_ghosts (new_forest); + new_user_data->lmi_idx = sc_array_new_count (sizeof (levelmultiindex), num_local_elements + num_ghost_elements); + new_user_data->mra_instance = &derived (); + + t8_forest_set_user_data (new_forest, new_user_data); + t8_forest_iterate_replace (new_forest, derived ().forest, static_iterate_replace_callback); + + auto *old_user_data = derived ().get_user_data (); + delete old_user_data->lmi_map; + sc_array_destroy (old_user_data->lmi_idx); + T8_FREE (old_user_data); + t8_forest_unref (&derived ().forest); + + derived ().forest = new_forest; + derived ().post_adaptation_hook (); + } + + public: + //============================================================================= + // Adaptation Callbacks + //============================================================================= + + /** + * @brief Coarsening callback for t8code + * + * A family is coarsened iff its (first) member is marked in + * coarsening_set. The stored LMI encodes the level, so set membership + * is exact. + * + * @return -1 to coarsen the family, 0 to keep + */ + int + coarsening_callback (t8_forest_t forest, t8_forest_t forest_from, t8_locidx_t which_tree, t8_eclass_t tree_class, + t8_locidx_t local_ele_idx, const t8_scheme_c *scheme, int is_family, int num_elements, + t8_element_t *elements[]) + { + using element_t = typename Derived::element_t; + + if (!is_family) + return 0; + + auto *user_data = reinterpret_cast *> (t8_forest_get_user_data (forest_from)); + + const auto offset = t8_forest_get_tree_element_offset (forest_from, which_tree); + const auto lmi = t8_mra::get_lmi_from_forest_data (user_data, local_ele_idx + offset); + + return derived ().coarsening_set.contains (lmi) ? -1 : 0; + } + + /** + * @brief Refinement callback for t8code + * + * A leaf is refined iff it is marked in refinement_set. The stored LMI + * encodes the level, so set membership is exact. + * + * @return 1 to refine, 0 to keep + */ + int + refinement_callback (t8_forest_t forest, t8_forest_t forest_from, t8_locidx_t which_tree, t8_eclass_t tree_class, + t8_locidx_t local_ele_idx, const t8_scheme_c *scheme, int is_family, int num_elements, + t8_element_t *elements[]) + { + using element_t = typename Derived::element_t; + + auto *user_data = reinterpret_cast *> (t8_forest_get_user_data (forest_from)); + + const auto offset = t8_forest_get_tree_element_offset (forest_from, which_tree); + const auto lmi = t8_mra::get_lmi_from_forest_data (user_data, local_ele_idx + offset); + + return derived ().refinement_set.contains (lmi) ? 1 : 0; + } + + //============================================================================= + // Static Callback Wrappers (Required by t8code C API) + //============================================================================= + + /** + * @brief Static coarsening callback wrapper, routes via forest user data + */ + static int + static_coarsening_callback (t8_forest_t forest, t8_forest_t forest_from, t8_locidx_t which_tree, + t8_eclass_t tree_class, t8_locidx_t local_ele_idx, const t8_scheme_c *scheme, + int is_family, int num_elements, t8_element_t *elements[]) + { + using element_t = typename Derived::element_t; + auto *user_data = reinterpret_cast *> (t8_forest_get_user_data (forest_from)); + + auto *mra = user_data->mra_instance; + return static_cast (mra)->coarsening_callback (forest, forest_from, which_tree, tree_class, + local_ele_idx, scheme, is_family, num_elements, elements); + } + + /** + * @brief Static refinement callback wrapper, routes via forest user data + */ + static int + static_refinement_callback (t8_forest_t forest, t8_forest_t forest_from, t8_locidx_t which_tree, + t8_eclass_t tree_class, t8_locidx_t local_ele_idx, const t8_scheme_c *scheme, + int is_family, int num_elements, t8_element_t *elements[]) + { + using element_t = typename Derived::element_t; + auto *user_data = reinterpret_cast *> (t8_forest_get_user_data (forest_from)); + + auto *mra = user_data->mra_instance; + return static_cast (mra)->refinement_callback (forest, forest_from, which_tree, tree_class, + local_ele_idx, scheme, is_family, num_elements, elements); + } + + //============================================================================= + // Replace Callback (rebuilds the per-leaf lmi index) + //============================================================================= + + /** + * @brief Replace callback: updates the lmi_idx array to match the new forest + * + * Only the lmi_idx array is updated here. The lmi_map was already + * populated by the MST operations and swapped to the new user data. + */ + void + iterate_replace_callback (t8_forest_t forest_old, t8_forest_t forest_new, t8_locidx_t which_tree, + const t8_eclass_t tree_class, const t8_scheme *scheme, int refine, int num_outgoing, + t8_locidx_t first_outgoing, int num_incoming, t8_locidx_t first_incoming) + { + using element_t = typename Derived::element_t; + using levelmultiindex = typename Derived::levelmultiindex; + + auto *old_user_data = reinterpret_cast *> (t8_forest_get_user_data (forest_old)); + auto *new_user_data = reinterpret_cast *> (t8_forest_get_user_data (forest_new)); + + // Adjust indices with tree offsets + const auto first_incoming_in_tree = first_incoming; + first_incoming += t8_forest_get_tree_element_offset (forest_new, which_tree); + first_outgoing += t8_forest_get_tree_element_offset (forest_old, which_tree); + + const auto old_lmi = t8_mra::get_lmi_from_forest_data (old_user_data, first_outgoing); + + if (refine == 0) { + // No change: copy LMI as-is + t8_mra::set_lmi_forest_data (new_user_data, first_incoming, old_lmi); + } + else if (refine == -1) { + // Coarsening: set parent LMI + t8_mra::set_lmi_forest_data (new_user_data, first_incoming, t8_mra::parent_lmi (old_lmi)); + } + else { + // Refinement: construct each child's LMI from the actual incoming + // element. The forest's child ordering (Bey, type-dependent for + // triangles) does not generally match the LMI reference ordering, so + // positional assignment of children_lmi(old_lmi) would mislabel cells. + const auto gtreeid = t8_forest_global_tree_id (forest_new, which_tree); + for (int i = 0; i < num_incoming; ++i) { + const auto *child_element + = t8_forest_get_leaf_element_in_tree (forest_new, which_tree, first_incoming_in_tree + i); + const auto child_lmi = levelmultiindex (gtreeid, child_element, scheme); + t8_mra::set_lmi_forest_data (new_user_data, first_incoming + i, child_lmi); + } + } + } + + /** + * @brief Static wrapper for iterate_replace_callback, routes via forest user data + */ + static void + static_iterate_replace_callback (t8_forest_t forest_old, t8_forest_t forest_new, t8_locidx_t which_tree, + const t8_eclass_t tree_class, const t8_scheme *scheme, int refine, int num_outgoing, + t8_locidx_t first_outgoing, int num_incoming, t8_locidx_t first_incoming) + { + using element_t = typename Derived::element_t; + auto *old_user_data = reinterpret_cast *> (t8_forest_get_user_data (forest_old)); + + auto *mra = old_user_data->mra_instance; + static_cast (mra)->iterate_replace_callback (forest_old, forest_new, which_tree, tree_class, scheme, + refine, num_outgoing, first_outgoing, num_incoming, + first_incoming); + } + + //============================================================================= + // Coarsening + //============================================================================= + + /** + * @brief Perform adaptive coarsening from max_level down to min_level + * + * Per level l (fine to coarse, levels depend on each other): + * 1. Two-scale transform of all complete leaf families at level l + * (parent data + details at level l-1). + * 2. Apply the criterion; non-significant families lose their details + * and their children (the current leaves) are marked for coarsening. + * 3. Inverse transform reconstructs the children of significant families + * exactly (details were kept). + * 4. Forest adapt removes the marked families. + * + * @param min_level Minimum level to coarsen to + * @param max_level Maximum level to start from + * @param criterion Coarsening criterion (default: hard thresholding) + */ + template + requires coarsening_criterion + void + coarsen (int min_level, int max_level, Criterion criterion = {}) + { + if constexpr (criterion_has_prepare) + criterion.prepare (derived ()); + + for (auto l = max_level; l > min_level; --l) { + clear_multiscale_state (); + + derived ().multiscale_transformation (l - 1, l); + + // Prune non-significant families: drop their details and mark their + // children for coarsening. Iterate over a copy since we erase entries. + const auto details = derived ().d_map[l - 1]; + for (const auto &[lmi, _] : details) { + if (!criterion.significant (derived (), lmi)) { + derived ().d_map.erase (lmi); + for (const auto &child : t8_mra::children_lmi (lmi)) + derived ().coarsening_set.insert (child); + } + } + + derived ().inverse_multiscale_transformation (l - 1, l); + + t8_debugf ("MRA coarsen pass %d: coarsening %zu of %zu leaves\n", l, derived ().coarsening_set[l].size (), + derived ().get_user_data ()->lmi_map->size ()); + + adapt_forest (static_coarsening_callback); + } + + clear_multiscale_state (); + } + + //============================================================================= + // Refinement + //============================================================================= + + /** + * @brief Harten's neighbour prediction on the leaf level + * + * A family marked by the refinement criterion (refine_neighbours -> parent + * in td_set) requires all its same-level neighbours to carry children too + * (Harten reference semantics). Leaf formulation used here: every leaf of + * a marked family constructs its same-level face neighbours and pulls the + * covering leaf up by one level if it is coarser. + * Repeated refine calls reach the fixpoint for level jumps larger + * than one. + * + * The same-level neighbour is constructed geometrically via + * t8_forest_element_face_neighbor (no balance or ghost layer required) and + * resolved to the covering leaf by walking up the LMI hierarchy in lmi_map. + * + * TODO MPI: a neighbour lmi whose covering leaf is not local resolves to + * "not found" here. Collect those lmis per owner rank and exchange them + * (cf. reference implementation: data_to_send), the owner then resolves + * them against its local lmi_map. + */ + void + neighbour_prediction () + { + using levelmultiindex = typename Derived::levelmultiindex; + + auto *user_data = derived ().get_user_data (); + auto *forest = derived ().forest; + const auto *scheme = t8_forest_get_scheme (forest); + auto *lmi_map = derived ().get_lmi_map (); + + const auto num_local_trees = t8_forest_get_num_local_trees (forest); + auto current_idx = t8_locidx_t { 0 }; + for (t8_locidx_t tree_idx = 0; tree_idx < num_local_trees; ++tree_idx) { + const auto tree_class = t8_forest_get_tree_class (forest, tree_idx); + const auto num_elements = t8_forest_get_tree_num_leaf_elements (forest, tree_idx); + + // Scratch element for the constructed neighbours, one per tree + t8_element_t *neigh_element; + scheme->element_new (tree_class, 1, &neigh_element); + + for (t8_locidx_t ele_idx = 0; ele_idx < num_elements; ++ele_idx, ++current_idx) { + const auto lmi = t8_mra::get_lmi_from_forest_data (user_data, current_idx); + if (lmi.level () == 0) + continue; + + // Only leaves of marked families (refine_neighbours) grade their neighbourhood + if (!derived ().td_set.contains (t8_mra::parent_lmi (lmi))) + continue; + + const auto *element = t8_forest_get_leaf_element_in_tree (forest, tree_idx, ele_idx); + const auto num_faces = scheme->element_get_num_faces (tree_class, element); + + for (auto face = 0; face < num_faces; ++face) { + int neigh_face; + const auto neigh_gtreeid + = t8_forest_element_face_neighbor (forest, tree_idx, element, neigh_element, tree_class, face, &neigh_face); + + // Domain boundary + if (neigh_gtreeid < 0) + continue; + + // Same-level neighbour as LMI, then resolve to the covering leaf: + // walk up until an entry exists in lmi_map. + auto walk = levelmultiindex (neigh_gtreeid, neigh_element, scheme); + while (walk.level () > 0 && !lmi_map->contains (walk)) + walk = t8_mra::parent_lmi (walk); + + // Covering leaf coarser than this leaf -> pull it up one level. + // Not found at all: neighbour region is refined finer (children + // handle themselves) or lives on another rank (TODO MPI above). + if (lmi_map->contains (walk) && walk.level () < lmi.level ()) + derived ().refinement_set.insert (walk); + } + } + + scheme->element_destroy (tree_class, 1, &neigh_element); + } + } + + /** + * @brief One refinement round: analysis, then a single forest adapt + * + * Analysis phase (whole grid, before any adaptation): + * 1. Compute details of ALL complete leaf families via the + * non-destructive two-scale transform (stored at the parent levels). + * 2. Apply the refinement criterion to each family detail (level L): + * - refine_neighbours: same-level neighbours must carry children + * -> neighbour_prediction pulls coarser neighbour leaves up + * - refine (only for L < max_level-1): the family's children + * (leaves at L+1) are refined one further level + * Adaptation phase: children data of all marked leaves is reconstructed by + * inverse two-scale with zero details (= exact polynomial subdivision), + * then a single forest adapt refines exactly the marked leaves; lmi_map + * and forest stay in sync by construction. + * + * Leaves at level 0 have no parent family and are never refinement + * candidates themselves (their families are, via their parents). + * + * @param min_level Minimum level to start from + * @param max_level Maximum level to refine to + * @param criterion Refinement criterion + * @return Number of leaves marked for refinement in this round + */ + template + requires refinement_criterion + unsigned int + refine_round (int min_level, int max_level, Criterion &criterion) + { + using element_t = typename Derived::element_t; + + clear_multiscale_state (); + + //-------------------------------------------------------------------------- + // Analysis phase + //-------------------------------------------------------------------------- + + // 1. Details of all complete leaf families; lmi_map stays untouched. + derived ().compute_leaf_details (0, max_level); + + // 2. Apply the criterion to every family detail + auto num_families = 0u; + + for (auto L = 0; L < max_level; ++L) { + for (const auto &[lmi, _] : derived ().d_map[L]) { + ++num_families; + + // Remember families whose neighbourhood must be graded + if (criterion.refine_neighbours (derived (), lmi)) + derived ().td_set.insert (lmi); + + // Refine the family's children (leaves at L+1). + // Guard keeps the result within max_level. + if (L < max_level - 1 && criterion.refine (derived (), lmi)) + for (const auto &child : t8_mra::children_lmi (lmi)) + derived ().refinement_set.insert (child); + } + } + + // 3. Grade the neighbourhood of the marked families + neighbour_prediction (); + + // Details are consumed; only the refinement_set is needed from here on + derived ().d_map.erase_all (); + + // Marks below min_level are outside the requested range + for (auto l = 0; l < min_level; ++l) + derived ().refinement_set.erase (l); + + auto num_marked = 0u; + for (auto l = min_level; l < max_level; ++l) + num_marked += derived ().refinement_set[l].size (); + t8_debugf ("MRA refine analysis: %u leaf families, %zu grading neighbourhoods, %u leaves marked\n", num_families, + derived ().td_set.size (), num_marked); + + if (num_marked == 0) { + clear_multiscale_state (); + return 0; + } + + //-------------------------------------------------------------------------- + // Adaptation phase + //-------------------------------------------------------------------------- + + // Reconstruct children data for all marked leaves: inverse two-scale + // with zero details. This moves each refined leaf's data one level down + // in lmi_map, exactly mirroring what the forest adapt below does. + for (auto l = min_level; l < max_level; ++l) + for (const auto &lmi : derived ().refinement_set[l]) + derived ().d_map.insert (lmi, element_t {}); + + derived ().inverse_multiscale_transformation (min_level, max_level); + + // A single forest adapt refines all marked leaves across all levels + adapt_forest (static_refinement_callback); + + clear_multiscale_state (); + + return num_marked; + } + + /** + * @brief Perform adaptive refinement from min_level up to max_level + * + * Iterates refinement rounds until the grading fixpoint is reached: + * the neighbour prediction lifts a coarser neighbour of a marked family + * by one level per round, so a neighbour across a multi-level jump needs + * several rounds to arrive at the family's leaf level. Newly created + * children carry zero details and trigger no further marks, hence the + * iteration terminates (bounded by max_level rounds). + * + * @param min_level Minimum level to start from + * @param max_level Maximum level to refine to + * @param criterion Refinement criterion (default: Harten's prediction) + */ + template + requires refinement_criterion + void + refine (int min_level, int max_level, Criterion criterion = {}) + { + if constexpr (criterion_has_prepare) + criterion.prepare (derived ()); + + for (auto round = 0; round < max_level; ++round) { + t8_debugf ("MRA refine round %d\n", round); + if (refine_round (min_level, max_level, criterion) == 0) + break; + } + } + + //============================================================================= + // Balancing + //============================================================================= + + /** + * @brief One balancing round: pull up covering leaves across faces + * + * Every leaf constructs its same-level face neighbours geometrically and + * resolves them to the covering leaf via the LMI hierarchy. A covering + * leaf more than one level coarser is marked and refined one level; + * children data comes from the inverse two-scale transform with zero + * details, so the represented data is unchanged. + * + * @return Number of leaves marked in this round + */ + unsigned int + balance_round () + { + using element_t = typename Derived::element_t; + using levelmultiindex = typename Derived::levelmultiindex; + + clear_multiscale_state (); + + auto *user_data = derived ().get_user_data (); + auto *forest = derived ().forest; + const auto *scheme = t8_forest_get_scheme (forest); + auto *lmi_map = derived ().get_lmi_map (); + + const auto num_local_trees = t8_forest_get_num_local_trees (forest); + auto current_idx = t8_locidx_t { 0 }; + for (t8_locidx_t tree_idx = 0; tree_idx < num_local_trees; ++tree_idx) { + const auto tree_class = t8_forest_get_tree_class (forest, tree_idx); + const auto num_elements = t8_forest_get_tree_num_leaf_elements (forest, tree_idx); + + t8_element_t *neigh_element; + scheme->element_new (tree_class, 1, &neigh_element); + + for (t8_locidx_t ele_idx = 0; ele_idx < num_elements; ++ele_idx, ++current_idx) { + const auto lmi = t8_mra::get_lmi_from_forest_data (user_data, current_idx); + if (lmi.level () < 2) + continue; + + const auto *element = t8_forest_get_leaf_element_in_tree (forest, tree_idx, ele_idx); + const auto num_faces = scheme->element_get_num_faces (tree_class, element); + + for (auto face = 0; face < num_faces; ++face) { + int neigh_face; + const auto neigh_gtreeid + = t8_forest_element_face_neighbor (forest, tree_idx, element, neigh_element, tree_class, face, &neigh_face); + + // Domain boundary + if (neigh_gtreeid < 0) + continue; + + auto walk = levelmultiindex (neigh_gtreeid, neigh_element, scheme); + while (walk.level () > 0 && !lmi_map->contains (walk)) + walk = t8_mra::parent_lmi (walk); + + // Not found: neighbour region is refined finer (it checks back + // itself) or lives on another rank (TODO MPI, cf. + // neighbour_prediction). + if (lmi_map->contains (walk) && walk.level () + 1 < lmi.level ()) + derived ().refinement_set.insert (walk); + } + } + + scheme->element_destroy (tree_class, 1, &neigh_element); + } + + auto num_marked = 0u; + for (auto l = 0; l < derived ().maximum_level; ++l) + num_marked += derived ().refinement_set[l].size (); + + if (num_marked == 0) { + clear_multiscale_state (); + return 0; + } + + for (auto l = 0; l < derived ().maximum_level; ++l) + for (const auto &lmi : derived ().refinement_set[l]) + derived ().d_map.insert (lmi, element_t {}); + + derived ().inverse_multiscale_transformation (0, derived ().maximum_level); + + adapt_forest (static_refinement_callback); + + clear_multiscale_state (); + + return num_marked; + } + + /** + * @brief Restore the 2:1 face balance of the grid + * + * Rounds iterate until no leaf has a face neighbour more than one level + * coarser. Each round lifts violating covering leaves by one level, so a + * jump of k levels resolves in k-1 rounds; refining a leaf can create new + * violations against its own coarser neighbours, which the next round + * catches. Terminates: every round refines at least one leaf and levels + * are bounded by max_level. + */ + void + balance () + { + auto round = 0; + while (balance_round () > 0) + t8_debugf ("MRA balance round %d\n", round++); + } + + //============================================================================= + // Bottom-up Initialization + //============================================================================= + + /** + * @brief Mean-value jump detection on the leaves of one level + * + * Compares each leaf's component means against its same-level face + * neighbours, normalized per component by the mean magnitude where it + * exceeds 1. Marks the leaf's family when the difference exceeds + * c_thresh * sqrt(h): smooth data decays as O(h) and falls below, a + * discontinuity stays O(1) and keeps firing on every level. Catches jumps + * aligned with family boundaries, where the detail coefficients vanish. + * + * @param level Leaves of this level are checked + * @param c_thresh Threshold constant + * @return Parent lmis of the jumping families + */ + auto + detect_jumps (int level, double c_thresh) + { + using element_t = typename Derived::element_t; + using levelmultiindex = typename Derived::levelmultiindex; + + typename Derived::index_set jumps; + + auto *user_data = derived ().get_user_data (); + auto *forest = derived ().forest; + const auto *scheme = t8_forest_get_scheme (forest); + auto *lmi_map = derived ().get_lmi_map (); + + const auto mean = [&] (const levelmultiindex &lmi) { + const auto &data = lmi_map->get (lmi); + std::array m; + for (auto u = 0u; u < Derived::U_DIM; ++u) + m[u] = data.u_coeffs[element_t::dg_idx (u, 0)]; + if constexpr (Derived::Shape == T8_ECLASS_TRIANGLE) + for (auto u = 0u; u < Derived::U_DIM; ++u) + m[u] /= std::sqrt (data.vol); + return m; + }; + + std::array v_max; + v_max.fill (1.0); + for (const auto &[lmi, _] : (*lmi_map)[level]) { + const auto m = mean (lmi); + for (auto u = 0u; u < Derived::U_DIM; ++u) + v_max[u] = std::max (v_max[u], std::abs (m[u])); + } + + const auto num_local_trees = t8_forest_get_num_local_trees (forest); + auto current_idx = t8_locidx_t { 0 }; + for (t8_locidx_t tree_idx = 0; tree_idx < num_local_trees; ++tree_idx) { + const auto tree_class = t8_forest_get_tree_class (forest, tree_idx); + const auto num_elements = t8_forest_get_tree_num_leaf_elements (forest, tree_idx); + + t8_element_t *neigh_element; + scheme->element_new (tree_class, 1, &neigh_element); + + for (t8_locidx_t ele_idx = 0; ele_idx < num_elements; ++ele_idx, ++current_idx) { + const auto lmi = t8_mra::get_lmi_from_forest_data (user_data, current_idx); + if (lmi.level () != static_cast (level)) + continue; + + const auto mean_inner = mean (lmi); + const auto *element = t8_forest_get_leaf_element_in_tree (forest, tree_idx, ele_idx); + const auto num_faces = scheme->element_get_num_faces (tree_class, element); + + auto max_diff = 0.0; + for (auto face = 0; face < num_faces; ++face) { + int neigh_face; + const auto neigh_gtreeid + = t8_forest_element_face_neighbor (forest, tree_idx, element, neigh_element, tree_class, face, &neigh_face); + + // Domain boundary + if (neigh_gtreeid < 0) + continue; + + // Same-level neighbour only; coarser neighbours are skipped + const auto neigh_lmi = levelmultiindex (neigh_gtreeid, neigh_element, scheme); + if (!lmi_map->contains (neigh_lmi)) + continue; + + const auto mean_neigh = mean (neigh_lmi); + for (auto u = 0u; u < Derived::U_DIM; ++u) + max_diff = std::max (max_diff, std::abs (mean_inner[u] - mean_neigh[u]) / v_max[u]); + } + + const auto h = std::pow (lmi_map->get (lmi).vol, 1.0 / Derived::DIM); + if (max_diff > c_thresh * std::sqrt (h)) + jumps.insert (t8_mra::parent_lmi (lmi)); + } + + scheme->element_destroy (tree_class, 1, &neigh_element); + } + + return jumps; + } + + /** + * @brief Coarsening criterion wrapper: families with a detected jump are + * always significant. Only used by the bottom-up initialization. + */ + template + struct jump_guarded + { + Criterion &criterion; + const typename Derived::index_set &jumps; + + void + prepare (Derived &mra) + { + if constexpr (criterion_has_prepare) + criterion.prepare (mra); + } + + bool + significant (Derived &mra, const typename Derived::levelmultiindex &lmi) + { + return jumps.contains (lmi) || criterion.significant (mra, lmi); + } + }; + + /** + * @brief Refine every leaf at the given level and project the initial data + * + * Unlike refine(), the children data is not predicted by the inverse + * two-scale transform but projected directly from the initial data, which + * is exact up to quadrature. Building block of the bottom-up + * initialization. + * + * @param level Level whose leaves are refined (children appear at level+1) + * @param func Initial data to project onto the new leaves + * @return Number of leaves refined + */ + template + unsigned int + refine_by_projection (int level, Func &&func) + { + clear_multiscale_state (); + + for (const auto &[lmi, _] : (*derived ().get_lmi_map ())[level]) + derived ().refinement_set.insert (lmi); + + const auto num_marked = derived ().refinement_set[level].size (); + if (num_marked == 0) + return 0; + + adapt_forest (static_refinement_callback); + + // The forest already carries the children, lmi_map still the parents. + // Project the initial data onto every leaf without map entry (exactly + // the new children), then drop the refined parents. + auto *lmi_map = derived ().get_lmi_map (); + auto *user_data = derived ().get_user_data (); + + const auto num_local_trees = t8_forest_get_num_local_trees (derived ().forest); + auto current_idx = t8_locidx_t { 0 }; + for (t8_locidx_t tree_idx = 0; tree_idx < num_local_trees; ++tree_idx) { + const auto num_elements = t8_forest_get_tree_num_leaf_elements (derived ().forest, tree_idx); + for (t8_locidx_t ele_idx = 0; ele_idx < num_elements; ++ele_idx, ++current_idx) { + const auto lmi = t8_mra::get_lmi_from_forest_data (user_data, current_idx); + if (lmi_map->contains (lmi)) + continue; + + const auto *element = t8_forest_get_leaf_element_in_tree (derived ().forest, tree_idx, ele_idx); + lmi_map->insert (lmi, derived ().project_leaf (tree_idx, element, func)); + } + } + + for (const auto &lmi : derived ().refinement_set[level]) + lmi_map->erase (lmi); + + clear_multiscale_state (); + + return num_marked; + } + + /** + * @brief Adaptive bottom-up initialization on given initial data + * + * Projects onto the uniform level-1 forest, then per level thresholds the + * details with the coarsening criterion and refines the significant leaves + * one further level by direct projection. Families with a mean-value jump + * across faces are kept regardless of their details (detect_jumps). Never + * builds the uniform max_level grid. + * + * @param mesh Coarse mesh + * @param scheme Element scheme + * @param max_level Maximum refinement level + * @param func Initial data to project + * @param criterion Coarsening criterion (default: hard thresholding) + */ + template + requires coarsening_criterion + void + initialize_data_adaptive (t8_cmesh_t mesh, const t8_scheme *scheme, int max_level, Func &&func, + Criterion criterion = {}) + { + // Jump tolerance follows the criterion's threshold constant where it has one + auto c_thresh = 1.0; + if constexpr (requires { criterion.c_thresh; }) + c_thresh = criterion.c_thresh; + + derived ().initialize_data (mesh, scheme, 1, func); + + for (auto l = 1; l < max_level; ++l) { + const auto jumps = detect_jumps (l, c_thresh); + coarsen (std::max (l - 1, 1), l, jump_guarded { criterion, jumps }); + refine_by_projection (l, func); + } + } +}; + +} // namespace t8_mra + +#endif // T8_ENABLE_MRA diff --git a/src/t8_mra/core/base.hxx b/src/t8_mra/core/base.hxx new file mode 100644 index 0000000000..dd4779b93d --- /dev/null +++ b/src/t8_mra/core/base.hxx @@ -0,0 +1,389 @@ +#pragma once + +#ifdef T8_ENABLE_MRA + +#include "t8_mra/core/mst.hxx" +#include "t8_mra/data/element_data.hxx" +#include "t8_mra/data/levelmultiindex.hxx" +#include "t8_mra/data/levelindex_map.hxx" +#include "t8_mra/data/levelindex_set.hxx" +#include "t8_mra/num/dg_basis.hxx" +#include "t8_mra/num/mat.hxx" +#include "t8_cmesh.h" +#include "t8_forest/t8_forest_general.h" +#include "t8_forest/t8_forest_geometrical.h" + +#include +#include +#include + +namespace t8_mra +{ + +/** + * @brief Forward declaration of multiscale_data + * + * Stores mask coefficients for MST operations. + * Should be specialized for each element type. + */ +template +struct multiscale_data +{ + static constexpr unsigned short DIM = 0; + std::vector mask_coefficients; + std::vector inverse_mask_coefficients; +}; + +/** + * @brief Forward declaration of multiscale (primary template) + * + * This is the primary template that will be specialized for + * different element types (triangle, quad, etc.) + */ +template +class multiscale; + +/** + * @brief Multiscale analysis base class + * + * This class template provides a complete MRA implementation that works + * for both triangular and cartesian elements. It contains all common + * functionality including: + * - Multiscale transformations (forward/inverse) + * - Thresholding and adaptation + * - Forest management + * - Data structure management + * + * Element-specific behavior is controlled via policy classes and + * virtual functions that can be overridden in derived classes. + * + * @tparam TShape Element shape (T8_ECLASS_TRIANGLE, T8_ECLASS_QUAD, etc.) + * @tparam U Number of solution components + * @tparam P Polynomial order (number of nodes per direction) + */ +template +class multiscale_base: public multiscale_data { + public: + using element_t = element_data; + using levelmultiindex = t8_mra::levelmultiindex; + using index_set = ankerl::unordered_dense::set; + using MST = mst; + + static constexpr auto Shape = TShape; + static constexpr unsigned int DIM = element_t::DIM; + static constexpr unsigned int U_DIM = U; + static constexpr unsigned int P_DIM = P; + static constexpr unsigned int DOF = element_t::DOF; + static constexpr unsigned int W_DOF = element_t::W_DOF; + + // Bring mask coefficients into scope + using multiscale_data::mask_coefficients; + using multiscale_data::inverse_mask_coefficients; + + //============================================================================= + // Member Variables (Common to all element types) + //============================================================================= + + /// Maximum refinement level + unsigned int maximum_level; + + /// Scaling factors for each solution component (set by criteria via prepare) + std::array c_scaling; + + /// DG basis for projection + t8_mra::dg_basis basis; + + /// Detail coefficient storage + levelindex_map d_map; + + /// Set of significant details (thresholding) + levelindex_set td_set; + + /// Set of elements marked for refinement + levelindex_set refinement_set; + + /// Set of elements marked for coarsening + levelindex_set coarsening_set; + + /// t8code forest + t8_forest_t forest; + + /// MPI communicator + sc_MPI_Comm comm; + + /// Default quadrature accuracy, derived from the polynomial order: exact + /// for products of two basis functions (degree 2(P-1)), with margin for + /// the projection of general data. + static constexpr int default_dunavant_rule = 2 * P; // rule number == polynomial exactness + static constexpr int default_num_quad_points_1d = P + 1; // n points exact to degree 2n-1 + + public: + //============================================================================= + // Constructors + //============================================================================= + + /** + * @brief Constructor for cartesian elements (QUAD, LINE, HEX) + * + * @param _max_level Maximum refinement level + * @param _comm MPI communicator + */ + multiscale_base (int _max_level, sc_MPI_Comm _comm) + requires is_cartesian + : maximum_level (_max_level), basis (default_num_quad_points_1d, P_DIM), d_map (maximum_level), + td_set (maximum_level), refinement_set (maximum_level), coarsening_set (maximum_level), comm (_comm) + { + c_scaling.fill (1.0); + } + + /** + * @brief Constructor for triangular elements + * + * @param _max_level Maximum refinement level + * @param _comm MPI communicator + */ + multiscale_base (int _max_level, sc_MPI_Comm _comm) + requires (TShape == T8_ECLASS_TRIANGLE) + : maximum_level (_max_level), basis (t8_mra::dunavant_order_num (default_dunavant_rule), default_dunavant_rule), + d_map (maximum_level), td_set (maximum_level), refinement_set (maximum_level), coarsening_set (maximum_level), + comm (_comm) + { + c_scaling.fill (1.0); + } + + virtual ~multiscale_base () = default; + + public: + //============================================================================= + // Forest and Data Access + //============================================================================= + + /** + * @brief Get the t8code forest + */ + t8_forest_t + get_forest () + { + return forest; + } + + /** + * @brief Get user data attached to the forest + */ + t8_mra::forest_data * + get_user_data () + { + return reinterpret_cast *> (t8_forest_get_user_data (forest)); + } + + /** + * @brief Get the level-multiindex map + */ + t8_mra::levelindex_map * + get_lmi_map () + { + return get_user_data ()->lmi_map; + } + + //============================================================================= + // Multiscale Transformation + //============================================================================= + + /** + * @brief Forward multiscale transformation (restriction: fine -> coarse) + * + * Computes parent coefficients and detail coefficients using the + * MST implementation. + * + * @param l_min Minimum refinement level + * @param l_max Maximum refinement level + */ + void + multiscale_transformation (unsigned int l_min, unsigned int l_max) + { + MST::forward_transformation (l_min, l_max, get_lmi_map (), d_map, mask_coefficients); + } + + /** + * @brief Inverse multiscale transformation (prolongation: coarse -> fine) + * + * Reconstructs children from parent and detail coefficients using the + * MST implementation. + * + * @param l_min Minimum refinement level + * @param l_max Maximum refinement level + */ + void + inverse_multiscale_transformation (unsigned int l_min, unsigned int l_max) + { + MST::inverse_transformation (l_min, l_max, get_lmi_map (), d_map, mask_coefficients); + } + + /** + * @brief Compute details of leaf families without modifying the grid data + * + * Non-destructive counterpart of multiscale_transformation: fills d_map at + * the parent levels of complete leaf families in (l_min, l_max], while + * lmi_map keeps the single-scale leaf representation. Basis for the + * refinement criterion (thresholding / Harten's prediction on details). + * + * @param l_min Minimum refinement level + * @param l_max Maximum refinement level + */ + void + compute_leaf_details (unsigned int l_min, unsigned int l_max) + { + MST::leaf_details (l_min, l_max, get_lmi_map (), d_map, mask_coefficients); + } + + //============================================================================= + // Thresholding (Element-specific via virtual function) + //============================================================================= + + /** + * @brief Compute local detail norm for a given element + * + * This function must be implemented by derived classes as the + * detail norm computation may be element-specific. + * + * @param lmi Level multi-index + * @return Array of detail norms (one per solution component) + */ + virtual std::array + local_detail_norm (const levelmultiindex &lmi) = 0; + + /** + * @brief Maximum detail norm over all components, scaled by c_scaling + * + * Common building block for detail-based adaptation criteria. + * + * @param lmi Level multi-index + * @return max_u ||d_u|| / c_scaling_u + */ + double + scaled_detail_norm (const levelmultiindex &lmi) + { + auto detail_norm = local_detail_norm (lmi); + for (auto u = 0u; u < U_DIM; ++u) + detail_norm[u] /= c_scaling[u]; + + return *std::max_element (detail_norm.begin (), detail_norm.end ()); + } + + /** + * @brief Compute local threshold value for an element + * + * Implements uniform subdivision thresholding (Veli eq. 2.44) + * + * @param lmi Level multi-index + * @param gamma Expected order of convergence (criterion parameter) + * @return Local threshold value + */ + double + local_threshold_value (const levelmultiindex &lmi, int gamma) + { + const auto vol = d_map.get (lmi).vol; + + const auto level_diff = maximum_level - lmi.level (); + const auto h_lambda = std::sqrt (vol); + const auto h_max_level = std::pow (vol / std::pow (levelmultiindex::NUM_CHILDREN, level_diff), (gamma + 1.0) / 2.0); + + return h_max_level / h_lambda; + } + + /** + * @brief Compute threshold scaling factor (eq. 2.39) + * + * Returns scaling factors for each solution component based on + * mean values over the domain. + * + * @return std::array Scaling factor for each component + */ + std::array + threshold_scaling_factor () + { + std::array res = {}; + + auto current_idx = 0u; + const auto num_local_trees = t8_forest_get_num_local_trees (forest); + for (auto tree_idx = 0u; tree_idx < num_local_trees; ++tree_idx) { + const auto num_elements = t8_forest_get_tree_num_leaf_elements (forest, tree_idx); + for (auto ele_idx = 0u; ele_idx < num_elements; ++ele_idx, ++current_idx) { + const auto element = t8_forest_get_leaf_element_in_tree (forest, tree_idx, ele_idx); + + const auto lmi = t8_mra::get_lmi_from_forest_data (get_user_data (), current_idx); + const auto vol = t8_forest_element_volume (forest, tree_idx, element); + + // Compute mean value for each component + for (auto u = 0u; u < U_DIM; ++u) { + // Mean value is approximately the first DG coefficient (constant mode) + // times the scaling function value at the element center + const auto mean_val = get_lmi_map ()->get (lmi).u_coeffs[element_t::dg_idx (u, 0)]; + res[u] += std::abs (mean_val) * vol; + } + } + } + + for (auto u = 0u; u < U_DIM; ++u) + res[u] = std::max (1.0, res[u]); + + return res; + } + + //============================================================================= + // Projection (Element-specific, must be implemented by derived classes) + //============================================================================= + + /** + * @brief Project a function onto the DG basis for an element + * + * This function must be implemented by derived classes as projection + * is element-specific (different quadrature, coordinate mappings, etc.) + * + * NOTE: This is a template method in derived classes and cannot be virtual. + * Templates cannot be virtual in C++. + * + * Derived classes should implement: + * template + * void project_impl(std::vector &dg_coeffs, int tree_idx, + * const t8_element_t *element, Func &&func) + */ + + //============================================================================= + // Post-Adaptation Hook + //============================================================================= + + /** + * @brief Post-adaptation hook (for element-specific operations) + * + * Override this in derived classes to perform element-specific operations + * after forest adaptation (e.g., update vertex orders for triangles). + * + * Default implementation does nothing (for cartesian elements). + */ + virtual void + post_adaptation_hook () + { + // Default: no-op + } + + //============================================================================= + // Cleanup + //============================================================================= + + /** + * @brief Clean up all data structures + */ + void + cleanup () + { + d_map.erase_all (); + td_set.erase_all (); + refinement_set.erase_all (); + coarsening_set.erase_all (); + } +}; + +} // namespace t8_mra + +#endif // T8_ENABLE_MRA diff --git a/src/t8_mra/core/mst.hxx b/src/t8_mra/core/mst.hxx new file mode 100644 index 0000000000..1ee742ecf6 --- /dev/null +++ b/src/t8_mra/core/mst.hxx @@ -0,0 +1,383 @@ +#pragma once + +#ifdef T8_ENABLE_MRA + +#include "t8_mra/data/element_data.hxx" +#include "t8_mra/data/levelmultiindex.hxx" +#include "t8_mra/data/levelindex_map.hxx" +#include "t8_mra/data/levelindex_set.hxx" +#include "t8_mra/data/triangle_order.hxx" +#include "t8_mra/num/mat.hxx" +#include "t8_mra/num/dg_basis.hxx" + +#include +#include + +namespace t8_mra +{ + +/** + * @brief Ordering policy for element-specific vertex handling + * + * Default policy: no-op for cartesian elements (QUAD, LINE, HEX) + */ +template +struct ordering_policy +{ + /** + * @brief Adjust parent element ordering (no-op for cartesian elements) + */ + template + static void + adjust_parent_order (T &data) + { + // No ordering adjustment needed for cartesian elements + } + + /** + * @brief Adjust child element ordering (no-op for cartesian elements) + */ + template + static void + adjust_child_order (T &child_data, int child_id, const T &parent_data) + { + // No ordering adjustment needed for cartesian elements + } +}; + +/** + * @brief Ordering policy specialization for triangles + * + * Triangles require complex vertex ordering to maintain consistency + * across refinement levels + */ +template <> +struct ordering_policy +{ + /** + * @brief Compute parent vertex order from child order + */ + template + static void + adjust_parent_order (T &data) + { + triangle_order::get_parent_order (data.order); + } + + /** + * @brief Compute child vertex order from parent order + */ + template + static void + adjust_child_order (T &child_data, int child_id, const T &parent_data) + { + child_data.order = parent_data.order; + triangle_order::get_point_order (child_data.order, child_id); + } +}; + +/** + * @brief Scaling policy for MST normalization + * + * Different element types may require different scaling factors + * in the multiscale transformation + */ +template +struct mst_scaling_policy +{ + /** + * @brief Forward MST normalization factor + * + * For cartesian elements with L² orthonormal basis on reference element, + * we need to average over children: factor = 1/NUM_CHILDREN + */ + static constexpr double + forward_scaling_factor (unsigned int num_children) + { + return 1.0 / static_cast (num_children); + } + + /** + * @brief Inverse MST scaling factor (typically 1.0) + */ + static constexpr double + inverse_scaling_factor () + { + return 1.0; + } +}; + +/** + * @brief Scaling policy specialization for triangles + * + * Triangles use a different normalization convention + */ +template <> +struct mst_scaling_policy +{ + /** + * @brief Forward MST normalization factor for triangles + * + * Triangle implementation does NOT divide by NUM_CHILDREN + */ + static constexpr double + forward_scaling_factor (unsigned int /* num_children */) + { + return 1.0; + } + + /** + * @brief Inverse MST scaling factor + */ + static constexpr double + inverse_scaling_factor () + { + return 1.0; + } +}; + +/** + * @brief Multiscale transformation (MST) operations + * + * This class template provides forward and inverse multiscale + * transformations that work for both triangular and cartesian elements. + * Element-specific behavior is controlled via policy classes. + * + * @tparam TElement Element type (element_data) + * @tparam ordering_policy_t Policy for vertex ordering (default: ordering_policy) + * @tparam scaling_policy_t Policy for scaling factors (default: mst_scaling_policy) + */ +template , + typename scaling_policy_t = mst_scaling_policy> +class mst +{ + public: + using element_t = TElement; + using levelmultiindex = t8_mra::levelmultiindex; + using index_set = ankerl::unordered_dense::set; + + static constexpr auto Shape = TElement::Shape; + static constexpr unsigned int U_DIM = TElement::U_DIM; + static constexpr unsigned int DOF = TElement::DOF; + + /** + * @brief Two-scale transform of one complete family (children -> parent + details) + * + * Pure computation, no map bookkeeping: + * u_parent[i] = scaling * Σ_k Σ_j u_child[k][j] * M[k](j,i) + * d[k][i] = u_child[k][i] - Σ_j M[k](i,j) * u_parent[j] + * + * @param data_on_siblings Element data of all NUM_CHILDREN children + * @param data_on_coarse Output: parent element with u_coeffs, d_coeffs, vol, order + * @param mask_coefficients Mask coefficient matrices M[k] + */ + static void + transform_family (const std::array &data_on_siblings, + element_t &data_on_coarse, const std::vector &mask_coefficients) + { + const double scaling_factor = scaling_policy_t::forward_scaling_factor (levelmultiindex::NUM_CHILDREN); + + for (auto u = 0u; u < U_DIM; ++u) { + // Parent coefficients: u_parent[i] = scaling * Σ_k Σ_j u_child[k][j] * M[k](j,i) + for (auto i = 0u; i < DOF; ++i) { + auto sum = 0.0; + + for (auto k = 0u; k < levelmultiindex::NUM_CHILDREN; ++k) + for (auto j = 0u; j < DOF; ++j) + sum += data_on_siblings[k].u_coeffs[element_t::dg_idx (u, j)] * mask_coefficients[k](j, i); + + data_on_coarse.u_coeffs[element_t::dg_idx (u, i)] = sum * scaling_factor; + } + + // Detail coefficients: d[k][i] = u_child[k][i] - Σ_j M[k](i,j) * u_parent[j] + for (auto i = 0u; i < DOF; ++i) { + for (auto k = 0u; k < levelmultiindex::NUM_CHILDREN; ++k) { + auto sum = 0.0; + for (auto j = 0u; j < DOF; ++j) + sum += mask_coefficients[k](i, j) * data_on_coarse.u_coeffs[element_t::dg_idx (u, j)]; + + data_on_coarse.d_coeffs[element_t::wavelet_idx (k, u, i)] + = data_on_siblings[k].u_coeffs[element_t::dg_idx (u, i)] - sum; + } + } + } + + data_on_coarse.vol = data_on_siblings[0].vol * levelmultiindex::NUM_CHILDREN; + data_on_coarse.order = data_on_siblings[0].order; + ordering_policy_t::adjust_parent_order (data_on_coarse); + } + + /** + * @brief Compute detail coefficients for leaf families without modifying the grid data + * + * For every complete family whose children are present in lmi_map at levels + * (l_min, l_max], the parent element (u_coeffs + details) is computed and + * stored in d_map at the parent level. lmi_map stays untouched, so the + * single-scale leaf representation remains valid. + * + * Used for refinement: details are needed for thresholding / Harten's + * prediction, but the grid keeps its leaves. + * + * @param l_min Minimum refinement level + * @param l_max Maximum refinement level + * @param lmi_map Map from levelmultiindex to element data (read-only here) + * @param d_map Detail coefficient storage (output) + * @param mask_coefficients Mask coefficient matrices M[k] + */ + static void + leaf_details (unsigned int l_min, unsigned int l_max, levelindex_map *lmi_map, + levelindex_map &d_map, const std::vector &mask_coefficients) + { + index_set I_set; + element_t data_on_coarse; + std::array data_on_siblings; + + for (auto l = l_max; l > l_min; --l) { + for (const auto &[lmi, _] : lmi_map->operator[] (l)) + I_set.emplace (t8_mra::parent_lmi (lmi)); + + for (const auto &lmi : I_set) { + const auto siblings_lmi = t8_mra::children_lmi (lmi); + + // Incomplete families (siblings on finer levels) carry no detail information + const auto family_complete + = std::all_of (siblings_lmi.begin (), siblings_lmi.end (), + [&] (const levelmultiindex &sibling) { return lmi_map->contains (sibling); }); + if (!family_complete) + continue; + + for (auto k = 0u; k < levelmultiindex::NUM_CHILDREN; ++k) + data_on_siblings[k] = lmi_map->get (siblings_lmi[k]); + + transform_family (data_on_siblings, data_on_coarse, mask_coefficients); + d_map.insert (lmi, data_on_coarse); + } + + I_set.clear (); + } + } + + /** + * @brief Forward multiscale transformation (restriction: fine -> coarse) + * + * Computes parent coefficients and detail coefficients: + * u_parent[i] = scaling * Σ_k Σ_j u_child[k][j] * M[k](j,i) + * d[k][i] = u_child[k][i] - Σ_j M[k](i,j) * u_parent[j] + * + * @param l_min Minimum refinement level + * @param l_max Maximum refinement level + * @param lmi_map Map from levelmultiindex to element data + * @param d_map Detail coefficient storage + * @param mask_coefficients Mask coefficient matrices M[k] + */ + static void + forward_transformation (unsigned int l_min, unsigned int l_max, + levelindex_map *lmi_map, + levelindex_map &d_map, + const std::vector &mask_coefficients) + { + index_set I_set; + element_t data_on_coarse; + std::array data_on_siblings; + + for (auto l = l_max; l > l_min; --l) { + // Collect all parent indices at level l-1 + for (const auto &[lmi, _] : lmi_map->operator[] (l)) + I_set.emplace (t8_mra::parent_lmi (lmi)); + + d_map[l - 1].reserve (lmi_map->size (l)); + + for (const auto &lmi : I_set) { + const auto siblings_lmi = t8_mra::children_lmi (lmi); + + // On an adaptive grid a family may be incomplete: some siblings stayed + // refined on finer levels. Such families cannot be two-scale transformed; + // their members remain leaves at level l. + const auto family_complete + = std::all_of (siblings_lmi.begin (), siblings_lmi.end (), + [&] (const levelmultiindex &sibling) { return lmi_map->contains (sibling); }); + if (!family_complete) + continue; + + // Load children - LMI structure encodes the ordering + for (auto k = 0u; k < levelmultiindex::NUM_CHILDREN; ++k) + data_on_siblings[k] = lmi_map->get (siblings_lmi[k]); + + transform_family (data_on_siblings, data_on_coarse, mask_coefficients); + + lmi_map->insert (lmi, data_on_coarse); + // insert (assignment) rather than emplace: a stale entry under this + // key must be overwritten, never silently kept + d_map.insert (lmi, data_on_coarse); + + // Consume only this family's children; members of skipped (incomplete) + // families must stay in the map as leaves. + for (auto k = 0u; k < levelmultiindex::NUM_CHILDREN; ++k) + lmi_map->erase (siblings_lmi[k]); + } + + I_set.clear (); + } + } + + /** + * @brief Inverse multiscale transformation (prolongation: coarse -> fine) + * + * Reconstructs children from parent and detail coefficients: + * u_child[k][i] = d[k][i] + Σ_j M[k](i,j) * u_parent[j] + * + * @param l_min Minimum refinement level + * @param l_max Maximum refinement level + * @param lmi_map Map from levelmultiindex to element data + * @param d_map Detail coefficient storage + * @param mask_coefficients Mask coefficient matrices M[k] + */ + static void + inverse_transformation (unsigned int l_min, unsigned int l_max, + levelindex_map *lmi_map, + levelindex_map &d_map, + const std::vector &mask_coefficients) + { + element_t new_data; + const double inv_scaling_factor = scaling_policy_t::inverse_scaling_factor (); + + for (auto l = l_min; l < l_max; ++l) { + lmi_map->operator[] (l + 1).reserve (d_map[l].size ()); + + for (const auto &[lmi, d] : d_map[l]) { + const auto children_lmi = t8_mra::children_lmi (lmi); + const auto lmi_data = lmi_map->get (lmi); + const auto &details = d.d_coeffs; + + // Inverse MST: Reconstruct children u_child[k][i] = d[k][i] + Σ_j M[k](i,j) * u_parent[j] + for (auto k = 0u; k < levelmultiindex::NUM_CHILDREN; ++k) { + for (auto u = 0u; u < U_DIM; ++u) { + for (auto i = 0u; i < DOF; ++i) { + auto sum = 0.0; + + for (auto j = 0u; j < DOF; ++j) + sum += lmi_data.u_coeffs[element_t::dg_idx (u, j)] * mask_coefficients[k](i, j); + + new_data.u_coeffs[element_t::dg_idx (u, i)] + = details[element_t::wavelet_idx (k, u, i)] + sum * inv_scaling_factor; + } + } + + // Apply element-specific ordering adjustments + new_data.vol = lmi_data.vol / levelmultiindex::NUM_CHILDREN; + ordering_policy_t::adjust_child_order (new_data, k, lmi_data); + + lmi_map->insert (children_lmi[k], new_data); + } + + lmi_map->erase (lmi); + } + + d_map.erase (l); + } + } +}; + +} // namespace t8_mra + +#endif // T8_ENABLE_MRA diff --git a/src/t8_mra/criteria/coarsening_criterion.hxx b/src/t8_mra/criteria/coarsening_criterion.hxx new file mode 100644 index 0000000000..e5c7ed50c6 --- /dev/null +++ b/src/t8_mra/criteria/coarsening_criterion.hxx @@ -0,0 +1,72 @@ +#pragma once + +#ifdef T8_ENABLE_MRA + +#include + +namespace t8_mra +{ + +/** + * @brief Requirements for a coarsening criterion + * + * A coarsening criterion decides per leaf family (identified by the parent + * lmi, whose detail data is available in mra.d_map) whether the family's + * detail information is essential: + * + * - significant(mra, lmi) == true: the family keeps its details and + * stays refined. + * - significant(mra, lmi) == false: the details are discarded and the + * family is coarsened into its parent. + * + * Optionally a criterion can provide prepare(mra), which is called once at + * the beginning of every coarsen() call (e.g. to compute global + * normalization factors). + */ +template +concept coarsening_criterion = requires (C c, MRA &mra, const typename MRA::levelmultiindex &lmi) { + { c.significant (mra, lmi) } -> std::convertible_to; +}; + +/** + * @brief Detect optional prepare() hook of a criterion + */ +template +concept criterion_has_prepare = requires (C c, MRA &mra) { c.prepare (mra); }; + +/** + * @brief Default coarsening criterion: hard thresholding + * + * Uses the level-dependent threshold of Veli eq. (2.44) on the scaled + * detail norms: + * + * significant: max_u ||d_u|| / c_scaling_u > c_thresh * eps(lmi) + * + * prepare() computes the global scaling factors c_scaling (eq. 2.39). + */ +struct hard_thresholding +{ + /// Threshold constant + double c_thresh = 1.0; + /// Expected order of convergence (enters the level-dependent threshold) + int gamma = 1; + + template + void + prepare (MRA &mra) + { + /// Scaling due to (2.39) + mra.c_scaling = mra.threshold_scaling_factor (); + } + + template + bool + significant (MRA &mra, const typename MRA::levelmultiindex &lmi) + { + return mra.scaled_detail_norm (lmi) > c_thresh * mra.local_threshold_value (lmi, gamma); + } +}; + +} // namespace t8_mra + +#endif // T8_ENABLE_MRA diff --git a/src/t8_mra/criteria/refinement_criterion.hxx b/src/t8_mra/criteria/refinement_criterion.hxx new file mode 100644 index 0000000000..26d1b8e8ec --- /dev/null +++ b/src/t8_mra/criteria/refinement_criterion.hxx @@ -0,0 +1,75 @@ +#pragma once + +#ifdef T8_ENABLE_MRA + +#include +#include + +namespace t8_mra +{ + +/** + * @brief Requirements for a refinement criterion + * + * A refinement criterion decides per leaf family (identified by the parent + * lmi, whose detail data is available in mra.d_map) how the grid refines: + * + * - refine_neighbours(mra, lmi): the grid around the family is graded so + * that all face neighbours reach the family's leaf level. + * - refine(mra, lmi): the family's children are refined one further + * level. + * + * Optionally a criterion can provide prepare(mra), which is called once at + * the beginning of every refine() call. + */ +template +concept refinement_criterion = requires (C c, MRA &mra, const typename MRA::levelmultiindex &lmi) { + { c.refine_neighbours (mra, lmi) } -> std::convertible_to; + { c.refine (mra, lmi) } -> std::convertible_to; +}; + +/** + * @brief Example refinement criterion: Harten's prediction + * + * Significant details (hard threshold) grade their neighbourhood, a + * steep-gradient detail additionally refines the family's children: + * + * refine_neighbours: max_u ||d_u|| / c_scaling_u > c_thresh * eps(lmi) + * refine: max_u ||d_u|| / c_scaling_u > 2^(P+1) * c_thresh * eps(lmi) + * + * prepare() computes the global scaling factors c_scaling (eq. 2.39). + */ +struct harten_prediction +{ + /// Threshold constant + double c_thresh = 1.0; + /// Expected order of convergence (enters the level-dependent threshold) + int gamma = 1; + + template + void + prepare (MRA &mra) + { + /// Scaling due to (2.39) + mra.c_scaling = mra.threshold_scaling_factor (); + } + + template + bool + refine_neighbours (MRA &mra, const typename MRA::levelmultiindex &lmi) + { + return mra.scaled_detail_norm (lmi) > c_thresh * mra.local_threshold_value (lmi, gamma); + } + + template + bool + refine (MRA &mra, const typename MRA::levelmultiindex &lmi) + { + const auto steep_factor = std::pow (2.0, static_cast (MRA::P_DIM) + 1); + return mra.scaled_detail_norm (lmi) > steep_factor * c_thresh * mra.local_threshold_value (lmi, gamma); + } +}; + +} // namespace t8_mra + +#endif // T8_ENABLE_MRA diff --git a/src/t8_mra/data/element_data.hxx b/src/t8_mra/data/element_data.hxx new file mode 100644 index 0000000000..f249503bce --- /dev/null +++ b/src/t8_mra/data/element_data.hxx @@ -0,0 +1,157 @@ +#pragma once + +#ifdef T8_ENABLE_MRA + +#include +#include +#include "t8_eclass.h" + +#include "t8_mra/data/levelindex_map.hxx" +#include "t8_mra/data/levelmultiindex.hxx" + +namespace t8_mra +{ + +constexpr inline size_t +binom (int n, int k) noexcept +{ + return (k > n) ? 0 + : (k == 0 || k == n) ? 1 + : (k == 1 || k == n - 1) ? n + : (2 * k < n) ? binom (n - 1, k - 1) * n / k + : binom (n - 1, k) * n / (n - k); +} + +template +static constexpr unsigned short +t8_eclass_dim () +{ + if constexpr (TShape == T8_ECLASS_LINE) + return 1; + else if constexpr (TShape == T8_ECLASS_TRIANGLE || TShape == T8_ECLASS_QUAD) + return 2; + else if constexpr (TShape == T8_ECLASS_TET || TShape == T8_ECLASS_HEX || TShape == T8_ECLASS_PRISM + || TShape == T8_ECLASS_PYRAMID) + return 3; + else + static_assert (false, "Invalid element class"); +} + +template +static constexpr unsigned short +t8_eclass_num_children () +{ + if constexpr (TShape == T8_ECLASS_LINE) + return 2; + else if constexpr (TShape == T8_ECLASS_TRIANGLE || TShape == T8_ECLASS_QUAD) + return 4; + else if constexpr (TShape == T8_ECLASS_TET || TShape == T8_ECLASS_HEX || TShape == T8_ECLASS_PRISM + || TShape == T8_ECLASS_PYRAMID) + return 8; /// TODO + else + static_assert (false, "Invalid element class"); +} + +/** + * @brief Compute number of DOF for a given element type and polynomial order + * + * For simplex elements (TRIANGLE, TET): uses binomial coefficient + * For cartesian elements (LINE, QUAD, HEX): uses tensor product P^DIM + */ +template +static constexpr unsigned short +compute_dof () +{ + constexpr unsigned short DIM = t8_eclass_dim (); + + if constexpr (TShape == T8_ECLASS_TRIANGLE || TShape == T8_ECLASS_TET) + return binom (DIM + P - 1, DIM); + else if constexpr (TShape == T8_ECLASS_LINE) + return P; + else if constexpr (TShape == T8_ECLASS_QUAD) + return P * P; + else if constexpr (TShape == T8_ECLASS_HEX) + return P * P * P; + else { + static_assert (false, "Unsupported element type for DOF computation"); + } +} + +/// TODO template specialization +/// TOOD change to std::array +template +struct element_data +{ + + static constexpr t8_eclass Shape = TShape; + static constexpr unsigned short DIM = t8_eclass_dim (); + static constexpr unsigned short NUM_CHILDREN = t8_eclass_num_children (); + static constexpr unsigned short U_DIM = U; + + static constexpr unsigned short P_DIM = P; + static constexpr unsigned short DOF = compute_dof (); + static constexpr unsigned short W_DOF = DOF * NUM_CHILDREN; + + std::vector u_coeffs; // Single-scale coefficients + /// TODO get rid of details + std::vector d_coeffs; // Detail coefficients + double vol; + + std::array order; // Point order + + explicit element_data (): u_coeffs (U_DIM * DOF, 0.0), d_coeffs (U_DIM * W_DOF, 0.0), vol (0.0), order ({}) + { + } + + size_t static dg_idx (size_t u, size_t p) noexcept + { + return u * DOF + p; + } + + // size_t static wavelet_idx (size_t u, size_t p) noexcept + // { + // return u * W_DOF + p; + // } + size_t static wavelet_idx (size_t k, size_t u, size_t p) noexcept + { + return k * U_DIM * DOF + u * DOF + p; + } +}; + +template +concept data_has_triangle = T::Shape == T8_ECLASS_TRIANGLE; + +// template +// struct element_data +// { +// t8_mra::levelmultiindex lmi_idx; +// }; + +template +struct forest_data +{ + using lmi_type = levelmultiindex; + + sc_array_t *lmi_idx; + t8_mra::levelindex_map *lmi_map; + + void *mra_instance; // Pointer to multiscale object for callbacks +}; + +template +t8_mra::levelmultiindex +get_lmi_from_forest_data (const t8_mra::forest_data *forest_data, size_t idx) +{ + return *reinterpret_cast *> (t8_sc_array_index_locidx (forest_data->lmi_idx, idx)); +} + +template +void +set_lmi_forest_data (t8_mra::forest_data *forest_data, size_t idx, const t8_mra::levelmultiindex &lmi) +{ + *reinterpret_cast *> (t8_sc_array_index_locidx (forest_data->lmi_idx, idx)) = lmi; +} + +} // namespace t8_mra + +#endif diff --git a/src/t8_mra/data/levelindex_map.hxx b/src/t8_mra/data/levelindex_map.hxx new file mode 100644 index 0000000000..5e50d55f29 --- /dev/null +++ b/src/t8_mra/data/levelindex_map.hxx @@ -0,0 +1,442 @@ +#pragma once + +#include +#include + +#ifdef T8_ENABLE_MRA + +#include +#include +#include "t8_mra/data/levelmultiindex.hxx" + +namespace t8_mra +{ + +/** + * @brief Stores corresponding data of an adaptive grid as an vector of + * hash_maps. Guarantees O(1) search, insertion, deletion. For performance + * reason we use a "dense hashmap" provided by the unordered_dense-library + * (see https://github.com/martinus/unordered_dense) + * + * The cell data is given as a levelmultiindex = (level, multiindex) describing + * the index of a cell on a given level + * + * @tparam TLmi Levelmultiindex type + * @tparam T Datatype + */ +template +class levelindex_map { + public: + using map = ankerl::unordered_dense::map; + using Value_type = typename map::mapped_type; + + using iterator = typename map::iterator; + using const_iterator = typename map::const_iterator; + + std::vector level_map; + unsigned int max_level; + + levelindex_map () = default; + explicit levelindex_map (unsigned int _max_level); + + // Constructors + levelindex_map (const levelindex_map &other) = default; + levelindex_map (levelindex_map &&other) noexcept = default; + levelindex_map & + operator= (const levelindex_map &other) + = default; + levelindex_map & + operator= (levelindex_map &&other) noexcept + = default; + + /** + * @brief Insert (level, key) -> data to map + * + * @param level Refinement level + * @param key Multiindex + * @param data Given data + */ + void + insert (unsigned int level, size_t key, const T &data); + + /** + * @brief Insert levelmuliindex -> data to map + * + * @param lmi levelmultiindex + * @param data Given data + */ + void + insert (const TLmi &lmi, const T &data); + + /** + * @brief Erase entry for given (level, key) + * + * @param level Refinement level + * @param key Multiindex + */ + void + erase (unsigned int level, size_t key); + + /** + * @brief Erase entry for given levelmultiindex + * + * @param lmi levelmultiindex + */ + void + erase (const TLmi &lmi); + + /** + * @brief Erase entries for a given refinement level + * + * @param level Refinement level + */ + void + erase (unsigned int level); + + /** + * @brief Erase whole map + */ + void + erase_all (); + + // Level-iterators + iterator + begin (unsigned int level); + + iterator + end (unsigned int level); + + const_iterator + begin (unsigned int level) const; + + const_iterator + end (unsigned int level) const; + + /** + * @brief Returns data for a given (level, multiindex) + * + * @param level Refinement level + * @param key Multiindex + * + * @return If (level, multiindex) exists return given data otherwise + * std::nullopt + */ + std::optional + find (unsigned int level, size_t key) const; + + /** + * @brief Returns data for a given levelmultiindex + * + * @param lmi levelmultiindex + * + * @return If lmi exists return given data otherwise + * std::nullopt + */ + std::optional + find (const TLmi &lmi) const; + + /** + * @brief Does the cell (level, multiindex) exists? + * + * @param level Refinement level + * @param key Multiindex + * + * @return Does cell exist? + */ + bool + contains (unsigned int level, size_t key) const; + + /** + * @brief Does the levelmultiindex exists? + * + * @param lmi levelmultiindex + * + * @return Does cell exist? + */ + bool + contains (const TLmi &lmi) const; + + /** + * @brief Returns number elements stored in the map + * + * @return Number elements + */ + size_t + size () const noexcept; + + /** + * @brief Returns number elements stored in the map on a level + * + * @return Number elements + */ + size_t + size (unsigned int level) const noexcept; + + /** + * @brief Get all cells of a given refinement level + * + * @param level Refinement level + * @return index_map + */ + map & + operator[] (unsigned int level); + + /** + * @brief Get all cells of a given refinement level + * + * @param level Refinement level + * @return index_map + */ + const map & + operator[] (unsigned int level) const; + + T & + get (unsigned int level, size_t key); // Access data at specific level and key + // + T & + get (const TLmi &lmi); + + const T & + get (unsigned int level, size_t key) const; // Access data at specific level and key + + const T & + get (const TLmi &lmi) const; + + private: + void + check_level (unsigned int level) const; +}; + +template +levelindex_map::levelindex_map (unsigned int _max_level): max_level (_max_level) +{ + level_map.resize (max_level + 1); +} + +template +void +levelindex_map::insert (unsigned int level, size_t key, const T &data) +{ + check_level (level); + + TLmi lmi; + lmi.index = key; + level_map[level][lmi] = data; +} + +template +void +levelindex_map::insert (const TLmi &lmi, const T &data) +{ + insert (lmi.level (), lmi.index, data); +} + +template +void +levelindex_map::erase (unsigned int level, size_t key) +{ + check_level (level); + + TLmi lmi; + lmi.index = key; + level_map[level].erase (lmi); +} + +template +void +levelindex_map::erase (const TLmi &lmi) +{ + erase (lmi.level (), lmi.index); +} + +template +void +levelindex_map::erase (unsigned int level) +{ + check_level (level); + + level_map[level].clear (); +} + +template +void +levelindex_map::erase_all () +{ + for (auto &map : level_map) + map.clear (); +} + +template +typename levelindex_map::iterator +levelindex_map::begin (unsigned int level) +{ + check_level (level); + + return level_map[level].begin (); +} + +template +typename levelindex_map::const_iterator +levelindex_map::begin (unsigned int level) const +{ + check_level (level); + + return level_map[level].begin (); +} + +template +typename levelindex_map::iterator +levelindex_map::end (unsigned int level) +{ + check_level (level); + + return level_map[level].end (); +} + +template +typename levelindex_map::const_iterator +levelindex_map::end (unsigned int level) const +{ + check_level (level); + + return level_map[level].end (); +} + +template +std::optional +levelindex_map::find (unsigned int level, size_t key) const +{ + check_level (level); + + TLmi lmi; + lmi.index = key; + const auto search = level_map[level].find (lmi); + + if (search != level_map[level].end ()) + return search->second; + + return std::nullopt; +} + +template +std::optional +levelindex_map::find (const TLmi &lmi) const +{ + return find (lmi.level (), lmi.index); +} + +template +bool +levelindex_map::contains (unsigned int level, size_t key) const +{ + check_level (level); + + TLmi lmi; + lmi.index = key; + return level_map[level].contains (lmi); +} + +template +bool +levelindex_map::contains (const TLmi &lmi) const +{ + return contains (lmi.level (), lmi.index); +} + +template +size_t +levelindex_map::size () const noexcept +{ + auto res = 0u; + + for (const auto &m : level_map) + res += m.size (); + + return res; +} + +template +size_t +levelindex_map::size (unsigned int level) const noexcept +{ + check_level (level); + + return level_map[level].size (); +} + +template +typename levelindex_map::map & +levelindex_map::operator[] (unsigned int level) +{ + check_level (level); + + return level_map[level]; +} + +template +const typename levelindex_map::map & +levelindex_map::operator[] (unsigned int level) const +{ + check_level (level); + + return level_map[level]; +} + +template +T & +levelindex_map::get (unsigned int level, size_t key) +{ + check_level (level); + + TLmi lmi; + lmi.index = key; + const auto it = level_map[level].find (lmi); + if (it == level_map[level].end ()) + SC_ABORTF ("levelindex_map::get: missing entry (level=%u, index=%zu)", level, key); + + return it->second; +} + +template +T & +levelindex_map::get (const TLmi &lmi) +{ + return get (lmi.level (), lmi.index); +} + +template +const T & +levelindex_map::get (unsigned int level, size_t key) const +{ + check_level (level); + + TLmi lmi; + lmi.index = key; + const auto it = level_map[level].find (lmi); + if (it == level_map[level].end ()) + SC_ABORTF ("levelindex_map::get: missing entry (level=%u, index=%zu)", level, key); + + return it->second; +} + +template +const T & +levelindex_map::get (const TLmi &lmi) const +{ + return get (lmi.level (), lmi.index); +} + +template +void +levelindex_map::check_level (unsigned int level) const +{ +#if T8_ENABLE_DEBUG + if (level >= level_map.size ()) { + throw std::out_of_range ("Level out of range."); + } +#endif +} + +} // namespace t8_mra + +#endif diff --git a/src/t8_mra/data/levelindex_set.hxx b/src/t8_mra/data/levelindex_set.hxx new file mode 100644 index 0000000000..d33a3bf97c --- /dev/null +++ b/src/t8_mra/data/levelindex_set.hxx @@ -0,0 +1,327 @@ +#pragma once + +#ifdef T8_ENABLE_MRA + +#include + +#include +#include "t8_mra/data/levelmultiindex.hxx" + +namespace t8_mra +{ + +/** + * @brief Stores corresponding cells of an adaptive grid as a vector of hash_sets. + * Guarantees O(1) search, insertion, deletion. For performance + * reason we use a "dense hashset" provided by the unordered_dense-library + * (see https://github.com/martinus/unordered_dense) + * + * The cell data is given as a levelmultiindex = (level, multiindex) describing + * the index of a cell on a given level + * + */ +template +class levelindex_set { + public: + using set = ankerl::unordered_dense::set; + + using iterator = typename set::iterator; + using const_iterator = typename set::const_iterator; + + std::vector level_set; + unsigned int max_level; + + levelindex_set () = default; + explicit levelindex_set (unsigned int _max_level); + + // Constructors + levelindex_set (const levelindex_set &other) = default; + levelindex_set (levelindex_set &&other) noexcept = default; + levelindex_set & + operator= (const levelindex_set &other) + = default; + levelindex_set & + operator= (levelindex_set &&other) noexcept + = default; + + /** + * @brief Insert (level, key) + * + * @param level Refinement level + * @param key Multiindex + */ + void + insert (unsigned int level, size_t key); + + /** + * @brief Insert levelmuliindex -> data to set + * + * @param lmi levelmultiindex + */ + void + insert (const TLmi &lmi); + + /** + * @brief Erase entry for given (level, key) + * + * @param level Refinement level + * @param key Multiindex + */ + void + erase (unsigned int level, size_t key); + + /** + * @brief Erase entry for given levelmultiindex + * + * @param lmi levelmultiindex + */ + void + erase (const TLmi &lmi); + + /** + * @brief Erase entries for a given refinement level + * + * @param level Refinement level + */ + void + erase (unsigned int level); + + /** + * @brief Erase whole set + */ + void + erase_all (); + + // Level-iterators + iterator + begin (unsigned int level); + + iterator + end (unsigned int level); + + const_iterator + begin (unsigned int level) const; + + const_iterator + end (unsigned int level) const; + + /** + * @brief Does the cell (level, multiindex) exists? + * + * @param level Refinement level + * @param key Multiindex + * + * @return Does cell exist? + */ + bool + contains (unsigned int level, size_t key) const; + + /** + * @brief Does the levelmultiindex exists? + * + * @param lmi levelmultiindex + * + * @return Does cell exist? + */ + bool + contains (const TLmi &lmi) const; + + /** + * @brief Returns number elements stored in the set + * + * @return Number elements + */ + size_t + size () const noexcept; + + /** + * @brief Returns number elements stored in the set on a level + * + * @return Number elements + */ + size_t + size (unsigned int level) const noexcept; + + /** + * @brief Get all cells of a given refinement level + * + * @param level Refinement level + * @return index_set + */ + set & + operator[] (unsigned int level); + + /** + * @brief Get all cells of a given refinement level + * + * @param level Refinement level + * @return index_set + */ + const set & + operator[] (unsigned int level) const; + + private: + void + check_level (unsigned int level) const; +}; + +template +inline levelindex_set::levelindex_set (unsigned int _max_level): max_level (_max_level) +{ + level_set.resize (max_level + 1); +} + +template +inline void +levelindex_set::insert (unsigned int level, size_t key) +{ + check_level (level); + + level_set[level].insert (key); +} + +template +void +levelindex_set::insert (const TLmi &lmi) +{ + insert (lmi.level (), lmi.index); +} + +template +inline void +levelindex_set::erase (unsigned int level, size_t key) +{ + check_level (level); + + level_set[level].erase (key); +} + +template +void +levelindex_set::erase (const TLmi &lmi) +{ + erase (lmi.level (), lmi.index); +} + +template +inline void +levelindex_set::erase (unsigned int level) +{ + check_level (level); + + level_set[level].clear (); +} + +template +inline void +levelindex_set::erase_all () +{ + for (auto &set : level_set) + set.clear (); +} + +template +inline typename levelindex_set::iterator +levelindex_set::begin (unsigned int level) +{ + check_level (level); + + return level_set[level].begin (); +} + +template +inline typename levelindex_set::const_iterator +levelindex_set::begin (unsigned int level) const +{ + check_level (level); + + return level_set[level].begin (); +} + +template +inline typename levelindex_set::iterator +levelindex_set::end (unsigned int level) +{ + check_level (level); + + return level_set[level].end (); +} + +template +typename levelindex_set::const_iterator +levelindex_set::end (unsigned int level) const +{ + check_level (level); + + return level_set[level].end (); +} + +template +inline bool +levelindex_set::contains (unsigned int level, size_t key) const +{ + check_level (level); + + return level_set[level].contains (key); +} + +template +bool +levelindex_set::contains (const TLmi &lmi) const +{ + return contains (lmi.level (), lmi.index); +} + +template +inline size_t +levelindex_set::size () const noexcept +{ + auto res = 0u; + + for (const auto &m : level_set) + res += m.size (); + + return res; +} + +template +inline size_t +levelindex_set::size (unsigned int level) const noexcept +{ + check_level (level); + + return level_set[level].size (); +} + +template +inline typename levelindex_set::set & +levelindex_set::operator[] (unsigned int level) +{ + check_level (level); + + return level_set[level]; +} + +template +inline const typename levelindex_set::set & +levelindex_set::operator[] (unsigned int level) const +{ + check_level (level); + + return level_set[level]; +} + +template +inline void +levelindex_set::check_level (unsigned int level) const +{ +#if T8_ENABLE_DEBUG + if (level >= level_set.size ()) { + throw std::out_of_range ("Level out of range."); + } +#endif +} + +} // namespace t8_mra + +#endif diff --git a/src/t8_mra/data/levelmultiindex.hxx b/src/t8_mra/data/levelmultiindex.hxx new file mode 100644 index 0000000000..32e0fa6415 --- /dev/null +++ b/src/t8_mra/data/levelmultiindex.hxx @@ -0,0 +1,554 @@ +#pragma once + +#include + +#include +#include +#include +#include +#include + +#ifdef T8_ENABLE_MRA + +namespace t8_mra +{ + +/** + * @brief Properties of a levelmultiindex. Has to be specialized + * for each t8_eclass. + */ +template +struct lmi_properties +{ + static constexpr int PATH_BITS = 0; + static constexpr int LEVEL_BITS = 0; + static constexpr int BASECELL_BITS = 0; + + static constexpr int NUM_CHILDREN = 0; +}; + +/** + * @brief Basic representation of levelmultiindex. Describes each cell in a grid + * by its refinement level and path starting from a base cell. + * Has to be specialized for each t8_eclass. + * See: http://www.esaim-proc.org/10.1051/proc/201134003 + */ +template +struct levelmultiindex: public lmi_properties +{ + + static constexpr auto ECLASS = TShape; + + levelmultiindex () { + // SC_ABORTF ("levelmultiindex has not been implemented for shape %d", TShape); + }; + + levelmultiindex (size_t _basecell) noexcept; + levelmultiindex (size_t _basecell, const t8_element_t *elem, const t8_scheme *scheme) noexcept; + + bool + operator== (const levelmultiindex &other) const noexcept + { + return index == other.index; + } + + [[nodiscard]] unsigned int + level () const noexcept; + + [[nodiscard]] size_t + multiindex () const noexcept; + + /** + * @brief Get parent of a given levelmultiindex. + * + * @param lmi Current levelmultiindex + * @return Parent levelmultiindex + */ + [[nodiscard]] static levelmultiindex + parent (levelmultiindex lmi); + + [[nodiscard]] static levelmultiindex + jth_child (levelmultiindex lmi, size_t j) noexcept; + + [[nodiscard]] static std::array::NUM_CHILDREN> + children (levelmultiindex lmi) noexcept; + + static std::array + point_order_at_level (const t8_element_t *elem, const t8_scheme *scheme) noexcept; + + // private: + size_t index; +}; + +// Specialization for TRIANGLE +template <> +struct lmi_properties +{ + static constexpr int PATH_BITS = 2; + static constexpr int LEVEL_BITS = 5; + static constexpr int BASECELL_BITS = 21; + + static constexpr int NUM_CHILDREN = 4; +}; + +// Specialization for LINE +template <> +struct lmi_properties +{ + static constexpr int PATH_BITS = 1; // 2 children per refinement + static constexpr int LEVEL_BITS = 6; + static constexpr int BASECELL_BITS = 25; + + static constexpr int NUM_CHILDREN = 2; +}; + +// Specialization for QUAD +template <> +struct lmi_properties +{ + static constexpr int PATH_BITS = 2; // 4 children per refinement + static constexpr int LEVEL_BITS = 5; + static constexpr int BASECELL_BITS = 21; + + static constexpr int NUM_CHILDREN = 4; +}; + +// Specialization for HEX +template <> +struct lmi_properties +{ + static constexpr int PATH_BITS = 3; // 8 children per refinement + static constexpr int LEVEL_BITS = 5; + static constexpr int BASECELL_BITS = 20; + + static constexpr int NUM_CHILDREN = 8; +}; + +// ============================================================================ +// TRIANGLE SPECIALIZATIONS +// ============================================================================ + +template <> +inline levelmultiindex::levelmultiindex (size_t _basecell) noexcept: index (0u) +{ + index = (index << (LEVEL_BITS + BASECELL_BITS)) | _basecell; +} + +template <> +inline levelmultiindex +levelmultiindex::jth_child (levelmultiindex lmi, size_t j) noexcept +{ + // Extract basecell and remove basecell from lmi + const size_t basecell = lmi.index & ((1u << BASECELL_BITS) - 1); + lmi.index >>= BASECELL_BITS; + + // Extract level and remove basecell from lmi + const size_t level = lmi.index & ((1u << LEVEL_BITS) - 1); + lmi.index >>= LEVEL_BITS; + + // Gives path for the jth-child + const auto jth_path = (lmi.index << PATH_BITS) | j; + + // Construct all children: Same basecell, increase level by one, concat new + // childpath + lmi.index = (jth_path << (LEVEL_BITS + BASECELL_BITS)) | ((level + 1) << BASECELL_BITS) | basecell; + + return lmi; +} + +template <> +inline levelmultiindex::levelmultiindex (size_t _basecell, const t8_element_t *elem, + const t8_scheme *scheme) noexcept + : levelmultiindex (_basecell) +{ + std::array order = { 0, 1, 2 }; + const auto level = scheme->element_get_level (ECLASS, elem); + t8_dtri_t ancestor; + + for (auto l = 0u; l < level; ++l) { + auto tmp = order; + + /// This should be possible in one function call... + const auto ancestor_id = scheme->element_get_ancestor_id (ECLASS, elem, l + 1); + t8_dtri_ancestor ((t8_dtri_t *) elem, l, &ancestor); + triangle_order::invert_order (tmp); + const auto child_id = triangle_order::get_reference_children_order (ancestor.type, ancestor_id, tmp); + + *this = jth_child (*this, child_id); + triangle_order::get_point_order (order, t8_dtri_type_cid_to_beyid[ancestor.type][ancestor_id]); + } +} + +template <> +inline unsigned int +levelmultiindex::level () const noexcept +{ + return static_cast ((index >> BASECELL_BITS) & ((1ULL << LEVEL_BITS) - 1)); +} + +template <> +inline levelmultiindex +levelmultiindex::parent (levelmultiindex lmi) +{ +#if T8_ENABLE_DEBUG + if (lmi.level () == 0) + SC_ABORTF ("levelmultiindices on level 0 do not have a parent %zu", lmi.index); +#endif + + // Extract basecell and remove basecell from lmi + const auto basecell = lmi.index & ((1u << BASECELL_BITS) - 1); + lmi.index >>= BASECELL_BITS; + + // Extract level and remove level from lmi. Reduce refinement level by 1 + const auto level = lmi.index & ((1u << LEVEL_BITS) - 1); + lmi.index >>= LEVEL_BITS; + + // Remove last path segment from lmi + lmi.index >>= PATH_BITS; + + // Re-encode lmi with same basecell, decreased level by one and shorten path + lmi.index = (lmi.index << (BASECELL_BITS + LEVEL_BITS)) | ((level - 1) << BASECELL_BITS) | basecell; + + return lmi; +} + +template <> +inline std::array, levelmultiindex::NUM_CHILDREN> +levelmultiindex::children (levelmultiindex lmi) noexcept +{ + std::array, levelmultiindex::NUM_CHILDREN> child_vec; + + for (size_t j = 0u; j < NUM_CHILDREN; ++j) + child_vec[j] = jth_child (lmi, j); + // child_vec[j] = jth_child (lmi.index, j); + + return child_vec; +} + +template <> +inline std::array +levelmultiindex::point_order_at_level (const t8_element_t *elem, const t8_scheme *scheme) noexcept +{ + std::array res = { 0, 1, 2 }; + const auto level = scheme->element_get_level (ECLASS, elem); + t8_dtri_t ancestor; + + for (auto l = 0; l < level; ++l) { + const auto ancestor_id = scheme->element_get_ancestor_id (ECLASS, elem, l + 1); + t8_dtri_ancestor ((t8_dtri_t *) elem, l, &ancestor); + triangle_order::get_point_order (res, t8_dtri_type_cid_to_beyid[ancestor.type][ancestor_id]); + } + + return res; +} + +// ============================================================================ +// QUAD SPECIALIZATIONS +// ============================================================================ + +template <> +inline levelmultiindex::levelmultiindex (size_t _basecell) noexcept: index (0u) +{ + index = (index << (LEVEL_BITS + BASECELL_BITS)) | _basecell; +} + +template <> +inline levelmultiindex +levelmultiindex::jth_child (levelmultiindex lmi, size_t j) noexcept +{ + // Extract basecell and remove basecell from lmi + const size_t basecell = lmi.index & ((1u << BASECELL_BITS) - 1); + lmi.index >>= BASECELL_BITS; + + // Extract level and remove level from lmi + const size_t level = lmi.index & ((1u << LEVEL_BITS) - 1); + lmi.index >>= LEVEL_BITS; + + // Append child ID to path + const auto jth_path = (lmi.index << PATH_BITS) | j; + + // Reconstruct: Same basecell, increase level by one, append child path + lmi.index = (jth_path << (LEVEL_BITS + BASECELL_BITS)) | ((level + 1) << BASECELL_BITS) | basecell; + + return lmi; +} + +template <> +inline levelmultiindex::levelmultiindex (size_t _basecell, const t8_element_t *elem, + const t8_scheme *scheme) noexcept + : levelmultiindex (_basecell) +{ + const auto level = scheme->element_get_level (ECLASS, elem); + + for (auto l = 0u; l < level; ++l) { + const auto child_id = scheme->element_get_ancestor_id (ECLASS, elem, l + 1); + *this = jth_child (*this, child_id); + } +} + +template <> +inline unsigned int +levelmultiindex::level () const noexcept +{ + return static_cast ((index >> BASECELL_BITS) & ((1ULL << LEVEL_BITS) - 1)); +} + +template <> +inline levelmultiindex +levelmultiindex::parent (levelmultiindex lmi) +{ + // Extract basecell and remove basecell from lmi + const auto basecell = lmi.index & ((1u << BASECELL_BITS) - 1); + lmi.index >>= BASECELL_BITS; + + // Extract level and remove level from lmi + const auto level = lmi.index & ((1u << LEVEL_BITS) - 1); + lmi.index >>= LEVEL_BITS; + + // Remove last path segment from lmi + lmi.index >>= PATH_BITS; + + // Re-encode lmi with same basecell, decreased level by one and shortened path + lmi.index = (lmi.index << (BASECELL_BITS + LEVEL_BITS)) | ((level - 1) << BASECELL_BITS) | basecell; + + return lmi; +} + +template <> +inline std::array, levelmultiindex::NUM_CHILDREN> +levelmultiindex::children (levelmultiindex lmi) noexcept +{ + std::array, levelmultiindex::NUM_CHILDREN> child_vec; + + // t8code QUAD Morton/z-curve order for children: + // childid=0: (0,0), childid=1: (1,0), childid=2: (0,1), childid=3: (1,1) + // This is x-fast ordering, which matches mask coefficient expectations. + // NO permutation needed! + + for (size_t k = 0u; k < NUM_CHILDREN; ++k) + child_vec[k] = jth_child (lmi, k); + + return child_vec; +} + +template <> +inline std::array +levelmultiindex::point_order_at_level (const t8_element_t *elem, const t8_scheme *scheme) noexcept +{ + // Cartesian elements don't need vertex ordering + return { 0, 1, 2 }; +} + +// ============================================================================ +// LINE SPECIALIZATIONS +// ============================================================================ + +template <> +inline levelmultiindex::levelmultiindex (size_t _basecell) noexcept: index (0u) +{ + index = (index << (LEVEL_BITS + BASECELL_BITS)) | _basecell; +} + +template <> +inline levelmultiindex +levelmultiindex::jth_child (levelmultiindex lmi, size_t j) noexcept +{ + const size_t basecell = lmi.index & ((1u << BASECELL_BITS) - 1); + lmi.index >>= BASECELL_BITS; + + const size_t level = lmi.index & ((1u << LEVEL_BITS) - 1); + lmi.index >>= LEVEL_BITS; + + const auto jth_path = (lmi.index << PATH_BITS) | j; + + lmi.index = (jth_path << (LEVEL_BITS + BASECELL_BITS)) | ((level + 1) << BASECELL_BITS) | basecell; + + return lmi; +} + +template <> +inline levelmultiindex::levelmultiindex (size_t _basecell, const t8_element_t *elem, + const t8_scheme *scheme) noexcept + : levelmultiindex (_basecell) +{ + const auto level = scheme->element_get_level (ECLASS, elem); + + for (auto l = 0u; l < level; ++l) { + const auto child_id = scheme->element_get_ancestor_id (ECLASS, elem, l + 1); + *this = jth_child (*this, child_id); + } +} + +template <> +inline unsigned int +levelmultiindex::level () const noexcept +{ + return static_cast ((index >> BASECELL_BITS) & ((1ULL << LEVEL_BITS) - 1)); +} + +template <> +inline levelmultiindex +levelmultiindex::parent (levelmultiindex lmi) +{ + const auto basecell = lmi.index & ((1u << BASECELL_BITS) - 1); + lmi.index >>= BASECELL_BITS; + + const auto level = lmi.index & ((1u << LEVEL_BITS) - 1); + lmi.index >>= LEVEL_BITS; + + lmi.index >>= PATH_BITS; + + lmi.index = (lmi.index << (BASECELL_BITS + LEVEL_BITS)) | ((level - 1) << BASECELL_BITS) | basecell; + + return lmi; +} + +template <> +inline std::array, levelmultiindex::NUM_CHILDREN> +levelmultiindex::children (levelmultiindex lmi) noexcept +{ + std::array, levelmultiindex::NUM_CHILDREN> child_vec; + + for (size_t j = 0u; j < NUM_CHILDREN; ++j) + child_vec[j] = jth_child (lmi, j); + + return child_vec; +} + +template <> +inline std::array +levelmultiindex::point_order_at_level (const t8_element_t *elem, const t8_scheme *scheme) noexcept +{ + return { 0, 1, 2 }; +} + +// ============================================================================ +// HEX SPECIALIZATIONS +// ============================================================================ + +template <> +inline levelmultiindex::levelmultiindex (size_t _basecell) noexcept: index (0u) +{ + index = (index << (LEVEL_BITS + BASECELL_BITS)) | _basecell; +} + +template <> +inline levelmultiindex +levelmultiindex::jth_child (levelmultiindex lmi, size_t j) noexcept +{ + const size_t basecell = lmi.index & ((1u << BASECELL_BITS) - 1); + lmi.index >>= BASECELL_BITS; + + const size_t level = lmi.index & ((1u << LEVEL_BITS) - 1); + lmi.index >>= LEVEL_BITS; + + const auto jth_path = (lmi.index << PATH_BITS) | j; + + lmi.index = (jth_path << (LEVEL_BITS + BASECELL_BITS)) | ((level + 1) << BASECELL_BITS) | basecell; + + return lmi; +} + +template <> +inline levelmultiindex::levelmultiindex (size_t _basecell, const t8_element_t *elem, + const t8_scheme *scheme) noexcept + : levelmultiindex (_basecell) +{ + const auto level = scheme->element_get_level (ECLASS, elem); + + for (auto l = 0u; l < level; ++l) { + const auto child_id = scheme->element_get_ancestor_id (ECLASS, elem, l + 1); + *this = jth_child (*this, child_id); + } +} + +template <> +inline unsigned int +levelmultiindex::level () const noexcept +{ + return static_cast ((index >> BASECELL_BITS) & ((1ULL << LEVEL_BITS) - 1)); +} + +template <> +inline levelmultiindex +levelmultiindex::parent (levelmultiindex lmi) +{ + const auto basecell = lmi.index & ((1u << BASECELL_BITS) - 1); + lmi.index >>= BASECELL_BITS; + + const auto level = lmi.index & ((1u << LEVEL_BITS) - 1); + lmi.index >>= LEVEL_BITS; + + lmi.index >>= PATH_BITS; + + lmi.index = (lmi.index << (BASECELL_BITS + LEVEL_BITS)) | ((level - 1) << BASECELL_BITS) | basecell; + + return lmi; +} + +template <> +inline std::array, levelmultiindex::NUM_CHILDREN> +levelmultiindex::children (levelmultiindex lmi) noexcept +{ + std::array, levelmultiindex::NUM_CHILDREN> child_vec; + + for (size_t j = 0u; j < NUM_CHILDREN; ++j) + child_vec[j] = jth_child (lmi, j); + + return child_vec; +} + +template <> +inline std::array +levelmultiindex::point_order_at_level (const t8_element_t *elem, const t8_scheme *scheme) noexcept +{ + return { 0, 1, 2 }; +} + +/// Levelmultiindex concept +template +concept lmi_type = std::is_same_v>; + +// F R E E - F U N C T I O N S +template +[[nodiscard]] inline TLmi +parent_lmi (TLmi lmi) +{ + return TLmi::parent (lmi); +} + +template +[[nodiscard]] inline TLmi +jth_child_lmi (TLmi lmi, size_t j) +{ + return TLmi::jth_child (lmi, j); +} + +template +[[nodiscard]] inline std::array +children_lmi (TLmi lmi) +{ + return TLmi::children (lmi); +} + +} // namespace t8_mra + +namespace std +{ + +template +struct hash +{ + using is_transparent = void; // enable heterogeneous overloadsc + using is_avalanching = void; + + size_t + operator() (const TLmi &lmi) const + { + return lmi.index; + } +}; + +} // namespace std + +#endif diff --git a/src/t8_mra/data/triangle_order.hxx b/src/t8_mra/data/triangle_order.hxx new file mode 100644 index 0000000000..03f0982e3a --- /dev/null +++ b/src/t8_mra/data/triangle_order.hxx @@ -0,0 +1,222 @@ +#pragma once + +#include + +#include +#include +#include +#include + +#ifdef T8_ENABLE_MRA +namespace t8_mra +{ +/// TODO write more general with templates +struct triangle_order +{ + + static constexpr t8_eclass ECLASS = T8_ECLASS_TRIANGLE; + + static void + get_point_order (std::array &order, int cube_id) + { + + const auto idx = (order == std::array { 0, 1, 2 }) ? 0 + : (order == std::array { 2, 0, 1 }) ? 1 + : (order == std::array { 1, 2, 0 }) ? 2 + : (order == std::array { 0, 2, 1 }) ? 3 + : (order == std::array { 1, 0, 2 }) ? 4 + : (order == std::array { 2, 1, 0 }) ? 5 + : -1; + + if (idx != -1) + order = { lookup[idx][cube_id][0], lookup[idx][cube_id][1], lookup[idx][cube_id][2] }; + } + + static void + invert_order (std::array &order) + { + const auto idx = (order == std::array { 0, 1, 2 }) ? 0 + : (order == std::array { 0, 2, 1 }) ? 1 + : (order == std::array { 1, 2, 0 }) ? 2 + : (order == std::array { 1, 0, 2 }) ? 3 + : (order == std::array { 2, 0, 1 }) ? 4 + : (order == std::array { 2, 1, 0 }) ? 5 + : -1; + + if (idx != -1) + order = { inverse_lookup[idx][0], inverse_lookup[idx][1], inverse_lookup[idx][2] }; + } + + static int + get_children_order (int type, int child_id, const std::array &order) + { + return 0; + } + + static void + get_parent_order (std::array &order) + { + const auto idx = (order == std::array { 0, 1, 2 }) ? 0 + : (order == std::array { 2, 0, 1 }) ? 1 + : (order == std::array { 1, 2, 0 }) ? 2 + : (order == std::array { 0, 2, 1 }) ? 3 + : (order == std::array { 1, 0, 2 }) ? 4 + : 5; + + order = { parent_lookup[idx][0], parent_lookup[idx][1], parent_lookup[idx][2] }; + } + + static int + get_reference_children_order (int type, int child_id, const std::array &order) + { + const auto idx = (order == std::array { 0, 1, 2 }) ? 0 + : (order == std::array { 2, 0, 1 }) ? 1 + : (order == std::array { 1, 2, 0 }) ? 2 + : (order == std::array { 0, 2, 1 }) ? 3 + : (order == std::array { 1, 0, 2 }) ? 4 + : 5; + + return (type == 1) ? lookup_type_1[idx][child_id] : lookup_type_2[idx][child_id]; + } + + // Reverse mapping: given parent order and reference child index k, find beyid + // Returns beyid for get_point_order + // Since we don't know parent type, we try both types and find the beyid that maps to k + // NOTE: This expects the INVERTED parent order (like the old code did) + static int + get_beyid_from_reference (int reference_k, const std::array &parent_order_inverted) + { + // Try each possible beyid (0,1,2,3) and check which one gives us reference_k + // Try type-1 first + for (int beyid = 0; beyid < 4; ++beyid) { + if (get_reference_children_order (1, beyid, parent_order_inverted) == reference_k) { + return beyid; + } + } + + // Try type-2 + for (int beyid = 0; beyid < 4; ++beyid) { + if (get_reference_children_order (2, beyid, parent_order_inverted) == reference_k) { + return beyid; + } + } + + // Should never reach here + return 0; + } + + // Given 4 children in reference order [0,1,2,3], find which reference k has geometric beyid=0 + // Uses the children's vertex orders to infer this + // Returns the reference index k that corresponds to beyid=0 + static int + find_geometric_child0 (const std::array, 4> &children_orders) + { + // Try to compute what the parent order should be from each child + // The child that gives the most consistent parent order is likely geometric child 0 + + // Compute candidate parent orders from each child + std::array, 4> candidate_parents; + for (int k = 0; k < 4; ++k) { + candidate_parents[k] = children_orders[k]; + get_parent_order (candidate_parents[k]); + } + + // The geometric child 0 has a specific relationship: parent_order should be computable from it + // Try each child as if it were geometric child 0 and see which makes sense + for (int k = 0; k < 4; ++k) { + auto test_parent_order = candidate_parents[k]; + + // Try to reconstruct all children from this parent order + // Count how many children match + int matches = 0; + for (int beyid = 0; beyid < 4; ++beyid) { + auto reconstructed = test_parent_order; + get_point_order (reconstructed, beyid); + + // Check if this matches any of the actual children + for (int j = 0; j < 4; ++j) { + if (reconstructed == children_orders[j]) { + matches++; + break; + } + } + } + + // If all 4 children can be reconstructed, we found the right parent order + // and the child k we used is likely geometric child 0 + if (matches == 4) { + return k; + } + } + + // Fallback: return 0 + return 0; + } + + static void + get_point_order_at_level (size_t basecell, const t8_element_t *elem, const t8_scheme *scheme, + std::array &order) + { + order = { 0, 1, 2 }; + const auto elem_level = scheme->element_get_level (ECLASS, elem); + t8_dtri_t ancestor; + + for (auto l = 0u; l < elem_level; ++l) { + const auto ancestor_id = scheme->element_get_ancestor_id (ECLASS, elem, l + 1); + t8_dtri_ancestor ((t8_dtri_t *) elem, l, &ancestor); + get_point_order (order, t8_dtri_type_cid_to_beyid[ancestor.type][ancestor_id]); + } + } + + private: + static constexpr int inverse_lookup[6][3] + = { { 0, 1, 2 }, { 0, 2, 1 }, { 2, 0, 1 }, { 1, 0, 2 }, { 1, 2, 0 }, { 2, 1, 0 } }; + + static constexpr int lookup[6][4][3] = { + // For permutation (0,1,2) + { { 0, 1, 2 }, { 2, 0, 1 }, { 1, 2, 0 }, { 0, 2, 1 } }, + // For permutation (0,2,1) + { { 0, 1, 2 }, { 2, 0, 1 }, { 1, 2, 0 }, { 2, 1, 0 } }, + // For permutation (1,2,0) + { { 0, 1, 2 }, { 2, 0, 1 }, { 1, 2, 0 }, { 1, 0, 2 } }, + // For permutation (0,2,1) + { { 0, 2, 1 }, { 1, 0, 2 }, { 2, 1, 0 }, { 2, 0, 1 } }, + // For permutation (2,0,1) + { { 0, 2, 1 }, { 1, 0, 2 }, { 2, 1, 0 }, { 0, 1, 2 } }, + // For permutation (2,1,0) + { { 0, 2, 1 }, { 1, 0, 2 }, { 2, 1, 0 }, { 1, 2, 0 } } + }; + + // Lookup tables for the two types (type == 1 or type == 2) + static constexpr int lookup_type_1[6][4] = { + { 1, 0, 2, 3 }, // order = {0, 1, 2} + { 2, 0, 3, 1 }, // order = {2, 0, 1} + { 3, 0, 1, 2 }, // order = {1, 2, 0} + { 1, 0, 3, 2 }, // order = {0, 2, 1} + { 2, 0, 1, 3 }, // order = {1, 0, 2} + { 3, 0, 2, 1 } // order = {2, 1, 0} + }; + static constexpr int lookup_type_2[6][4] = { + { 1, 2, 0, 3 }, // order = {0, 1, 2} + { 2, 3, 0, 1 }, // order = {2, 0, 1} + { 3, 1, 0, 2 }, // order = {1, 2, 0} + { 1, 3, 0, 2 }, // order = {0, 2, 1} + { 2, 1, 0, 3 }, // order = {1, 0, 2} + { 3, 2, 0, 1 } // order = {2, 1, 0} + }; + + // Lookup table with the 6 possible permutations of (first, second, third) + // Each row corresponds to the transformed triple (first, second, third) + static constexpr int parent_lookup[6][3] = { + { 1, 0, 2 }, // (0,1,2) -> (1,0,2) + { 0, 2, 1 }, // (2,0,1) -> (0,2,1) + { 2, 1, 0 }, // (1,2,0) -> (2,1,0) + { 0, 1, 2 }, // (0,2,1) -> (0,1,2) + { 1, 2, 0 }, // (1,0,2) -> (1,2,0) + { 2, 0, 1 } // (2,1,0) -> (2,0,1) + }; +}; + +} // namespace t8_mra + +#endif diff --git a/src/t8_mra/io/vtk.hxx b/src/t8_mra/io/vtk.hxx new file mode 100644 index 0000000000..1edca8eafc --- /dev/null +++ b/src/t8_mra/io/vtk.hxx @@ -0,0 +1,674 @@ +#pragma once + +#ifdef T8_ENABLE_MRA + +#include +#include +#include +#include +#include + +#include "t8.h" +// #include "t8_forest.h" +#include "t8_forest/t8_forest_general.h" +#include "t8_forest/t8_forest_geometrical.h" +#include "t8_mra/data/element_data.hxx" +#include "t8_mra/num/basis_functions.hxx" +#include "t8_mra/num/legendre_basis.hxx" +#include "t8_mra/num/multiindex.hxx" + +namespace t8_mra +{ + +/** + * @brief Barycentric index of the node at a given position in VTK's Lagrange + * triangle ordering (port of vtkHigherOrderTriangle::BarycentricIndex) + */ +static std::array +vtk_triangle_barycentric_index (int index, int order) +{ + int max = order; + int min = 0; + + // Scope into the correct inner triangle + while (index != 0 && index >= 3 * order) { + index -= 3 * order; + max -= 2; + ++min; + order -= 3; + } + + if (index == 0) + return { min, min, max }; + if (index == 1) + return { max, min, min }; + if (index == 2) + return { min, max, min }; + + std::array bindex; + index -= 3; + const int dim = index / (order - 1); + const int offset = index - dim * (order - 1); + bindex[dim] = min + 1 + offset; + bindex[(dim + 1) % 3] = min; + bindex[(dim + 2) % 3] = max - 1 - offset; + + return bindex; +} + +/** + * @brief Lagrange node positions for a triangle of given order in reference + * coordinates, in VTK's Lagrange triangle ordering + */ +static std::vector> +get_triangle_lagrange_nodes (int order) +{ + const int num_nodes = (order + 1) * (order + 2) / 2; + std::vector> nodes (num_nodes); + + for (int idx = 0; idx < num_nodes; ++idx) { + const auto bindex = vtk_triangle_barycentric_index (idx, order); + nodes[idx] = { static_cast (bindex[0]) / order, static_cast (bindex[1]) / order }; + } + + return nodes; +} + +/** + * @brief Get Lagrange node positions for a line of given order in reference coordinates + * + * Follows VTK's Lagrange line ordering: + * - First 2 nodes: endpoints + * - Remaining nodes: interior nodes + */ +static std::vector> +get_line_lagrange_nodes (int order) +{ + std::vector> nodes; + nodes.reserve (order + 1); + + // Add endpoints + nodes.push_back ({ 0.0 }); + nodes.push_back ({ 1.0 }); + + // Add interior nodes + for (int i = 1; i < order; ++i) { + double xi = static_cast (i) / order; + nodes.push_back ({ xi }); + } + + return nodes; +} + +/** + * @brief Index of the grid node (i, j) in VTK's Lagrange quad ordering + * (port of vtkHigherOrderQuadrilateral::PointIndexFromIJK, uniform order) + */ +static int +vtk_quad_point_index (int i, int j, int order) +{ + const bool ibdy = (i == 0 || i == order); + const bool jbdy = (j == 0 || j == order); + + if (ibdy && jbdy) // vertex + return (i ? (j ? 2 : 1) : (j ? 3 : 0)); + + int offset = 4; + if (ibdy || jbdy) { // edge + if (!ibdy) + return (i - 1) + (j ? 2 * (order - 1) : 0) + offset; + return (j - 1) + (i ? order - 1 : 3 * (order - 1)) + offset; + } + + offset += 4 * (order - 1); // interior + return offset + (i - 1) + (order - 1) * (j - 1); +} + +/** + * @brief Lagrange node positions for a quad of given order in reference + * coordinates, in VTK's Lagrange quad ordering + */ +static std::vector> +get_quad_lagrange_nodes (int order) +{ + const int num_nodes = (order + 1) * (order + 1); + std::vector> nodes (num_nodes); + + for (int j = 0; j <= order; ++j) + for (int i = 0; i <= order; ++i) + nodes[vtk_quad_point_index (i, j, order)] + = { static_cast (i) / order, static_cast (j) / order }; + + return nodes; +} + +/** + * @brief Index of the grid node (i, j, k) in VTK's Lagrange hex ordering + * (port of vtkHigherOrderHexahedron::PointIndexFromIJK, uniform order) + */ +static int +vtk_hex_point_index (int i, int j, int k, int order) +{ + const bool ibdy = (i == 0 || i == order); + const bool jbdy = (j == 0 || j == order); + const bool kbdy = (k == 0 || k == order); + const int nbdy = (ibdy ? 1 : 0) + (jbdy ? 1 : 0) + (kbdy ? 1 : 0); + + if (nbdy == 3) // vertex + return (i ? (j ? 2 : 1) : (j ? 3 : 0)) + (k ? 4 : 0); + + int offset = 8; + if (nbdy == 2) { // edge + if (!ibdy) + return (i - 1) + (j ? 2 * (order - 1) : 0) + (k ? 4 * (order - 1) : 0) + offset; + if (!jbdy) + return (j - 1) + (i ? order - 1 : 3 * (order - 1)) + (k ? 4 * (order - 1) : 0) + offset; + offset += 8 * (order - 1); + return (k - 1) + (order - 1) * (i ? (j ? 2 : 1) : (j ? 3 : 0)) + offset; + } + + offset += 12 * (order - 1); + const int face_size = (order - 1) * (order - 1); + if (nbdy == 1) { // face + if (ibdy) + return (j - 1) + (order - 1) * (k - 1) + (i ? face_size : 0) + offset; + offset += 2 * face_size; + if (jbdy) + return (i - 1) + (order - 1) * (k - 1) + (j ? face_size : 0) + offset; + offset += 2 * face_size; + return (i - 1) + (order - 1) * (j - 1) + (k ? face_size : 0) + offset; + } + + offset += 6 * face_size; // interior + return offset + (i - 1) + (order - 1) * ((j - 1) + (order - 1) * (k - 1)); +} + +/** + * @brief Lagrange node positions for a hex of given order in reference + * coordinates [0,1]^3, in VTK's Lagrange hex ordering + */ +static std::vector> +get_hex_lagrange_nodes (int order) +{ + const int num_nodes = (order + 1) * (order + 1) * (order + 1); + std::vector> nodes (num_nodes); + + for (int k = 0; k <= order; ++k) + for (int j = 0; j <= order; ++j) + for (int i = 0; i <= order; ++i) + nodes[vtk_hex_point_index (i, j, k, order)] = { static_cast (i) / order, + static_cast (j) / order, + static_cast (k) / order }; + + return nodes; +} + +/** + * @brief Write VTK file header for Lagrange elements + */ +static void +write_vtk_header (std::ofstream &file, int num_points, int num_cells) +{ + file << "\n"; + // Version >= 2.2 required: for older versions VTK's reader assumes the + // pre-9.0 Lagrange hex numbering and permutes the cell connectivity. + file << "\n"; + file << " \n"; + file << " \n"; +} + +/** + * @brief Write VTK footer + */ +static void +write_vtk_footer (std::ofstream &file) +{ + file << " \n"; + file << " \n"; + file << "\n"; +} + +/** + * @brief Evaluate Legendre basis at given reference point for triangle + * + * IMPORTANT: ref_point contains (xi, eta) but scaling_function expects barycentric coords (λ0, λ1) + * Conversion: λ0 = 1 - xi - eta, λ1 = xi + */ +template +static std::array +eval_legendre_basis_triangle (const std::array &ref_point) +{ + std::array basis_vals = {}; + + // Convert from reference coords (xi, eta) to barycentric coords (λ0, λ1) + // Reference: xi = λ1, eta = λ2, so λ0 = 1 - xi - eta + const double lambda0 = 1.0 - ref_point[0] - ref_point[1]; + const double lambda1 = ref_point[0]; + + // Use existing scaling_function for triangle basis (expects barycentric coords) + for (size_t i = 0; i < DOF; ++i) { + basis_vals[i] = t8_mra::scaling_function (i, lambda0, lambda1); + } + + return basis_vals; +} + +/** + * @brief Evaluate Legendre basis at given reference point for line + */ +template +static std::array +eval_legendre_basis_line (const std::array &ref_point) +{ + std::array basis_vals = {}; + + // 1D Legendre polynomials + for (size_t i = 0; i < DOF; ++i) { + basis_vals[i] = t8_mra::phi_1d (ref_point[0], i); + } + + return basis_vals; +} + +/** + * @brief Evaluate Legendre basis at given reference point for quad + */ +template +static std::array +eval_legendre_basis_quad (const std::array &ref_point) +{ + std::array basis_vals = {}; + + // Tensor product of 1D Legendre polynomials + int idx = 0; + for (int j = 0; j < P_DIM; ++j) { + for (int i = 0; i < P_DIM; ++i) { + const double val = t8_mra::phi_1d (ref_point[0], i) * t8_mra::phi_1d (ref_point[1], j); + basis_vals[idx++] = val; + } + } + + return basis_vals; +} + +/** + * @brief Evaluate Legendre basis at given reference point for hex + */ +template +static std::array +eval_legendre_basis_hex (const std::array &ref_point) +{ + std::array basis_vals = {}; + + // Tensor product of 1D Legendre polynomials (3D) + int idx = 0; + for (int k = 0; k < P_DIM; ++k) { + for (int j = 0; j < P_DIM; ++j) { + for (int i = 0; i < P_DIM; ++i) { + const double val + = t8_mra::phi_1d (ref_point[0], i) * t8_mra::phi_1d (ref_point[1], j) * t8_mra::phi_1d (ref_point[2], k); + basis_vals[idx++] = val; + } + } + } + + return basis_vals; +} + +/** + * @brief Write a VTK Lagrange file for any MRA implementation + * + * @tparam MRA The multiscale class type (triangle, quad, line, or hex specialization) + * @param mra The multiscale object + * @param prefix Output file prefix + * @param lagrange_order Polynomial order for Lagrange interpolation (P-1) + */ +template +void +write_forest_lagrange_vtk (MRA &mra, const char *prefix, int lagrange_order) +{ + static constexpr auto TShape = MRA::Shape; + static constexpr int U_DIM = MRA::U_DIM; + static constexpr int P_DIM = MRA::P_DIM; + static constexpr int DOF = MRA::DOF; + + t8_forest_t forest = mra.get_forest (); + auto *lmi_map = mra.get_lmi_map (); + + const auto num_local_elements = t8_forest_get_local_num_leaf_elements (forest); + const auto num_local_trees = t8_forest_get_num_local_trees (forest); + + // Determine element-specific parameters + int num_nodes_per_elem; + int vtk_cell_type; + int num_vertices; + + if constexpr (TShape == T8_ECLASS_LINE) { + num_nodes_per_elem = lagrange_order + 1; + vtk_cell_type = 68; // VTK_LAGRANGE_CURVE + num_vertices = 2; + } + else if constexpr (TShape == T8_ECLASS_TRIANGLE) { + num_nodes_per_elem = (lagrange_order + 1) * (lagrange_order + 2) / 2; + vtk_cell_type = 69; // VTK_LAGRANGE_TRIANGLE + num_vertices = 3; + } + else if constexpr (TShape == T8_ECLASS_QUAD) { + num_nodes_per_elem = (lagrange_order + 1) * (lagrange_order + 1); + vtk_cell_type = 70; // VTK_LAGRANGE_QUADRILATERAL + num_vertices = 4; + } + else if constexpr (TShape == T8_ECLASS_HEX) { + num_nodes_per_elem = (lagrange_order + 1) * (lagrange_order + 1) * (lagrange_order + 1); + vtk_cell_type = 72; // VTK_LAGRANGE_HEXAHEDRON + num_vertices = 8; + } + else { + T8_ASSERT (false && "Unsupported element type for VTK output"); + } + + const int total_points = num_local_elements * num_nodes_per_elem; + + // Open output file + std::string filename = std::string (prefix) + ".vtu"; + std::ofstream file (filename); + file << std::scientific << std::setprecision (16); + + // Write header + write_vtk_header (file, total_points, num_local_elements); + + // Write points + file << " \n"; + file << " \n"; + + std::vector> all_points; + all_points.reserve (total_points); + + auto *scheme = t8_forest_get_scheme (forest); + + for (t8_locidx_t tree_idx = 0; tree_idx < num_local_trees; ++tree_idx) { + const auto num_elem_in_tree = t8_forest_get_tree_num_leaf_elements (forest, tree_idx); + + for (t8_locidx_t elem_in_tree = 0; elem_in_tree < num_elem_in_tree; ++elem_in_tree) { + const auto *element = t8_forest_get_leaf_element_in_tree (forest, tree_idx, elem_in_tree); + + // Get LMI to look up vertex ordering (for triangles) + const auto base_tree = t8_forest_global_tree_id (forest, tree_idx); + const auto lmi = typename MRA::levelmultiindex (base_tree, element, scheme); + + // Get element vertices with proper ordering + double vertices[8][3] = {}; + + if constexpr (TShape == T8_ECLASS_LINE) { + // LINE: just 2 endpoints + for (int i = 0; i < 2; ++i) + t8_forest_element_coordinate (forest, tree_idx, element, i, vertices[i]); + } + else if constexpr (TShape == T8_ECLASS_TRIANGLE) { + // Apply triangle vertex ordering from element data + if (lmi_map->contains (lmi)) { + const auto &elem_data = lmi_map->get (lmi); + const auto &point_order = elem_data.order; + + // Apply permutation: t8code vertex v goes to position order[v] + for (int v = 0; v < 3; ++v) { + double coords[3]; + t8_forest_element_coordinate (forest, tree_idx, element, v, coords); + const int ref_v = point_order[v]; + vertices[ref_v][0] = coords[0]; + vertices[ref_v][1] = coords[1]; + vertices[ref_v][2] = coords[2]; + } + } + else { + // Fallback: no permutation if element not in map + for (int i = 0; i < 3; ++i) + t8_forest_element_coordinate (forest, tree_idx, element, i, vertices[i]); + } + } + else if constexpr (TShape == T8_ECLASS_QUAD) { + // Reorder quad vertices for standard ordering + const int vertex_perm[4] = { 0, 1, 3, 2 }; + for (int i = 0; i < 4; ++i) + t8_forest_element_coordinate (forest, tree_idx, element, vertex_perm[i], vertices[i]); + } + else if constexpr (TShape == T8_ECLASS_HEX) { + // HEX: Apply z-order permutation like QUAD + // t8code (z-order): 0:(0,0,0), 1:(1,0,0), 2:(0,1,0), 3:(1,1,0), 4:(0,0,1), 5:(1,0,1), 6:(0,1,1), 7:(1,1,1) + // VTK: 0:(0,0,0), 1:(1,0,0), 2:(1,1,0), 3:(0,1,0), 4:(0,0,1), 5:(1,0,1), 6:(1,1,1), 7:(0,1,1) + const int vertex_perm[8] = { 0, 1, 3, 2, 4, 5, 7, 6 }; + for (int i = 0; i < 8; ++i) + t8_forest_element_coordinate (forest, tree_idx, element, vertex_perm[i], vertices[i]); + } + + // Map reference nodes to physical coordinates (element-specific) + if constexpr (TShape == T8_ECLASS_LINE) { + const auto lagrange_nodes = get_line_lagrange_nodes (lagrange_order); + for (const auto &ref_node : lagrange_nodes) { + std::array phys_point = { 0, 0, 0 }; + const double xi = ref_node[0]; + for (int d = 0; d < 3; ++d) + phys_point[d] = (1.0 - xi) * vertices[0][d] + xi * vertices[1][d]; + all_points.push_back (phys_point); + file << " " << phys_point[0] << " " << phys_point[1] << " " << phys_point[2] << "\n"; + } + } + else if constexpr (TShape == T8_ECLASS_TRIANGLE) { + const auto lagrange_nodes = get_triangle_lagrange_nodes (lagrange_order); + for (const auto &ref_node : lagrange_nodes) { + std::array phys_point = { 0, 0, 0 }; + const double w0 = 1.0 - ref_node[0] - ref_node[1]; + const double w1 = ref_node[0]; + const double w2 = ref_node[1]; + for (int d = 0; d < 3; ++d) + phys_point[d] = w0 * vertices[0][d] + w1 * vertices[1][d] + w2 * vertices[2][d]; + all_points.push_back (phys_point); + file << " " << phys_point[0] << " " << phys_point[1] << " " << phys_point[2] << "\n"; + } + } + else if constexpr (TShape == T8_ECLASS_QUAD) { + const auto lagrange_nodes = get_quad_lagrange_nodes (lagrange_order); + for (const auto &ref_node : lagrange_nodes) { + std::array phys_point = { 0, 0, 0 }; + const double xi = ref_node[0]; + const double eta = ref_node[1]; + for (int d = 0; d < 3; ++d) { + phys_point[d] = (1 - xi) * (1 - eta) * vertices[0][d] + xi * (1 - eta) * vertices[1][d] + + xi * eta * vertices[2][d] + (1 - xi) * eta * vertices[3][d]; + } + all_points.push_back (phys_point); + file << " " << phys_point[0] << " " << phys_point[1] << " " << phys_point[2] << "\n"; + } + } + else if constexpr (TShape == T8_ECLASS_HEX) { + const auto lagrange_nodes = get_hex_lagrange_nodes (lagrange_order); + int node_idx = 0; + for (const auto &ref_node : lagrange_nodes) { + std::array phys_point = { 0, 0, 0 }; + const double xi = ref_node[0]; + const double eta = ref_node[1]; + const double zeta = ref_node[2]; + // Trilinear mapping for VTK hex vertex ordering (after permutation): + // v0:(0,0,0), v1:(1,0,0), v2:(1,1,0), v3:(0,1,0), + // v4:(0,0,1), v5:(1,0,1), v6:(1,1,1), v7:(0,1,1) + for (int d = 0; d < 3; ++d) { + phys_point[d] = (1 - xi) * (1 - eta) * (1 - zeta) * vertices[0][d] // v0: (0,0,0) + + xi * (1 - eta) * (1 - zeta) * vertices[1][d] // v1: (1,0,0) + + xi * eta * (1 - zeta) * vertices[2][d] // v2: (1,1,0) + + (1 - xi) * eta * (1 - zeta) * vertices[3][d] // v3: (0,1,0) + + (1 - xi) * (1 - eta) * zeta * vertices[4][d] // v4: (0,0,1) + + xi * (1 - eta) * zeta * vertices[5][d] // v5: (1,0,1) + + xi * eta * zeta * vertices[6][d] // v6: (1,1,1) + + (1 - xi) * eta * zeta * vertices[7][d]; // v7: (0,1,1) + } + + all_points.push_back (phys_point); + file << " " << phys_point[0] << " " << phys_point[1] << " " << phys_point[2] << "\n"; + node_idx++; + } + } + } + } + + file << " \n"; + file << " \n"; + + // Write cells + file << " \n"; + file << " \n"; + + for (t8_locidx_t elem_idx = 0; elem_idx < num_local_elements; ++elem_idx) { + file << " "; + const int base_idx = elem_idx * num_nodes_per_elem; + for (int i = 0; i < num_nodes_per_elem; ++i) + file << (base_idx + i) << " "; + file << "\n"; + } + + file << " \n"; + file << " \n"; + + for (t8_locidx_t elem_idx = 1; elem_idx <= num_local_elements; ++elem_idx) + file << " " << (elem_idx * num_nodes_per_elem) << "\n"; + + file << " \n"; + file << " \n"; + + for (t8_locidx_t elem_idx = 0; elem_idx < num_local_elements; ++elem_idx) + file << " " << vtk_cell_type << "\n"; + + file << " \n"; + file << " \n"; + + // Write cell data (HigherOrderDegrees and Level) + file << " \n"; + file << " \n"; + + for (t8_locidx_t elem_idx = 0; elem_idx < num_local_elements; ++elem_idx) { + if constexpr (TShape == T8_ECLASS_LINE) + file << " " << lagrange_order << " 1 1\n"; + else if constexpr (TShape == T8_ECLASS_TRIANGLE) + file << " " << lagrange_order << " " << lagrange_order << " 1\n"; + else if constexpr (TShape == T8_ECLASS_QUAD) + file << " " << lagrange_order << " " << lagrange_order << " 1\n"; + else if constexpr (TShape == T8_ECLASS_HEX) + file << " " << lagrange_order << " " << lagrange_order << " " << lagrange_order << "\n"; + } + + file << " \n"; + + // Write refinement level for each element + file << " \n"; + + for (t8_locidx_t tree_idx = 0; tree_idx < num_local_trees; ++tree_idx) { + const auto num_elem_in_tree = t8_forest_get_tree_num_leaf_elements (forest, tree_idx); + const auto tree_class = t8_forest_get_tree_class (forest, tree_idx); + + for (t8_locidx_t elem_in_tree = 0; elem_in_tree < num_elem_in_tree; ++elem_in_tree) { + const auto *element = t8_forest_get_leaf_element_in_tree (forest, tree_idx, elem_in_tree); + const int level = scheme->element_get_level (tree_class, element); + file << " " << level << "\n"; + } + } + + file << " \n"; + file << " \n"; + + // Write point data (solution values) + file << " \n"; + + for (int u = 0; u < U_DIM; ++u) { + file << " \n"; + + for (t8_locidx_t tree_idx = 0; tree_idx < num_local_trees; ++tree_idx) { + const auto num_elem_in_tree = t8_forest_get_tree_num_leaf_elements (forest, tree_idx); + const auto base_tree = t8_forest_global_tree_id (forest, tree_idx); + + for (t8_locidx_t elem_in_tree = 0; elem_in_tree < num_elem_in_tree; ++elem_in_tree) { + const auto *element = t8_forest_get_leaf_element_in_tree (forest, tree_idx, elem_in_tree); + + // Get LMI for this element + const auto lmi = typename MRA::levelmultiindex (base_tree, element, scheme); + + if (!lmi_map->contains (lmi)) { + // Element not in map, output zeros + for (int i = 0; i < num_nodes_per_elem; ++i) + file << " 0.0\n"; + continue; + } + + const auto &data = lmi_map->get (lmi); + const auto &u_coeffs = data.u_coeffs; + + // Get element volume for scaling factor (element-specific) + const auto volume = t8_forest_element_volume (forest, tree_idx, element); + double scaling; + if constexpr (TShape == T8_ECLASS_TRIANGLE) { + // Triangles use sqrt(1/(2*volume)) scaling + scaling = std::sqrt (1.0 / (2.0 * volume)); + } + else { + // Cartesian elements (LINE, QUAD, HEX) use orthonormal Legendre basis - no volume scaling + scaling = 1.0; + } + + // Evaluate solution at Lagrange nodes (element-specific) + if constexpr (TShape == T8_ECLASS_LINE) { + const auto lagrange_nodes = get_line_lagrange_nodes (lagrange_order); + for (const auto &ref_node : lagrange_nodes) { + const auto basis_vals = eval_legendre_basis_line (ref_node); + double u_val = 0.0; + for (int i = 0; i < DOF; ++i) + u_val += u_coeffs[MRA::element_t::dg_idx (u, i)] * basis_vals[i] * scaling; + file << " " << u_val << "\n"; + } + } + else if constexpr (TShape == T8_ECLASS_TRIANGLE) { + const auto lagrange_nodes = get_triangle_lagrange_nodes (lagrange_order); + for (const auto &ref_node : lagrange_nodes) { + const auto basis_vals = eval_legendre_basis_triangle (ref_node); + double u_val = 0.0; + for (int i = 0; i < DOF; ++i) + u_val += u_coeffs[MRA::element_t::dg_idx (u, i)] * basis_vals[i] * scaling; + file << " " << u_val << "\n"; + } + } + else if constexpr (TShape == T8_ECLASS_QUAD) { + const auto lagrange_nodes = get_quad_lagrange_nodes (lagrange_order); + for (const auto &ref_node : lagrange_nodes) { + const auto basis_vals = eval_legendre_basis_quad (ref_node); + double u_val = 0.0; + for (int i = 0; i < DOF; ++i) + u_val += u_coeffs[MRA::element_t::dg_idx (u, i)] * basis_vals[i] * scaling; + file << " " << u_val << "\n"; + } + } + else if constexpr (TShape == T8_ECLASS_HEX) { + const auto lagrange_nodes = get_hex_lagrange_nodes (lagrange_order); + for (const auto &ref_node : lagrange_nodes) { + const auto basis_vals = eval_legendre_basis_hex (ref_node); + double u_val = 0.0; + for (int i = 0; i < DOF; ++i) + u_val += u_coeffs[MRA::element_t::dg_idx (u, i)] * basis_vals[i] * scaling; + file << " " << u_val << "\n"; + } + } + } + } + + file << " \n"; + } + + file << " \n"; + + // Write footer + write_vtk_footer (file); + + file.close (); + + t8_debugf ("Wrote VTK file: %s\n", filename.c_str ()); +} + +} // namespace t8_mra + +#endif // T8_ENABLE_MRA diff --git a/src/t8_mra/num/basis_functions.cxx b/src/t8_mra/num/basis_functions.cxx new file mode 100644 index 0000000000..f60b9be6d0 --- /dev/null +++ b/src/t8_mra/num/basis_functions.cxx @@ -0,0 +1,1271 @@ +#ifdef T8_ENABLE_MRA + +#include +#include "t8_mra/num/basis_functions.hxx" + +/* Note from Veli Ünlü about rights: These are the basis functions from + * the bachelor thesis in mathematics of Florian Sieglar, "Konstruktion von Multiwavelets auf Dreiecksgittern" + * from November 2013. + */ + +namespace t8_mra +{ +double +scaling_function (int i, double tau1, double tau2) +{ + switch (i) { + case 0: + return sqrt (2.); + case 1: + return 6. * tau2 - 2.; + case 2: + return 4. * (0.5 - tau1 - 0.5 * tau2) * sqrt (3.); + case 3: + return (10. * (tau2 * tau2 + 1. / 10. - (4. / 5.) * tau2)) * sqrt (6.); + case 4: + return (30. + * (tau2 * (1. - tau1 - tau2) - 1. / 10. - (2. / 5.) * tau2 + (1. / 5.) * tau1 + (1. / 2.) * tau2 * tau2)) + * sqrt (2.); + case 5: + return (6. + * ((1. - tau1 - tau2) * (1. - tau1 - tau2) - 5. / 6. + (2. / 3.) * tau2 + tau1 + (1. / 6.) * tau2 * tau2 + + tau2 * (1. - tau1 - tau2))) + * sqrt (30.); + case 6: + return (70. * (tau2 * tau2 * tau2 - 1. / 35. + (3. / 7.) * tau2 - (9. / 7.) * tau2 * tau2)) * sqrt (2.); + case 7: + return (84. + * (tau2 * tau2 * (1. - tau1 - tau2) + 1. / 42. + (11. / 42.) * tau2 - (1. / 21.) * tau1 + - (11. / 14.) * tau2 * tau2 - (4. / 7.) * tau2 * (1. - tau1 - tau2) + (1. / 2.) * tau2 * tau2 * tau2)) + * sqrt (6.); + case 8: + return (84. + * (tau2 * (1. - tau1 - tau2) * (1. - tau1 - tau2) + 5. / 42. + (1. / 14.) * tau2 - (1. / 7.) * tau1 + - (5. / 14.) * tau2 * tau2 - (1. / 7.) * (1. - tau1 - tau2) * (1. - tau1 - tau2) + + (1. / 6.) * tau2 * tau2 * tau2 - (8. / 7.) * tau2 * (1. - tau1 - tau2) + + tau2 * tau2 * (1. - tau1 - tau2))) + * sqrt (10.); + case 9: + return (40. + * ((1. - tau1 - tau2) * (1. - tau1 - tau2) * (1. - tau1 - tau2) + 11. / 20. - (9. / 20.) * tau2 + - (3. / 5.) * tau1 - (3. / 20.) * tau2 * tau2 - (3. / 2.) * (1. - tau1 - tau2) * (1. - tau1 - tau2) + + (1. / 20.) * tau2 * tau2 * tau2 - (6. / 5.) * tau2 * (1. - tau1 - tau2) + + (3. / 5.) * tau2 * tau2 * (1. - tau1 - tau2) + + (3. / 2.) * tau2 * (1. - tau1 - tau2) * (1. - tau1 - tau2))) + * sqrt (14.); + default:; + }; +} + +double +muttermultiwavelets (int p, int i, double tau1, double tau2, int e) +{ + switch (p) { + case 1: + switch (i) { + case 0: + if (e == 0) + return 0.; + else if (e == 1) + return (2. / 3.) * sqrt (3.); + else if (e == 2) + return -(4. / 3.) * sqrt (3.); + else + return (2. / 3.) * sqrt (3.); + case 1: + if (e == 0) + return 0.; + else if (e == 1) + return 2.; + else if (e == 2) + return 0.; + else + return -2.; + case 2: + if (e == 0) + return -sqrt (2.) * sqrt (3.); + else if (e == 1) + return (1. / 3.) * sqrt (2.) * sqrt (3.); + else if (e == 2) + return (1. / 3.) * sqrt (2.) * sqrt (3.); + else + return (1. / 3.) * sqrt (2.) * sqrt (3.); + default:; + }; + case 2: + switch (i) { + case 0: + if (e == 0) + return -(1. / 5.) * sqrt (2.) * (-1. + 8. * tau2) * sqrt (5.); + else if (e == 1) + return -(1. / 5.) * sqrt (2.) * (-3. + 16. * tau2) * sqrt (5.); + else if (e == 2) + return (3. / 5.) * sqrt (2.) * (-5. + 8. * tau2) * sqrt (5.); + else + return -(1. / 5.) * sqrt (2.) * (-3. + 16. * tau2) * sqrt (5.); + case 1: + if (e == 0) + return (4. / 5.) * sqrt (2.) * (-1. + tau2 + 2. * tau1) * sqrt (15.); + else if (e == 1) + return -(1. / 5.) * sqrt (2.) * (-1. - 8. * tau2 + 4. * tau1) * sqrt (15.); + else if (e == 2) + return (8. / 5.) * sqrt (2.) * (-1. + tau2 + 2. * tau1) * sqrt (15.); + else + return -(1. / 5.) * sqrt (2.) * (-3. + 12. * tau2 + 4. * tau1) * sqrt (15.); + case 2: + if (e == 0) + return -2. * sqrt (2.) + 4. * sqrt (2.) * tau2; + else if (e == 1) + return -9. * sqrt (2.) + 8. * sqrt (2.) * tau2 + 12. * sqrt (2.) * tau1; + else if (e == 2) + return 0.; + else + return 3. * sqrt (2.) - 4. * sqrt (2.) * tau2 - 12. * sqrt (2.) * tau1; + case 3: + if (e == 0) + return (2. / 35. * (16. * tau2 - 7.)) * sqrt (35.); + else if (e == 1) + return -(8. / 35. * (7. * tau2 - 1.)) * sqrt (35.); + else if (e == 2) + return -(2. / 35. * (48. * tau2 - 35.)) * sqrt (35.); + else + return -(8. / 35. * (7. * tau2 - 1.)) * sqrt (35.); + case 4: + if (e == 0) + return -(32. / 965. * (-1. + tau2 + 2. * tau1)) * sqrt (2895.); + else if (e == 1) + return (2. / 965. * (16. * tau1 - 92. * tau2 + 1.)) * sqrt (2895.); + else if (e == 2) + return (176. / 965. * (-1. + tau2 + 2. * tau1)) * sqrt (2895.); + else + return (2. / 965. * (16. * tau1 + 108. * tau2 - 17.)) * sqrt (2895.); + case 5: + if (e == 0) + return -(2. / 203. * (7. + 8. * tau2)) * sqrt (406.); + else if (e == 1) + return -(1. / 203. * (84. * tau1 - 59. - 28. * tau2)) * sqrt (406.); + else if (e == 2) + return -(4. / 203. * (-7. + 9. * tau2)) * sqrt (406.); + else + return (1. / 203. * (84. * tau1 - 25. + 112. * tau2)) * sqrt (406.); + case 6: + if (e == 0) + return (144. / 5597. * (-1. + tau2 + 2. * tau1)) * sqrt (11194.); + else if (e == 1) + return -(1. / 5597. * (-763. + 916. * tau1 + 716. * tau2)) * sqrt (11194.); + else if (e == 2) + return -(20. / 5597. * (-1. + tau2 + 2. * tau1)) * sqrt (11194.); + else + return -(1. / 5597. * (-153. + 916. * tau1 + 200. * tau2)) * sqrt (11194.); + case 7: + if (e == 0) + return (22. / 29.) * sqrt (3.) * (3. * tau2 - 1.) * sqrt (29.); + else if (e == 1) + return -(2. / 29.) * sqrt (3.) * (7. * tau2 + 8. * tau1 - 7.) * sqrt (29.); + else if (e == 2) + return (2. / 29.) * sqrt (3.) * (-7. + 9. * tau2) * sqrt (29.); + else + return (2. / 29.) * sqrt (3.) * (tau2 + 8. * tau1 - 1.) * sqrt (29.); + case 8: + if (e == 0) + return -(66. / 29. * (-1. + tau2 + 2. * tau1)) * sqrt (29.); + else if (e == 1) + return -(2. / 29. * (-7. + 10. * tau1 - 7. * tau2)) * sqrt (29.); + else if (e == 2) + return (14. / 29. * (-1. + tau2 + 2. * tau1)) * sqrt (29.); + else + return -(2. / 29. * (-3. + 10. * tau1 + 17. * tau2)) * sqrt (29.); + default:; + }; + case 3: + switch (i) { + case 0: + if (e == 0) + return -(8. / 21.) * sqrt (2.) * (1. - 15. * tau2 + 30. * tau2 * tau2) * sqrt (7.); + else if (e == 1) + return -(2. / 21.) * sqrt (2.) * (7. + 180. * tau2 * tau2 - 90. * tau2) * sqrt (7.); + else if (e == 2) + return (8. / 21.) * sqrt (2.) * (22. - 75. * tau2 + 60. * tau2 * tau2) * sqrt (7.); + else + return -(2. / 21.) * sqrt (2.) * (7. + 180. * tau2 * tau2 - 90. * tau2) * sqrt (7.); + case 1: + if (e == 0) + return -(8. / 7. * (-3. * tau2 + 1. + 4. * tau2 * tau1 - 2. * tau1 + 2. * tau2 * tau2)) * sqrt (2.) + * sqrt (21.); + else if (e == 1) + return (2. / 21.) * sqrt (2.) * (5. - 36. * tau2 * tau2 - 30. * tau2 - 12. * tau1 + 96. * tau2 * tau1) + * sqrt (21.); + else if (e == 2) + return -(40. / 7. * (-3. * tau2 + 1. + 4. * tau2 * tau1 - 2. * tau1 + 2. * tau2 * tau2)) * sqrt (2.) + * sqrt (21.); + else + return (2. / 21.) * sqrt (2.) * (7. + 132. * tau2 * tau2 - 78. * tau2 - 12. * tau1 + 96. * tau2 * tau1) + * sqrt (21.); + case 2: + if (e == 0) + return (8. / 21.) * sqrt (2.) + * (7. - 21. * tau2 - 24. * tau1 + 18. * tau2 * tau2 + 24. * tau2 * tau1 + 24. * tau1 * tau1) + * sqrt (35.); + else if (e == 1) + return -(2. / 21.) * sqrt (2.) + * (-5. + 126. * tau2 - 12. * tau1 - 108. * tau2 * tau2 - 144. * tau2 * tau1 + 24. * tau1 * tau1) + * sqrt (35.); + else if (e == 2) + return (16. / 7.) * sqrt (2.) * (6. * tau2 * tau1 - 2. * tau2 + tau2 * tau2 + 6. * tau1 * tau1 - 6. * tau1 + 1.) + * sqrt (35.); + else + return -(2. / 21.) * sqrt (2.) + * (7. - 54. * tau2 - 36. * tau1 + 60. * tau2 * tau2 + 192. * tau2 * tau1 + 24. * tau1 * tau1) + * sqrt (35.); + case 3: + if (e == 0) + return (8. * (-3. * tau2 + 1. + 4. * tau2 * tau1 - 2. * tau1 + 2. * tau2 * tau2)) * sqrt (2.); + else if (e == 1) + return (2. / 3. + * (-90. * tau2 + 55. + 144. * tau2 * tau1 - 168. * tau1 + 120. * tau1 * tau1 + 36. * tau2 * tau2)) + * sqrt (2.); + else if (e == 2) + return 0.0; + else + return -(2. / 3. * (-18. * tau2 + 7. + 96. * tau2 * tau1 - 72. * tau1 + 120. * tau1 * tau1 + 12. * tau2 * tau2)) + * sqrt (2.); + case 4: + if (e == 0) + return (5. / 148533. * (-169. + 456. * tau2 + 600. * tau2 * tau2)) * sqrt (99022.); + else if (e == 1) + return (1. / 148533. * (931. - 15480. * tau2 + 38520. * tau2 * tau2)) * sqrt (99022.); + else if (e == 2) + return (1. / 148533. * (29363. - 87000. * tau2 + 62040. * tau2 * tau2)) * sqrt (99022.); + else + return (1. / 148533. * (931. - 15480. * tau2 + 38520. * tau2 * tau2)) * sqrt (99022.); + case 5: + if (e == 0) + return -(20. / 119. * (-4. * tau1 + 2. + 14. * tau2 * tau1 + 7. * tau2 * tau2 - 9. * tau2)) * sqrt (714.); + else if (e == 1) + return (1. / 357. * (-60. * tau1 + 17. + 600. * tau2 * tau1 - 900. * tau2 * tau2)) * sqrt (714.); + else if (e == 2) + return (100. / 119. * (-6. * tau1 + 3. + 10. * tau2 * tau1 + 5. * tau2 * tau2 - 8. * tau2)) * sqrt (714.); + else + return (1. / 357. * (-60. * tau1 + 43. + 600. * tau2 * tau1 + 1500. * tau2 * tau2 - 660. * tau2)) * sqrt (714.); + case 6: + if (e == 0) + return (2. / 13630367358069. + * (11771011. - 45917916. * tau1 + 45917916. * tau1 * tau1 - 12669510. * tau2 - 19247958. * tau2 * tau2 + + 45917916. * tau2 * tau1)) + * sqrt (45434557860230.); + else if (e == 1) + return (1. / 13630367358069. + * (-10972727. + 2121900. * tau1 + 17144952. * tau1 * tau1 + 195960240. * tau2 - 93890124. * tau2 * tau2 + - 263285352. * tau2 * tau1)) + * sqrt (45434557860230.); + else if (e == 2) + return (4. / 4543455786023. + * (4510579. - 31531434. * tau1 + 31531434. * tau1 * tau1 - 8359658. * tau2 + 3758359. * tau2 * tau2 + + 31531434. * tau2 * tau1)) + * sqrt (45434557860230.); + else + return (1. / 13630367358069. + * (8294125. - 36411804. * tau1 + 17144952. * tau1 * tau1 - 103736916. * tau2 + 186540180. * tau2 * tau2 + + 297575256. * tau2 * tau1)) + * sqrt (45434557860230.); + case 7: + if (e == 0) + return (8. / 710311.) * sqrt (2.) + * (979. * tau2 * tau2 - 1572. * tau2 + 593. - 1186. * tau1 + 1958. * tau2 * tau1) * sqrt (3551555.); + else if (e == 1) + return -(1. / 2130933.) * sqrt (2.) + * (-104904. * tau2 * tau2 + 9911. + 104652. * tau2 + 39984. * tau1 * tau1 - 45876. * tau1 + - 104688. * tau2 * tau1) + * sqrt (3551555.); + else if (e == 2) + return -(12. / 710311.) * sqrt (2.) * (6. * tau2 * tau2 + 7. - 13. * tau2 - 14. * tau1 + 12. * tau2 * tau1) + * sqrt (3551555.); + else + return (1. / 2130933.) * sqrt (2.) + * (39768. * tau2 * tau2 + 4019. - 34128. * tau2 + 39984. * tau1 * tau1 - 34092. * tau1 + + 184656. * tau2 * tau1) + * sqrt (3551555.); + case 8: + if (e == 0) + return (6. / 8002149779950753.) * sqrt (2.) + * (5585291. - 74690060. * tau2 * tau2 + 34995320. * tau2 + 99240520. * tau2 * tau1 - 99240520. * tau1 + + 99240520. * tau1 * tau1) + * sqrt (40010748899753765.); + else if (e == 1) + return -(3. / 8002149779950753.) * sqrt (2.) + * (891343943. + 629296536. * tau2 * tau2 - 1507965276. * tau2 + 2094287056. * tau2 * tau1 + - 2411211524. * tau1 + 1572337984. * tau1 * tau1) + * sqrt (40010748899753765.); + else if (e == 2) + return -(12. / 8002149779950753.) * sqrt (2.) + * (730663. + 248486. * tau2 * tau2 - 1037301. * tau2 + 7247964. * tau2 * tau1 - 7247964. * tau1 + + 7247964. * tau1 * tau1) + * sqrt (40010748899753765.); + else + return -(3. / 8002149779950753.) * sqrt (2.) + * (52470403. + 107347464. * tau2 * tau2 - 147142664. * tau2 + 1050388912. * tau2 * tau1 + - 733464444. * tau1 + 1572337984. * tau1 * tau1) + * sqrt (40010748899753765.); + case 9: + if (e == 0) + return (6. / 3267475795710091. + * (-227001675. * tau2 - 31397600. * tau1 + 338286776. * tau2 * tau2 + 34827437. + + 31397600. * tau2 * tau1 + 31397600. * tau1 * tau1)) + * sqrt (16337378978550455.); + else if (e == 1) + return (6. / 3267475795710091. + * (165910845. * tau2 - 7535320. * tau1 - 320509544. * tau2 * tau2 - 6185613. - 84778720. * tau2 * tau1 + + 11385920. * tau1 * tau1)) + * sqrt (16337378978550455.); + else if (e == 2) + return (6. / 3267475795710091. + * (267081993. - 715539635. * tau2 - 132269280. * tau1 + 470342264. * tau2 * tau2 + + 132269280. * tau2 * tau1 + 132269280. * tau1 * tau1)) + * sqrt (16337378978550455.); + else + return (6. / 3267475795710091. + * (65895605. * tau2 - 224344904. * tau2 * tau2 - 2335013. + 107550560. * tau2 * tau1 + + 11385920. * tau1 * tau1 - 15236520. * tau1)) + * sqrt (16337378978550455.); + case 10: + if (e == 0) + return -(1. / 67813809.) * sqrt (3.) + * (80383. - 160766. * tau1 - 232909. * tau2 + 152526. * tau2 * tau2 + 305052. * tau2 * tau1) + * sqrt (22604603.); + else if (e == 1) + return (1. / 67813809.) * sqrt (3.) + * (-2545. + 7014. * tau1 - 4195. * tau2 + 474982. * tau2 * tau2 - 232116. * tau2 * tau1 + + 9800. * tau1 * tau1) + * sqrt (22604603.); + else if (e == 2) + return (5. / 67813809.) * sqrt (3.) + * (106401. - 212802. * tau1 - 265819. * tau2 + 159418. * tau2 * tau2 + 318836. * tau2 * tau1) + * sqrt (22604603.); + else + return -(1. / 67813809.) * sqrt (3.) + * (14269. - 26614. * tau1 - 262925. * tau2 + 716898. * tau2 * tau2 + 251716. * tau2 * tau1 + + 9800. * tau1 * tau1) + * sqrt (22604603.); + case 11: + if (e == 0) + return (4. / 2090724953684663.) * sqrt (2.) + * (302997438. * tau1 * tau1 - 302997438. * tau1 + 98047139. + 235688625. * tau2 * tau2 + - 293824914. * tau2 + 302997438. * tau2 * tau1) + * sqrt (10453624768423315.); + else if (e == 1) + return (4. / 2090724953684663.) * sqrt (2.) + * (24456488. * tau1 * tau1 - 9860609. - 5548132. * tau1 - 40311757. * tau2 * tau2 + 236858050. * tau2 + - 332175154. * tau2 * tau1) + * sqrt (10453624768423315.); + else if (e == 2) + return -(4. / 2090724953684663.) * sqrt (2.) + * (475844038. * tau1 * tau1 - 475844038. * tau1 + 67323083. + 59252053. * tau2 * tau2 - 127308866. * tau2 + + 475844038. * tau2 * tau1) + * sqrt (10453624768423315.); + else + return (4. / 2090724953684663.) * sqrt (2.) + * (24456488. * tau1 * tau1 + 9047747. - 43364844. * tau1 + 316319885. * tau2 * tau2 - 138681948. * tau2 + + 381088130. * tau2 * tau1) + * sqrt (10453624768423315.); + case 12: + if (e == 0) + return (1. / 11229548361. + * (-227207. * tau2 + 402996. * tau2 * tau1 + 201498. * tau2 * tau2 + 25709. - 51418. * tau1)) + * sqrt (4031407861599.); + else if (e == 1) + return -(1. / 11229548361. + * (374455. * tau2 - 466428. * tau2 * tau1 - 174814. * tau2 * tau2 + 19645. - 96078. * tau1 + + 88120. * tau1 * tau1)) + * sqrt (4031407861599.); + else if (e == 2) + return (5. / 11229548361. + * (-43663. * tau2 + 51140. * tau2 * tau1 + 25570. * tau2 * tau2 + 18093. - 36186. * tau1)) + * sqrt (4031407861599.); + else + return (1. / 11229548361. + * (-172135. * tau2 + 642668. * tau2 * tau1 + 379734. * tau2 * tau2 + 11687. - 80162. * tau1 + + 88120. * tau1 * tau1)) + * sqrt (4031407861599.); + case 13: + if (e == 0) + return (1. / 1537694113448282. + * (-2998716. * tau2 - 6553980. * tau1 + 2549628. * tau2 * tau2 + 6553980. * tau1 * tau1 + 1333543. + + 6553980. * tau2 * tau1)) + * sqrt (404363576778211096835.); + else if (e == 1) + return (1. / 1537694113448282. + * (1335668. * tau2 - 7463684. * tau1 - 4154804. * tau2 * tau2 + 4931668. * tau1 * tau1 + 2702051. + - 89012. * tau2 * tau1)) + * sqrt (404363576778211096835.); + else if (e == 2) + return -(1. / 1537694113448282. + * (937117. - 2441404. * tau2 - 64436. * tau1 + 1561004. * tau2 * tau2 + 64436. * tau1 * tau1 + + 64436. * tau2 * tau1)) + * sqrt (404363576778211096835.); + else + return (1. / 1537694113448282. + * (-1152996. * tau2 - 2399652. * tau1 + 865876. * tau2 * tau2 + 4931668. * tau1 * tau1 + 170035. + + 9952348. * tau2 * tau1)) + * sqrt (404363576778211096835.); + case 14: + if (e == 0) + return (6. / 739755559. * (1711. - 5671. * tau2 - 3422. * tau1 + 7920. * tau2 * tau1 + 3960. * tau2 * tau2)) + * sqrt (1083741893935.); + else if (e == 1) + return -(2. / 739755559. + * (30961. - 56675. * tau2 - 78022. * tau1 + 48072. * tau1 * tau1 + 72192. * tau2 * tau1 + + 25752. * tau2 * tau2)) + * sqrt (1083741893935.); + else if (e == 2) + return (2. / 739755559. * (517. - 1237. * tau2 - 1034. * tau1 + 1440. * tau2 * tau1 + 720. * tau2 * tau2)) + * sqrt (1083741893935.); + else + return (2. / 739755559. + * (1011. - 2605. * tau2 - 18122. * tau1 + 48072. * tau1 * tau1 + 23952. * tau2 * tau1 + + 1632. * tau2 * tau2)) + * sqrt (1083741893935.); + case 15: + if (e == 0) + return -(1. / 59474.) * sqrt (5.) + * (-985. - 3420. * tau1 + 12132. * tau2 + 3420. * tau1 * tau1 + 3420. * tau2 * tau1 + - 22932. * tau2 * tau2) + * sqrt (29737.); + else if (e == 1) + return -(1. / 59474.) * sqrt (5.) + * (2947. - 7204. * tau1 - 4844. * tau2 + 4340. * tau1 * tau1 + 5804. * tau2 * tau1 + 2492. * tau2 * tau2) + * sqrt (29737.); + else if (e == 2) + return (1. / 59474.) * sqrt (5.) + * (-3139. - 3412. * tau1 + 8548. * tau2 + 3412. * tau1 * tau1 + 3412. * tau2 * tau1 + - 5636. * tau2 * tau2) + * sqrt (29737.); + else + return -(1. / 59474.) * sqrt (5.) + * (83. - 1476. * tau1 - 516. * tau2 + 4340. * tau1 * tau1 + 2876. * tau2 * tau1 + 1028. * tau2 * tau2) + * sqrt (29737.); + case 16: + if (e == 0) + return (9. / 131.) * sqrt (2.) * sqrt (5.) + * (122. * tau2 * tau1 + 61. * tau2 * tau2 + 25. - 86. * tau2 - 50. * tau1) * sqrt (131.); + else if (e == 1) + return (1. / 131.) * sqrt (2.) * sqrt (5.) + * (-54. * tau2 * tau1 - 123. * tau2 * tau2 + 96. * tau1 * tau1 + 53. + 74. * tau2 - 146. * tau1) + * sqrt (131.); + else if (e == 2) + return (1. / 131.) * sqrt (2.) * sqrt (5.) + * (138. * tau2 * tau1 + 49. - 118. * tau2 + 69. * tau2 * tau2 - 98. * tau1) * sqrt (131.); + else + return -(1. / 131.) * sqrt (2.) * sqrt (5.) + * (246. * tau2 * tau1 + 27. * tau2 * tau2 + 96. * tau1 * tau1 + 3. - 26. * tau2 - 46. * tau1) + * sqrt (131.); + case 17: + if (e == 0) + return -(1. / 227.) * sqrt (2.) * sqrt (3.) * sqrt (5.) + * (582. * tau2 * tau1 - 258. * tau2 + 33. * tau2 * tau2 + 582. * tau1 * tau1 - 582. * tau1 + 145.) + * sqrt (227.); + else if (e == 1) + return (1. / 227.) * sqrt (2.) * sqrt (3.) * sqrt (5.) + * (-102. * tau2 * tau1 + 19. + 82. * tau2 - 33. * tau2 * tau2 + 42. * tau1 * tau1 - 58. * tau1) + * sqrt (227.); + else if (e == 2) + return (15. / 227.) * sqrt (2.) * sqrt (3.) * sqrt (5.) + * (6. * tau2 * tau1 - 2. * tau2 + tau2 * tau2 + 6. * tau1 * tau1 - 6. * tau1 + 1.) * sqrt (227.); + else + return (1. / 227.) * sqrt (2.) * sqrt (3.) * sqrt (5.) + * (186. * tau2 * tau1 + 3. - 46. * tau2 + 111. * tau2 * tau2 + 42. * tau1 * tau1 - 26. * tau1) + * sqrt (227.); + default:; + }; + case 4: + switch (i) { + case 0: + if (e == 0) + return (1. / 51.) * sqrt (2.) * (-11. - 1176. * tau2 * tau2 + 264. * tau2 + 1344. * tau2 * tau2 * tau2) + * sqrt (51.); + else if (e == 1) + return (1. / 51.) * sqrt (2.) * (-15. - 1512. * tau2 * tau2 + 336. * tau2 + 1792. * tau2 * tau2 * tau2) + * sqrt (51.); + else if (e == 2) + return -(5. / 51.) * sqrt (2.) * (-97. - 840. * tau2 * tau2 + 504. * tau2 + 448. * tau2 * tau2 * tau2) + * sqrt (51.); + else + return (1. / 51.) * sqrt (2.) * (-15. - 1512. * tau2 * tau2 + 336. * tau2 + 1792. * tau2 * tau2 * tau2) + * sqrt (51.); + case 1: + if (e == 0) + return (12. / 17.) * sqrt (2.) * (2. - 9. * tau2 + 7. * tau2 * tau2 - 4. * tau1 + 14. * tau2 * tau1) + * sqrt (17.); + else if (e == 1) + return (1. / 17.) * sqrt (2.) + * (-13. + 224. * tau2 - 588. * tau2 * tau2 + 28. * tau1 - 504. * tau2 * tau1 + + 1344. * tau2 * tau2 * tau1) + * sqrt (17.); + else if (e == 2) + return -(28. / 17.) * sqrt (2.) + * (-13. + 64. * tau2 - 99. * tau2 * tau2 + 48. * tau2 * tau2 * tau2 + 26. * tau1 - 102. * tau2 * tau1 + + 96. * tau2 * tau2 * tau1) + * sqrt (17.); + else + return (1. / 17.) * sqrt (2.) + * (-15. + 308. * tau2 - 1260. * tau2 * tau2 + 1344. * tau2 * tau2 * tau2 + 28. * tau1 + - 504. * tau2 * tau1 + 1344. * tau2 * tau2 * tau1) + * sqrt (17.); + case 2: + if (e == 0) + return -(2. / 51.) * sqrt (2.) + * (318. * tau2 - 594. * tau2 * tau2 + 384. * tau2 * tau2 * tau2 - 780. * tau2 * tau1 + + 576. * tau2 * tau2 * tau1 + 576. * tau2 * tau1 * tau1 - 55. + 204. * tau1 - 204. * tau1 * tau1) + * sqrt (255.); + else if (e == 1) + return (1. / 51.) * sqrt (2.) + * (-144. * tau2 + 1284. * tau2 * tau2 - 1024. * tau2 * tau2 * tau2 - 456. * tau2 * tau1 + - 960. * tau2 * tau2 * tau1 + 768. * tau2 * tau1 * tau1 - 3. + 60. * tau1 - 72. * tau1 * tau1) + * sqrt (255.); + else if (e == 2) + return -(28. / 51.) * sqrt (2.) + * (30. * tau2 - 39. * tau2 * tau2 + 16. * tau2 * tau2 * tau2 - 138. * tau2 * tau1 + + 96. * tau2 * tau2 * tau1 + 96. * tau2 * tau1 * tau1 - 7. + 42. * tau1 - 42. * tau1 * tau1) + * sqrt (255.); + else + return (1. / 51.) * sqrt (2.) + * (252. * tau2 - 828. * tau2 * tau2 + 704. * tau2 * tau2 * tau2 - 1224. * tau2 * tau1 + + 2496. * tau2 * tau2 * tau1 + 768. * tau2 * tau1 * tau1 - 15. + 84. * tau1 - 72. * tau1 * tau1) + * sqrt (255.); + case 3: + if (e == 0) + return (8. / 17.) * sqrt (2.) * sqrt (3.) + * (-41. * tau2 * tau2 + 27. * tau2 + 32. * tau1 - 60. * tau1 * tau1 + 40. * tau1 * tau1 * tau1 - 6. + - 82. * tau2 * tau1 + 60. * tau2 * tau1 * tau1 + 60. * tau2 * tau2 * tau1 + 20. * tau2 * tau2 * tau2) + * sqrt (119.); + else if (e == 1) + return -(1. / 51.) * sqrt (2.) * sqrt (3.) + * (1632. * tau2 * tau2 - 1056. * tau2 - 72. * tau1 - 120. * tau1 * tau1 + 160. * tau1 * tau1 * tau1 + 47. + + 2976. * tau2 * tau1 - 1920. * tau2 * tau1 * tau1 - 2496. * tau2 * tau2 * tau1 + - 640. * tau2 * tau2 * tau2) + * sqrt (119.); + else if (e == 2) + return (64. / 51.) * sqrt (2.) * sqrt (3.) + * (-3. * tau2 * tau2 + 3. * tau2 + 12. * tau1 - 30. * tau1 * tau1 + 20. * tau1 * tau1 * tau1 - 1. + - 24. * tau2 * tau1 + 30. * tau2 * tau1 * tau1 + 12. * tau2 * tau2 * tau1 + tau2 * tau2 * tau2) + * sqrt (119.); + else + return -(1. / 51.) * sqrt (2.) * sqrt (3.) + * (-360. * tau2 * tau2 + 168. * tau2 + 168. * tau1 - 360. * tau1 * tau1 + 160. * tau1 * tau1 * tau1 - 15. + - 1584. * tau2 * tau1 + 2400. * tau2 * tau1 * tau1 + 1824. * tau2 * tau2 * tau1 + + 224. * tau2 * tau2 * tau2) + * sqrt (119.); + case 4: + if (e == 0) + return -(6. / 17.) * sqrt (2.) * sqrt (3.) + * (-44. * tau2 * tau2 + 36. * tau2 + 16. * tau2 * tau2 * tau2 + 40. * tau1 - 40. * tau1 * tau1 - 9. + - 120. * tau2 * tau1 + 80. * tau2 * tau1 * tau1 + 80. * tau2 * tau2 * tau1) + * sqrt (17.); + else if (e == 1) + return -(1. / 17.) * sqrt (2.) * sqrt (3.) + * (-528. * tau2 * tau2 + 704. * tau2 + 128. * tau2 * tau2 * tau2 + 1480. * tau1 - 2280. * tau1 * tau1 + - 305. - 2400. * tau2 * tau1 + 1920. * tau2 * tau1 * tau1 + 960. * tau2 * tau2 * tau1 + + 1120. * tau1 * tau1 * tau1) + * sqrt (17.); + else if (e == 2) + return 0.; + else + return (1. / 17.) * sqrt (2.) * sqrt (3.) + * (-72. * tau2 * tau2 + 56. * tau2 + 32. * tau2 * tau2 * tau2 + 280. * tau1 - 1080. * tau1 * tau1 - 15. + - 720. * tau2 * tau1 + 1440. * tau2 * tau1 * tau1 + 480. * tau2 * tau2 * tau1 + + 1120. * tau1 * tau1 * tau1) + * sqrt (17.); + case 5: + if (e == 0) + return -(2. / 30243. * (50. + 789. * tau2 - 7896. * tau2 * tau2 + 13104. * tau2 * tau2 * tau2)) * sqrt (60486.); + else if (e == 1) + return -(2. / 30243. * (-159. + 4551. * tau2 - 25452. * tau2 * tau2 + 36512. * tau2 * tau2 * tau2)) + * sqrt (60486.); + else if (e == 2) + return -(2. / 30243. * (-16466. + 75645. * tau2 - 112560. * tau2 * tau2 + 54320. * tau2 * tau2 * tau2)) + * sqrt (60486.); + else + return -(2. / 30243. * (-159. + 4551. * tau2 - 25452. * tau2 * tau2 + 36512. * tau2 * tau2 * tau2)) + * sqrt (60486.); + case 6: + if (e == 0) + return -(2. / 9367. + * (-515. + 1030. * tau1 + 4281. * tau2 - 9478. * tau2 * tau2 - 7532. * tau2 * tau1 + + 5712. * tau2 * tau2 * tau2 + 11424. * tau2 * tau2 * tau1)) + * sqrt (28101.); + else if (e == 1) + return (1. / 28101. + * (-471. + 1208. * tau1 + 8338. * tau2 - 9660. * tau2 * tau2 - 27048. * tau2 * tau1 + - 53312. * tau2 * tau2 * tau2 + 87360. * tau2 * tau2 * tau1)) + * sqrt (28101.); + else if (e == 2) + return (14. / 28101. + * (-3971. + 7942. * tau1 + 17333. * tau2 - 24258. * tau2 * tau2 - 26724. * tau2 * tau1 + + 10896. * tau2 * tau2 * tau2 + 21792. * tau2 * tau2 * tau1)) + * sqrt (28101.); + else + return (1. / 28101. + * (-737. + 1208. * tau1 + 19918. * tau2 - 104748. * tau2 * tau2 - 27048. * tau2 * tau1 + + 140672. * tau2 * tau2 * tau2 + 87360. * tau2 * tau2 * tau1)) + * sqrt (28101.); + case 7: + if (e == 0) + return -(2. / 904640832569121. + * (-4234020. * tau1 - 35277354. * tau2 * tau2 + 44348976. * tau2 * tau2 * tau2 + 4234020. * tau1 * tau1 + + 7172928. * tau2 * tau2 * tau1 + 7172928. * tau2 * tau1 * tau1 + 949516. - 2938908. * tau2 * tau1 + + 5518869. * tau2)) + * sqrt (8892619384154459430.); + else if (e == 1) + return (1. / 904640832569121. + * (3408564. * tau1 + 286030164. * tau2 * tau2 - 172101440. * tau2 * tau2 * tau2 - 5977440. * tau1 * tau1 + - 310029888. * tau2 * tau2 * tau1 + 77308224. * tau2 * tau1 * tau1 + 857691. + 1095864. * tau2 * tau1 + - 46352034. * tau2)) + * sqrt (8892619384154459430.); + else if (e == 2) + return (2. / 904640832569121. + * (194216988. * tau1 - 146235138. * tau2 * tau2 + 57097040. * tau2 * tau2 * tau2 + - 194216988. * tau1 * tau1 + 362631360. * tau2 * tau2 * tau1 + 362631360. * tau2 * tau1 * tau1 + - 31318235. - 556848348. * tau2 * tau1 + 120405741. * tau2)) + * sqrt (8892619384154459430.); + else + return (1. / 904640832569121. + * (8546316. * tau1 - 1711185. - 185689476. * tau2 * tau2 + 215236672. * tau2 * tau2 * tau2 + - 5977440. * tau1 * tau1 + 464646336. * tau2 * tau2 * tau1 + 77308224. * tau2 * tau1 * tau1 + - 167667192. * tau2 * tau1 + 40598370. * tau2)) + * sqrt (8892619384154459430.); + case 8: + if (e == 0) + return (2. / 844880549868557.) * sqrt (3.) + * (-11048795. + 52777270. * tau1 + 44847573. * tau2 - 92039040. * tau1 * tau1 + + 61359360. * tau1 * tau1 * tau1 - 128956916. * tau2 * tau1 + 92039040. * tau2 * tau1 * tau1 + - 59297674. * tau2 * tau2 + 25498896. * tau2 * tau2 * tau2 + 81677472. * tau2 * tau2 * tau1) + * sqrt (5813623063645540717.); + else if (e == 1) + return (1. / 2534641649605671.) * sqrt (3.) + * (11964793. - 33749584. * tau1 - 200649314. * tau2 + 3306000. * tau1 * tau1 + + 22568960. * tau1 * tau1 * tau1 + 812678904. * tau2 * tau1 - 648504960. * tau2 * tau1 * tau1 + - 224511180. * tau2 * tau2 + 364940096. * tau2 * tau2 * tau2 - 55120320. * tau2 * tau2 * tau1) + * sqrt (5813623063645540717.); + else if (e == 2) + return (2. / 2534641649605671.) * sqrt (3.) + * (-19878199. + 246403438. * tau1 + 58705037. * tau2 - 619941120. * tau1 * tau1 + + 413294080. * tau1 * tau1 * tau1 - 490947756. * tau2 * tau1 + 619941120. * tau2 * tau1 * tau1 + - 57578022. * tau2 * tau2 + 18751184. * tau2 * tau2 * tau2 + 244149408. * tau2 * tau2 * tau1) + * sqrt (5813623063645540717.); + else + return (1. / 2534641649605671.) * sqrt (3.) + * (-4090169. + 40569296. * tau1 + 77044666. * tau2 - 71012880. * tau1 * tau1 + + 22568960. * tau1 * tau1 * tau1 - 626356776. * tau2 * tau1 + 716211840. * tau2 * tau1 * tau1 + - 275712396. * tau2 * tau2 + 251013504. * tau2 * tau2 * tau2 + 1309596480. * tau2 * tau2 * tau1) + * sqrt (5813623063645540717.); + case 9: + if (e == 0) + return -(2. / 10765691411249447639. + * (161209100100. * tau1 + 186071273154. * tau2 - 283831332126. * tau2 * tau2 + + 157399264224. * tau2 * tau2 * tau2 - 461612434260. * tau2 * tau1 + + 300403334160. * tau2 * tau2 * tau1 - 42240490525. - 161209100100. * tau1 * tau1 + + 300403334160. * tau2 * tau1 * tau1)) + * sqrt (150719679757492266946.); + else if (e == 1) + return (1. / 10765691411249447639. + * (187982776440. * tau1 - 992699014632. * tau2 + 1708127525124. * tau2 * tau2 + - 714127013504. * tau2 * tau2 * tau2 + 2439191566680. * tau2 * tau1 + - 2260670213760. * tau2 * tau2 * tau1 - 13652221827. - 425611235640. * tau1 * tau1 + + 258043932320. * tau1 * tau1 * tau1 - 1419282286080. * tau2 * tau1 * tau1)) + * sqrt (150719679757492266946.); + else if (e == 2) + return -(4. / 633275965367614567. + * (-5897464. + 85721730. * tau1 + 19354035. * tau2 - 16054755. * tau2 * tau2 + + 2194440. * tau2 * tau2 * tau2 - 258888210. * tau2 * tau1 + 173166480. * tau2 * tau2 * tau1 + - 85721730. * tau1 * tau1 + 173166480. * tau2 * tau1 * tau1)) + * sqrt (150719679757492266946.); + else + return -(1. / 10765691411249447639. + * (110892102120. * tau1 + 83681836152. * tau2 - 195350878164. * tau2 * tau2 + + 130783018144. * tau2 * tau2 * tau2 - 1096414128120. * tau2 * tau1 + + 1352026155360. * tau2 * tau2 * tau1 - 6763251293. - 348520561320. * tau1 * tau1 + + 258043932320. * tau1 * tau1 * tau1 + 2193414083040. * tau2 * tau1 * tau1)) + * sqrt (150719679757492266946.); + case 10: + if (e == 0) + return -(24. / 920240407198300427.) * sqrt (3.) + * (127279662. + 204039544. * tau1 - 940664913. * tau2 + 1882607643. * tau2 * tau2 + - 1375796604. * tau1 * tau1 - 1069222392. * tau2 * tau2 * tau2 + 709572766. * tau2 * tau1 + - 1679845916. * tau2 * tau2 * tau1 + 1375796604. * tau2 * tau1 * tau1 + + 917197736. * tau1 * tau1 * tau1) + * sqrt (4601202035991502135.); + else if (e == 1) + return (2. / 2760721221594901281.) * sqrt (3.) + * (-177394216769. + 763898820394. * tau1 + 426324583460. * tau2 - 336092076180. * tau2 * tau2 + - 1062952041984. * tau1 * tau1 + 86762894400. * tau2 * tau2 * tau2 - 1261049609064. * tau2 * tau1 + + 514694054112. * tau2 * tau2 * tau1 + 898559860800. * tau2 * tau1 * tau1 + + 479569493648. * tau1 * tau1 * tau1) + * sqrt (4601202035991502135.); + else if (e == 2) + return (4. / 2760721221594901281.) * sqrt (3.) + * (-47762895. + 697977710. * tau1 + 128536021. * tau2 - 110649630. * tau2 * tau2 + - 1807355760. * tau1 * tau1 + 29876504. * tau2 * tau2 * tau2 - 1366450092. * tau2 * tau1 + + 662204928. * tau2 * tau2 * tau1 + 1807355760. * tau2 * tau1 * tau1 + + 1204903840. * tau1 * tau1 * tau1) + * sqrt (4601202035991502135.); + else + return (2. / 2760721221594901281.) * sqrt (3.) + * (-3122055289. + 76703217370. * tau1 + 12868382174. * tau2 - 18288304356. * tau2 * tau2 + - 375756438960. * tau1 * tau1 + 8940792560. * tau2 * tau2 * tau2 - 215442765384. * tau2 * tau1 + + 156282813456. * tau2 * tau2 * tau1 + 540148620144. * tau2 * tau1 * tau1 + + 479569493648. * tau1 * tau1 * tau1) + * sqrt (4601202035991502135.); + case 11: + if (e == 0) + return (1. / 905676852258502428609.) * sqrt (2.) * sqrt (3.) + * (7544141429440. * tau2 * tau2 * tau2 + 144804769200. * tau1 - 144804769200. * tau1 * tau1 + - 247573720553. + 39483400320. * tau2 * tau1 * tau1 + 39483400320. * tau2 * tau2 * tau1 + + 2796129345102. * tau2 - 8729779876230. * tau2 * tau2 - 184288169520. * tau2 * tau1) + * sqrt (100630761362055825401.); + else if (e == 1) + return -(1. / 905676852258502428609.) * sqrt (2.) * sqrt (3.) + * (24266273541440. * tau2 * tau2 * tau2 + 20612007000. * tau1 - 59971373280. * tau1 * tau1 + + 64071047040. * tau1 * tau1 * tau1 - 77084019997. - 943529408640. * tau2 * tau1 * tau1 + + 1809294963840. * tau2 * tau2 * tau1 + 2345646250218. * tau2 - 16088530112970. * tau2 * tau2 + + 597259943280. * tau2 * tau1) + * sqrt (100630761362055825401.); + else if (e == 2) + return (1. / 905676852258502428609.) * sqrt (2.) * sqrt (3.) + * (37820822392640. * tau2 * tau2 * tau2 + 2971563613200. * tau1 - 2971563613200. * tau1 * tau1 + - 13847389199837. + 5548562772480. * tau2 * tau1 * tau1 + 5548562772480. * tau2 * tau2 * tau1 + + 59417823663318. * tau2 - 82988273790870. * tau2 * tau2 - 8520126385680. * tau2 * tau1) + * sqrt (100630761362055825401.); + else + return (1. / 905676852258502428609.) * sqrt (2.) * sqrt (3.) + * (-21449378121920. * tau2 * tau2 * tau2 + 92882401560. * tau1 - 132241767840. * tau1 * tau1 + + 64071047040. * tau1 * tau1 * tau1 + 52372339237. + 1135742549760. * tau2 * tau1 * tau1 + + 3888566922240. * tau2 * tau2 * tau1 - 1906494383298. * tau2 + 12857194507290. * tau2 * tau2 + - 1554282409680. * tau2 * tau1) + * sqrt (100630761362055825401.); + case 12: + if (e == 0) + return -(72. / 24280578051280066842136053269.) * sqrt (2.) + * (-89913208265. * tau2 + 297997576. * tau1 + 346354066891. * tau2 * tau2 - 2597609196. * tau1 * tau1 + + 1731739464. * tau1 * tau1 * tau1 + 177526804910. * tau2 * tau1 - 512583719676. * tau2 * tau2 * tau1 + + 2597609196. * tau2 * tau1 * tau1 + 283936078. - 256724794704. * tau2 * tau2 * tau2) + * sqrt (10381525354495561779357901615959985.); + else if (e == 1) + return -(4. / 24280578051280066842136053269.) * sqrt (2.) + * (742622726374. * tau2 + 169713834857. * tau1 + 3170650107534. * tau2 * tau2 - 3545105382. * tau1 * tau1 + + 3644377464. * tau1 * tau1 * tau1 - 4311394474968. * tau2 * tau1 + + 15972451837104. * tau2 * tau2 * tau1 - 51883082160. * tau2 * tau1 * tau1 - 53427686327. + - 20656458047232. * tau2 * tau2 * tau2) + * sqrt (10381525354495561779357901615959985.); + else if (e == 2) + return -(4. / 24280578051280066842136053269.) * sqrt (2.) + * (-77050203333827. * tau2 - 37443894884206. * tau1 + 102499197109260. * tau2 * tau2 + - 105392523600. * tau1 * tau1 + 70261682400. * tau1 * tau1 * tau1 + 116551119259848. * tau2 * tau1 + - 88341882435072. * tau2 * tau2 * tau1 + 105392523600. * tau2 * tau1 * tau1 + 18739512862703. + - 44188506638136. * tau2 * tau2 * tau2) + * sqrt (10381525354495561779357901615959985.); + else + return -(4. / 24280578051280066842136053269.) * sqrt (2.) + * (3794211587239. * tau2 + 173556756485. * tau1 - 23565650610936. * tau2 * tau2 + - 7388027010. * tau1 * tau1 + 3644377464. * tau1 * tau1 * tau1 - 4429936693308. * tau2 * tau1 + + 16087151133816. * tau2 * tau2 * tau1 + 62816214552. * tau2 * tau1 * tau1 - 116385420612. + + 36684437343960. * tau2 * tau2 * tau2) + * sqrt (10381525354495561779357901615959985.); + case 13: + if (e == 0) + return (4. / 450190343850906324042093431419389. + * (7723887049488. * tau2 + 5633936712900. * tau1 - 11310827442900. * tau2 * tau2 + - 5633936712900. * tau1 * tau1 + 4307107230800. * tau2 * tau2 * tau2 - 20941320604140. * tau2 * tau1 + + 15307383891240. * tau2 * tau2 * tau1 + 15307383891240. * tau2 * tau1 * tau1 - 1511837033851.)) + * sqrt (34560885477513793515221944312683818679790383.); + else if (e == 1) + return -(4. / 450190343850906324042093431419389. + * (2564480914314. * tau2 - 28384522317. * tau1 - 15633177382020. * tau2 * tau2 + + 107501634540. * tau1 * tau1 + 6549688499104. * tau2 * tau2 * tau2 - 980844788268. * tau2 * tau1 + + 19462977054288. * tau2 * tau2 * tau1 - 3349829668320. * tau2 * tau1 * tau1 + + 62214683880. * tau1 * tau1 * tau1 - 67122037922.)) + * sqrt (34560885477513793515221944312683818679790383.); + else if (e == 2) + return -(4. / 450190343850906324042093431419389. + * (17036960891259. * tau2 + 30770051855220. * tau1 - 19841738599500. * tau2 * tau2 + - 30770051855220. * tau1 * tau1 + 7487604817160. * tau2 * tau2 * tau2 + - 82446561694980. * tau2 * tau1 + 51676509839760. * tau2 * tau2 * tau1 + + 51676509839760. * tau2 * tau1 * tau1 - 4692563956424.)) + * sqrt (34560885477513793515221944312683818679790383.); + else + return (4. / 450190343850906324042093431419389. + * (2139456340677. * tau2 + 373262798403. * tau1 - 11804449483356. * tau2 * tau2 + - 294145686180. * tau1 * tau1 + 16325332907384. * tau2 * tau2 * tau2 - 8268795497268. * tau2 * tau1 + + 26349280442568. * tau2 * tau2 * tau1 + 3536473719960. * tau2 * tau1 * tau1 + + 62214683880. * tau1 * tau1 * tau1 - 74209758181.)) + * sqrt (34560885477513793515221944312683818679790383.); + case 14: + if (e == 0) + return (8. / 6380720215578715256716408976310607022277. + * (-865666978019157. * tau2 - 55556560994562. * tau1 - 302892267120612. * tau1 * tau1 + - 1312841251683268. * tau2 * tau2 * tau2 + 201928178080408. * tau1 * tau1 * tau1 + + 1372885127923140. * tau2 * tau1 - 2524718414326332. * tau2 * tau2 * tau1 + + 302892267120612. * tau2 * tau1 * tau1 + 78260325017383. + 2100247904685042. * tau2 * tau2)) + * sqrt (38697087397201085046824763022410609242891822943821145.); + else if (e == 1) + return -(4. / 6380720215578715256716408976310607022277. + * (-522961534414587. * tau2 - 284427010918203. * tau1 + 168637635998520. * tau1 * tau1 + + 6088129267565552. * tau2 * tau2 * tau2 + 76974385176312. * tau1 * tau1 * tau1 + + 4891309274804220. * tau2 * tau1 + 6079189293644784. * tau2 * tau2 * tau1 + - 4946764497496536. * tau2 * tau1 * tau1 + 72312277135198. - 7310482464324780. * tau2 * tau2)) + * sqrt (38697087397201085046824763022410609242891822943821145.); + else if (e == 2) + return (4. / 6380720215578715256716408976310607022277. + * (1444448045208459. * tau2 + 7586203235228550. * tau1 - 19591382245714032. * tau1 * tau1 + + 359309007353848. * tau2 * tau2 * tau2 + 13060921497142688. * tau1 * tau1 * tau1 + - 14894075100902400. * tau2 * tau1 + 7249078763279040. * tau2 * tau2 * tau1 + + 19591382245714032. * tau2 * tau1 * tau1 - 527871243328603. - 1275885809233704. * tau2 * tau2)) + * sqrt (38697087397201085046824763022410609242891822943821145.); + else + return -(4. / 6380720215578715256716408976310607022277. + * (862188173714676. * tau2 + 283771416607773. * tau1 - 399560791527456. * tau1 * tau1 + + 5014798908752080. * tau2 * tau2 * tau2 + 76974385176312. * tau1 * tau1 * tau1 + - 5801341303243764. * tau2 * tau1 + 16203641444166792. * tau2 * tau2 * tau1 + + 5177687653025472. * tau2 * tau1 * tau1 - 33497287391827. - 4170487341036312. * tau2 * tau2)) + * sqrt (38697087397201085046824763022410609242891822943821145.); + case 15: + if (e == 0) + return -(4. / 35516731602837412764420243765174837. + * (-12837488172413. - 123415494481620. * tau2 * tau1 + 73509208386120. * tau2 * tau1 * tau1 + + 73509208386120. * tau2 * tau2 * tau1 + 49906286095500. * tau1 + 42690194479914. * tau2 + - 49906286095500. * tau1 * tau1 - 29319191117400. * tau2 * tau2 + - 16354402687400. * tau2 * tau2 * tau2)) + * sqrt (4587144914088926523642597305294551825737974887.); + else if (e == 1) + return -(4. / 35516731602837412764420243765174837. + * (1863564729991. + 293378451286824. * tau2 * tau1 - 192708078064200. * tau2 * tau1 * tau1 + - 132611304892464. * tau2 * tau2 * tau1 + 14128387753800. * tau1 * tau1 * tau1 + + 2683151627571. * tau1 - 103745743940397. * tau2 - 18078506976780. * tau1 * tau1 + + 60644719016880. * tau2 * tau2 + 33991136549968. * tau2 * tau2 * tau2)) + * sqrt (4587144914088926523642597305294551825737974887.); + else if (e == 2) + return (4. / 35516731602837412764420243765174837. + * (-3855190312. - 91426887169020. * tau2 * tau1 + 55766887370640. * tau2 * tau1 * tau1 + + 55766887370640. * tau2 * tau2 * tau1 + 35659999798380. * tau1 - 3794741004603. * tau2 + - 35659999798380. * tau1 * tau1 + 9706701686460. * tau2 * tau2 + - 6040814783960. * tau2 * tau2 * tau2)) + * sqrt (4587144914088926523642597305294551825737974887.); + else + return (4. / 35516731602837412764420243765174837. + * (-596597134582. - 140651017410816. * tau2 * tau1 + 235093241325600. * tau2 * tau1 * tau1 + + 295190014497336. * tau2 * tau2 * tau1 + 14128387753800. * tau1 * tau1 * tau1 + + 8911300935411. * tau1 + 11986671653184. * tau2 - 24306656284620. * tau1 * tau1 + - 44377775250612. * tau2 * tau2 + 40234024375568. * tau2 * tau2 * tau2)) + * sqrt (4587144914088926523642597305294551825737974887.); + case 16: + if (e == 0) + return -(2. / 4429162483638155085928905495293501. + * (-902027828018790. * tau2 * tau1 + 1114817925578880. * tau2 * tau1 * tau1 + + 419714427181440. * tau2 * tau2 * tau1 - 1114817925578880. * tau1 * tau1 + - 103462164810675. * tau2 * tau2 + 24054225994240. * tau2 * tau2 * tau2 + 467843784396552. * tau1 + + 743211950385920. * tau1 * tau1 * tau1 - 48118904601796. + 127526843418231. * tau2)) + * sqrt (1977693186713807260499140827732350041750787.); + else if (e == 1) + return -(2. / 4429162483638155085928905495293501. + * (3861292811332650. * tau2 * tau1 - 2047919222304000. * tau2 * tau1 * tau1 + - 4240917078976800. * tau2 * tau2 * tau1 - 1446732052196520. * tau1 * tau1 + + 3506259687314565. * tau2 * tau2 - 1619202493724960. * tau2 * tau2 * tau2 + 892404533563320. * tau1 + + 726050022346080. * tau1 * tau1 * tau1 - 163614571731043. - 1736827118543049. * tau2)) + * sqrt (1977693186713807260499140827732350041750787.); + else if (e == 2) + return (2. / 4429162483638155085928905495293501. + * (589738585996. - 116842040777610. * tau2 * tau1 + 204619294393440. * tau2 * tau1 * tau1 + + 46139163828000. * tau2 * tau2 * tau1 + 67026954292488. * tau1 - 10375149661671. * tau2 + - 204619294393440. * tau1 * tau1 + 20819044893915. * tau2 * tau2 + - 11033633818240. * tau2 * tau2 * tau2 + 136412862928960. * tau1 * tau1 * tau1)) + * sqrt (1977693186713807260499140827732350041750787.); + else + return -(2. / 4429162483638155085928905495293501. + * (-1697381662958790. * tau2 * tau1 + 4226069289342240. * tau2 * tau1 * tau1 + + 2033071432669440. * tau2 * tau2 * tau1 - 731418014841720. * tau1 * tau1 + - 231306256454835. * tau2 * tau2 + 152254659398240. * tau2 * tau2 * tau2 + 177090496208520. * tau1 + + 726050022346080. * tau1 * tau1 * tau1 - 8107931981837. + 100544025722919. * tau2)) + * sqrt (1977693186713807260499140827732350041750787.); + case 17: + if (e == 0) + return -(2. / 2347070638132331273272455.) * sqrt (5.) + * (-5396852251249. - 81879933691710. * tau2 * tau1 + 56363129182080. * tau2 * tau1 * tau1 + + 56363129182080. * tau2 * tau2 * tau1 + 25516804509630. * tau1 + 20019115103835. * tau2 + - 25516804509630. * tau1 * tau1 - 17380798498455. * tau2 * tau2 - 1108238286400. * tau2 * tau2 * tau2) + * sqrt (9023182675486518006136327.); + else if (e == 1) + return (2. / 2347070638132331273272455.) * sqrt (5.) + * (-109424567344688. - 756000640994130. * tau2 * tau1 + 507350406064320. * tau2 * tau1 * tau1 + + 318653554050720. * tau2 * tau2 * tau1 + 443580065894538. * tau1 + 274131673456605. * tau2 + - 585513182972970. * tau1 * tau1 - 225817070108385. * tau2 * tau2 + + 61185468551200. * tau2 * tau2 * tau2 + 252343928010720. * tau1 * tau1 * tau1) + * sqrt (9023182675486518006136327.); + else if (e == 2) + return -(14. / 2347070638132331273272455.) * sqrt (5.) + * (-429235621. - 726587567010. * tau2 * tau1 + 437189459040. * tau2 * tau1 * tau1 + + 437189459040. * tau2 * tau2 * tau1 + 289398107970. * tau1 - 31687044699. * tau2 + - 289398107970. * tau1 * tau1 + 81609115335. * tau2 * tau2 - 50560943360. * tau2 * tau2 * tau2) + * sqrt (9023182675486518006136327.); + else + return -(2. / 2347070638132331273272455.) * sqrt (5.) + * (-986243587600. - 84337030983870. * tau2 * tau1 + 249681377967840. * tau2 * tau1 * tau1 + + 60984525954240. * tau2 * tau2 * tau1 + 29585483980758. * tau1 + 4104045453963. * tau2 + - 171518601059190. * tau1 * tau1 - 5654913867015. * tau2 * tau2 + 2461607445920. * tau2 * tau2 * tau2 + + 252343928010720. * tau1 * tau1 * tau1) + * sqrt (9023182675486518006136327.); + case 18: + if (e == 0) + return (1. / 4685971895550392990415.) * sqrt (2.) + * (15979364504400. * tau2 * tau2 * tau2 - 943114772473. + 6026917395480. * tau2 + - 16185801835185. * tau2 * tau2 + 2851410481710. * tau1 - 2851410481710. * tau1 * tau1 + - 7853829170670. * tau2 * tau1 + 5002418688960. * tau2 * tau2 * tau1 + + 5002418688960. * tau2 * tau1 * tau1) + * sqrt (32801803268852750932905.); + else if (e == 1) + return (1. / 4685971895550392990415.) * sqrt (2.) + * (-8590475828400. * tau2 * tau2 * tau2 - 27199413019. - 4934866738800. * tau2 + + 10439985152895. * tau2 * tau2 + 657180239154. * tau1 - 1432137090510. * tau1 * tau1 + + 825312928160. * tau1 * tau1 * tau1 + 10428888842610. * tau2 * tau1 + - 9003435769440. * tau2 * tau2 * tau1 - 5851383884640. * tau2 * tau1 * tau1) + * sqrt (32801803268852750932905.); + else if (e == 2) + return -(1. / 4685971895550392990415.) * sqrt (2.) + * (12776635041040. * tau2 * tau2 * tau2 - 5500681931591. + 22207156652916. * tau2 + - 29395021886415. * tau2 * tau2 + 3307928504370. * tau1 - 3307928504370. * tau1 * tau1 + - 8256801806610. * tau2 * tau1 + 4948873302240. * tau2 * tau2 * tau1 + + 4948873302240. * tau2 * tau1 * tau1) + * sqrt (32801803268852750932905.); + else + return -(1. / 4685971895550392990415.) * sqrt (2.) + * (6263736871760. * tau2 * tau2 * tau2 - 23156663785. + 626206623444. * tau2 + - 3754230004095. * tau2 * tau2 + 268844842614. * tau1 - 1043801693970. * tau1 * tau1 + + 825312928160. * tau1 * tau1 * tau1 - 3361482314610. * tau2 * tau1 + + 5175270784320. * tau2 * tau2 * tau1 + 8327322669120. * tau2 * tau1 * tau1) + * sqrt (32801803268852750932905.); + case 19: + if (e == 0) + return (1. / 224288466335587227020110639.) * sqrt (2.) + * (26401542816284. * tau2 + 7569453530758. * tau1 - 53912941735305. * tau2 * tau2 + - 447340032960. * tau1 * tau1 + 31221569012240. * tau2 * tau2 * tau2 + + 298226688640. * tau1 * tau1 * tau1 - 45680972134770. * tau2 * tau1 + + 62592251368800. * tau2 * tau2 * tau1 + 447340032960. * tau2 * tau1 * tau1 - 3710170093219.) + * sqrt (3810436754575291399844659645971.); + else if (e == 1) + return -(1. / 672865399006761681060331917.) * sqrt (2.) + * (-16828358446012. * tau2 + 2703515000770. * tau1 - 1950569881365. * tau2 * tau2 + - 6315542069040. * tau1 * tau1 + 86557884476880. * tau2 * tau2 * tau2 + + 3602896084320. * tau1 * tau1 * tau1 + 51120651827190. * tau2 * tau1 + - 70486646544960. * tau2 * tau2 * tau1 - 24140323097760. * tau2 * tau1 * tau1 - 199629664249.) + * sqrt (3810436754575291399844659645971.); + else if (e == 2) + return (1. / 672865399006761681060331917.) * sqrt (2.) + * (334299298799272. * tau2 + 200999473364474. * tau1 - 424257177681195. * tau2 * tau2 + - 86599091838240. * tau1 * tau1 + 176024433591120. * tau2 * tau2 * tau2 + + 57732727892160. * tau1 * tau1 * tau1 - 554198216072310. * tau2 * tau1 + + 380915231128320. * tau2 * tau2 * tau1 + 86599091838240. * tau2 * tau1 * tau1 - 86066554709197.) + * sqrt (3810436754575291399844659645971.); + else + return -(1. / 672865399006761681060331917.) * sqrt (2.) + * (-9270851167768. * tau2 + 881119115650. * tau1 + 70784075874075. * tau2 * tau2 + - 4493146183920. * tau1 * tau1 - 129301311839760. * tau2 * tau2 * tau2 + + 3602896084320. * tau1 * tau1 * tau1 - 6146286736170. * tau2 * tau1 + - 11397312096480. * tau2 * tau2 * tau1 + 34949011350720. * tau2 * tau1 * tau1 + 208760648199.) + * sqrt (3810436754575291399844659645971.); + case 20: + if (e == 0) + return (4. / 4891272024616371501303055622019.) * sqrt (2.) + * (2916945992442. * tau1 - 2916945992442. * tau1 * tau1 - 1263543266489. + 11203578173532. * tau2 + - 32224956773007. * tau2 * tau2 + 30463180770552. * tau2 * tau2 * tau2 - 12641311089594. * tau2 * tau1 + + 9724365097152. * tau2 * tau2 * tau1 + 9724365097152. * tau2 * tau1 * tau1) + * sqrt (338531287325211031860850338067150911335.); + else if (e == 1) + return (4. / 4891272024616371501303055622019.) * sqrt (2.) + * (1151007682716. * tau1 - 2943675525354. * tau1 * tau1 + 1690378577200. * tau1 * tau1 * tau1 + + 61957824709. - 12956432108676. * tau2 + 29505156081585. * tau2 * tau2 + - 3451882567800. * tau2 * tau2 * tau2 + 26852854261950. * tau2 * tau1 + - 42974094337392. * tau2 * tau2 * tau1 - 11578107511848. * tau2 * tau1 * tau1) + * sqrt (338531287325211031860850338067150911335.); + else if (e == 2) + return (4. / 4891272024616371501303055622019.) * sqrt (2.) + * (51646435564170. * tau1 - 51646435564170. * tau1 * tau1 - 8760244672247. + 31302616329774. * tau2 + - 36358388734263. * tau2 * tau2 + 13817755030144. * tau2 * tau2 * tau2 + - 132558664209354. * tau2 * tau1 + 80912228645184. * tau2 * tau2 * tau1 + + 80912228645184. * tau2 * tau1 * tau1) + * sqrt (338531287325211031860850338067150911335.); + else + return -(4. / 4891272024616371501303055622019.) * sqrt (2.) + * (334792363608. * tau1 - 2127460206246. * tau1 * tau1 + 1690378577200. * tau1 * tau1 * tau1 + + 40331440729. - 1983522277818. * tau2 + 15038117287815. * tau2 * tau2 + - 26253725680544. * tau2 * tau2 * tau2 - 558281174238. * tau2 * tau1 + - 14746743582096. * tau2 * tau2 * tau1 + 16649243243448. * tau2 * tau1 * tau1) + * sqrt (338531287325211031860850338067150911335.); + case 21: + if (e == 0) + return (4. / 8010895569642034387093225507772921797.) * sqrt (2.) * sqrt (3.) + * (-544074453531273. * tau2 * tau2 + 430223607436900. * tau2 + 222581252051144. * tau2 * tau2 * tau2 + + 511981397866982. * tau1 - 108730405956771. - 1232027574867138. * tau2 * tau1 + + 739683090055728. * tau2 * tau2 * tau1 - 883561757860320. * tau1 * tau1 + + 589041171906880. * tau1 * tau1 * tau1 + 883561757860320. * tau2 * tau1 * tau1) + * sqrt (2206416960936351241282951190627144110360471628935.); + else if (e == 1) + return -(4. / 24032686708926103161279676523318765391.) * sqrt (2) * sqrt (3) + * (-454480788017877. * tau2 * tau2 - 325425903991988. * tau2 + 418098131421528. * tau2 * tau2 * tau2 + + 31792165898948. * tau1 + 633733829767. + 1128878245892526. * tau2 * tau1 + + 339357636579840. * tau2 * tau2 * tau1 - 98098390802376. * tau1 * tau1 + + 69600670060224. * tau1 * tau1 * tau1 - 868498944007416. * tau2 * tau1 * tau1) + * sqrt (2206416960936351241282951190627144110360471628935.); + else if (e == 2) + return -(4. / 24032686708926103161279676523318765391.) * sqrt (2.) * sqrt (3.) + * (145211743860843. * tau2 * tau2 - 76312238624402. * tau2 - 74523035155968. * tau2 * tau2 * tau2 + + 674082719974322. * tau1 + 5623529919527. - 1229282142217002. * tau2 * tau1 + + 536283709501440. * tau2 * tau2 * tau1 - 2055989339440128. * tau1 * tau1 + + 1370659559626752. * tau1 * tau1 * tau1 + 2055989339440128. * tau2 * tau1 * tau1) + * sqrt (2206416960936351241282951190627144110360471628935.); + else + return -(4. / 24032686708926103161279676523318765391.) * sqrt (2.) * sqrt (3.) + * (-603700110062565. * tau2 * tau2 + 109443996581746. * tau2 + 859359119225952. * tau2 * tau2 * tau2 + + 44397394474868. * tau1 - 3928178986563. - 829526880878898. * tau2 * tau1 + + 2285157534775344. * tau2 * tau2 * tau1 - 110703619378296. * tau1 * tau1 + + 69600670060224. * tau1 * tau1 * tau1 + 1077300954188088. * tau2 * tau1 * tau1) + * sqrt (2206416960936351241282951190627144110360471628935.); + case 22: + if (e == 0) + return (1. / 36794210061790373790848509118616973701. + * (-810501145781685. * tau2 * tau2 + 547993219860726. * tau2 + 339256701212520. * tau2 * tau2 * tau2 + + 399493881998430. * tau1 - 399493881998430. * tau1 * tau1 + 1204911521342400. * tau2 * tau1 * tau1 + + 1204911521342400. * tau2 * tau2 * tau1 - 104211082717321. - 1604405403340830. * tau2 * tau1)) + * sqrt (2170343179656283287883941488614423306932736027111121.); + else if (e == 1) + return (1. / 36794210061790373790848509118616973701. + * (-2433875842005. * tau2 * tau2 - 239334306942864. * tau2 + 107884877384280. * tau2 * tau2 * tau2 + + 25050161456406. * tau1 - 65328395848230. * tau1 * tau1 + 42242443327040. * tau1 * tau1 * tau1 + - 475351055700720. * tau2 * tau1 * tau1 - 93080754512160. * tau2 * tau2 * tau1 - 426028325995. + + 694645326664650. * tau2 * tau1)) + * sqrt (2170343179656283287883941488614423306932736027111121.); + else if (e == 2) + return (1. / 36794210061790373790848509118616973701. + * (-226081054029765. * tau2 * tau2 + 188089757677170. * tau2 + 89659331725880. * tau2 * tau2 * tau2 + + 193907952084750. * tau1 - 193907952084750. * tau1 * tau1 - 51450006715717. + - 481676057880750. * tau2 * tau1 + 287768105796000. * tau2 * tau1 * tau1 + + 287768105796000. * tau2 * tau2 * tau1)) + * sqrt (2170343179656283287883941488614423306932736027111121.); + else + return -(1. / 36794210061790373790848509118616973701. + * (-221941088515515. * tau2 * tau2 + 41160735720000. * tau2 + 316627867131320. * tau2 * tau2 * tau2 + + 21120699741066. * tau1 - 61398934132890. * tau1 * tau1 + 42242443327040. * tau1 * tau1 * tau1 + + 602078385681840. * tau2 * tau1 * tau1 + 984348686870400. * tau2 * tau2 * tau1 - 1538180609221. + - 378854653002570. * tau2 * tau1)) + * sqrt (2170343179656283287883941488614423306932736027111121.); + case 23: + if (e == 0) + return -(1. / 473145761369413066487006334443891328327.) * sqrt (3.) + * (454765962881490. * tau2 + 593669146897674. * tau1 - 1153945315866048. * tau1 * tau1 + + 769296877244032. * tau1 * tau1 * tau1 - 104510354137829. - 1469808094731354. * tau2 * tau1 + + 1153945315866048. * tau2 * tau1 * tau1 - 728103048224517. * tau2 * tau2 + + 377847439480856. * tau2 * tau2 * tau2 + 1140343317583728. * tau2 * tau2 * tau1) + * sqrt (115935593470518835817766866162978250334523437784363455.); + else if (e == 1) + return (1. / 473145761369413066487006334443891328327.) * sqrt (3.) + * (-374908740803664. * tau2 + 142540313543538. * tau1 - 227459974019856. * tau1 * tau1 + + 113528693807904. * tau1 * tau1 * tau1 - 27349684692337. + 935860201978914. * tau2 * tau1 + - 554277230276784. * tau2 * tau1 * tau1 + 453425348549205. * tau2 * tau2 + - 23324339238248. * tau2 * tau2 * tau2 - 683569706048304. * tau2 * tau2 * tau1) + * sqrt (115935593470518835817766866162978250334523437784363455.); + else if (e == 2) + return -(1. / 473145761369413066487006334443891328327.) * sqrt (3.) + * (-252914338292682. * tau2 - 117930052625598. * tau1 - 44709478109664. * tau1 * tau1 + + 29806318739776. * tau1 * tau1 * tau1 + 66416605997743. + 343189145850102. * tau2 * tau1 + + 44709478109664. * tau2 * tau1 * tau1 + 315004838691411. * tau2 * tau2 + - 128507106396472. * tau2 * tau2 * tau2 - 242111053423056. * tau2 * tau2 * tau1) + * sqrt (115935593470518835817766866162978250334523437784363455.); + else + return (1. / 473145761369413066487006334443891328327.) * sqrt (3.) + * (21532216029072. * tau2 + 28206446927538. * tau1 - 113126107403856. * tau1 * tau1 + + 113528693807904. * tau1 * tau1 * tau1 - 1259348639249. - 398946473382366. * tau2 * tau1 + + 894863311700496. * tau2 * tau1 * tau1 - 55676008479411. * tau2 * tau2 + + 7560557274632. * tau2 * tau2 * tau2 + 765570835928976. * tau2 * tau2 * tau1) + * sqrt (115935593470518835817766866162978250334523437784363455.); + case 24: + if (e == 0) + return -(1. / 12008131465057998535947.) * sqrt (5.) + * (-1236464420742. * tau2 * tau1 + 844896575904. * tau2 * tau1 * tau1 + + 844896575904. * tau2 * tau2 * tau1 - 92201316317. + 377683669254. * tau2 + - 473373785937. * tau2 * tau2 + 195277801704. * tau2 * tau2 * tau2 + 391567844838. * tau1 + - 391567844838. * tau1 * tau1) + * sqrt (77136233821044229928744879.); + else if (e == 1) + return -(1. / 12008131465057998535947.) * sqrt (5.) + * (8953699554. * tau2 * tau1 + 64684666032. * tau2 * tau1 * tau1 - 469802454528. * tau2 * tau2 * tau1 + - 118869058463. - 51848336400. * tau2 + 438289085391. * tau2 * tau2 + - 266962421928. * tau2 * tau2 * tau2 + 484688476422. * tau1 - 642448007502. * tau1 * tau1 + + 277725306880. * tau1 * tau1 * tau1) + * sqrt (77136233821044229928744879.); + else if (e == 2) + return (1. / 12008131465057998535947.) * sqrt (5.) + * (-45938028330. * tau2 * tau1 + 27915256128. * tau2 * tau1 * tau1 + 27915256128. * tau2 * tau2 * tau1 + + 21460463777. - 85872627018. * tau2 + 112800364737. * tau2 * tau2 - 48666996472. * tau2 * tau2 * tau2 + + 18022772202. * tau1 - 18022772202. * tau1 * tau1) + * sqrt (77136233821044229928744879.); + else + return (1. / 12008131465057998535947.) * sqrt (5.) + * (-243132794658. * tau2 * tau1 + 768491254608. * tau2 * tau1 * tau1 + 234004134048. * tau2 * tau2 * tau1 + - 1096717337. + 11178352872. * tau2 - 20891512383. * tau2 * tau2 + 10200608248. * tau2 * tau2 * tau2 + + 32968382058. * tau1 - 190727913138. * tau1 * tau1 + 277725306880. * tau1 * tau1 * tau1) + * sqrt (77136233821044229928744879.); + case 25: + if (e == 0) + return (1. / 1555339646295669787201. + * (161849280390. * tau2 * tau1 + 42195622944. * tau2 * tau1 * tau1 - 238651009584. * tau2 * tau2 * tau1 + + 221347956459. * tau2 * tau2 - 126358108616. * tau2 * tau2 * tau2 - 42195622944. * tau1 * tau1 + - 18838110822. * tau1 + 16451659235. - 111441507078. * tau2 + 28130415296. * tau1 * tau1 * tau1)) + * sqrt (9902847527964529535108767.); + else if (e == 1) + return (1. / 1555339646295669787201. + * (-1650197576610. * tau2 * tau1 + 1049759965968. * tau2 * tau1 * tau1 + + 752444691024. * tau2 * tau2 * tau1 - 574802546469. * tau2 * tau2 + + 171461098696. * tau2 * tau2 * tau2 - 1135971347472. * tau1 * tau1 + 899615172742. * tau1 + - 233516057775. + 636822295568. * tau2 + 471006069600. * tau1 * tau1 * tau1)) + * sqrt (9902847527964529535108767.); + else if (e == 2) + return -(1. / 1555339646295669787201. + * (5001081450. * tau2 * tau1 + 7244473344. * tau2 * tau1 * tau1 - 4393240272. * tau2 * tau2 * tau1 + + 1726856865. - 6642222038. * tau2 + 8319397533. * tau2 * tau2 - 3404032360. * tau2 * tau2 * tau2 + - 1038889282. * tau1 - 7244473344. * tau1 * tau1 + 4829648896. * tau1 * tau1 * tau1)) + * sqrt (9902847527964529535108767.); + else + return (1. / 1555339646295669787201. + * (-104771367330. * tau2 * tau1 + 363258242832. * tau2 * tau1 * tau1 + 65942967888. * tau2 * tau2 * tau1 + - 5366650557. * tau2 * tau2 + 2229695960. * tau2 * tau2 * tau2 - 277046861328. * tau1 * tau1 + + 40690686598. * tau1 - 1133837095. + 4306001672. * tau2 + 471006069600. * tau1 * tau1 * tau1)) + * sqrt (9902847527964529535108767.); + case 26: + if (e == 0) + return -(2. / 2676102628445301.) * sqrt (2.) * sqrt (5.) + * (-79707420. * tau2 * tau1 + 59898912. * tau2 * tau1 * tau1 + 59898912. * tau2 * tau2 * tau1 - 3337763. + + 2836659. * tau2 + 56291073. * tau2 * tau2 + 19808508. * tau1 - 19808508. * tau1 * tau1 + - 99945333. * tau2 * tau2 * tau2) + * sqrt (14155690870266160523.); + else if (e == 1) + return -(2. / 2676102628445301.) * sqrt (2.) * sqrt (5.) + * (13981120. * tau1 * tau1 * tau1 - 38797740. * tau2 * tau1 + 23864640. * tau2 * tau1 * tau1 + + 18024864. * tau2 * tau2 * tau1 - 7280351. + 15723903. * tau2 - 15723903. * tau2 * tau2 + + 27473508. * tau1 - 34141452. * tau1 * tau1 + 7117227. * tau2 * tau2 * tau2) + * sqrt (14155690870266160523.); + else if (e == 2) + return -(2. / 2676102628445301.) * sqrt (2.) * sqrt (5.) + * (-68969916. * tau2 * tau1 + 41383968. * tau2 * tau1 * tau1 + 41383968. * tau2 * tau2 * tau1 + 8535709. + - 35729901. * tau2 + 48771201. * tau2 * tau2 + 27585948. * tau1 - 27585948. * tau1 * tau1 + - 21726677. * tau2 * tau2 * tau2) + * sqrt (14155690870266160523.); + else + return (2. / 2676102628445301.) * sqrt (2.) * sqrt (5.) + * (13981120. * tau1 * tau1 * tau1 - 6672276. * tau2 * tau1 + 18078720. * tau2 * tau1 * tau1 + + 12238944. * tau2 * tau2 * tau1 - 32825. + 343161. * tau2 - 1171329. * tau2 * tau2 + 1133964. * tau1 + - 7801908. * tau1 * tau1 + 1024117. * tau2 * tau2 * tau2) + * sqrt (14155690870266160523.); + case 27: + if (e == 0) + return -(4. / 481435005651514309593.) * sqrt (7.) + * (128335460. - 1019851173. * tau2 - 68609487. * tau1 + 2210359119. * tau2 * tau2 + - 1318843406. * tau2 * tau2 * tau2 - 564184299. * tau1 * tau1 + 376122866. * tau1 * tau1 * tau1 + - 2449625379. * tau2 * tau2 * tau1 + 564184299. * tau2 * tau1 * tau1 + 1406908560. * tau2 * tau1) + * sqrt (2111308236157200531699814841.); + else if (e == 1) + return (4. / 481435005651514309593.) * sqrt (7.) + * (-67398760. - 118444963. * tau2 + 274520323. * tau1 + 386770449. * tau2 * tau2 + - 210605746. * tau2 * tau2 * tau2 - 362797173. * tau1 * tau1 + 156249390. * tau1 * tau1 * tau1 + - 422918589. * tau2 * tau2 * tau1 - 83614923. * tau2 * tau1 * tau1 + 211588080. * tau2 * tau1) + * sqrt (2111308236157200531699814841.); + else if (e == 2) + return (4. / 481435005651514309593.) * sqrt (7.) + * (62426000. - 241814863. * tau2 - 53115257. * tau1 + 304845393. * tau2 * tau2 + - 125456530. * tau2 * tau2 * tau2 - 215210229. * tau1 * tau1 + 143473486. * tau1 * tau1 * tau1 + - 179176317. * tau2 * tau2 * tau1 + 215210229. * tau2 * tau1 * tau1 + 215304240. * tau2 * tau1) + * sqrt (2111308236157200531699814841.); + else + return (4. / 481435005651514309593.) * sqrt (7.) + * (-573780. + 8145953. * tau2 + 17674147. * tau1 - 25444623. * tau2 * tau2 + + 27551470. * tau2 * tau2 * tau2 - 105950997. * tau1 * tau1 + 156249390. * tau1 * tau1 * tau1 + + 213059427. * tau2 * tau2 * tau1 + 552363093. * tau2 * tau1 * tau1 - 167543760. * tau2 * tau1) + * sqrt (2111308236157200531699814841.); + case 28: + if (e == 0) + return (2. / 37417.) * sqrt (2.) * sqrt (7.) + * (4929. * tau2 * tau2 * tau2 + 39174. * tau2 * tau2 * tau1 + 39174. * tau2 * tau1 * tau1 + 17214. * tau1 + - 17214. * tau1 * tau1 - 4301. + 18051. * tau2 - 20967. * tau2 * tau2 - 56388. * tau2 * tau1) + * sqrt (187085.); + else if (e == 1) + return -(2. / 37417.) * sqrt (2.) * sqrt (7.) + * (351. * tau2 * tau2 * tau2 + 1280. * tau1 * tau1 * tau1 - 4038. * tau2 * tau2 * tau1 + - 3750. * tau2 * tau1 * tau1 + 1986. * tau1 - 2802. * tau1 * tau1 - 455. - 2535. * tau2 + + 2535. * tau2 * tau2 + 6324. * tau2 * tau1) + * sqrt (187085.); + else if (e == 2) + return (26. / 37417.) * sqrt (2.) * sqrt (7.) + * (-25. + 87. * tau2 + 150. * tau1 - 99. * tau2 * tau2 + 37. * tau2 * tau2 * tau2 - 150. * tau1 * tau1 + + 222. * tau2 * tau2 * tau1 + 222. * tau2 * tau1 * tau1 - 372. * tau2 * tau1) + * sqrt (187085.); + else + return (2. / 37417.) * sqrt (2.) * sqrt (7.) + * (641. * tau2 * tau2 * tau2 + 1280. * tau1 * tau1 * tau1 + 7302. * tau2 * tau2 * tau1 + + 7590. * tau2 * tau1 * tau1 + 222. * tau1 - 1038. * tau1 * tau1 - 9. + 183. * tau2 + - 711. * tau2 * tau2 - 3252. * tau2 * tau1) + * sqrt (187085.); + case 29: + if (e == 0) + return (4. / 47457369.) * sqrt (7.) + * (-7381. + 16722. * tau2 + 45879. * tau1 - 1326. * tau2 * tau2 - 8015. * tau2 * tau2 * tau2 + - 93351. * tau1 * tau1 + 62234. * tau1 * tau1 * tau1 + 15087. * tau2 * tau2 * tau1 + + 93351. * tau2 * tau1 * tau1 - 80916. * tau2 * tau1) + * sqrt (13736271805.); + else if (e == 1) + return (4. / 47457369.) * sqrt (7.) + * (-533. - 5642. * tau2 + 2531. * tau1 - 1326. * tau2 * tau2 + 3601. * tau2 * tau2 * tau2 + - 3879. * tau1 * tau1 + 1914. * tau1 * tau1 * tau1 - 753. * tau2 * tau2 * tau1 + - 11097. * tau2 * tau1 * tau1 + 16284. * tau2 * tau1) + * sqrt (13736271805.); + else if (e == 2) + return -(52. / 47457369.) * sqrt (7.) + * (-19. + 50. * tau2 + 325. * tau1 - 42. * tau2 * tau2 + 11. * tau2 * tau2 * tau2 - 861. * tau1 * tau1 + + 574. * tau1 * tau1 * tau1 + 309. * tau2 * tau2 * tau1 + 861. * tau2 * tau1 * tau1 + - 636. * tau2 * tau1) + * sqrt (13736271805.); + else + return (4. / 47457369.) * sqrt (7.) + * (-33. + 970. * tau2 + 515. * tau1 - 5694. * tau2 * tau2 + 8657. * tau2 * tau2 * tau2 + - 1863. * tau1 * tau1 + 1914. * tau1 * tau1 * tau1 + 27183. * tau2 * tau2 * tau1 + + 16839. * tau2 * tau1 * tau1 - 9636. * tau2 * tau1) + * sqrt (13736271805.); + default:; + } + default:; + }; +} + +/* Here we define a function that calls the correct motherwavelet. */ +double +muttermultiwavelet (int p, int i, double tau1, double tau2) +{ + if ((tau1 < 0.) || (tau1 > 1.) || (tau2 < 0.) || (tau2 > 1.) || (tau1 + tau2 > 1.)) + return 0.; + int e; + if ((tau1 <= 0.5) && (tau2 <= 0.5) && (tau1 + tau2 >= 0.5)) + e = 0; + else if (tau1 > 0.5) + e = 1; + else if (tau2 > 0.5) + e = 2; + else + e = 3; + return muttermultiwavelets (p, i, tau1, tau2, e); +} + +double +scaling_function_nextlevel (int i, double tau1, double tau2) +{ + if ((tau1 < 0.) || (tau1 > 1.) || (tau2 < 0.) || (tau2 > 1.) || (tau1 + tau2 > 1.)) + return 0.; + return scaling_function (i, tau1, tau2); +} + +} // namespace t8_mra + +#endif diff --git a/src/t8_mra/num/basis_functions.hxx b/src/t8_mra/num/basis_functions.hxx new file mode 100644 index 0000000000..a3dd6f121e --- /dev/null +++ b/src/t8_mra/num/basis_functions.hxx @@ -0,0 +1,19 @@ +#pragma once +#ifdef T8_ENABLE_MRA + +namespace t8_mra +{ +double +scaling_function (int i, double tau1, double tau2); + +double +muttermultiwavelets (int p, int i, double tau1, double tau2, int e); + +double +muttermultiwavelet (int p, int i, double tau1, double tau2); + +double +scaling_function_nextlevel (int i, double tau1, double tau2); + +} // namespace t8_mra +#endif diff --git a/src/t8_mra/num/dg_basis.hxx b/src/t8_mra/num/dg_basis.hxx new file mode 100644 index 0000000000..1786aae38c --- /dev/null +++ b/src/t8_mra/num/dg_basis.hxx @@ -0,0 +1,349 @@ +#pragma once + +#ifdef T8_ENABLE_MRA + +#include +#include +#include + +#include "t8_eclass.h" +#include "t8_mra/num/basis_functions.hxx" +#include "t8_mra/num/dunavant.hxx" +#include "t8_mra/num/mat.hxx" +#include "t8_mra/num/quadrature.hxx" +#include "t8_mra/num/legendre_basis.hxx" +#include "t8_mra/num/multiindex.hxx" +#include "t8_mra/num/geometry.hxx" + +namespace t8_mra +{ + +template +concept is_cartesian = (TShape == T8_ECLASS_LINE || TShape == T8_ECLASS_QUAD || TShape == T8_ECLASS_HEX); + +template +struct dg_basis_base +{ + static constexpr unsigned int DIM = 0; + static constexpr t8_eclass Shape = TShape; + + size_t num_quad_points; + std::vector ref_quad_points; + std::vector quad_weights; + /// TODO error message +}; + +template +struct dg_basis_base>> +{ + static constexpr unsigned int DIM = T == T8_ECLASS_LINE ? 1 : (T == T8_ECLASS_QUAD ? 2 : 3); + static constexpr t8_eclass Shape = T; + + int num_quad_points_1d; // Number of 1D quadrature points + size_t num_quad_points; // Total number of quad points (num_quad_points_1d^DIM) + int P; // Polynomial order (stored for pset generation) + + std::vector ref_quad_points_1d; // 1D quadrature points + std::vector quad_weights_1d; // 1D quadrature weights + + std::vector ref_quad_points; // Multi-D quadrature points (flattened) + std::vector quad_weights; // Multi-D quadrature weights + + dg_basis_base () = default; + + /** + * @brief Constructor for cartesian elements using Gauss-Legendre quadrature + * + * @param _num_quad_points_1d Number of quadrature points in each dimension + * @param _P Polynomial order for basis (used to generate pset later in dg_basis) + */ + dg_basis_base (int _num_quad_points_1d, int _P): num_quad_points_1d (_num_quad_points_1d), P (_P) + { + // Generate 1D Gauss-Legendre quadrature on [0,1] + t8_mra::gauss_legendre_1d (num_quad_points_1d, ref_quad_points_1d, quad_weights_1d); + + // Build multi-dimensional quadrature via tensor product + build_tensor_quadrature (); + } + + private: + /** + * @brief Builds multi-dimensional quadrature from 1D quadrature via tensor product + */ + void + build_tensor_quadrature () + { + if constexpr (DIM == 1) { + num_quad_points = num_quad_points_1d; + ref_quad_points = ref_quad_points_1d; + quad_weights = quad_weights_1d; + } + else if constexpr (DIM == 2) { + num_quad_points = num_quad_points_1d * num_quad_points_1d; + ref_quad_points.resize (2 * num_quad_points); + quad_weights.resize (num_quad_points); + + size_t idx = 0; + for (int i = 0; i < num_quad_points_1d; ++i) { + for (int j = 0; j < num_quad_points_1d; ++j) { + ref_quad_points[2 * idx] = ref_quad_points_1d[i]; + ref_quad_points[2 * idx + 1] = ref_quad_points_1d[j]; + quad_weights[idx] = quad_weights_1d[i] * quad_weights_1d[j]; + ++idx; + } + } + } + else if constexpr (DIM == 3) { + num_quad_points = num_quad_points_1d * num_quad_points_1d * num_quad_points_1d; + ref_quad_points.resize (3 * num_quad_points); + quad_weights.resize (num_quad_points); + + size_t idx = 0; + for (int i = 0; i < num_quad_points_1d; ++i) { + for (int j = 0; j < num_quad_points_1d; ++j) { + for (int k = 0; k < num_quad_points_1d; ++k) { + ref_quad_points[3 * idx] = ref_quad_points_1d[i]; + ref_quad_points[3 * idx + 1] = ref_quad_points_1d[j]; + ref_quad_points[3 * idx + 2] = ref_quad_points_1d[k]; + quad_weights[idx] = quad_weights_1d[i] * quad_weights_1d[j] * quad_weights_1d[k]; + ++idx; + } + } + } + } + } + + public: + /** + * @brief Computes Jacobian determinant for the physical element + * + * @param physical_vertices Vertex coordinates of the physical element + * @return double Absolute value of Jacobian determinant + */ + double + jacobian_det (const double physical_vertices[][3]) + { + std::array vertices_min, vertices_max; + extract_cartesian_vertices (physical_vertices, vertices_min, vertices_max); + + return jacobian_determinant (vertices_min, vertices_max); + } + + /** + * @brief Maps quadrature points from reference element to physical element + * + * @param physical_vertices Vertex coordinates of the physical element + * @return std::vector Physical quadrature points (flattened) + */ + std::vector + deref_quad_points (const double physical_vertices[][3]) + { + std::array vertices_min, vertices_max; + extract_cartesian_vertices (physical_vertices, vertices_min, vertices_max); + + return transform_quad_points (ref_quad_points, num_quad_points, vertices_min, vertices_max); + } + + /** + * @brief Maps a point from physical element to reference element [0,1]^DIM + * + * @param physical_vertices Vertex coordinates of the physical element + * @param grid_point Point in physical element (must have at least DIM coordinates) + * @return std::vector Point in reference element [0,1]^DIM + */ + std::vector + ref_point (const double physical_vertices[][3], const std::vector &grid_point) + { + std::array vertices_min, vertices_max; + extract_cartesian_vertices (physical_vertices, vertices_min, vertices_max); + + std::array x_phys; + for (unsigned int d = 0; d < DIM; ++d) + x_phys[d] = grid_point[d]; + + std::array x_ref = ref (x_phys, vertices_min, vertices_max); + + std::vector result (DIM); + for (unsigned int d = 0; d < DIM; ++d) + result[d] = x_ref[d]; + + return result; + } + + /** + * @brief Maps a point from reference element to physical element + * + * @param physical_vertices Vertex coordinates of the physical element + * @param ref_point_coords Point in reference element [0,1]^DIM + * @return std::vector Point in physical element + */ + std::vector + deref_point (const double physical_vertices[][3], const std::vector &ref_point_coords) + { + std::array vertices_min, vertices_max; + extract_cartesian_vertices (physical_vertices, vertices_min, vertices_max); + + std::array x_ref; + for (unsigned int d = 0; d < DIM; ++d) { + x_ref[d] = ref_point_coords[d]; + } + + std::array x_phys = deref (x_ref, vertices_min, vertices_max); + + std::vector result (DIM); + for (unsigned int d = 0; d < DIM; ++d) { + result[d] = x_phys[d]; + } + return result; + } +}; + +template <> +struct dg_basis_base +{ + static constexpr unsigned int DIM = 2; + static constexpr t8_eclass Shape = T8_ECLASS_TRIANGLE; + + size_t num_quad_points; + int dunavant_rule; + + std::vector ref_quad_points; + std::vector quad_weights; + + dg_basis_base () = default; + + dg_basis_base (int _num_quad_points, int _dunavant_rule) + : num_quad_points (t8_mra::dunavant_order_num (_dunavant_rule)), dunavant_rule (_dunavant_rule), + ref_quad_points (2u * num_quad_points, 0.0), quad_weights (num_quad_points, 0.0) + { + t8_mra::dunavant_rule (dunavant_rule, num_quad_points, ref_quad_points.data (), quad_weights.data ()); + } + + std::pair> + trafo_matrix_to_ref_element (const double physical_vertices[3][3]) + { + t8_mra::mat transform_to_ref (3, 3); + std::vector permuation_vec (3, 0u); + + for (auto i = 0; i < 3; ++i) + for (auto j = 0; j < 3; ++j) + transform_to_ref (i, j) = i == 2 ? 1.0 : physical_vertices[j][i]; + t8_mra::lu_factors (transform_to_ref, permuation_vec); + + return { transform_to_ref, permuation_vec }; + } + + /// returns list of dereferenced quad points [x_0, y_0, x_1, y_1, ...] + std::vector + deref_quad_points (const double physical_vertices[3][3]) + { + std::vector deref_quad_points (ref_quad_points.size (), 0.0); + + std::array corners { physical_vertices[0][0], physical_vertices[0][1], physical_vertices[1][0], + physical_vertices[1][1], physical_vertices[2][0], physical_vertices[2][1] }; + + t8_mra::reference_to_physical_t3 (corners.data (), num_quad_points, ref_quad_points.data (), + deref_quad_points.data ()); + + return deref_quad_points; + } + + std::vector + ref_point (const t8_mra::mat &trafo_mat, const std::vector &permuation_vec, + const std::vector &grid_point) + { + std::vector ret = { grid_point[0], grid_point[1], 1.0 }; + t8_mra::lu_solve (trafo_mat, permuation_vec, ret); + + return ret; + } +}; + +template +class dg_basis: public dg_basis_base { + using Element = TElement; + using Base = dg_basis_base; + + static constexpr unsigned int DIM = Element::DIM; + static constexpr auto Shape = TElement::Shape; + + static constexpr unsigned int P_DIM = Element::P_DIM; + static constexpr unsigned int DOF = Element::DOF; + static constexpr unsigned int W_DOF = Element::W_DOF; + + // Multiindex set for tensor basis (cartesian elements only) + + public: + std::vector> pset; + dg_basis () = default; + + // Constructor for triangular elements + dg_basis (int _num_quad_points, int _dunavant_rule) + requires (Shape == T8_ECLASS_TRIANGLE) + : Base (_num_quad_points, _dunavant_rule) + { + } + + // Constructor for cartesian elements (LINE, QUAD, HEX) + dg_basis (int _num_quad_points_1d, int _P) + requires is_cartesian + : Base (_num_quad_points_1d, _P) + { + // Generate multiindex set for tensor-structured basis + pset = generate_tensor_pset (_P); + } + + std::array + basis_value (const std::vector &x_ref) + { + std::array res; + + if constexpr (is_cartesian) { + // Cartesian elements: use tensor product of Legendre polynomials + std::array x_array; + for (unsigned int d = 0; d < DIM; ++d) { + x_array[d] = x_ref[d]; + } + + for (auto i = 0u; i < DOF; ++i) { + res[i] = eval_tensor_basis (x_array, i, pset, phi_1d); + } + } + else if constexpr (Shape == T8_ECLASS_TRIANGLE) { + // Triangle elements: use existing scaling functions + for (auto i = 0u; i < DOF; ++i) + res[i] = t8_mra::scaling_function (i, x_ref[0], x_ref[1]); + } + + return res; + } + + /** + * @brief Evaluates gradient of all basis functions at a point (cartesian elements only) + * + * @param x_ref Point in reference element [0,1]^DIM + * @return std::array, DIM> grad[dir][i] = d(phi_i)/dx_dir at x_ref + */ + std::array, DIM> + basis_gradient (const std::vector &x_ref) + requires is_cartesian + { + std::array, DIM> grad; + + std::array x_array; + for (unsigned int d = 0; d < DIM; ++d) { + x_array[d] = x_ref[d]; + } + + for (unsigned int dir = 0; dir < DIM; ++dir) { + for (auto i = 0u; i < DOF; ++i) { + grad[dir][i] = eval_tensor_basis_gradient (x_array, i, dir, pset, phi_1d, phi_prime_1d); + } + } + + return grad; + } +}; + +} // namespace t8_mra +#endif diff --git a/src/t8_mra/num/dunavant.cxx b/src/t8_mra/num/dunavant.cxx new file mode 100644 index 0000000000..4880bfdc3b --- /dev/null +++ b/src/t8_mra/num/dunavant.cxx @@ -0,0 +1,3231 @@ +#ifdef T8_ENABLE_MRA +#include +#include +#include +#include +#include +#include +#include +#include "t8_mra/num/dunavant.hxx" + +//T8_EXTERN_C_BEGIN (); +using namespace std; +//****************************************************************************80 + +namespace t8_mra +{ + +int +dunavant_degree (int rule) + +//****************************************************************************80 +// +// Purpose: +// +// DUNAVANT_DEGREE returns the degree of a Dunavant rule for the triangle. +// +// Licensing: +// +// This code is distributed under the GNU LGPL license. +// +// Modified: +// +// 11 December 2006 +// +// Author: +// +// John Burkardt +// +// Reference: +// +// David Dunavant, +// High Degree Efficient Symmetrical Gaussian Quadrature Rules +// for the Triangle, +// International Journal for Numerical Methods in Engineering, +// Volume 21, 1985, pages 1129-1148. +// +// James Lyness, Dennis Jespersen, +// Moderate Degree Symmetric Quadrature Rules for the Triangle, +// Journal of the Institute of Mathematics and its Applications, +// Volume 15, Number 1, February 1975, pages 19-32. +// +// Parameters: +// +// Input, int RULE, the index of the rule. +// +// Output, int DUNAVANT_DEGREE, the polynomial degree of exactness of +// the rule. +// +{ + int degree; + + if (1 <= rule && rule <= 20) { + degree = rule; + } + else { + degree = -1; + cout << "\n"; + cout << "DUNAVANT_DEGREE - Fatal error!\n"; + cout << " Illegal RULE = " << rule << "\n"; + exit (1); + } + + return degree; +} +//****************************************************************************80 + +int +dunavant_order_num (int rule) + +//****************************************************************************80 +// +// Purpose: +// +// DUNAVANT_ORDER_NUM returns the order of a Dunavant rule for the triangle. +// +// Licensing: +// +// This code is distributed under the GNU LGPL license. +// +// Modified: +// +// 11 December 2006 +// +// Author: +// +// John Burkardt +// +// Reference: +// +// David Dunavant, +// High Degree Efficient Symmetrical Gaussian Quadrature Rules +// for the Triangle, +// International Journal for Numerical Methods in Engineering, +// Volume 21, 1985, pages 1129-1148. +// +// James Lyness, Dennis Jespersen, +// Moderate Degree Symmetric Quadrature Rules for the Triangle, +// Journal of the Institute of Mathematics and its Applications, +// Volume 15, Number 1, February 1975, pages 19-32. +// +// Parameters: +// +// Input, int RULE, the index of the rule. +// +// Output, int DUNAVANT_ORDER_NUM, the order (number of points) of the rule. +// +{ + int order; + int order_num; + int *suborder; + int suborder_num; + + suborder_num = dunavant_suborder_num (rule); + + suborder = dunavant_suborder (rule, suborder_num); + + order_num = 0; + for (order = 0; order < suborder_num; order++) { + order_num = order_num + suborder[order]; + } + + delete[] suborder; + + return order_num; +} +//****************************************************************************80 + +void +dunavant_rule (int rule, int order_num, double xy[], double w[]) + +//****************************************************************************80 +// +// Purpose: +// +// DUNAVANT_RULE returns the points and weights of a Dunavant rule. +// +// Licensing: +// +// This code is distributed under the GNU LGPL license. +// +// Modified: +// +// 11 December 2006 +// +// Author: +// +// John Burkardt +// +// Reference: +// +// David Dunavant, +// High Degree Efficient Symmetrical Gaussian Quadrature Rules +// for the Triangle, +// International Journal for Numerical Methods in Engineering, +// Volume 21, 1985, pages 1129-1148. +// +// James Lyness, Dennis Jespersen, +// Moderate Degree Symmetric Quadrature Rules for the Triangle, +// Journal of the Institute of Mathematics and its Applications, +// Volume 15, Number 1, February 1975, pages 19-32. +// +// Parameters: +// +// Input, int RULE, the index of the rule. +// +// Input, int ORDER_NUM, the order (number of points) of the rule. +// +// Output, double XY[2*ORDER_NUM], the points of the rule. +// +// Output, double W[ORDER_NUM], the weights of the rule. +// +{ + int k; + int o; + int s; + int *suborder; + int suborder_num; + double *suborder_w; + double *suborder_xyz; + // + // Get the suborder information. + // + suborder_num = dunavant_suborder_num (rule); + + suborder_xyz = new double[3 * suborder_num]; + suborder_w = new double[suborder_num]; + + suborder = dunavant_suborder (rule, suborder_num); + + dunavant_subrule (rule, suborder_num, suborder_xyz, suborder_w); + // + // Expand the suborder information to a full order rule. + // + o = 0; + + for (s = 0; s < suborder_num; s++) { + if (suborder[s] == 1) { + xy[0 + o * 2] = suborder_xyz[0 + s * 3]; + xy[1 + o * 2] = suborder_xyz[1 + s * 3]; + w[o] = suborder_w[s]; + o = o + 1; + } + else if (suborder[s] == 3) { + for (k = 0; k < 3; k++) { + xy[0 + o * 2] = suborder_xyz[i4_wrap (k, 0, 2) + s * 3]; + xy[1 + o * 2] = suborder_xyz[i4_wrap (k + 1, 0, 2) + s * 3]; + w[o] = suborder_w[s]; + o = o + 1; + } + } + else if (suborder[s] == 6) { + for (k = 0; k < 3; k++) { + xy[0 + o * 2] = suborder_xyz[i4_wrap (k, 0, 2) + s * 3]; + xy[1 + o * 2] = suborder_xyz[i4_wrap (k + 1, 0, 2) + s * 3]; + w[o] = suborder_w[s]; + o = o + 1; + } + + for (k = 0; k < 3; k++) { + xy[0 + o * 2] = suborder_xyz[i4_wrap (k + 1, 0, 2) + s * 3]; + xy[1 + o * 2] = suborder_xyz[i4_wrap (k, 0, 2) + s * 3]; + w[o] = suborder_w[s]; + o = o + 1; + } + } + else { + cout << "\n"; + cout << "DUNAVANT_RULE - Fatal error!\n;"; + cout << " Illegal SUBORDER(" << s << ") = " << suborder[s] << "\n"; + exit (1); + } + } + + delete[] suborder; + delete[] suborder_xyz; + delete[] suborder_w; + + return; +} +//****************************************************************************80 + +int +dunavant_rule_num () + +//****************************************************************************80 +// +// Purpose: +// +// DUNAVANT_RULE_NUM returns the number of Dunavant rules available. +// +// Licensing: +// +// This code is distributed under the GNU LGPL license. +// +// Modified: +// +// 11 December 2006 +// +// Author: +// +// John Burkardt +// +// Reference: +// +// David Dunavant, +// High Degree Efficient Symmetrical Gaussian Quadrature Rules +// for the Triangle, +// International Journal for Numerical Methods in Engineering, +// Volume 21, 1985, pages 1129-1148. +// +// James Lyness, Dennis Jespersen, +// Moderate Degree Symmetric Quadrature Rules for the Triangle, +// Journal of the Institute of Mathematics and its Applications, +// Volume 15, Number 1, February 1975, pages 19-32. +// +// Parameters: +// +// Output, int DUNAVANT_RULE_NUM, the number of rules available. +// +{ + int rule_num; + + rule_num = 20; + + return rule_num; +} +//****************************************************************************80 + +int * +dunavant_suborder (int rule, int suborder_num) + +//****************************************************************************80 +// +// Purpose: +// +// DUNAVANT_SUBORDER returns the suborders for a Dunavant rule. +// +// Licensing: +// +// This code is distributed under the GNU LGPL license. +// +// Modified: +// +// 11 December 2006 +// +// Author: +// +// John Burkardt +// +// Reference: +// +// David Dunavant, +// High Degree Efficient Symmetrical Gaussian Quadrature Rules +// for the Triangle, +// International Journal for Numerical Methods in Engineering, +// Volume 21, 1985, pages 1129-1148. +// +// James Lyness, Dennis Jespersen, +// Moderate Degree Symmetric Quadrature Rules for the Triangle, +// Journal of the Institute of Mathematics and its Applications, +// Volume 15, Number 1, February 1975, pages 19-32. +// +// Parameters: +// +// Input, int RULE, the index of the rule. +// +// Input, int SUBORDER_NUM, the number of suborders of the rule. +// +// Output, int DUNAVANT_SUBORDER[SUBORDER_NUM], the suborders of the rule. +// +{ + int *suborder; + + suborder = new int[suborder_num]; + + if (rule == 1) { + suborder[0] = 1; + } + else if (rule == 2) { + suborder[0] = 3; + } + else if (rule == 3) { + suborder[0] = 1; + suborder[1] = 3; + } + else if (rule == 4) { + suborder[0] = 3; + suborder[1] = 3; + } + else if (rule == 5) { + suborder[0] = 1; + suborder[1] = 3; + suborder[2] = 3; + } + else if (rule == 6) { + suborder[0] = 3; + suborder[1] = 3; + suborder[2] = 6; + } + else if (rule == 7) { + suborder[0] = 1; + suborder[1] = 3; + suborder[2] = 3; + suborder[3] = 6; + } + else if (rule == 8) { + suborder[0] = 1; + suborder[1] = 3; + suborder[2] = 3; + suborder[3] = 3; + suborder[4] = 6; + } + else if (rule == 9) { + suborder[0] = 1; + suborder[1] = 3; + suborder[2] = 3; + suborder[3] = 3; + suborder[4] = 3; + suborder[5] = 6; + } + else if (rule == 10) { + suborder[0] = 1; + suborder[1] = 3; + suborder[2] = 3; + suborder[3] = 6; + suborder[4] = 6; + suborder[5] = 6; + } + else if (rule == 11) { + suborder[0] = 3; + suborder[1] = 3; + suborder[2] = 3; + suborder[3] = 3; + suborder[4] = 3; + suborder[5] = 6; + suborder[6] = 6; + } + else if (rule == 12) { + suborder[0] = 3; + suborder[1] = 3; + suborder[2] = 3; + suborder[3] = 3; + suborder[4] = 3; + suborder[5] = 6; + suborder[6] = 6; + suborder[7] = 6; + } + else if (rule == 13) { + suborder[0] = 1; + suborder[1] = 3; + suborder[2] = 3; + suborder[3] = 3; + suborder[4] = 3; + suborder[5] = 3; + suborder[6] = 3; + suborder[7] = 6; + suborder[8] = 6; + suborder[9] = 6; + } + else if (rule == 14) { + suborder[0] = 3; + suborder[1] = 3; + suborder[2] = 3; + suborder[3] = 3; + suborder[4] = 3; + suborder[5] = 3; + suborder[6] = 6; + suborder[7] = 6; + suborder[8] = 6; + suborder[9] = 6; + } + else if (rule == 15) { + suborder[0] = 3; + suborder[1] = 3; + suborder[2] = 3; + suborder[3] = 3; + suborder[4] = 3; + suborder[5] = 3; + suborder[6] = 6; + suborder[7] = 6; + suborder[8] = 6; + suborder[9] = 6; + suborder[10] = 6; + } + else if (rule == 16) { + suborder[0] = 1; + suborder[1] = 3; + suborder[2] = 3; + suborder[3] = 3; + suborder[4] = 3; + suborder[5] = 3; + suborder[6] = 3; + suborder[7] = 3; + suborder[8] = 6; + suborder[9] = 6; + suborder[10] = 6; + suborder[11] = 6; + suborder[12] = 6; + } + else if (rule == 17) { + suborder[0] = 1; + suborder[1] = 3; + suborder[2] = 3; + suborder[3] = 3; + suborder[4] = 3; + suborder[5] = 3; + suborder[6] = 3; + suborder[7] = 3; + suborder[8] = 3; + suborder[9] = 6; + suborder[10] = 6; + suborder[11] = 6; + suborder[12] = 6; + suborder[13] = 6; + suborder[14] = 6; + } + else if (rule == 18) { + suborder[0] = 1; + suborder[1] = 3; + suborder[2] = 3; + suborder[3] = 3; + suborder[4] = 3; + suborder[5] = 3; + suborder[6] = 3; + suborder[7] = 3; + suborder[8] = 3; + suborder[9] = 3; + suborder[10] = 6; + suborder[11] = 6; + suborder[12] = 6; + suborder[13] = 6; + suborder[14] = 6; + suborder[15] = 6; + suborder[16] = 6; + } + else if (rule == 19) { + suborder[0] = 1; + suborder[1] = 3; + suborder[2] = 3; + suborder[3] = 3; + suborder[4] = 3; + suborder[5] = 3; + suborder[6] = 3; + suborder[7] = 3; + suborder[8] = 3; + suborder[9] = 6; + suborder[10] = 6; + suborder[11] = 6; + suborder[12] = 6; + suborder[13] = 6; + suborder[14] = 6; + suborder[15] = 6; + suborder[16] = 6; + } + else if (rule == 20) { + suborder[0] = 1; + suborder[1] = 3; + suborder[2] = 3; + suborder[3] = 3; + suborder[4] = 3; + suborder[5] = 3; + suborder[6] = 3; + suborder[7] = 3; + suborder[8] = 3; + suborder[9] = 3; + suborder[10] = 3; + suborder[11] = 6; + suborder[12] = 6; + suborder[13] = 6; + suborder[14] = 6; + suborder[15] = 6; + suborder[16] = 6; + suborder[17] = 6; + suborder[18] = 6; + } + else { + cout << "\n"; + cout << "DUNAVANT_SUBORDER - Fatal error!\n"; + cout << " Illegal RULE = " << rule << "\n"; + exit (1); + } + + return suborder; +} +//****************************************************************************80 + +int +dunavant_suborder_num (int rule) + +//****************************************************************************80 +// +// Purpose: +// +// DUNAVANT_SUBORDER_NUM returns the number of suborders for a Dunavant rule. +// +// Licensing: +// +// This code is distributed under the GNU LGPL license. +// +// Modified: +// +// 11 December 2006 +// +// Author: +// +// John Burkardt +// +// Reference: +// +// David Dunavant, +// High Degree Efficient Symmetrical Gaussian Quadrature Rules +// for the Triangle, +// International Journal for Numerical Methods in Engineering, +// Volume 21, 1985, pages 1129-1148. +// +// James Lyness, Dennis Jespersen, +// Moderate Degree Symmetric Quadrature Rules for the Triangle, +// Journal of the Institute of Mathematics and its Applications, +// Volume 15, Number 1, February 1975, pages 19-32. +// +// Parameters: +// +// Input, int RULE, the index of the rule. +// +// Output, int DUNAVANT_SUBORDER_NUM, the number of suborders of the rule. +// +{ + int suborder_num; + + if (rule == 1) { + suborder_num = 1; + } + else if (rule == 2) { + suborder_num = 1; + } + else if (rule == 3) { + suborder_num = 2; + } + else if (rule == 4) { + suborder_num = 2; + } + else if (rule == 5) { + suborder_num = 3; + } + else if (rule == 6) { + suborder_num = 3; + } + else if (rule == 7) { + suborder_num = 4; + } + else if (rule == 8) { + suborder_num = 5; + } + else if (rule == 9) { + suborder_num = 6; + } + else if (rule == 10) { + suborder_num = 6; + } + else if (rule == 11) { + suborder_num = 7; + } + else if (rule == 12) { + suborder_num = 8; + } + else if (rule == 13) { + suborder_num = 10; + } + else if (rule == 14) { + suborder_num = 10; + } + else if (rule == 15) { + suborder_num = 11; + } + else if (rule == 16) { + suborder_num = 13; + } + else if (rule == 17) { + suborder_num = 15; + } + else if (rule == 18) { + suborder_num = 17; + } + else if (rule == 19) { + suborder_num = 17; + } + else if (rule == 20) { + suborder_num = 19; + } + else { + suborder_num = -1; + cout << "\n"; + cout << "DUNAVANT_SUBORDER_NUM - Fatal error!\n"; + cout << " Illegal RULE = " << rule << "\n"; + exit (1); + } + + return suborder_num; +} +//****************************************************************************80 + +void +dunavant_subrule (int rule, int suborder_num, double suborder_xyz[], double suborder_w[]) + +//****************************************************************************80 +// +// Purpose: +// +// DUNAVANT_SUBRULE returns a compressed Dunavant rule. +// +// Licensing: +// +// This code is distributed under the GNU LGPL license. +// +// Modified: +// +// 11 December 2006 +// +// Author: +// +// John Burkardt +// +// Reference: +// +// David Dunavant, +// High Degree Efficient Symmetrical Gaussian Quadrature Rules +// for the Triangle, +// International Journal for Numerical Methods in Engineering, +// Volume 21, 1985, pages 1129-1148. +// +// James Lyness, Dennis Jespersen, +// Moderate Degree Symmetric Quadrature Rules for the Triangle, +// Journal of the Institute of Mathematics and its Applications, +// Volume 15, Number 1, February 1975, pages 19-32. +// +// Parameters: +// +// Input, int RULE, the index of the rule. +// +// Input, int SUBORDER_NUM, the number of suborders of the rule. +// +// Output, double SUBORDER_XYZ[3*SUBORDER_NUM], +// the barycentric coordinates of the abscissas. +// +// Output, double SUBORDER_W[SUBORDER_NUM], the suborder weights. +// +{ + if (rule == 1) { + dunavant_subrule_01 (suborder_num, suborder_xyz, suborder_w); + } + else if (rule == 2) { + dunavant_subrule_02 (suborder_num, suborder_xyz, suborder_w); + } + else if (rule == 3) { + dunavant_subrule_03 (suborder_num, suborder_xyz, suborder_w); + } + else if (rule == 4) { + dunavant_subrule_04 (suborder_num, suborder_xyz, suborder_w); + } + else if (rule == 5) { + dunavant_subrule_05 (suborder_num, suborder_xyz, suborder_w); + } + else if (rule == 6) { + dunavant_subrule_06 (suborder_num, suborder_xyz, suborder_w); + } + else if (rule == 7) { + dunavant_subrule_07 (suborder_num, suborder_xyz, suborder_w); + } + else if (rule == 8) { + dunavant_subrule_08 (suborder_num, suborder_xyz, suborder_w); + } + else if (rule == 9) { + dunavant_subrule_09 (suborder_num, suborder_xyz, suborder_w); + } + else if (rule == 10) { + dunavant_subrule_10 (suborder_num, suborder_xyz, suborder_w); + } + else if (rule == 11) { + dunavant_subrule_11 (suborder_num, suborder_xyz, suborder_w); + } + else if (rule == 12) { + dunavant_subrule_12 (suborder_num, suborder_xyz, suborder_w); + } + else if (rule == 13) { + dunavant_subrule_13 (suborder_num, suborder_xyz, suborder_w); + } + else if (rule == 14) { + dunavant_subrule_14 (suborder_num, suborder_xyz, suborder_w); + } + else if (rule == 15) { + dunavant_subrule_15 (suborder_num, suborder_xyz, suborder_w); + } + else if (rule == 16) { + dunavant_subrule_16 (suborder_num, suborder_xyz, suborder_w); + } + else if (rule == 17) { + dunavant_subrule_17 (suborder_num, suborder_xyz, suborder_w); + } + else if (rule == 18) { + dunavant_subrule_18 (suborder_num, suborder_xyz, suborder_w); + } + else if (rule == 19) { + dunavant_subrule_19 (suborder_num, suborder_xyz, suborder_w); + } + else if (rule == 20) { + dunavant_subrule_20 (suborder_num, suborder_xyz, suborder_w); + } + else { + cout << "\n"; + cout << "DUNAVANT_SUBRULE - Fatal error!\n"; + cout << " Illegal RULE = " << rule << "\n"; + exit (1); + } + + return; +} +//****************************************************************************80 + +void +dunavant_subrule_01 (int suborder_num, double suborder_xyz[], double suborder_w[]) + +//****************************************************************************80 +// +// Purpose: +// +// DUNAVANT_SUBRULE_01 returns a compressed Dunavant rule 1. +// +// Licensing: +// +// This code is distributed under the GNU LGPL license. +// +// Modified: +// +// 11 December 2006 +// +// Author: +// +// John Burkardt +// +// Reference: +// +// David Dunavant, +// High Degree Efficient Symmetrical Gaussian Quadrature Rules +// for the Triangle, +// International Journal for Numerical Methods in Engineering, +// Volume 21, 1985, pages 1129-1148. +// +// James Lyness, Dennis Jespersen, +// Moderate Degree Symmetric Quadrature Rules for the Triangle, +// Journal of the Institute of Mathematics and its Applications, +// Volume 15, Number 1, February 1975, pages 19-32. +// +// Parameters: +// +// Input, int SUBORDER_NUM, the number of suborders of the rule. +// +// Output, double SUBORDER_XYZ[3*SUBORDER_NUM], +// the barycentric coordinates of the abscissas. +// +// Output, double SUBORDER_W[SUBORDER_NUM], the suborder weights. +// +{ + int s; + double suborder_xy_rule_01[3 * 1] = { 0.333333333333333, 0.333333333333333, 0.333333333333333 }; + double suborder_w_rule_01[1] = { 1.000000000000000 }; + + for (s = 0; s < suborder_num; s++) { + suborder_xyz[0 + s * 3] = suborder_xy_rule_01[0 + s * 3]; + suborder_xyz[1 + s * 3] = suborder_xy_rule_01[1 + s * 3]; + suborder_xyz[2 + s * 3] = suborder_xy_rule_01[2 + s * 3]; + } + + for (s = 0; s < suborder_num; s++) { + suborder_w[s] = suborder_w_rule_01[s]; + } + + return; +} +//****************************************************************************80 + +void +dunavant_subrule_02 (int suborder_num, double suborder_xyz[], double suborder_w[]) + +//****************************************************************************80 +// +// Purpose: +// +// DUNAVANT_SUBRULE_02 returns a compressed Dunavant rule 2. +// +// Licensing: +// +// This code is distributed under the GNU LGPL license. +// +// Modified: +// +// 11 December 2006 +// +// Author: +// +// John Burkardt +// +// Reference: +// +// David Dunavant, +// High Degree Efficient Symmetrical Gaussian Quadrature Rules +// for the Triangle, +// International Journal for Numerical Methods in Engineering, +// Volume 21, 1985, pages 1129-1148. +// +// James Lyness, Dennis Jespersen, +// Moderate Degree Symmetric Quadrature Rules for the Triangle, +// Journal of the Institute of Mathematics and its Applications, +// Volume 15, Number 1, February 1975, pages 19-32. +// +// Parameters: +// +// Input, int SUBORDER_NUM, the number of suborders of the rule. +// +// Output, double SUBORDER_XYZ[3*SUBORDER_NUM], +// the barycentric coordinates of the abscissas. +// +// Output, double SUBORDER_W[SUBORDER_NUM], the suborder weights. +// +{ + int s; + double suborder_xy_rule_02[3 * 1] = { 0.666666666666667, 0.166666666666667, 0.166666666666667 }; + double suborder_w_rule_02[1] = { 0.333333333333333 }; + + for (s = 0; s < suborder_num; s++) { + suborder_xyz[0 + s * 3] = suborder_xy_rule_02[0 + s * 3]; + suborder_xyz[1 + s * 3] = suborder_xy_rule_02[1 + s * 3]; + suborder_xyz[2 + s * 3] = suborder_xy_rule_02[2 + s * 3]; + } + + for (s = 0; s < suborder_num; s++) { + suborder_w[s] = suborder_w_rule_02[s]; + } + + return; +} +//****************************************************************************80 + +void +dunavant_subrule_03 (int suborder_num, double suborder_xyz[], double suborder_w[]) + +//****************************************************************************80 +// +// Purpose: +// +// DUNAVANT_SUBRULE_03 returns a compressed Dunavant rule 3. +// +// Licensing: +// +// This code is distributed under the GNU LGPL license. +// +// Modified: +// +// 11 December 2006 +// +// Author: +// +// John Burkardt +// +// Reference: +// +// David Dunavant, +// High Degree Efficient Symmetrical Gaussian Quadrature Rules +// for the Triangle, +// International Journal for Numerical Methods in Engineering, +// Volume 21, 1985, pages 1129-1148. +// +// James Lyness, Dennis Jespersen, +// Moderate Degree Symmetric Quadrature Rules for the Triangle, +// Journal of the Institute of Mathematics and its Applications, +// Volume 15, Number 1, February 1975, pages 19-32. +// +// Parameters: +// +// Input, int SUBORDER_NUM, the number of suborders of the rule. +// +// Output, double SUBORDER_XYZ[3*SUBORDER_NUM], +// the barycentric coordinates of the abscissas. +// +// Output, double SUBORDER_W[SUBORDER_NUM], the suborder weights. +// +{ + int s; + double suborder_xy_rule_03[3 * 2] = { 0.333333333333333, 0.333333333333333, 0.333333333333333, + 0.600000000000000, 0.200000000000000, 0.200000000000000 }; + double suborder_w_rule_03[2] = { -0.562500000000000, 0.520833333333333 }; + + for (s = 0; s < suborder_num; s++) { + suborder_xyz[0 + s * 3] = suborder_xy_rule_03[0 + s * 3]; + suborder_xyz[1 + s * 3] = suborder_xy_rule_03[1 + s * 3]; + suborder_xyz[2 + s * 3] = suborder_xy_rule_03[2 + s * 3]; + } + + for (s = 0; s < suborder_num; s++) { + suborder_w[s] = suborder_w_rule_03[s]; + } + + return; +} +//****************************************************************************80 + +void +dunavant_subrule_04 (int suborder_num, double suborder_xyz[], double suborder_w[]) + +//****************************************************************************80 +// +// Purpose: +// +// DUNAVANT_SUBRULE_04 returns a compressed Dunavant rule 4. +// +// Licensing: +// +// This code is distributed under the GNU LGPL license. +// +// Modified: +// +// 11 December 2006 +// +// Author: +// +// John Burkardt +// +// Reference: +// +// David Dunavant, +// High Degree Efficient Symmetrical Gaussian Quadrature Rules +// for the Triangle, +// International Journal for Numerical Methods in Engineering, +// Volume 21, 1985, pages 1129-1148. +// +// James Lyness, Dennis Jespersen, +// Moderate Degree Symmetric Quadrature Rules for the Triangle, +// Journal of the Institute of Mathematics and its Applications, +// Volume 15, Number 1, February 1975, pages 19-32. +// +// Parameters: +// +// Input, int SUBORDER_NUM, the number of suborders of the rule. +// +// Output, double SUBORDER_XYZ[3*SUBORDER_NUM], +// the barycentric coordinates of the abscissas. +// +// Output, double SUBORDER_W[SUBORDER_NUM], the suborder weights. +// +{ + int s; + double suborder_xy_rule_04[3 * 2] = { 0.108103018168070, 0.445948490915965, 0.445948490915965, + 0.816847572980459, 0.091576213509771, 0.091576213509771 }; + double suborder_w_rule_04[2] = { 0.223381589678011, 0.109951743655322 }; + + for (s = 0; s < suborder_num; s++) { + suborder_xyz[0 + s * 3] = suborder_xy_rule_04[0 + s * 3]; + suborder_xyz[1 + s * 3] = suborder_xy_rule_04[1 + s * 3]; + suborder_xyz[2 + s * 3] = suborder_xy_rule_04[2 + s * 3]; + } + + for (s = 0; s < suborder_num; s++) { + suborder_w[s] = suborder_w_rule_04[s]; + } + + return; +} +//****************************************************************************80 + +void +dunavant_subrule_05 (int suborder_num, double suborder_xyz[], double suborder_w[]) + +//****************************************************************************80 +// +// Purpose: +// +// DUNAVANT_SUBRULE_05 returns a compressed Dunavant rule 5. +// +// Licensing: +// +// This code is distributed under the GNU LGPL license. +// +// Modified: +// +// 11 December 2006 +// +// Author: +// +// John Burkardt +// +// Reference: +// +// David Dunavant, +// High Degree Efficient Symmetrical Gaussian Quadrature Rules +// for the Triangle, +// International Journal for Numerical Methods in Engineering, +// Volume 21, 1985, pages 1129-1148. +// +// James Lyness, Dennis Jespersen, +// Moderate Degree Symmetric Quadrature Rules for the Triangle, +// Journal of the Institute of Mathematics and its Applications, +// Volume 15, Number 1, February 1975, pages 19-32. +// +// Parameters: +// +// Input, int SUBORDER_NUM, the number of suborders of the rule. +// +// Output, double SUBORDER_XYZ[3*SUBORDER_NUM], +// the barycentric coordinates of the abscissas. +// +// Output, double SUBORDER_W[SUBORDER_NUM], the suborder weights. +// +{ + int s; + double suborder_xy_rule_05[3 * 3] + = { 0.333333333333333, 0.333333333333333, 0.333333333333333, 0.059715871789770, 0.470142064105115, + 0.470142064105115, 0.797426985353087, 0.101286507323456, 0.101286507323456 }; + double suborder_w_rule_05[3] = { 0.225000000000000, 0.132394152788506, 0.125939180544827 }; + + for (s = 0; s < suborder_num; s++) { + suborder_xyz[0 + s * 3] = suborder_xy_rule_05[0 + s * 3]; + suborder_xyz[1 + s * 3] = suborder_xy_rule_05[1 + s * 3]; + suborder_xyz[2 + s * 3] = suborder_xy_rule_05[2 + s * 3]; + } + + for (s = 0; s < suborder_num; s++) { + suborder_w[s] = suborder_w_rule_05[s]; + } + + return; +} +//****************************************************************************80 + +void +dunavant_subrule_06 (int suborder_num, double suborder_xyz[], double suborder_w[]) + +//****************************************************************************80 +// +// Purpose: +// +// DUNAVANT_SUBRULE_06 returns a compressed Dunavant rule 6. +// +// Licensing: +// +// This code is distributed under the GNU LGPL license. +// +// Modified: +// +// 11 December 2006 +// +// Author: +// +// John Burkardt +// +// Reference: +// +// David Dunavant, +// High Degree Efficient Symmetrical Gaussian Quadrature Rules +// for the Triangle, +// International Journal for Numerical Methods in Engineering, +// Volume 21, 1985, pages 1129-1148. +// +// James Lyness, Dennis Jespersen, +// Moderate Degree Symmetric Quadrature Rules for the Triangle, +// Journal of the Institute of Mathematics and its Applications, +// Volume 15, Number 1, February 1975, pages 19-32. +// +// Parameters: +// +// Input, int SUBORDER_NUM, the number of suborders of the rule. +// +// Output, double SUBORDER_XYZ[3*SUBORDER_NUM], +// the barycentric coordinates of the abscissas. +// +// Output, double SUBORDER_W[SUBORDER_NUM], the suborder weights. +// +{ + int s; + double suborder_xy_rule_06[3 * 3] + = { 0.501426509658179, 0.249286745170910, 0.249286745170910, 0.873821971016996, 0.063089014491502, + 0.063089014491502, 0.053145049844817, 0.310352451033784, 0.636502499121399 }; + double suborder_w_rule_06[3] = { 0.116786275726379, 0.050844906370207, 0.082851075618374 }; + + for (s = 0; s < suborder_num; s++) { + suborder_xyz[0 + s * 3] = suborder_xy_rule_06[0 + s * 3]; + suborder_xyz[1 + s * 3] = suborder_xy_rule_06[1 + s * 3]; + suborder_xyz[2 + s * 3] = suborder_xy_rule_06[2 + s * 3]; + } + + for (s = 0; s < suborder_num; s++) { + suborder_w[s] = suborder_w_rule_06[s]; + } + + return; +} +//****************************************************************************80 + +void +dunavant_subrule_07 (int suborder_num, double suborder_xyz[], double suborder_w[]) + +//****************************************************************************80 +// +// Purpose: +// +// DUNAVANT_SUBRULE_07 returns a compressed Dunavant rule 7. +// +// Licensing: +// +// This code is distributed under the GNU LGPL license. +// +// Modified: +// +// 11 December 2006 +// +// Author: +// +// John Burkardt +// +// Reference: +// +// David Dunavant, +// High Degree Efficient Symmetrical Gaussian Quadrature Rules +// for the Triangle, +// International Journal for Numerical Methods in Engineering, +// Volume 21, 1985, pages 1129-1148. +// +// James Lyness, Dennis Jespersen, +// Moderate Degree Symmetric Quadrature Rules for the Triangle, +// Journal of the Institute of Mathematics and its Applications, +// Volume 15, Number 1, February 1975, pages 19-32. +// +// Parameters: +// +// Input, int SUBORDER_NUM, the number of suborders of the rule. +// +// Output, double SUBORDER_XYZ[3*SUBORDER_NUM], +// the barycentric coordinates of the abscissas. +// +// Output, double SUBORDER_W[SUBORDER_NUM], the suborder weights. +// +{ + int s; + double suborder_xy_rule_07[3 * 4] = { 0.333333333333333, 0.333333333333333, 0.333333333333333, 0.479308067841920, + 0.260345966079040, 0.260345966079040, 0.869739794195568, 0.065130102902216, + 0.065130102902216, 0.048690315425316, 0.312865496004874, 0.638444188569810 }; + double suborder_w_rule_07[4] = { -0.149570044467682, 0.175615257433208, 0.053347235608838, 0.077113760890257 }; + + for (s = 0; s < suborder_num; s++) { + suborder_xyz[0 + s * 3] = suborder_xy_rule_07[0 + s * 3]; + suborder_xyz[1 + s * 3] = suborder_xy_rule_07[1 + s * 3]; + suborder_xyz[2 + s * 3] = suborder_xy_rule_07[2 + s * 3]; + } + + for (s = 0; s < suborder_num; s++) { + suborder_w[s] = suborder_w_rule_07[s]; + } + + return; +} +//****************************************************************************80 + +void +dunavant_subrule_08 (int suborder_num, double suborder_xyz[], double suborder_w[]) + +//****************************************************************************80 +// +// Purpose: +// +// DUNAVANT_SUBRULE_08 returns a compressed Dunavant rule 8. +// +// Licensing: +// +// This code is distributed under the GNU LGPL license. +// +// Modified: +// +// 11 December 2006 +// +// Author: +// +// John Burkardt +// +// Reference: +// +// David Dunavant, +// High Degree Efficient Symmetrical Gaussian Quadrature Rules +// for the Triangle, +// International Journal for Numerical Methods in Engineering, +// Volume 21, 1985, pages 1129-1148. +// +// James Lyness, Dennis Jespersen, +// Moderate Degree Symmetric Quadrature Rules for the Triangle, +// Journal of the Institute of Mathematics and its Applications, +// Volume 15, Number 1, February 1975, pages 19-32. +// +// Parameters: +// +// Input, int SUBORDER_NUM, the number of suborders of the rule. +// +// Output, double SUBORDER_XYZ[3*SUBORDER_NUM], +// the barycentric coordinates of the abscissas. +// +// Output, double SUBORDER_W[SUBORDER_NUM], the suborder weights. +// +{ + int s; + double suborder_xy_rule_08[3 * 5] + = { 0.333333333333333, 0.333333333333333, 0.333333333333333, 0.081414823414554, 0.459292588292723, + 0.459292588292723, 0.658861384496480, 0.170569307751760, 0.170569307751760, 0.898905543365938, + 0.050547228317031, 0.050547228317031, 0.008394777409958, 0.263112829634638, 0.728492392955404 }; + double suborder_w_rule_08[5] + = { 0.144315607677787, 0.095091634267285, 0.103217370534718, 0.032458497623198, 0.027230314174435 }; + + for (s = 0; s < suborder_num; s++) { + suborder_xyz[0 + s * 3] = suborder_xy_rule_08[0 + s * 3]; + suborder_xyz[1 + s * 3] = suborder_xy_rule_08[1 + s * 3]; + suborder_xyz[2 + s * 3] = suborder_xy_rule_08[2 + s * 3]; + } + + for (s = 0; s < suborder_num; s++) { + suborder_w[s] = suborder_w_rule_08[s]; + } + + return; +} +//****************************************************************************80 + +void +dunavant_subrule_09 (int suborder_num, double suborder_xyz[], double suborder_w[]) + +//****************************************************************************80 +// +// Purpose: +// +// DUNAVANT_SUBRULE_09 returns a compressed Dunavant rule 9. +// +// Licensing: +// +// This code is distributed under the GNU LGPL license. +// +// Modified: +// +// 11 December 2006 +// +// Author: +// +// John Burkardt +// +// Reference: +// +// David Dunavant, +// High Degree Efficient Symmetrical Gaussian Quadrature Rules +// for the Triangle, +// International Journal for Numerical Methods in Engineering, +// Volume 21, 1985, pages 1129-1148. +// +// James Lyness, Dennis Jespersen, +// Moderate Degree Symmetric Quadrature Rules for the Triangle, +// Journal of the Institute of Mathematics and its Applications, +// Volume 15, Number 1, February 1975, pages 19-32. +// +// Parameters: +// +// Input, int SUBORDER_NUM, the number of suborders of the rule. +// +// Output, double SUBORDER_XYZ[3*SUBORDER_NUM], +// the barycentric coordinates of the abscissas. +// +// Output, double SUBORDER_W[SUBORDER_NUM], the suborder weights. +// +{ + int s; + double suborder_xy_rule_09[3 * 6] + = { 0.333333333333333, 0.333333333333333, 0.333333333333333, 0.020634961602525, 0.489682519198738, + 0.489682519198738, 0.125820817014127, 0.437089591492937, 0.437089591492937, 0.623592928761935, + 0.188203535619033, 0.188203535619033, 0.910540973211095, 0.044729513394453, 0.044729513394453, + 0.036838412054736, 0.221962989160766, 0.741198598784498 }; + double suborder_w_rule_09[6] = { 0.097135796282799, 0.031334700227139, 0.077827541004774, + 0.079647738927210, 0.025577675658698, 0.043283539377289 }; + + for (s = 0; s < suborder_num; s++) { + suborder_xyz[0 + s * 3] = suborder_xy_rule_09[0 + s * 3]; + suborder_xyz[1 + s * 3] = suborder_xy_rule_09[1 + s * 3]; + suborder_xyz[2 + s * 3] = suborder_xy_rule_09[2 + s * 3]; + } + + for (s = 0; s < suborder_num; s++) { + suborder_w[s] = suborder_w_rule_09[s]; + } + + return; +} +//****************************************************************************80 + +void +dunavant_subrule_10 (int suborder_num, double suborder_xyz[], double suborder_w[]) + +//****************************************************************************80 +// +// Purpose: +// +// DUNAVANT_SUBRULE_10 returns a compressed Dunavant rule 10. +// +// Licensing: +// +// This code is distributed under the GNU LGPL license. +// +// Modified: +// +// 11 December 2006 +// +// Author: +// +// John Burkardt +// +// Reference: +// +// David Dunavant, +// High Degree Efficient Symmetrical Gaussian Quadrature Rules +// for the Triangle, +// International Journal for Numerical Methods in Engineering, +// Volume 21, 1985, pages 1129-1148. +// +// James Lyness, Dennis Jespersen, +// Moderate Degree Symmetric Quadrature Rules for the Triangle, +// Journal of the Institute of Mathematics and its Applications, +// Volume 15, Number 1, February 1975, pages 19-32. +// +// Parameters: +// +// Input, int SUBORDER_NUM, the number of suborders of the rule. +// +// Output, double SUBORDER_XYZ[3*SUBORDER_NUM], +// the barycentric coordinates of the abscissas. +// +// Output, double SUBORDER_W[SUBORDER_NUM], the suborder weights. +// +{ + int s; + double suborder_xy_rule_10[3 * 6] + = { 0.333333333333333, 0.333333333333333, 0.333333333333333, 0.028844733232685, 0.485577633383657, + 0.485577633383657, 0.781036849029926, 0.109481575485037, 0.109481575485037, 0.141707219414880, + 0.307939838764121, 0.550352941820999, 0.025003534762686, 0.246672560639903, 0.728323904597411, + 0.009540815400299, 0.066803251012200, 0.923655933587500 }; + double suborder_w_rule_10[6] = { 0.090817990382754, 0.036725957756467, 0.045321059435528, + 0.072757916845420, 0.028327242531057, 0.009421666963733 }; + + for (s = 0; s < suborder_num; s++) { + suborder_xyz[0 + s * 3] = suborder_xy_rule_10[0 + s * 3]; + suborder_xyz[1 + s * 3] = suborder_xy_rule_10[1 + s * 3]; + suborder_xyz[2 + s * 3] = suborder_xy_rule_10[2 + s * 3]; + } + + for (s = 0; s < suborder_num; s++) { + suborder_w[s] = suborder_w_rule_10[s]; + } + + return; +} +//****************************************************************************80 + +void +dunavant_subrule_11 (int suborder_num, double suborder_xyz[], double suborder_w[]) + +//****************************************************************************80 +// +// Purpose: +// +// DUNAVANT_SUBRULE_11 returns a compressed Dunavant rule 11. +// +// Licensing: +// +// This code is distributed under the GNU LGPL license. +// +// Modified: +// +// 11 December 2006 +// +// Author: +// +// John Burkardt +// +// Reference: +// +// David Dunavant, +// High Degree Efficient Symmetrical Gaussian Quadrature Rules +// for the Triangle, +// International Journal for Numerical Methods in Engineering, +// Volume 21, 1985, pages 1129-1148. +// +// James Lyness, Dennis Jespersen, +// Moderate Degree Symmetric Quadrature Rules for the Triangle, +// Journal of the Institute of Mathematics and its Applications, +// Volume 15, Number 1, February 1975, pages 19-32. +// +// Parameters: +// +// Input, int SUBORDER_NUM, the number of suborders of the rule. +// +// Output, double SUBORDER_XYZ[3*SUBORDER_NUM], +// the barycentric coordinates of the abscissas. +// +// Output, double SUBORDER_W[SUBORDER_NUM], the suborder weights. +// +{ + int s; + double suborder_xy_rule_11[3 * 7] + = { -0.069222096541517, 0.534611048270758, 0.534611048270758, 0.202061394068290, 0.398969302965855, + 0.398969302965855, 0.593380199137435, 0.203309900431282, 0.203309900431282, 0.761298175434837, + 0.119350912282581, 0.119350912282581, 0.935270103777448, 0.032364948111276, 0.032364948111276, + 0.050178138310495, 0.356620648261293, 0.593201213428213, 0.021022016536166, 0.171488980304042, + 0.807489003159792 }; + double suborder_w_rule_11[7] = { 0.000927006328961, 0.077149534914813, 0.059322977380774, 0.036184540503418, + 0.013659731002678, 0.052337111962204, 0.020707659639141 }; + + for (s = 0; s < suborder_num; s++) { + suborder_xyz[0 + s * 3] = suborder_xy_rule_11[0 + s * 3]; + suborder_xyz[1 + s * 3] = suborder_xy_rule_11[1 + s * 3]; + suborder_xyz[2 + s * 3] = suborder_xy_rule_11[2 + s * 3]; + } + + for (s = 0; s < suborder_num; s++) { + suborder_w[s] = suborder_w_rule_11[s]; + } + + return; +} +//****************************************************************************80 + +void +dunavant_subrule_12 (int suborder_num, double suborder_xyz[], double suborder_w[]) + +//****************************************************************************80 +// +// Purpose: +// +// DUNAVANT_SUBRULE_12 returns a compressed Dunavant rule 12. +// +// Licensing: +// +// This code is distributed under the GNU LGPL license. +// +// Modified: +// +// 11 December 2006 +// +// Author: +// +// John Burkardt +// +// Reference: +// +// David Dunavant, +// High Degree Efficient Symmetrical Gaussian Quadrature Rules +// for the Triangle, +// International Journal for Numerical Methods in Engineering, +// Volume 21, 1985, pages 1129-1148. +// +// James Lyness, Dennis Jespersen, +// Moderate Degree Symmetric Quadrature Rules for the Triangle, +// Journal of the Institute of Mathematics and its Applications, +// Volume 15, Number 1, February 1975, pages 19-32. +// +// Parameters: +// +// Input, int SUBORDER_NUM, the number of suborders of the rule. +// +// Output, double SUBORDER_XYZ[3*SUBORDER_NUM], +// the barycentric coordinates of the abscissas. +// +// Output, double SUBORDER_W[SUBORDER_NUM], the suborder weights. +// +{ + int s; + double suborder_xy_rule_12[3 * 8] + = { 0.023565220452390, 0.488217389773805, 0.488217389773805, 0.120551215411079, 0.439724392294460, + 0.439724392294460, 0.457579229975768, 0.271210385012116, 0.271210385012116, 0.744847708916828, + 0.127576145541586, 0.127576145541586, 0.957365299093579, 0.021317350453210, 0.021317350453210, + 0.115343494534698, 0.275713269685514, 0.608943235779788, 0.022838332222257, 0.281325580989940, + 0.695836086787803, 0.025734050548330, 0.116251915907597, 0.858014033544073 }; + double suborder_w_rule_12[8] = { 0.025731066440455, 0.043692544538038, 0.062858224217885, 0.034796112930709, + 0.006166261051559, 0.040371557766381, 0.022356773202303, 0.017316231108659 }; + + for (s = 0; s < suborder_num; s++) { + suborder_xyz[0 + s * 3] = suborder_xy_rule_12[0 + s * 3]; + suborder_xyz[1 + s * 3] = suborder_xy_rule_12[1 + s * 3]; + suborder_xyz[2 + s * 3] = suborder_xy_rule_12[2 + s * 3]; + } + + for (s = 0; s < suborder_num; s++) { + suborder_w[s] = suborder_w_rule_12[s]; + } + + return; +} +//****************************************************************************80 + +void +dunavant_subrule_13 (int suborder_num, double suborder_xyz[], double suborder_w[]) + +//****************************************************************************80 +// +// Purpose: +// +// DUNAVANT_SUBRULE_13 returns a compressed Dunavant rule 13. +// +// Licensing: +// +// This code is distributed under the GNU LGPL license. +// +// Modified: +// +// 11 December 2006 +// +// Author: +// +// John Burkardt +// +// Reference: +// +// David Dunavant, +// High Degree Efficient Symmetrical Gaussian Quadrature Rules +// for the Triangle, +// International Journal for Numerical Methods in Engineering, +// Volume 21, 1985, pages 1129-1148. +// +// James Lyness, Dennis Jespersen, +// Moderate Degree Symmetric Quadrature Rules for the Triangle, +// Journal of the Institute of Mathematics and its Applications, +// Volume 15, Number 1, February 1975, pages 19-32. +// +// Parameters: +// +// Input, int SUBORDER_NUM, the number of suborders of the rule. +// +// Output, double SUBORDER_XYZ[3*SUBORDER_NUM], +// the barycentric coordinates of the abscissas. +// +// Output, double SUBORDER_W[SUBORDER_NUM], the suborder weights. +// +{ + int s; + double suborder_xy_rule_13[3 * 10] + = { 0.333333333333333, 0.333333333333333, 0.333333333333333, 0.009903630120591, 0.495048184939705, + 0.495048184939705, 0.062566729780852, 0.468716635109574, 0.468716635109574, 0.170957326397447, + 0.414521336801277, 0.414521336801277, 0.541200855914337, 0.229399572042831, 0.229399572042831, + 0.771151009607340, 0.114424495196330, 0.114424495196330, 0.950377217273082, 0.024811391363459, + 0.024811391363459, 0.094853828379579, 0.268794997058761, 0.636351174561660, 0.018100773278807, + 0.291730066734288, 0.690169159986905, 0.022233076674090, 0.126357385491669, 0.851409537834241 }; + double suborder_w_rule_13[10] + = { 0.052520923400802, 0.011280145209330, 0.031423518362454, 0.047072502504194, 0.047363586536355, + 0.031167529045794, 0.007975771465074, 0.036848402728732, 0.017401463303822, 0.015521786839045 }; + + for (s = 0; s < suborder_num; s++) { + suborder_xyz[0 + s * 3] = suborder_xy_rule_13[0 + s * 3]; + suborder_xyz[1 + s * 3] = suborder_xy_rule_13[1 + s * 3]; + suborder_xyz[2 + s * 3] = suborder_xy_rule_13[2 + s * 3]; + } + + for (s = 0; s < suborder_num; s++) { + suborder_w[s] = suborder_w_rule_13[s]; + } + + return; +} +//****************************************************************************80 + +void +dunavant_subrule_14 (int suborder_num, double suborder_xyz[], double suborder_w[]) + +//****************************************************************************80 +// +// Purpose: +// +// DUNAVANT_SUBRULE_14 returns a compressed Dunavant rule 14. +// +// Licensing: +// +// This code is distributed under the GNU LGPL license. +// +// Modified: +// +// 11 December 2006 +// +// Author: +// +// John Burkardt +// +// Reference: +// +// David Dunavant, +// High Degree Efficient Symmetrical Gaussian Quadrature Rules +// for the Triangle, +// International Journal for Numerical Methods in Engineering, +// Volume 21, 1985, pages 1129-1148. +// +// James Lyness, Dennis Jespersen, +// Moderate Degree Symmetric Quadrature Rules for the Triangle, +// Journal of the Institute of Mathematics and its Applications, +// Volume 15, Number 1, February 1975, pages 19-32. +// +// Parameters: +// +// Input, int SUBORDER_NUM, the number of suborders of the rule. +// +// Output, double SUBORDER_XYZ[3*SUBORDER_NUM], +// the barycentric coordinates of the abscissas. +// +// Output, double SUBORDER_W[SUBORDER_NUM], the suborder weights. +// +{ + int s; + double suborder_xy_rule_14[3 * 10] + = { 0.022072179275643, 0.488963910362179, 0.488963910362179, 0.164710561319092, 0.417644719340454, + 0.417644719340454, 0.453044943382323, 0.273477528308839, 0.273477528308839, 0.645588935174913, + 0.177205532412543, 0.177205532412543, 0.876400233818255, 0.061799883090873, 0.061799883090873, + 0.961218077502598, 0.019390961248701, 0.019390961248701, 0.057124757403648, 0.172266687821356, + 0.770608554774996, 0.092916249356972, 0.336861459796345, 0.570222290846683, 0.014646950055654, + 0.298372882136258, 0.686980167808088, 0.001268330932872, 0.118974497696957, 0.879757171370171 }; + double suborder_w_rule_14[10] + = { 0.021883581369429, 0.032788353544125, 0.051774104507292, 0.042162588736993, 0.014433699669777, + 0.004923403602400, 0.024665753212564, 0.038571510787061, 0.014436308113534, 0.005010228838501 }; + + for (s = 0; s < suborder_num; s++) { + suborder_xyz[0 + s * 3] = suborder_xy_rule_14[0 + s * 3]; + suborder_xyz[1 + s * 3] = suborder_xy_rule_14[1 + s * 3]; + suborder_xyz[2 + s * 3] = suborder_xy_rule_14[2 + s * 3]; + } + + for (s = 0; s < suborder_num; s++) { + suborder_w[s] = suborder_w_rule_14[s]; + } + + return; +} +//****************************************************************************80 + +void +dunavant_subrule_15 (int suborder_num, double suborder_xyz[], double suborder_w[]) + +//****************************************************************************80 +// +// Purpose: +// +// DUNAVANT_SUBRULE_15 returns a compressed Dunavant rule 15. +// +// Licensing: +// +// This code is distributed under the GNU LGPL license. +// +// Modified: +// +// 11 December 2006 +// +// Author: +// +// John Burkardt +// +// Reference: +// +// David Dunavant, +// High Degree Efficient Symmetrical Gaussian Quadrature Rules +// for the Triangle, +// International Journal for Numerical Methods in Engineering, +// Volume 21, 1985, pages 1129-1148. +// +// James Lyness, Dennis Jespersen, +// Moderate Degree Symmetric Quadrature Rules for the Triangle, +// Journal of the Institute of Mathematics and its Applications, +// Volume 15, Number 1, February 1975, pages 19-32. +// +// Parameters: +// +// Input, int SUBORDER_NUM, the number of suborders of the rule. +// +// Output, double SUBORDER_XYZ[3*SUBORDER_NUM], +// the barycentric coordinates of the abscissas. +// +// Output, double SUBORDER_W[SUBORDER_NUM], the suborder weights. +// +{ + int s; + double suborder_xy_rule_15[3 * 11] + = { -0.013945833716486, 0.506972916858243, 0.506972916858243, 0.137187291433955, 0.431406354283023, + 0.431406354283023, 0.444612710305711, 0.277693644847144, 0.277693644847144, 0.747070217917492, + 0.126464891041254, 0.126464891041254, 0.858383228050628, 0.070808385974686, 0.070808385974686, + 0.962069659517853, 0.018965170241073, 0.018965170241073, 0.133734161966621, 0.261311371140087, + 0.604954466893291, 0.036366677396917, 0.388046767090269, 0.575586555512814, -0.010174883126571, + 0.285712220049916, 0.724462663076655, 0.036843869875878, 0.215599664072284, 0.747556466051838, + 0.012459809331199, 0.103575616576386, 0.883964574092416 }; + double suborder_w_rule_15[11] = { 0.001916875642849, 0.044249027271145, 0.051186548718852, 0.023687735870688, + 0.013289775690021, 0.004748916608192, 0.038550072599593, 0.027215814320624, + 0.002182077366797, 0.021505319847731, 0.007673942631049 }; + + for (s = 0; s < suborder_num; s++) { + suborder_xyz[0 + s * 3] = suborder_xy_rule_15[0 + s * 3]; + suborder_xyz[1 + s * 3] = suborder_xy_rule_15[1 + s * 3]; + suborder_xyz[2 + s * 3] = suborder_xy_rule_15[2 + s * 3]; + } + + for (s = 0; s < suborder_num; s++) { + suborder_w[s] = suborder_w_rule_15[s]; + } + + return; +} +//****************************************************************************80 + +void +dunavant_subrule_16 (int suborder_num, double suborder_xyz[], double suborder_w[]) + +//****************************************************************************80 +// +// Purpose: +// +// DUNAVANT_SUBRULE_16 returns a compressed Dunavant rule 16. +// +// Licensing: +// +// This code is distributed under the GNU LGPL license. +// +// Modified: +// +// 11 December 2006 +// +// Author: +// +// John Burkardt +// +// Reference: +// +// David Dunavant, +// High Degree Efficient Symmetrical Gaussian Quadrature Rules +// for the Triangle, +// International Journal for Numerical Methods in Engineering, +// Volume 21, 1985, pages 1129-1148. +// +// James Lyness, Dennis Jespersen, +// Moderate Degree Symmetric Quadrature Rules for the Triangle, +// Journal of the Institute of Mathematics and its Applications, +// Volume 15, Number 1, February 1975, pages 19-32. +// +// Parameters: +// +// Input, int SUBORDER_NUM, the number of suborders of the rule. +// +// Output, double SUBORDER_XYZ[3*SUBORDER_NUM], +// the barycentric coordinates of the abscissas. +// +// Output, double SUBORDER_W[SUBORDER_NUM], the suborder weights. +// +{ + int s; + double suborder_xy_rule_16[3 * 13] + = { 0.333333333333333, 0.333333333333333, 0.333333333333333, 0.005238916103123, 0.497380541948438, + 0.497380541948438, 0.173061122901295, 0.413469438549352, 0.413469438549352, 0.059082801866017, + 0.470458599066991, 0.470458599066991, 0.518892500060958, 0.240553749969521, 0.240553749969521, + 0.704068411554854, 0.147965794222573, 0.147965794222573, 0.849069624685052, 0.075465187657474, + 0.075465187657474, 0.966807194753950, 0.016596402623025, 0.016596402623025, 0.103575692245252, + 0.296555596579887, 0.599868711174861, 0.020083411655416, 0.337723063403079, 0.642193524941505, + -0.004341002614139, 0.204748281642812, 0.799592720971327, 0.041941786468010, 0.189358492130623, + 0.768699721401368, 0.014317320230681, 0.085283615682657, 0.900399064086661 }; + double suborder_w_rule_16[13] + = { 0.046875697427642, 0.006405878578585, 0.041710296739387, 0.026891484250064, 0.042132522761650, + 0.030000266842773, 0.014200098925024, 0.003582462351273, 0.032773147460627, 0.015298306248441, + 0.002386244192839, 0.019084792755899, 0.006850054546542 }; + + for (s = 0; s < suborder_num; s++) { + suborder_xyz[0 + s * 3] = suborder_xy_rule_16[0 + s * 3]; + suborder_xyz[1 + s * 3] = suborder_xy_rule_16[1 + s * 3]; + suborder_xyz[2 + s * 3] = suborder_xy_rule_16[2 + s * 3]; + } + + for (s = 0; s < suborder_num; s++) { + suborder_w[s] = suborder_w_rule_16[s]; + } + + return; +} +//****************************************************************************80 + +void +dunavant_subrule_17 (int suborder_num, double suborder_xyz[], double suborder_w[]) + +//****************************************************************************80 +// +// Purpose: +// +// DUNAVANT_SUBRULE_17 returns a compressed Dunavant rule 17. +// +// Licensing: +// +// This code is distributed under the GNU LGPL license. +// +// Modified: +// +// 11 December 2006 +// +// Author: +// +// John Burkardt +// +// Reference: +// +// David Dunavant, +// High Degree Efficient Symmetrical Gaussian Quadrature Rules +// for the Triangle, +// International Journal for Numerical Methods in Engineering, +// Volume 21, 1985, pages 1129-1148. +// +// James Lyness, Dennis Jespersen, +// Moderate Degree Symmetric Quadrature Rules for the Triangle, +// Journal of the Institute of Mathematics and its Applications, +// Volume 15, Number 1, February 1975, pages 19-32. +// +// Parameters: +// +// Input, int SUBORDER_NUM, the number of suborders of the rule. +// +// Output, double SUBORDER_XYZ[3*SUBORDER_NUM], +// the barycentric coordinates of the abscissas. +// +// Output, double SUBORDER_W[SUBORDER_NUM], the suborder weights. +// +{ + int s; + double suborder_xy_rule_17[3 * 15] + = { 0.333333333333333, 0.333333333333333, 0.333333333333333, 0.005658918886452, 0.497170540556774, + 0.497170540556774, 0.035647354750751, 0.482176322624625, 0.482176322624625, 0.099520061958437, + 0.450239969020782, 0.450239969020782, 0.199467521245206, 0.400266239377397, 0.400266239377397, + 0.495717464058095, 0.252141267970953, 0.252141267970953, 0.675905990683077, 0.162047004658461, + 0.162047004658461, 0.848248235478508, 0.075875882260746, 0.075875882260746, 0.968690546064356, + 0.015654726967822, 0.015654726967822, 0.010186928826919, 0.334319867363658, 0.655493203809423, + 0.135440871671036, 0.292221537796944, 0.572337590532020, 0.054423924290583, 0.319574885423190, + 0.626001190286228, 0.012868560833637, 0.190704224192292, 0.796427214974071, 0.067165782413524, + 0.180483211648746, 0.752351005937729, 0.014663182224828, 0.080711313679564, 0.904625504095608 }; + double suborder_w_rule_17[15] + = { 0.033437199290803, 0.005093415440507, 0.014670864527638, 0.024350878353672, 0.031107550868969, + 0.031257111218620, 0.024815654339665, 0.014056073070557, 0.003194676173779, 0.008119655318993, + 0.026805742283163, 0.018459993210822, 0.008476868534328, 0.018292796770025, 0.006665632004165 }; + + for (s = 0; s < suborder_num; s++) { + suborder_xyz[0 + s * 3] = suborder_xy_rule_17[0 + s * 3]; + suborder_xyz[1 + s * 3] = suborder_xy_rule_17[1 + s * 3]; + suborder_xyz[2 + s * 3] = suborder_xy_rule_17[2 + s * 3]; + } + + for (s = 0; s < suborder_num; s++) { + suborder_w[s] = suborder_w_rule_17[s]; + } + + return; +} +//****************************************************************************80 + +void +dunavant_subrule_18 (int suborder_num, double suborder_xyz[], double suborder_w[]) + +//****************************************************************************80 +// +// Purpose: +// +// DUNAVANT_SUBRULE_18 returns a compressed Dunavant rule 18. +// +// Licensing: +// +// This code is distributed under the GNU LGPL license. +// +// Modified: +// +// 11 December 2006 +// +// Author: +// +// John Burkardt +// +// Reference: +// +// David Dunavant, +// High Degree Efficient Symmetrical Gaussian Quadrature Rules +// for the Triangle, +// International Journal for Numerical Methods in Engineering, +// Volume 21, 1985, pages 1129-1148. +// +// James Lyness, Dennis Jespersen, +// Moderate Degree Symmetric Quadrature Rules for the Triangle, +// Journal of the Institute of Mathematics and its Applications, +// Volume 15, Number 1, February 1975, pages 19-32. +// +// Parameters: +// +// Input, int SUBORDER_NUM, the number of suborders of the rule. +// +// Output, double SUBORDER_XYZ[3*SUBORDER_NUM], +// the barycentric coordinates of the abscissas. +// +// Output, double SUBORDER_W[SUBORDER_NUM], the suborder weights. +// +{ + int s; + double suborder_xy_rule_18[3 * 17] = { + 0.333333333333333, 0.333333333333333, 0.333333333333333, 0.013310382738157, 0.493344808630921, 0.493344808630921, + 0.061578811516086, 0.469210594241957, 0.469210594241957, 0.127437208225989, 0.436281395887006, 0.436281395887006, + 0.210307658653168, 0.394846170673416, 0.394846170673416, 0.500410862393686, 0.249794568803157, 0.249794568803157, + 0.677135612512315, 0.161432193743843, 0.161432193743843, 0.846803545029257, 0.076598227485371, 0.076598227485371, + 0.951495121293100, 0.024252439353450, 0.024252439353450, 0.913707265566071, 0.043146367216965, 0.043146367216965, + 0.008430536202420, 0.358911494940944, 0.632657968856636, 0.131186551737188, 0.294402476751957, 0.574410971510855, + 0.050203151565675, 0.325017801641814, 0.624779046792512, 0.066329263810916, 0.184737559666046, 0.748933176523037, + 0.011996194566236, 0.218796800013321, 0.769207005420443, 0.014858100590125, 0.101179597136408, 0.883962302273467, + -0.035222015287949, 0.020874755282586, 1.014347260005363 + }; + double suborder_w_rule_18[17] + = { 0.030809939937647, 0.009072436679404, 0.018761316939594, 0.019441097985477, 0.027753948610810, + 0.032256225351457, 0.025074032616922, 0.015271927971832, 0.006793922022963, -0.002223098729920, + 0.006331914076406, 0.027257538049138, 0.017676785649465, 0.018379484638070, 0.008104732808192, + 0.007634129070725, 0.000046187660794 }; + + for (s = 0; s < suborder_num; s++) { + suborder_xyz[0 + s * 3] = suborder_xy_rule_18[0 + s * 3]; + suborder_xyz[1 + s * 3] = suborder_xy_rule_18[1 + s * 3]; + suborder_xyz[2 + s * 3] = suborder_xy_rule_18[2 + s * 3]; + } + + for (s = 0; s < suborder_num; s++) { + suborder_w[s] = suborder_w_rule_18[s]; + } + + return; +} +//****************************************************************************80 + +void +dunavant_subrule_19 (int suborder_num, double suborder_xyz[], double suborder_w[]) + +//****************************************************************************80 +// +// Purpose: +// +// DUNAVANT_SUBRULE_19 returns a compressed Dunavant rule 19. +// +// Licensing: +// +// This code is distributed under the GNU LGPL license. +// +// Modified: +// +// 11 December 2006 +// +// Author: +// +// John Burkardt +// +// Reference: +// +// David Dunavant, +// High Degree Efficient Symmetrical Gaussian Quadrature Rules +// for the Triangle, +// International Journal for Numerical Methods in Engineering, +// Volume 21, 1985, pages 1129-1148. +// +// James Lyness, Dennis Jespersen, +// Moderate Degree Symmetric Quadrature Rules for the Triangle, +// Journal of the Institute of Mathematics and its Applications, +// Volume 15, Number 1, February 1975, pages 19-32. +// +// Parameters: +// +// Input, int SUBORDER_NUM, the number of suborders of the rule. +// +// Output, double SUBORDER_XYZ[3*SUBORDER_NUM], +// the barycentric coordinates of the abscissas. +// +// Output, double SUBORDER_W[SUBORDER_NUM], the suborder weights. +// +{ + int s; + double suborder_xy_rule_19[3 * 17] = { + 0.333333333333333, 0.333333333333333, 0.333333333333333, 0.020780025853987, 0.489609987073006, 0.489609987073006, + 0.090926214604215, 0.454536892697893, 0.454536892697893, 0.197166638701138, 0.401416680649431, 0.401416680649431, + 0.488896691193805, 0.255551654403098, 0.255551654403098, 0.645844115695741, 0.177077942152130, 0.177077942152130, + 0.779877893544096, 0.110061053227952, 0.110061053227952, 0.888942751496321, 0.055528624251840, 0.055528624251840, + 0.974756272445543, 0.012621863777229, 0.012621863777229, 0.003611417848412, 0.395754787356943, 0.600633794794645, + 0.134466754530780, 0.307929983880436, 0.557603261588784, 0.014446025776115, 0.264566948406520, 0.720987025817365, + 0.046933578838178, 0.358539352205951, 0.594527068955871, 0.002861120350567, 0.157807405968595, 0.839331473680839, + 0.223861424097916, 0.075050596975911, 0.701087978926173, 0.034647074816760, 0.142421601113383, 0.822931324069857, + 0.010161119296278, 0.065494628082938, 0.924344252620784 + }; + double suborder_w_rule_19[17] + = { 0.032906331388919, 0.010330731891272, 0.022387247263016, 0.030266125869468, 0.030490967802198, + 0.024159212741641, 0.016050803586801, 0.008084580261784, 0.002079362027485, 0.003884876904981, + 0.025574160612022, 0.008880903573338, 0.016124546761731, 0.002491941817491, 0.018242840118951, + 0.010258563736199, 0.003799928855302 }; + + for (s = 0; s < suborder_num; s++) { + suborder_xyz[0 + s * 3] = suborder_xy_rule_19[0 + s * 3]; + suborder_xyz[1 + s * 3] = suborder_xy_rule_19[1 + s * 3]; + suborder_xyz[2 + s * 3] = suborder_xy_rule_19[2 + s * 3]; + } + + for (s = 0; s < suborder_num; s++) { + suborder_w[s] = suborder_w_rule_19[s]; + } + + return; +} +//****************************************************************************80 + +void +dunavant_subrule_20 (int suborder_num, double suborder_xyz[], double suborder_w[]) + +//****************************************************************************80 +// +// Purpose: +// +// DUNAVANT_SUBRULE_20 returns a compressed Dunavant rule 20. +// +// Licensing: +// +// This code is distributed under the GNU LGPL license. +// +// Modified: +// +// 11 December 2006 +// +// Author: +// +// John Burkardt +// +// Reference: +// +// David Dunavant, +// High Degree Efficient Symmetrical Gaussian Quadrature Rules +// for the Triangle, +// International Journal for Numerical Methods in Engineering, +// Volume 21, 1985, pages 1129-1148. +// +// James Lyness, Dennis Jespersen, +// Moderate Degree Symmetric Quadrature Rules for the Triangle, +// Journal of the Institute of Mathematics and its Applications, +// Volume 15, Number 1, February 1975, pages 19-32. +// +// Parameters: +// +// Input, int SUBORDER_NUM, the number of suborders of the rule. +// +// Output, double SUBORDER_XYZ[3*SUBORDER_NUM], +// the barycentric coordinates of the abscissas. +// +// Output, double SUBORDER_W[SUBORDER_NUM], the suborder weights. +// +{ + int s; + double suborder_xy_rule_20[3 * 19] = { + 0.333333333333333, 0.333333333333333, 0.333333333333333, -0.001900928704400, 0.500950464352200, 0.500950464352200, + 0.023574084130543, 0.488212957934729, 0.488212957934729, 0.089726636099435, 0.455136681950283, 0.455136681950283, + 0.196007481363421, 0.401996259318289, 0.401996259318289, 0.488214180481157, 0.255892909759421, 0.255892909759421, + 0.647023488009788, 0.176488255995106, 0.176488255995106, 0.791658289326483, 0.104170855336758, 0.104170855336758, + 0.893862072318140, 0.053068963840930, 0.053068963840930, 0.916762569607942, 0.041618715196029, 0.041618715196029, + 0.976836157186356, 0.011581921406822, 0.011581921406822, 0.048741583664839, 0.344855770229001, 0.606402646106160, + 0.006314115948605, 0.377843269594854, 0.615842614456541, 0.134316520547348, 0.306635479062357, 0.559048000390295, + 0.013973893962392, 0.249419362774742, 0.736606743262866, 0.075549132909764, 0.212775724802802, 0.711675142287434, + -0.008368153208227, 0.146965436053239, 0.861402717154987, 0.026686063258714, 0.137726978828923, 0.835586957912363, + 0.010547719294141, 0.059696109149007, 0.929756171556853 + }; + double suborder_w_rule_20[19] + = { 0.033057055541624, 0.000867019185663, 0.011660052716448, 0.022876936356421, 0.030448982673938, + 0.030624891725355, 0.024368057676800, 0.015997432032024, 0.007698301815602, -0.000632060497488, + 0.001751134301193, 0.016465839189576, 0.004839033540485, 0.025804906534650, 0.008471091054441, + 0.018354914106280, 0.000704404677908, 0.010112684927462, 0.003573909385950 }; + + for (s = 0; s < suborder_num; s++) { + suborder_xyz[0 + s * 3] = suborder_xy_rule_20[0 + s * 3]; + suborder_xyz[1 + s * 3] = suborder_xy_rule_20[1 + s * 3]; + suborder_xyz[2 + s * 3] = suborder_xy_rule_20[2 + s * 3]; + } + + for (s = 0; s < suborder_num; s++) { + suborder_w[s] = suborder_w_rule_20[s]; + } + + return; +} +//****************************************************************************80 + +void +file_name_inc (char *file_name) + +//****************************************************************************80 +// +// Purpose: +// +// FILE_NAME_INC increments a partially numeric file name. +// +// Discussion: +// +// It is assumed that the digits in the name, whether scattered or +// connected, represent a number that is to be increased by 1 on +// each call. If this number is all 9's on input, the output number +// is all 0's. Non-numeric letters of the name are unaffected. +// +// If the input string contains no digits, a blank string is returned. +// +// If a blank string is input, then an error condition results. +// +// Example: +// +// Input Output +// ----- ------ +// "a7to11.txt" "a7to12.txt" (typical case. Last digit incremented) +// "a7to99.txt" "a8to00.txt" (last digit incremented, with carry.) +// "a9to99.txt" "a0to00.txt" (wrap around) +// "cat.txt" " " (no digits to increment) +// " " STOP! (error) +// +// Licensing: +// +// This code is distributed under the GNU LGPL license. +// +// Modified: +// +// 14 September 2005 +// +// Author: +// +// John Burkardt +// +// Parameters: +// +// Input/output, character *FILE_NAME, (a pointer to) the character string +// to be incremented. +// +{ + char c; + int change; + int i; + int lens; + + lens = s_len_trim (file_name); + + if (lens <= 0) { + cout << "\n"; + cout << "FILE_NAME_INC - Fatal error!\n"; + cout << " Input file name is blank.\n"; + exit (1); + } + + change = 0; + + for (i = lens - 1; 0 <= i; i--) { + c = *(file_name + i); + + if ('0' <= c && c <= '9') { + change = change + 1; + if (c == '9') { + c = '0'; + *(file_name + i) = c; + } + else { + c = c + 1; + *(file_name + i) = c; + return; + } + } + } + + if (change == 0) { + strcpy (file_name, " "); + } + + return; +} +//****************************************************************************80 + +int +i4_max (int i1, int i2) + +//****************************************************************************80 +// +// Purpose: +// +// I4_MAX returns the maximum of two I4's. +// +// Licensing: +// +// This code is distributed under the GNU LGPL license. +// +// Modified: +// +// 13 October 1998 +// +// Author: +// +// John Burkardt +// +// Parameters: +// +// Input, int I1, I2, are two integers to be compared. +// +// Output, int I4_MAX, the larger of I1 and I2. +// +{ + int value; + + if (i2 < i1) { + value = i1; + } + else { + value = i2; + } + return value; +} +//****************************************************************************80 + +int +i4_min (int i1, int i2) + +//****************************************************************************80 +// +// Purpose: +// +// I4_MIN returns the smaller of two I4's. +// +// Licensing: +// +// This code is distributed under the GNU LGPL license. +// +// Modified: +// +// 13 October 1998 +// +// Author: +// +// John Burkardt +// +// Parameters: +// +// Input, int I1, I2, two integers to be compared. +// +// Output, int I4_MIN, the smaller of I1 and I2. +// +{ + int value; + + if (i1 < i2) { + value = i1; + } + else { + value = i2; + } + return value; +} +//****************************************************************************80 + +int +i4_modp (int i, int j) + +//****************************************************************************80 +// +// Purpose: +// +// I4_MODP returns the nonnegative remainder of I4 division. +// +// Formula: +// +// If +// NREM = I4_MODP ( I, J ) +// NMULT = ( I - NREM ) / J +// then +// I = J * NMULT + NREM +// where NREM is always nonnegative. +// +// Discussion: +// +// The MOD function computes a result with the same sign as the +// quantity being divided. Thus, suppose you had an angle A, +// and you wanted to ensure that it was between 0 and 360. +// Then mod(A,360) would do, if A was positive, but if A +// was negative, your result would be between -360 and 0. +// +// On the other hand, I4_MODP(A,360) is between 0 and 360, always. +// +// Example: +// +// I J MOD I4_MODP I4_MODP Factorization +// +// 107 50 7 7 107 = 2 * 50 + 7 +// 107 -50 7 7 107 = -2 * -50 + 7 +// -107 50 -7 43 -107 = -3 * 50 + 43 +// -107 -50 -7 43 -107 = 3 * -50 + 43 +// +// Licensing: +// +// This code is distributed under the GNU LGPL license. +// +// Modified: +// +// 26 May 1999 +// +// Author: +// +// John Burkardt +// +// Parameters: +// +// Input, int I, the number to be divided. +// +// Input, int J, the number that divides I. +// +// Output, int I4_MODP, the nonnegative remainder when I is +// divided by J. +// +{ + int value; + + if (j == 0) { + cout << "\n"; + cout << "I4_MODP - Fatal error!\n"; + cout << " I4_MODP ( I, J ) called with J = " << j << "\n"; + exit (1); + } + + value = i % j; + + if (value < 0) { + value = value + abs (j); + } + + return value; +} +//****************************************************************************80* + +int +i4_wrap (int ival, int ilo, int ihi) + +//****************************************************************************80* +// +// Purpose: +// +// I4_WRAP forces an integer to lie between given limits by wrapping. +// +// Example: +// +// ILO = 4, IHI = 8 +// +// I Value +// +// -2 8 +// -1 4 +// 0 5 +// 1 6 +// 2 7 +// 3 8 +// 4 4 +// 5 5 +// 6 6 +// 7 7 +// 8 8 +// 9 4 +// 10 5 +// 11 6 +// 12 7 +// 13 8 +// 14 4 +// +// Licensing: +// +// This code is distributed under the GNU LGPL license. +// +// Modified: +// +// 19 August 2003 +// +// Author: +// +// John Burkardt +// +// Parameters: +// +// Input, int IVAL, an integer value. +// +// Input, int ILO, IHI, the desired bounds for the integer value. +// +// Output, int I4_WRAP, a "wrapped" version of IVAL. +// +{ + int jhi; + int jlo; + int value; + int wide; + + jlo = i4_min (ilo, ihi); + jhi = i4_max (ilo, ihi); + + wide = jhi + 1 - jlo; + + if (wide == 1) { + value = jlo; + } + else { + value = jlo + i4_modp (ival - jlo, wide); + } + + return value; +} +//****************************************************************************80 + +double +r8_huge () + +//****************************************************************************80 +// +// Purpose: +// +// R8_HUGE returns a "huge" R8. +// +// Discussion: +// +// HUGE_VAL is the largest representable legal double precision number, +// and is usually defined in math.h, or sometimes in stdlib.h. +// +// Licensing: +// +// This code is distributed under the GNU LGPL license. +// +// Modified: +// +// 31 August 2004 +// +// Author: +// +// John Burkardt +// +// Parameters: +// +// Output, double R8_HUGE, a "huge" R8 value. +// +{ + return HUGE_VAL; +} +//****************************************************************************80 + +int +r8_nint (double x) + +//****************************************************************************80 +// +// Purpose: +// +// R8_NINT returns the nearest integer to an R8. +// +// Example: +// +// X Value +// +// 1.3 1 +// 1.4 1 +// 1.5 1 or 2 +// 1.6 2 +// 0.0 0 +// -0.7 -1 +// -1.1 -1 +// -1.6 -2 +// +// Licensing: +// +// This code is distributed under the GNU LGPL license. +// +// Modified: +// +// 26 August 2004 +// +// Author: +// +// John Burkardt +// +// Parameters: +// +// Input, double X, the value. +// +// Output, int R8_NINT, the nearest integer to X. +// +{ + int s; + int value; + + if (x < 0.0) { + s = -1; + } + else { + s = 1; + } + value = s * (int) (fabs (x) + 0.5); + + return value; +} +//****************************************************************************80 + +void +reference_to_physical_t3 (double t[], int n, double ref[], double phy[]) + +//****************************************************************************80 +// +// Purpose: +// +// REFERENCE_TO_PHYSICAL_T3 maps T3 reference points to physical points. +// +// Discussion: +// +// Given the vertices of an order 3 physical triangle and a point +// (XSI,ETA) in the reference triangle, the routine computes the value +// of the corresponding image point (X,Y) in physical space. +// +// Note that this routine may also be appropriate for an order 6 +// triangle, if the mapping between reference and physical space +// is linear. This implies, in particular, that the sides of the +// image triangle are straight and that the "midside" nodes in the +// physical triangle are literally halfway along the sides of +// the physical triangle. +// +// Reference Element T3: +// +// | +// 1 3 +// | |\ +// | | \ +// S | \ +// | | \ +// | | \ +// 0 1-----2 +// | +// +--0--R--1--> +// +// Licensing: +// +// This code is distributed under the GNU LGPL license. +// +// Modified: +// +// 24 June 2005 +// +// Author: +// +// John Burkardt +// +// Parameters: +// +// Input, double T[2*3], the coordinates of the vertices. +// The vertices are assumed to be the images of (0,0), (1,0) and +// (0,1) respectively. +// +// Input, int N, the number of objects to transform. +// +// Input, double REF[2*N], points in the reference triangle. +// +// Output, double PHY[2*N], corresponding points in the +// physical triangle. +// +{ + int i; + int j; + + for (i = 0; i < 2; i++) { + for (j = 0; j < n; j++) { + phy[i + j * 2] = t[i + 0 * 2] * (1.0 - ref[0 + j * 2] - ref[1 + j * 2]) + t[i + 1 * 2] * +ref[0 + j * 2] + + t[i + 2 * 2] * +ref[1 + j * 2]; + } + } + + return; +} +//****************************************************************************80 + +int +s_len_trim (char *s) + +//****************************************************************************80 +// +// Purpose: +// +// S_LEN_TRIM returns the length of a string to the last nonblank. +// +// Licensing: +// +// This code is distributed under the GNU LGPL license. +// +// Modified: +// +// 26 April 2003 +// +// Author: +// +// John Burkardt +// +// Parameters: +// +// Input, char *S, a pointer to a string. +// +// Output, int S_LEN_TRIM, the length of the string to the last nonblank. +// If S_LEN_TRIM is 0, then the string is entirely blank. +// +{ + int n; + char *t; + + n = strlen (s); + t = s + strlen (s) - 1; + + while (0 < n) { + if (*t != ' ') { + return n; + } + t--; + n--; + } + + return n; +} +//****************************************************************************80 + +void +timestamp () + +//****************************************************************************80 +// +// Purpose: +// +// TIMESTAMP prints the current YMDHMS date as a time stamp. +// +// Example: +// +// 31 May 2001 09:45:54 AM +// +// Licensing: +// +// This code is distributed under the GNU LGPL license. +// +// Modified: +// +// 24 September 2003 +// +// Author: +// +// John Burkardt +// +// Parameters: +// +// None +// +{ +#define TIME_SIZE 40 + + static char time_buffer[TIME_SIZE]; + const struct tm *tm; + size_t len; + time_t now; + + now = time (NULL); + tm = localtime (&now); + + len = strftime (time_buffer, TIME_SIZE, "%d %B %Y %I:%M:%S %p", tm); + + cout << time_buffer << "\n"; + + return; +#undef TIME_SIZE +} +//****************************************************************************80 + +char * +timestring () + +//****************************************************************************80 +// +// Purpose: +// +// TIMESTRING returns the current YMDHMS date as a string. +// +// Example: +// +// 31 May 2001 09:45:54 AM +// +// Licensing: +// +// This code is distributed under the GNU LGPL license. +// +// Modified: +// +// 24 September 2003 +// +// Author: +// +// John Burkardt +// +// Parameters: +// +// Output, char *TIMESTRING, a string containing the current YMDHMS date. +// +{ +#define TIME_SIZE 40 + + const struct tm *tm; + size_t len; + time_t now; + char *s; + + now = time (NULL); + tm = localtime (&now); + + s = new char[TIME_SIZE]; + + len = strftime (s, TIME_SIZE, "%d %B %Y %I:%M:%S %p", tm); + + return s; +#undef TIME_SIZE +} +//****************************************************************************80 + +double +triangle_area (double t[2 * 3]) + +//****************************************************************************80 +// +// Purpose: +// +// TRIANGLE_AREA computes the area of a triangle. +// +// Discussion: +// +// If the triangle's vertices are given in counter clockwise order, +// the area will be positive. If the triangle's vertices are given +// in clockwise order, the area will be negative! +// +// An earlier version of this routine always returned the absolute +// value of the computed area. I am convinced now that that is +// a less useful result! For instance, by returning the signed +// area of a triangle, it is possible to easily compute the area +// of a nonconvex polygon as the sum of the (possibly negative) +// areas of triangles formed by node 1 and successive pairs of vertices. +// +// Licensing: +// +// This code is distributed under the GNU LGPL license. +// +// Modified: +// +// 17 October 2005 +// +// Author: +// +// John Burkardt +// +// Parameters: +// +// Input, double T[2*3], the vertices of the triangle. +// +// Output, double TRIANGLE_AREA, the area of the triangle. +// +{ + double area; + + area = 0.5 + * (t[0 + 0 * 2] * (t[1 + 1 * 2] - t[1 + 2 * 2]) + t[0 + 1 * 2] * (t[1 + 2 * 2] - t[1 + 0 * 2]) + + t[0 + 2 * 2] * (t[1 + 0 * 2] - t[1 + 1 * 2])); + + return area; +} +//****************************************************************************80 + +void +triangle_points_plot (char *file_name, double node_xy[], int node_show, int point_num, double point_xy[], + int point_show) + +//****************************************************************************80 +// +// Purpose: +// +// TRIANGLE_POINTS_PLOT plots a triangle and some points. +// +// Licensing: +// +// This code is distributed under the GNU LGPL license. +// +// Modified: +// +// 04 October 2006 +// +// Author: +// +// John Burkardt +// +// Parameters: +// +// Input, char *FILE_NAME, the name of the output file. +// +// Input, double NODE_XY[2*3], the coordinates of the nodes +// of the triangle. +// +// Input, int NODE_SHOW, +// -1, do not show the triangle, or the nodes. +// 0, show the triangle, do not show the nodes; +// 1, show the triangle and the nodes; +// 2, show the triangle, the nodes and number them. +// +// Input, int POINT_NUM, the number of points. +// +// Input, double POINT_XY[2*POINT_NUM], the coordinates of the +// points. +// +// Input, int POINT_SHOW, +// 0, do not show the points; +// 1, show the points; +// 2, show the points and number them. +// +{ + char *date_time; + int circle_size; + int delta; + int e; + ofstream file_unit; + int i; + int node; + int node_num = 3; + int point; + char string[40]; + double x_max; + double x_min; + int x_ps; + int x_ps_max = 576; + int x_ps_max_clip = 594; + int x_ps_min = 36; + int x_ps_min_clip = 18; + double x_scale; + double y_max; + double y_min; + int y_ps; + int y_ps_max = 666; + int y_ps_max_clip = 684; + int y_ps_min = 126; + int y_ps_min_clip = 108; + double y_scale; + + date_time = timestring (); + // + // We need to do some figuring here, so that we can determine + // the range of the data, and hence the height and width + // of the piece of paper. + // + x_max = -r8_huge (); + for (node = 0; node < node_num; node++) { + if (x_max < node_xy[0 + node * 2]) { + x_max = node_xy[0 + node * 2]; + } + } + for (point = 0; point < point_num; point++) { + if (x_max < point_xy[0 + point * 2]) { + x_max = point_xy[0 + point * 2]; + } + } + + x_min = r8_huge (); + for (node = 0; node < node_num; node++) { + if (node_xy[0 + node * 2] < x_min) { + x_min = node_xy[0 + node * 2]; + } + } + for (point = 0; point < point_num; point++) { + if (point_xy[0 + point * 2] < x_min) { + x_min = point_xy[0 + point * 2]; + } + } + x_scale = x_max - x_min; + + x_max = x_max + 0.05 * x_scale; + x_min = x_min - 0.05 * x_scale; + x_scale = x_max - x_min; + + y_max = -r8_huge (); + for (node = 0; node < node_num; node++) { + if (y_max < node_xy[1 + node * 2]) { + y_max = node_xy[1 + node * 2]; + } + } + for (point = 0; point < point_num; point++) { + if (y_max < point_xy[1 + point * 2]) { + y_max = point_xy[1 + point * 2]; + } + } + + y_min = r8_huge (); + for (node = 0; node < node_num; node++) { + if (node_xy[1 + node * 2] < y_min) { + y_min = node_xy[1 + node * 2]; + } + } + for (point = 0; point < point_num; point++) { + if (point_xy[1 + point * 2] < y_min) { + y_min = point_xy[1 + point * 2]; + } + } + y_scale = y_max - y_min; + + y_max = y_max + 0.05 * y_scale; + y_min = y_min - 0.05 * y_scale; + y_scale = y_max - y_min; + + if (x_scale < y_scale) { + delta = r8_nint ((double) (x_ps_max - x_ps_min) * (y_scale - x_scale) / (2.0 * y_scale)); + + x_ps_max = x_ps_max - delta; + x_ps_min = x_ps_min + delta; + + x_ps_max_clip = x_ps_max_clip - delta; + x_ps_min_clip = x_ps_min_clip + delta; + + x_scale = y_scale; + } + else if (y_scale < x_scale) { + delta = r8_nint ((double) (y_ps_max - y_ps_min) * (x_scale - y_scale) / (2.0 * x_scale)); + + y_ps_max = y_ps_max - delta; + y_ps_min = y_ps_min + delta; + + y_ps_max_clip = y_ps_max_clip - delta; + y_ps_min_clip = y_ps_min_clip + delta; + + y_scale = x_scale; + } + + file_unit.open (file_name); + + if (!file_unit) { + cout << "\n"; + cout << "TRIANGLE_POINTS_PLOT - Fatal error!\n"; + cout << " Could not open the output EPS file.\n"; + exit (1); + } + + file_unit << "%//PS-Adobe-3.0 EPSF-3.0\n"; + file_unit << "%%Creator: triangulation_order3_plot.C\n"; + file_unit << "%%Title: " << file_name << "\n"; + file_unit << "%%CreationDate: " << date_time << "\n"; + delete[] date_time; + file_unit << "%%Pages: 1\n"; + file_unit << "%%BoundingBox: " << x_ps_min << " " << y_ps_min << " " << x_ps_max << " " << y_ps_max << "\n"; + file_unit << "%%Document-Fonts: Times-Roman\n"; + file_unit << "%%LanguageLevel: 1\n"; + file_unit << "%%EndComments\n"; + file_unit << "%%BeginProlog\n"; + file_unit << "/inch {72 mul} def\n"; + file_unit << "%%EndProlog\n"; + file_unit << "%%Page: 1 1\n"; + file_unit << "save\n"; + file_unit << "%\n"; + file_unit << "% Set the RGB line color to very light gray.\n"; + file_unit << "%\n"; + file_unit << "0.900 0.900 0.900 setrgbcolor\n"; + file_unit << "%\n"; + file_unit << "% Draw a gray border around the page.\n"; + file_unit << "%\n"; + file_unit << "newpath\n"; + file_unit << x_ps_min << " " << y_ps_min << " moveto\n"; + file_unit << x_ps_max << " " << y_ps_min << " lineto\n"; + file_unit << x_ps_max << " " << y_ps_max << " lineto\n"; + file_unit << x_ps_min << " " << y_ps_max << " lineto\n"; + file_unit << x_ps_min << " " << y_ps_min << " lineto\n"; + file_unit << "stroke\n"; + file_unit << "%\n"; + file_unit << "% Set the RGB color to black.\n"; + file_unit << "%\n"; + file_unit << "0.000 0.000 0.000 setrgbcolor\n"; + file_unit << "%\n"; + file_unit << "% Set the font and its size.\n"; + file_unit << "%\n"; + file_unit << "/Times-Roman findfont\n"; + file_unit << "0.50 inch scalefont\n"; + file_unit << "setfont\n"; + file_unit << "%\n"; + file_unit << "% Print a title.\n"; + file_unit << "%\n"; + file_unit << "% 210 702 moveto\n"; + file_unit << "% (Triangulation) show\n"; + file_unit << "%\n"; + file_unit << "% Define a clipping polygon.\n"; + file_unit << "%\n"; + file_unit << "newpath\n"; + file_unit << x_ps_min_clip << " " << y_ps_min_clip << " moveto\n"; + file_unit << x_ps_max_clip << " " << y_ps_min_clip << " lineto\n"; + file_unit << x_ps_max_clip << " " << y_ps_max_clip << " lineto\n"; + file_unit << x_ps_min_clip << " " << y_ps_max_clip << " lineto\n"; + file_unit << x_ps_min_clip << " " << y_ps_min_clip << " lineto\n"; + file_unit << "clip newpath\n"; + // + // Draw the nodes. + // + if (1 <= node_show) { + circle_size = 5; + + file_unit << "%\n"; + file_unit << "% Draw filled dots at the nodes.\n"; + file_unit << "%\n"; + file_unit << "% Set the RGB color to blue.\n"; + file_unit << "%\n"; + file_unit << "0.000 0.150 0.750 setrgbcolor\n"; + file_unit << "%\n"; + + for (node = 0; node < 3; node++) { + x_ps = r8_nint ( + ((x_max - node_xy[0 + node * 2]) * (double) (x_ps_min) + (node_xy[0 + node * 2] - x_min) * (double) (x_ps_max)) + / (x_max - x_min)); + + y_ps = r8_nint ( + ((y_max - node_xy[1 + node * 2]) * (double) (y_ps_min) + (node_xy[1 + node * 2] - y_min) * (double) (y_ps_max)) + / (y_max - y_min)); + + file_unit << "newpath " << x_ps << " " << y_ps << " " << circle_size << " 0 360 arc closepath fill\n"; + } + } + // + // Label the nodes. + // + if (2 <= node_show) { + file_unit << "%\n"; + file_unit << "% Label the nodes:\n"; + file_unit << "%\n"; + file_unit << "% Set the RGB color to darker blue.\n"; + file_unit << "%\n"; + file_unit << "0.000 0.250 0.850 setrgbcolor\n"; + file_unit << "/Times-Roman findfont\n"; + file_unit << "0.20 inch scalefont\n"; + file_unit << "setfont\n"; + file_unit << "%\n"; + + for (node = 0; node < node_num; node++) { + x_ps = r8_nint ( + ((x_max - node_xy[0 + node * 2]) * (double) (x_ps_min) + (+node_xy[0 + node * 2] - x_min) * (double) (x_ps_max)) + / (x_max - x_min)); + + y_ps = r8_nint ( + ((y_max - node_xy[1 + node * 2]) * (double) (y_ps_min) + (node_xy[1 + node * 2] - y_min) * (double) (y_ps_max)) + / (y_max - y_min)); + + file_unit << " " << x_ps << " " << y_ps + 5 << " moveto (" << node + 1 << ") show\n"; + } + } + // + // Draw the points. + // + if (point_num <= 200) { + circle_size = 5; + } + else if (point_num <= 500) { + circle_size = 4; + } + else if (point_num <= 1000) { + circle_size = 3; + } + else if (point_num <= 5000) { + circle_size = 2; + } + else { + circle_size = 1; + } + + if (1 <= point_show) { + file_unit << "%\n"; + file_unit << "% Draw filled dots at the points.\n"; + file_unit << "%\n"; + file_unit << "% Set the RGB color to green.\n"; + file_unit << "%\n"; + file_unit << "0.150 0.750 0.000 setrgbcolor\n"; + file_unit << "%\n"; + + for (point = 0; point < point_num; point++) { + x_ps = r8_nint (((x_max - point_xy[0 + point * 2]) * (double) (x_ps_min) + + (point_xy[0 + point * 2] - x_min) * (double) (x_ps_max)) + / (x_max - x_min)); + + y_ps = r8_nint (((y_max - point_xy[1 + point * 2]) * (double) (y_ps_min) + + (point_xy[1 + point * 2] - y_min) * (double) (y_ps_max)) + / (y_max - y_min)); + + file_unit << "newpath " << x_ps << " " << y_ps << " " << circle_size << " 0 360 arc closepath fill\n"; + } + } + // + // Label the points. + // + if (2 <= point_show) { + file_unit << "%\n"; + file_unit << "% Label the point:\n"; + file_unit << "%\n"; + file_unit << "% Set the RGB color to darker green.\n"; + file_unit << "%\n"; + file_unit << "0.250 0.850 0.000 setrgbcolor\n"; + file_unit << "/Times-Roman findfont\n"; + file_unit << "0.20 inch scalefont\n"; + file_unit << "setfont\n"; + file_unit << "%\n"; + + for (point = 0; point < point_num; point++) { + x_ps = r8_nint (((x_max - point_xy[0 + point * 2]) * (double) (x_ps_min) + + (+point_xy[0 + point * 2] - x_min) * (double) (x_ps_max)) + / (x_max - x_min)); + + y_ps = r8_nint (((y_max - point_xy[1 + point * 2]) * (double) (y_ps_min) + + (point_xy[1 + point * 2] - y_min) * (double) (y_ps_max)) + / (y_max - y_min)); + + file_unit << " " << x_ps << " " << y_ps + 5 << " moveto (" << point + 1 << ") show\n"; + } + } + // + // Draw the triangle. + // + if (0 <= node_show) { + file_unit << "%\n"; + file_unit << "% Set the RGB color to red.\n"; + file_unit << "%\n"; + file_unit << "0.900 0.200 0.100 setrgbcolor\n"; + file_unit << "%\n"; + file_unit << "% Draw the triangle.\n"; + file_unit << "%\n"; + + file_unit << "newpath\n"; + + for (i = 0; i <= 3; i++) { + node = i4_wrap (i, 0, 2); + + x_ps = (r8_nint) (((x_max - node_xy[0 + node * 2]) * (double) (x_ps_min) + + (node_xy[0 + node * 2] - x_min) * (double) (x_ps_max)) + / (x_max - x_min)); + + y_ps = (r8_nint) (((y_max - node_xy[1 + node * 2]) * (double) (y_ps_min) + + (node_xy[1 + node * 2] - y_min) * (double) (y_ps_max)) + / (y_max - y_min)); + + if (i == 0) { + file_unit << x_ps << " " << y_ps << " moveto\n"; + } + else { + file_unit << x_ps << " " << y_ps << " lineto\n"; + } + } + file_unit << "stroke\n"; + } + + file_unit << "%\n"; + file_unit << "restore showpage\n"; + file_unit << "%\n"; + file_unit << "% End of page.\n"; + file_unit << "%\n"; + file_unit << "%%Trailer\n"; + file_unit << "%%EOF\n"; + + file_unit.close (); + + return; +} + +} // namespace t8_mra + +#endif diff --git a/src/t8_mra/num/dunavant.hxx b/src/t8_mra/num/dunavant.hxx new file mode 100644 index 0000000000..3fee0d2d06 --- /dev/null +++ b/src/t8_mra/num/dunavant.hxx @@ -0,0 +1,96 @@ +/** + * @file + * @brief Dunavant quadrature rule over the interior of a triangle in 2D + * See https://people.sc.fsu.edu/~jburkardt/m_src/triangle_dunavant_rule/triangle_dunavant_rule.html + */ + +#pragma once + +#ifdef T8_ENABLE_MRA + +namespace t8_mra +{ +int +dunavant_degree (int rule); +int +dunavant_order_num (int rule); +void +dunavant_rule (int rule, int order_num, double xy[], double w[]); +int +dunavant_rule_num (void); +int * +dunavant_suborder (int rule, int suborder_num); +int +dunavant_suborder_num (int rule); +void +dunavant_subrule (int rule, int suborder_num, double suborder_xyz[], double suborder_w[]); +void +dunavant_subrule_01 (int suborder_num, double suborder_xyz[], double suborder_w[]); +void +dunavant_subrule_02 (int suborder_num, double suborder_xyz[], double suborder_w[]); +void +dunavant_subrule_03 (int suborder_num, double suborder_xyz[], double suborder_w[]); +void +dunavant_subrule_04 (int suborder_num, double suborder_xyz[], double suborder_w[]); +void +dunavant_subrule_05 (int suborder_num, double suborder_xyz[], double suborder_w[]); +void +dunavant_subrule_06 (int suborder_num, double suborder_xyz[], double suborder_w[]); +void +dunavant_subrule_07 (int suborder_num, double suborder_xyz[], double suborder_w[]); +void +dunavant_subrule_08 (int suborder_num, double suborder_xyz[], double suborder_w[]); +void +dunavant_subrule_09 (int suborder_num, double suborder_xyz[], double suborder_w[]); +void +dunavant_subrule_10 (int suborder_num, double suborder_xyz[], double suborder_w[]); +void +dunavant_subrule_11 (int suborder_num, double suborder_xyz[], double suborder_w[]); +void +dunavant_subrule_12 (int suborder_num, double suborder_xyz[], double suborder_w[]); +void +dunavant_subrule_13 (int suborder_num, double suborder_xyz[], double suborder_w[]); +void +dunavant_subrule_14 (int suborder_num, double suborder_xyz[], double suborder_w[]); +void +dunavant_subrule_15 (int suborder_num, double suborder_xyz[], double suborder_w[]); +void +dunavant_subrule_16 (int suborder_num, double suborder_xyz[], double suborder_w[]); +void +dunavant_subrule_17 (int suborder_num, double suborder_xyz[], double suborder_w[]); +void +dunavant_subrule_18 (int suborder_num, double suborder_xyz[], double suborder_w[]); +void +dunavant_subrule_19 (int suborder_num, double suborder_xyz[], double suborder_w[]); +void +dunavant_subrule_20 (int suborder_num, double suborder_xyz[], double suborder_w[]); +void +file_name_inc (char *file_name); +int +i4_max (int i1, int i2); +int +i4_min (int i1, int i2); +int +i4_modp (int i, int j); +int +i4_wrap (int ival, int ilo, int ihi); +double +r8_huge (void); +int +r8_nint (double x); +void +reference_to_physical_t3 (double t[], int n, double ref[], double phy[]); +int +s_len_trim (char *s); +void +timestamp (void); +char * +timestring (void); +double +triangle_area (double t[2 * 3]); +void +triangle_points_plot (char *file_name, double node_xy[], int node_show, int point_num, double point_xy[], + int point_show); +} // namespace t8_mra + +#endif diff --git a/src/t8_mra/num/geometry.hxx b/src/t8_mra/num/geometry.hxx new file mode 100644 index 0000000000..32d53c9c22 --- /dev/null +++ b/src/t8_mra/num/geometry.hxx @@ -0,0 +1,194 @@ +#pragma once + +#ifdef T8_ENABLE_MRA + +#include +#include +#include + +///TODO Maybe nested namespace for cartesian +namespace t8_mra +{ + +/** + * @brief Maps a point from physical interval [xL, xR] to reference interval [0, 1] + * + * Performs affine transformation: x_ref = (x - xL) / (xR - xL) + * + * @param x Point in physical interval [xL, xR] + * @param xL Left endpoint of physical interval + * @param xR Right endpoint of physical interval + * @return double Point in reference interval [0, 1] + */ +inline double +ref_1d (double x, double xL, double xR) +{ + return (x - xL) / (xR - xL); +} + +/** + * @brief Maps a point from reference interval [0, 1] to physical interval [xL, xR] + * + * Performs affine transformation: x = x_ref * (xR - xL) + xL + * + * @param x_ref Point in reference interval [0, 1] + * @param xL Left endpoint of physical interval + * @param xR Right endpoint of physical interval + * @return double Point in physical interval [xL, xR] + */ +inline double +deref_1d (double x_ref, double xL, double xR) +{ + return x_ref * (xR - xL) + xL; +} + +/** + * @brief Maps a multi-dimensional point from physical element to reference element [0,1]^DIM + * + * For cartesian elements, this is a dimension-wise affine transformation. + * + * @tparam DIM Spatial dimension + * @param x Point in physical element + * @param vertices_min Lower corner of physical element (coordinates of vertex 0) + * @param vertices_max Upper corner of physical element (coordinates of opposite vertex) + * @return std::array Point in reference element [0,1]^DIM + */ +template +inline std::array +ref (const std::array &x, const std::array &vertices_min, + const std::array &vertices_max) +{ + std::array x_ref; + for (unsigned int d = 0; d < DIM; ++d) + x_ref[d] = ref_1d (x[d], vertices_min[d], vertices_max[d]); + + return x_ref; +} + +/** + * @brief Maps a multi-dimensional point from reference element [0,1]^DIM to physical element + * + * For cartesian elements, this is a dimension-wise affine transformation. + * + * @tparam DIM Spatial dimension + * @param x_ref Point in reference element [0,1]^DIM + * @param vertices_min Lower corner of physical element + * @param vertices_max Upper corner of physical element + * @return std::array Point in physical element + */ +template +inline std::array +deref (const std::array &x_ref, const std::array &vertices_min, + const std::array &vertices_max) +{ + std::array x; + for (unsigned int d = 0; d < DIM; ++d) + x[d] = deref_1d (x_ref[d], vertices_min[d], vertices_max[d]); + + return x; +} + +/** + * @brief Computes the Jacobian determinant of the mapping from reference to physical element + * + * For cartesian elements with axis-aligned edges, the Jacobian is diagonal: + * J = diag(h_0, h_1, ..., h_{DIM-1}) where h_i = vertices_max[i] - vertices_min[i] + * det(J) = product of edge lengths + * + * @tparam DIM Spatial dimension + * @param vertices_min Lower corner of physical element + * @param vertices_max Upper corner of physical element + * @return double Absolute value of Jacobian determinant + */ +template +inline double +jacobian_determinant (const std::array &vertices_min, const std::array &vertices_max) +{ + double det = 1.0; + for (unsigned int d = 0; d < DIM; ++d) + det *= (vertices_max[d] - vertices_min[d]); + + return std::abs (det); +} + +/** + * @brief Extracts vertices from t8code vertex array for cartesian elements + * + * For cartesian elements (LINE, QUAD, HEX), extracts the min and max vertices + * which define the axis-aligned bounding box. + * + * @tparam DIM Spatial dimension + * @param physical_vertices Array of vertex coordinates from t8code (layout depends on element type) + * @param vertices_min Output: lower corner coordinates + * @param vertices_max Output: upper corner coordinates + */ +template +inline void +extract_cartesian_vertices (const double physical_vertices[][3], std::array &vertices_min, + std::array &vertices_max) +{ + if constexpr (DIM == 1) { + // LINE: vertices[0] and vertices[1] + vertices_min[0] = physical_vertices[0][0]; + vertices_max[0] = physical_vertices[1][0]; + } + else if constexpr (DIM == 2) { + // QUAD: After permutation [0,1,3,2] applied in shapes/cartesian.hxx: + // vertices[0] = t8code vertex 0 = (xmin, ymin) + // vertices[1] = t8code vertex 1 = (xmax, ymin) + // vertices[2] = t8code vertex 3 = (xmax, ymax) + // vertices[3] = t8code vertex 2 = (xmin, ymax) + vertices_min[0] = physical_vertices[0][0]; + vertices_min[1] = physical_vertices[0][1]; + vertices_max[0] = physical_vertices[2][0]; + vertices_max[1] = physical_vertices[2][1]; + } + else if constexpr (DIM == 3) { + // HEX: After permutation [0,1,3,2,4,5,7,6]: + // vertices[0] = t8code vertex 0 = (xmin, ymin, zmin) + // vertices[7] = t8code vertex 6 = (xmax, ymax, zmax) - NOTE: index 7 not 6! + vertices_min[0] = physical_vertices[0][0]; + vertices_min[1] = physical_vertices[0][1]; + vertices_min[2] = physical_vertices[0][2]; + vertices_max[0] = physical_vertices[7][0]; + vertices_max[1] = physical_vertices[7][1]; + vertices_max[2] = physical_vertices[7][2]; + } +} + +/** + * @brief Transforms quadrature points from reference to physical element + * + * Takes quadrature points on [0,1]^DIM and maps them to the physical element. + * + * @tparam DIM Spatial dimension + * @param ref_quad_points Reference quadrature points (flattened: [x0,y0,z0, x1,y1,z1, ...]) + * @param num_points Number of quadrature points + * @param vertices_min Lower corner of physical element + * @param vertices_max Upper corner of physical element + * @return std::vector Physical quadrature points (flattened, same layout) + */ +template +inline std::vector +transform_quad_points (const std::vector &ref_quad_points, size_t num_points, + const std::array &vertices_min, const std::array &vertices_max) +{ + std::vector phys_quad_points (DIM * num_points); + + for (size_t i = 0; i < num_points; ++i) { + std::array x_ref; + for (unsigned int d = 0; d < DIM; ++d) + x_ref[d] = ref_quad_points[DIM * i + d]; + + std::array x_phys = deref (x_ref, vertices_min, vertices_max); + + for (unsigned int d = 0; d < DIM; ++d) + phys_quad_points[DIM * i + d] = x_phys[d]; + } + + return phys_quad_points; +} + +} // namespace t8_mra + +#endif // T8_ENABLE_MRA diff --git a/src/t8_mra/num/legendre_basis.hxx b/src/t8_mra/num/legendre_basis.hxx new file mode 100644 index 0000000000..d6195a9980 --- /dev/null +++ b/src/t8_mra/num/legendre_basis.hxx @@ -0,0 +1,120 @@ +#pragma once + +#ifdef T8_ENABLE_MRA + +#include +#include +#include + +namespace t8_mra +{ + +/** + * @brief Evaluates the p-th Legendre polynomial at point x on [0,1] + * + * The Legendre polynomials are shifted from the standard [-1,1] interval + * to [0,1] and L2-normalized. + * + * For orthonormality on [0,1]: + * - Legendre on [-1,1] with norm sqrt(2/(2p+1)) + * - Shift to [0,1]: x_std = 2x-1, dx = d(x_std)/2 + * - This gives normalization factor sqrt(2(2p+1)) for [0,1] + * + * @param x Point in [0,1] where to evaluate + * @param p Polynomial degree (0, 1, 2, ...) + * @return double Value of the p-th normalized Legendre polynomial at x + */ +inline double +phi_1d (double x, int p) +{ + // Transform x from [0,1] to [-1,1] for standard Legendre polynomials + double x_std = 2.0 * x - 1.0; + + // Evaluate Legendre polynomial using GSL + double leg_value = gsl_sf_legendre_Pl (p, x_std); + + // Apply L2-normalization for [0,1] interval: sqrt(2*p + 1) + return leg_value * std::sqrt (2.0 * p + 1.0); +} + +/** + * @brief Evaluates the derivative of the p-th Legendre polynomial at point x on [0,1] + * + * Computes d/dx of the p-th Legendre polynomial, taking into account the + * transformation from [0,1] to [-1,1] and the L2-normalization. + * + * @param x Point in [0,1] where to evaluate the derivative + * @param p Polynomial degree (0, 1, 2, ...) + * @return double Value of the derivative at x + */ +inline double +phi_prime_1d (double x, int p) +{ + // Transform x from [0,1] to [-1,1] + const auto x_std = 2.0 * x - 1.0; + + // Derivative of constant polynomial is zero + if (p == 0) + return 0.0; + + std::vector leg_array (p + 1); + std::vector deriv_array (p + 1); + + // Compute all Legendre polynomials and derivatives up to order p + gsl_sf_legendre_Pl_deriv_array (p, x_std, leg_array.data (), deriv_array.data ()); + const auto deriv_std = deriv_array[p]; + + // Apply chain rule: d/dx_01 = d/dx_std * dx_std/dx_01 + // dx_std/dx_01 = 2.0 (from x_std = 2*x - 1) + // Also apply L2-normalization factor sqrt(2*p + 1) for [0,1] + return 2.0 * deriv_std * std::sqrt (2.0 * p + 1.0); +} + +/** + * @brief Evaluates all Legendre basis functions up to order p_max at point x + * + * @param x Point in [0,1] where to evaluate + * @param p_max Maximum polynomial degree + * @param values Output array of size (p_max + 1) to store the values + */ +inline void +phi_1d_array (double x, int p_max, double *values) +{ + // Transform x from [0,1] to [-1,1] + const auto x_std = 2.0 * x - 1.0; + + // Compute all Legendre polynomials up to p_max + std::vector leg_array (p_max + 1); + gsl_sf_legendre_Pl_array (p_max, x_std, leg_array.data ()); + + // Apply L2-normalization for [0,1] interval to each polynomial + for (int p = 0; p <= p_max; ++p) + values[p] = leg_array[p] * std::sqrt (2.0 * p + 1.0); +} + +/** + * @brief Evaluates derivatives of all Legendre basis functions up to order p_max at point x + * + * @param x Point in [0,1] where to evaluate + * @param p_max Maximum polynomial degree + * @param derivs Output array of size (p_max + 1) to store the derivatives + */ +inline void +phi_prime_1d_array (double x, int p_max, double *derivs) +{ + // Transform x from [0,1] to [-1,1] + const auto x_std = 2.0 * x - 1.0; + + // Compute all Legendre polynomials and derivatives up to p_max + std::vector leg_array (p_max + 1); + std::vector deriv_array (p_max + 1); + gsl_sf_legendre_Pl_deriv_array (p_max, x_std, leg_array.data (), deriv_array.data ()); + + // Apply chain rule and L2-normalization for [0,1] interval to each derivative + for (int p = 0; p <= p_max; ++p) + derivs[p] = 2.0 * deriv_array[p] * std::sqrt (2.0 * p + 1.0); +} + +} // namespace t8_mra + +#endif // T8_ENABLE_MRA diff --git a/src/t8_mra/num/mask_coefficients.hxx b/src/t8_mra/num/mask_coefficients.hxx new file mode 100644 index 0000000000..97957268a7 --- /dev/null +++ b/src/t8_mra/num/mask_coefficients.hxx @@ -0,0 +1,31 @@ +#pragma once + +#ifdef T8_ENABLE_MRA + +#include +#include + +#include +#include + +namespace t8_mra +{ +template +void +initialize_mask_coefficients (size_t order, size_t dof, std::vector &mask_coeffs, + std::vector &inv_mask_coeffs) +{ + throw std::out_of_range ("Element shape is not supported in " + "t8_mra::mask_coefficients::initialize"); +} + +template <> +void +initialize_mask_coefficients (size_t order, size_t dof, std::vector &mask_coeffs, + std::vector &inv_mask_coeffs); + +} // namespace t8_mra + +#include + +#endif diff --git a/src/t8_mra/num/mask_coefficients_compute.hxx b/src/t8_mra/num/mask_coefficients_compute.hxx new file mode 100644 index 0000000000..122e3ac356 --- /dev/null +++ b/src/t8_mra/num/mask_coefficients_compute.hxx @@ -0,0 +1,229 @@ +#pragma once + +#ifdef T8_ENABLE_MRA + +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace t8_mra +{ + +/** + * @brief Compute wavelet basis function in 1D + * + * For Legendre wavelets, the wavelet is constructed as: + * ψ_p(x) = φ_p(2x-1) - projection onto V_0 + * + * For our purposes, we use the standard construction from the 2-scale relation. + * + * @param x Point in [0,1] where to evaluate + * @param p Polynomial degree (0, 1, 2, ...) + * @return double Value of the wavelet at x + */ +inline double +psi_1d (double x, int p) +{ + // Wavelet on [0,1]: difference between fine and coarse scale + // ψ_p(x) = √2 φ_p(2x) - φ_p(x) for x ∈ [0, 0.5] + // = √2 φ_p(2x-1) - φ_p(x) for x ∈ [0.5, 1] + + // For simplicity, use the definition: + // ψ_p(x) = φ_p(2x) - φ_p(2x-1) (for x ∈ [0.5, 1]) + // This captures the detail (high-frequency) component + + if (x < 0.5) { + return std::sqrt (2.0) * phi_1d (2.0 * x, p); + } + else { + return std::sqrt (2.0) * phi_1d (2.0 * x - 1.0, p); + } +} + +/** + * @brief Compute mask coefficients for cartesian elements (LINE, QUAD, HEX) + * + * Computes the mask matrices M_k such that: + * - Forward MST: u_parent[i] = (1/2^D) * sum_k sum_j M_k[j,i] * u_child_k[j] + * - Inverse MST: u_child_k[i] = sum_j M_k[i,j] * u_parent[j] + * + * The mask coefficients are computed via numerical integration: + * M_k[i,j] = ∫ φ_i(2x - s_k) φ_j(x) dx + * + * where s_k is the shift vector for child k (e.g., [0,0], [1,0], [0,1], [1,1] for QUAD). + * + * For wavelets (e ≠ 0), we compute: + * M_k[i,j,e] = ∫ ψ_i^e(2x - s_k) φ_j(x) dx + * + * where ψ^e indicates wavelet in directions specified by e ∈ {0,1}^D. + * + * @tparam ECLASS Element class (T8_ECLASS_LINE, T8_ECLASS_QUAD, T8_ECLASS_HEX) + * @param order Polynomial order P (degree = P-1) + * @param dof Degrees of freedom per element (should be P^D) + * @param mask_coeffs Output: mask matrices for each child, size [2^D][dof x dof] + */ +template +void +compute_mask_coefficients (size_t order, size_t dof, std::vector &mask_coeffs) +{ + // Get dimensionality + constexpr int DIM = (ECLASS == T8_ECLASS_LINE) ? 1 + : (ECLASS == T8_ECLASS_QUAD) ? 2 + : (ECLASS == T8_ECLASS_HEX) ? 3 + : -1; + static_assert (DIM > 0, "Unsupported element class for mask coefficient computation"); + + constexpr int NUM_CHILDREN = (1 << DIM); // 2^D + + // Initialize output + mask_coeffs.resize (NUM_CHILDREN, t8_mra::mat { dof, dof }); + + // Generate multiindices for tensor product basis + // P_set[i] = (i_0, i_1, ..., i_{D-1}) where each i_d ∈ [0, P-1] + std::vector> P_set (dof); + for (size_t idx = 0; idx < dof; ++idx) { + size_t temp = idx; + for (int d = 0; d < DIM; ++d) { + P_set[idx][d] = temp % order; + temp /= order; + } + } + + // Generate child shift vectors E_set[k] ∈ {0,1}^D + std::vector> E_set (NUM_CHILDREN); + for (int k = 0; k < NUM_CHILDREN; ++k) { + for (int d = 0; d < DIM; ++d) { + E_set[k][d] = (k >> d) & 1; + } + } + + // Setup quadrature for integration + // Use high-order Gauss-Legendre quadrature to accurately integrate polynomials + const int quad_order = 2 * (order - 1) + 1; // Exact for degree 2*(P-1) + std::vector quad_nodes_1d, quad_weights_1d; + gauss_legendre_1d (quad_order, quad_nodes_1d, quad_weights_1d); + + // For tensor product in D dimensions, we need quad_order^D points + // Generate tensor product quadrature + const size_t num_quad_1d = quad_nodes_1d.size (); + std::vector> gauss_nodes; + std::vector gauss_weights; + + // Generate all D-dimensional quadrature points + auto generate_tensor_quad = [&] (auto &self, int dim, std::array pt, double wt) -> void { + if (dim == DIM) { + gauss_nodes.push_back (pt); + gauss_weights.push_back (wt); + return; + } + for (size_t q = 0; q < num_quad_1d; ++q) { + pt[dim] = quad_nodes_1d[q]; + self (self, dim + 1, pt, wt * quad_weights_1d[q]); + } + }; + + std::array init_pt {}; + generate_tensor_quad (generate_tensor_quad, 0, init_pt, 1.0); + + // Compute mask coefficients for scaling functions following multilaepsch + // Formula: M_k[i,j] = ∫_{[0,1]^D} φ_i(0.5*(x + s_k)) * φ_j(x) dx + // + // where: + // - x is integration variable on parent reference element [0,1]^D + // - φ_j(x) is parent basis function j evaluated at x + // - φ_i(0.5*(x + s_k)) is child basis function i evaluated at child reference coords + // - s_k is the shift vector for child k (e.g., [0,0], [1,0], [0,1], [1,1] for QUAD) + // + // This gives the prolongation matrix for inverse MST: u_child_k = M_k * u_parent + // For forward MST (restriction), we use the transpose: u_parent = (1/2^D) * sum_k M_k^T * u_child_k + + t8_debugf ("Computing mask coefficients for %s, P=%zu\n", t8_eclass_to_string[ECLASS], order); + + for (int k = 0; k < NUM_CHILDREN; ++k) { + for (size_t i = 0; i < dof; ++i) { + for (size_t j = 0; j < dof; ++j) { + double integral = 0.0; + + // Integrate over the child's region in parent reference coords + // Child k occupies parent region [s_k/2, (s_k+1)/2]^D + // We integrate over [0,1]^D but map it to the child's region + for (size_t q = 0; q < gauss_nodes.size (); ++q) { + const auto &x_unit = gauss_nodes[q]; // Integration point on [0,1]^D + const double gaussW = gauss_weights[q]; + + // Map to child's region in parent coords: x = s_k/2 + x_unit/2 + // Then map to child reference coords: xs = 2*x - s_k = x_unit + // So child reference coords are just x_unit! + std::array xs; + for (int d = 0; d < DIM; ++d) { + xs[d] = x_unit[d]; + } + + // Parent coords where we evaluate parent basis + std::array x_parent; + for (int d = 0; d < DIM; ++d) { + x_parent[d] = 0.5 * (E_set[k][d] + x_unit[d]); + } + + // Evaluate φ_i at child reference coords: φ_i(xs) + double phi_i_val = 1.0; + for (int d = 0; d < DIM; ++d) { + phi_i_val *= phi_1d (xs[d], P_set[i][d]); + } + + // Evaluate φ_j at parent reference coords: φ_j(x_parent) + double phi_j_val = 1.0; + for (int d = 0; d < DIM; ++d) { + phi_j_val *= phi_1d (x_parent[d], P_set[j][d]); + } + + // Accumulate integral + // The Jacobian factor will be applied in the forward MST, not here + integral += gaussW * phi_i_val * phi_j_val; + } + + // Store the mask coefficient + // mask_coeffs[k](i, j) = integral * 0.5; + mask_coeffs[k](i, j) = integral; + } + } + } + +} + +/** + * @brief Convenience function to compute and initialize mask coefficients + * + * This function replaces the hardcoded values in mask_coeffs_*.hpp + */ +template +void +initialize_mask_coefficients_computed (size_t order, size_t dof, std::vector &mask_coeffs, + std::vector &inv_mask_coeffs) +{ + // Compute mask coefficients + compute_mask_coefficients (order, dof, mask_coeffs); + + // Note: inv_mask_coeffs are for wavelets (e ≠ 0) + // For now, we only compute scaling function masks + // Wavelet masks would require computing the wavelet basis functions + // and their integrals, which is more complex + + // Initialize inv_mask_coeffs to appropriate size (but leave empty for now) + constexpr int DIM = (ECLASS == T8_ECLASS_LINE) ? 1 + : (ECLASS == T8_ECLASS_QUAD) ? 2 + : (ECLASS == T8_ECLASS_HEX) ? 3 + : -1; + constexpr int NUM_CHILDREN = (1 << DIM); + inv_mask_coeffs.resize (NUM_CHILDREN, t8_mra::mat { dof, dof }); +} + +} // namespace t8_mra + +#endif // T8_ENABLE_MRA diff --git a/src/t8_mra/num/mask_coefficients_triangle.hxx b/src/t8_mra/num/mask_coefficients_triangle.hxx new file mode 100644 index 0000000000..0d7bb3e436 --- /dev/null +++ b/src/t8_mra/num/mask_coefficients_triangle.hxx @@ -0,0 +1,2769 @@ +#pragma once + +#ifdef T8_ENABLE_MRA + +#include +#include + +#include +#include + +namespace t8_mra +{ + +template <> +void +initialize_mask_coefficients (size_t order, size_t dof, std::vector &mask_coeffs, + std::vector &inv_mask_coeffs) +{ + mask_coeffs.resize (4, t8_mra::mat { dof, dof }); + inv_mask_coeffs.resize (4, t8_mra::mat { 3 * dof, dof }); + + switch (order) { + case 1: + mask_coeffs[0] = { 1. / 2. }; + mask_coeffs[1] = { 1. / 2. }; + mask_coeffs[2] = { 1. / 2. }; + mask_coeffs[3] = { 1. / 2. }; + + inv_mask_coeffs[0] = { 0., 0., -1. / 2. * std::sqrt (3.) }; + inv_mask_coeffs[1] + = { (1. / 6.) * std::sqrt (2.) * std::sqrt (3.), (1. / 2.) * std::sqrt (2.), (1. / 6.) * std::sqrt (3.) }; + inv_mask_coeffs[2] = { -(1. / 3.) * std::sqrt (2.) * std::sqrt (3.), 0., (1. / 6.) * std::sqrt (3.) }; + inv_mask_coeffs[3] + = { (1. / 6.) * std::sqrt (2.) * std::sqrt (3.), -(1. / 2.) * std::sqrt (2.), (1. / 6.) * std::sqrt (3.) }; + break; + + case 2: + /// TODO Fix that (is transpose) + // mask_coeffs[0](0, 0) = 1. / 2.; + // mask_coeffs[0](0, 1) = 0.; + // mask_coeffs[0](0, 2) = 0.; + // mask_coeffs[0](1, 0) = 0.; + // mask_coeffs[0](1, 1) = 1. / 8.; + // mask_coeffs[0](1, 2) = -1. / 8. * std::sqrt (3.); + // mask_coeffs[0](2, 0) = 0.; + // mask_coeffs[0](2, 1) = 1. / 8. * std::sqrt (3.); + // mask_coeffs[0](2, 2) = 1. / 8.; + // + // mask_coeffs[1](0, 0) = 1. / 2.; + // mask_coeffs[1](0, 1) = 0.; + // mask_coeffs[1](0, 2) = 0.; + // mask_coeffs[1](1, 0) = -1. / 4. * std::sqrt (2.); + // mask_coeffs[1](1, 1) = 1. / 4.; + // mask_coeffs[1](1, 2) = 0.; + // mask_coeffs[1](2, 0) = -1. / 4. * std::sqrt (2.) * sqrt (3.); + // mask_coeffs[1](2, 1) = 0.; + // mask_coeffs[1](2, 2) = 1. / 4.; + // + // mask_coeffs[2](0, 0) = 1. / 2.; + // mask_coeffs[2](0, 1) = 0.; + // mask_coeffs[2](0, 2) = 0.; + // mask_coeffs[2](1, 0) = 1. / 2. * std::sqrt (2.); + // mask_coeffs[2](1, 1) = -1. / 8.; + // mask_coeffs[2](1, 2) = -1. / 8. * std::sqrt (3.); + // mask_coeffs[2](2, 0) = 0.; + // mask_coeffs[2](2, 1) = 1. / 8. * std::sqrt (3.); + // mask_coeffs[2](2, 2) = -1. / 8.; + // + // mask_coeffs[3](0, 0) = 1. / 2.; + // mask_coeffs[3](0, 1) = 0.; + // mask_coeffs[3](0, 2) = 0.; + // mask_coeffs[3](1, 0) = -1. / 4. * std::sqrt (2.); + // mask_coeffs[3](1, 1) = -1. / 8.; + // mask_coeffs[3](1, 2) = 1. / 8. * std::sqrt (3.); + // mask_coeffs[3](2, 0) = 1. / 4. * std::sqrt (2.) * sqrt (3.); + // mask_coeffs[3](2, 1) = -1. / 8. * std::sqrt (3.); + // mask_coeffs[3](2, 2) = -1. / 8.; + mask_coeffs[0] = { 1. / 2., 0., 0., 0., 1. / 8., -1. / 8. * std::sqrt (3.), 0., 1. / 8. * std::sqrt (3.), 1. / 8. }; + mask_coeffs[1] + = { 1. / 2., 0., 0., -1. / 4. * std::sqrt (2.), 1. / 4., 0., -1. / 4. * std::sqrt (2.) * std::sqrt (3.), + 0., 1. / 4. }; + mask_coeffs[2] + = { 1. / 2., 0., 0., 1. / 2. * std::sqrt (2.), -1. / 8., -1. / 8. * std::sqrt (3.), 0., 1. / 8. * std::sqrt (3.), + -1. / 8. }; + mask_coeffs[3] = { 1. / 2., + 0., + 0., + -1. / 4. * std::sqrt (2.), + -1. / 8., + 1. / 8. * std::sqrt (3.), + 1. / 4. * std::sqrt (2.) * std::sqrt (3.), + -1. / 8. * std::sqrt (3.), + -1. / 8. }; + inv_mask_coeffs[0] = { -(1. / 6.) * std::sqrt (5.), + -(1. / 30.) * std::sqrt (2.) * std::sqrt (5.), + (1. / 30.) * std::sqrt (2.) * std::sqrt (5.) * std::sqrt (3.), + 0., + -(1. / 20.) * std::sqrt (2.) * std::sqrt (15.), + -(1. / 20.) * std::sqrt (2.) * std::sqrt (5.), + -1. / 3., + (1. / 12.) * std::sqrt (2.), + -(1. / 12.) * std::sqrt (2.) * std::sqrt (3.), + -(1. / 42.) * std::sqrt (35.) * std::sqrt (2.), + (2. / 105.) * std::sqrt (35.), + -(2. / 105.) * std::sqrt (35.) * std::sqrt (3.), + 0., + (2. / 965.) * std::sqrt (2895.), + (2. / 965.) * std::sqrt (965.), + -(1. / 21.) * std::sqrt (203.), + -(1. / 609.) * std::sqrt (406.), + (1. / 609.) * std::sqrt (406.) * std::sqrt (3.), + 0., + -(9. / 5597.) * std::sqrt (11194.), + -(3. / 5597.) * std::sqrt (11194.) * std::sqrt (3.), + 0., + (11. / 232.) * std::sqrt (3.) * std::sqrt (29.), + -(33. / 232.) * std::sqrt (29.), + 0., + (33. / 232.) * std::sqrt (29.), + (11. / 232.) * std::sqrt (3.) * std::sqrt (29.) }; + inv_mask_coeffs[1] = { (1. / 30.) * std::sqrt (5.), + -(2. / 15.) * std::sqrt (2.) * std::sqrt (5.), + 0., + -(1. / 30.) * std::sqrt (15.), + (1. / 12.) * std::sqrt (2.) * std::sqrt (15.), + (1. / 20.) * std::sqrt (2.) * std::sqrt (5.), + 1. / 6., + (1. / 12.) * std::sqrt (2.), + -(1. / 4.) * std::sqrt (2.) * std::sqrt (3.), + -(1. / 105.) * std::sqrt (35.) * std::sqrt (2.), + -(1. / 15.) * std::sqrt (35.), + 0., + -(11. / 5790.) * std::sqrt (2895.) * std::sqrt (2.), + -(5. / 579.) * std::sqrt (2895.), + -(2. / 965.) * std::sqrt (965.), + (23. / 1218.) * std::sqrt (203.), + (5. / 348.) * std::sqrt (406.), + (1. / 116.) * std::sqrt (406.) * std::sqrt (3.), + (33. / 11194.) * std::sqrt (5597.), + -(43. / 22388.) * std::sqrt (11194.), + (229. / 67164.) * std::sqrt (11194.) * std::sqrt (3.), + (1. / 116.) * std::sqrt (3.) * std::sqrt (29.) * std::sqrt (2.), + -(1. / 116.) * std::sqrt (3.) * std::sqrt (29.), + (1. / 29.) * std::sqrt (29.), + (3. / 116.) * std::sqrt (29.) * std::sqrt (2.), + (1. / 29.) * std::sqrt (29.), + (5. / 348.) * std::sqrt (3.) * std::sqrt (29.) }; + inv_mask_coeffs[2] = { (1. / 10.) * std::sqrt (5.), + -(1. / 10.) * std::sqrt (2.) * std::sqrt (5.), + -(1. / 10.) * std::sqrt (2.) * std::sqrt (5.) * std::sqrt (3.), + 0., + -(1. / 10.) * std::sqrt (2.) * std::sqrt (15.), + (1. / 10.) * std::sqrt (2.) * std::sqrt (5.), + 0., + 0., + 0., + (3. / 70.) * std::sqrt (35.) * std::sqrt (2.), + (2. / 35.) * std::sqrt (35.), + (2. / 35.) * std::sqrt (35.) * std::sqrt (3.), + 0., + -(11. / 965.) * std::sqrt (2895.), + (11. / 965.) * std::sqrt (965.), + (2. / 203.) * std::sqrt (203.), + (3. / 812.) * std::sqrt (406.), + (3. / 812.) * std::sqrt (406.) * std::sqrt (3.), + 0., + (5. / 22388.) * std::sqrt (11194.), + -(5. / 67164.) * std::sqrt (11194.) * std::sqrt (3.), + -(1. / 58.) * std::sqrt (3.) * std::sqrt (29.) * std::sqrt (2.), + -(3. / 232.) * std::sqrt (3.) * std::sqrt (29.), + -(9. / 232.) * std::sqrt (29.), + 0., + -(7. / 232.) * std::sqrt (29.), + (7. / 696.) * std::sqrt (3.) * std::sqrt (29.) }; + inv_mask_coeffs[3] = { (1. / 30.) * std::sqrt (5.), + (1. / 15.) * std::sqrt (2.) * std::sqrt (5.), + -(1. / 15.) * std::sqrt (2.) * std::sqrt (5.) * std::sqrt (3.), + (1. / 30.) * std::sqrt (15.), + (1. / 60.) * std::sqrt (2.) * std::sqrt (15.), + -(3. / 20.) * std::sqrt (2.) * std::sqrt (5.), + 1. / 6., + -(5. / 12.) * std::sqrt (2.), + -(1. / 12.) * std::sqrt (2.) * std::sqrt (3.), + -(1. / 105.) * std::sqrt (35.) * std::sqrt (2.), + (1. / 30.) * std::sqrt (35.), + -(1. / 30.) * std::sqrt (35.) * std::sqrt (3.), + (11. / 5790.) * std::sqrt (2895.) * std::sqrt (2.), + -(19. / 5790.) * std::sqrt (2895.), + (27. / 1930.) * std::sqrt (965.), + (23. / 1218.) * std::sqrt (203.), + (1. / 174.) * std::sqrt (406.), + (1. / 87.) * std::sqrt (406.) * std::sqrt (3.), + -(33. / 11194.) * std::sqrt (5597.), + -(34. / 5597.) * std::sqrt (11194.), + -(25. / 33582.) * std::sqrt (11194.) * std::sqrt (3.), + (1. / 116.) * std::sqrt (3.) * std::sqrt (29.) * std::sqrt (2.), + (5. / 232.) * std::sqrt (3.) * std::sqrt (29.), + (1. / 232.) * std::sqrt (29.), + -(3. / 116.) * std::sqrt (29.) * std::sqrt (2.), + -(1. / 232.) * std::sqrt (29.), + -(17. / 696.) * std::sqrt (3.) * std::sqrt (29.) }; + break; + + case 3: + mask_coeffs[0] = { 1. / 2., + 0., + 0., + 0., + 0., + 0., + 0., + 1. / 8., + -1. / 8. * std::sqrt (3.), + 0., + 0., + 0., + 0., + 1. / 8. * std::sqrt (3.), + 1. / 8., + 0., + 0., + 0., + -5. / 24. * std::sqrt (3.), + -1. / 24. * std::sqrt (6.), + 1. / 8. * std::sqrt (2.), + 1. / 24., + -1. / 24. * std::sqrt (3.), + 1. / 24. * std::sqrt (5.), + 0., + 3. / 16. * std::sqrt (2.), + 1. / 16. * std::sqrt (2.) * std::sqrt (3.), + 1. / 24. * std::sqrt (3.), + -1. / 16., + -1. / 48. * std::sqrt (15.), + -1. / 12. * std::sqrt (15.), + 1. / 48. * std::sqrt (30.), + -1. / 16. * std::sqrt (10.), + 1. / 24. * std::sqrt (5.), + 1. / 48. * std::sqrt (15.), + 1. / 48. }; + mask_coeffs[1] = { 1. / 2., + 0., + 0., + 0., + 0., + 0., + -1. / 4. * std::sqrt (2.), + 1. / 4., + 0., + 0., + 0., + 0., + -1. / 4. * std::sqrt (2.) * std::sqrt (3.), + 0., + 1. / 4., + 0., + 0., + 0., + 1. / 24. * std::sqrt (3.), + -1. / 6. * std::sqrt (6.), + 0., + 1. / 8., + 0., + 0., + 1. / 8., + -5. / 16. * std::sqrt (2.), + -1. / 16. * std::sqrt (2.) * std::sqrt (3.), + 0., + 1. / 8., + 0., + 1. / 24. * std::sqrt (15.), + 1. / 48. * std::sqrt (30.), + -3. / 16. * std::sqrt (10.), + 0., + 0., + 1. / 8. }; + mask_coeffs[2] = { 1. / 2., + 0., + 0., + 0., + 0., + 0., + 1. / 2. * std::sqrt (2.), + -1. / 8., + -1. / 8. * std::sqrt (3.), + 0., + 0., + 0., + 0., + 1. / 8. * std::sqrt (3.), + -1. / 8., + 0., + 0., + 0., + 1. / 8. * std::sqrt (3.), + -1. / 8. * std::sqrt (6.), + -3. / 8. * std::sqrt (2.), + 1. / 24., + 1. / 24. * std::sqrt (3.), + 1. / 24. * std::sqrt (5.), + 0., + 3. / 8. * std::sqrt (2.), + -1. / 8. * std::sqrt (2.) * std::sqrt (3.), + -1. / 24. * std::sqrt (3.), + -1. / 16., + 1. / 48. * std::sqrt (15.), + 0., + 0., + 0., + 1. / 24. * std::sqrt (5.), + -1. / 48. * std::sqrt (15.), + 1. / 48. }; + mask_coeffs[3] = { 1. / 2., + 0., + 0., + 0., + 0., + 0., + -1. / 4. * std::sqrt (2.), + -1. / 8., + 1. / 8. * std::sqrt (3.), + 0., + 0., + 0., + 1. / 4. * std::sqrt (2.) * std::sqrt (3.), + -1. / 8. * std::sqrt (3.), + -1. / 8., + 0., + 0., + 0., + 1. / 24. * std::sqrt (3.), + 1. / 12. * std::sqrt (6.), + -1. / 4. * std::sqrt (2.), + 1. / 24., + -1. / 24. * std::sqrt (3.), + 1. / 24. * std::sqrt (5.), + -1. / 8., + -1. / 16. * std::sqrt (2.), + 3. / 16. * std::sqrt (2.) * std::sqrt (3.), + 1. / 24. * std::sqrt (3.), + -1. / 16., + -1. / 48. * std::sqrt (15.), + 1. / 24. * std::sqrt (15.), + -5. / 48. * std::sqrt (30.), + -1. / 16. * std::sqrt (10.), + 1. / 24. * std::sqrt (5.), + 1. / 48. * std::sqrt (15.), + 1. / 48. }; + inv_mask_coeffs[0] + = { 1. / 21. * std::sqrt (7.), + -1. / 42. * std::sqrt (2.) * std::sqrt (7.), + 1. / 42. * std::sqrt (2.) * std::sqrt (7.) * std::sqrt (3.), + -1. / 63. * std::sqrt (7.) * std::sqrt (3.), + 1. / 21. * std::sqrt (7.), + -1. / 63. * std::sqrt (7.) * std::sqrt (15.), + 0., + -1. / 70. * std::sqrt (2.) * std::sqrt (21.), + -1. / 70. * std::sqrt (2.) * std::sqrt (7.), + 2. / 105. * std::sqrt (7.), + -1. / 105. * std::sqrt (21.), + -1. / 105. * std::sqrt (7.) * std::sqrt (5.), + -1. / 21. * std::sqrt (35.), + -1. / 210. * std::sqrt (2.) * std::sqrt (35.), + 1. / 210. * std::sqrt (2.) * std::sqrt (35.) * std::sqrt (3.), + 1. / 35. * std::sqrt (35.) * std::sqrt (3.), + 1. / 105. * std::sqrt (35.), + 1. / 21. * std::sqrt (7.) * std::sqrt (3.), + 0., + 1. / 10. * std::sqrt (2.), + 1. / 30. * std::sqrt (2.) * std::sqrt (3.), + -2. / 45. * std::sqrt (3.), + 1. / 15., + 1. / 45. * std::sqrt (15.), + 145. / 148533. * std::sqrt (49511.), + 85. / 148533. * std::sqrt (99022.), + -85. / 148533. * std::sqrt (99022.) * std::sqrt (3.), + 25. / 891198. * std::sqrt (49511.) * std::sqrt (3.), + -25. / 297066. * std::sqrt (49511.), + 25. / 891198. * std::sqrt (49511.) * std::sqrt (15.), + 0., + 1. / 119. * std::sqrt (714.), + 1. / 119. * std::sqrt (238.), + 1. / 102. * std::sqrt (119.), + -1. / 204. * std::sqrt (357.), + -1. / 204. * std::sqrt (119.) * std::sqrt (5.), + 358730. / 13630367358069. * std::sqrt (22717278930115.), + -1218983. / 68151836790345. * std::sqrt (45434557860230.), + 1218983. / 68151836790345. * std::sqrt (45434557860230.) * std::sqrt (3.), + 459527. / 45434557860230. * std::sqrt (22717278930115.) * std::sqrt (3.), + 10860979. / 272607347161380. * std::sqrt (22717278930115.), + -1069331. / 54521469432276. * std::sqrt (4543455786023.) * std::sqrt (3.), + 0., + 1007. / 7103110. * std::sqrt (2.) * std::sqrt (3551555.), + 1007. / 21309330. * std::sqrt (2.) * std::sqrt (3551555.) * std::sqrt (3.), + -979. / 31963995. * std::sqrt (3551555.) * std::sqrt (3.), + 979. / 21309330. * std::sqrt (3551555.), + 979. / 12785598. * std::sqrt (710311.) * std::sqrt (3.), + -174. / 191516879591. * std::sqrt (40010748899753765.), + 3579. / 7668567110638. * std::sqrt (2.) * std::sqrt (40010748899753765.), + -3579. / 7668567110638. * std::sqrt (2.) * std::sqrt (40010748899753765.) * std::sqrt (3.), + 1854268. / 24006449339852259. * std::sqrt (40010748899753765.) * std::sqrt (3.), + 4167. / 7668567110638. * std::sqrt (40010748899753765.), + -18672515. / 48012898679704518. * std::sqrt (8002149779950753.) * std::sqrt (3.), + -131. / 156402163354. * std::sqrt (16337378978550455.) * std::sqrt (2.), + -27481. / 62625314723720. * std::sqrt (16337378978550455.), + 27481. / 62625314723720. * std::sqrt (16337378978550455.) * std::sqrt (3.), + 48172897. / 98024273871302730. * std::sqrt (16337378978550455.) * std::sqrt (6.), + -18381. / 15656328680930. * std::sqrt (16337378978550455.) * std::sqrt (2.), + 42285847. / 19604854774260546. * std::sqrt (3267475795710091.) * std::sqrt (6.), + 0., + -96863. / 5425104720. * std::sqrt (3.) * std::sqrt (22604603.), + -96863. / 5425104720. * std::sqrt (22604603.), + 25421. / 2712552360. * std::sqrt (22604603.) * std::sqrt (2.), + -25421. / 5425104720. * std::sqrt (3.) * std::sqrt (22604603.) * std::sqrt (2.), + -25421. / 5425104720. * std::sqrt (22604603.) * std::sqrt (10.), + -3991308. / 2090724953684663. * std::sqrt (10453624768423315.), + -52021273. / 41814499073693260. * std::sqrt (2.) * std::sqrt (10453624768423315.), + 52021273. / 41814499073693260. * std::sqrt (2.) * std::sqrt (10453624768423315.) * std::sqrt (3.), + 38343599. / 20907249536846630. * std::sqrt (10453624768423315.) * std::sqrt (3.), + 22436271. / 41814499073693260. * std::sqrt (10453624768423315.), + 26187625. / 8362899814738652. * std::sqrt (2090724953684663.) * std::sqrt (3.), + 0., + -274451. / 898363868880. * std::sqrt (4031407861599.), + -274451. / 898363868880. * std::sqrt (1343802620533.), + -33583. / 449181934440. * std::sqrt (1343802620533.) * std::sqrt (2.), + 33583. / 898363868880. * std::sqrt (4031407861599.) * std::sqrt (2.), + 33583. / 898363868880. * std::sqrt (1343802620533.) * std::sqrt (10.), + -917. / 187809968054752. * std::sqrt (404363576778211096835.) * std::sqrt (2.), + 60034. / 3844235283620705. * std::sqrt (404363576778211096835.), + -60034. / 3844235283620705. * std::sqrt (404363576778211096835.) * std::sqrt (3.), + 687811. / 123015529075862560. * std::sqrt (404363576778211096835.) * std::sqrt (6.), + 20856. / 3844235283620705. * std::sqrt (404363576778211096835.) * std::sqrt (2.), + 70823. / 12301552907586256. * std::sqrt (80872715355642219367.) * std::sqrt (6.), + 0., + 381. / 5918044472. * std::sqrt (1083741893935.), + 127. / 5918044472. * std::sqrt (1083741893935.) * std::sqrt (3.), + -33. / 739755559. * std::sqrt (1083741893935.) * std::sqrt (6.), + 99. / 1479511118. * std::sqrt (1083741893935.) * std::sqrt (2.), + 165. / 1479511118. * std::sqrt (216748378787.) * std::sqrt (6.), + 5. / 7264. * std::sqrt (5.) * std::sqrt (29737.) * std::sqrt (2.), + 27. / 297370. * std::sqrt (5.) * std::sqrt (29737.), + -27. / 297370. * std::sqrt (5.) * std::sqrt (29737.) * std::sqrt (3.), + 989. / 4757920. * std::sqrt (5.) * std::sqrt (29737.) * std::sqrt (6.), + -549. / 594740. * std::sqrt (5.) * std::sqrt (29737.) * std::sqrt (2.), + 637. / 475792. * std::sqrt (29737.) * std::sqrt (6.), + 0., + 27. / 10480. * std::sqrt (2.) * std::sqrt (5.) * std::sqrt (131.), + 9. / 10480. * std::sqrt (2.) * std::sqrt (5.) * std::sqrt (131.) * std::sqrt (3.), + -61. / 5240. * std::sqrt (5.) * std::sqrt (131.) * std::sqrt (3.), + 183. / 10480. * std::sqrt (5.) * std::sqrt (131.), + 61. / 2096. * std::sqrt (131.) * std::sqrt (3.), + -5. / 908. * std::sqrt (3.) * std::sqrt (5.) * std::sqrt (227.), + 9. / 18160. * std::sqrt (2.) * std::sqrt (3.) * std::sqrt (5.) * std::sqrt (227.), + -27. / 18160. * std::sqrt (2.) * std::sqrt (5.) * std::sqrt (227.), + -151. / 9080. * std::sqrt (5.) * std::sqrt (227.), + -183. / 18160. * std::sqrt (3.) * std::sqrt (5.) * std::sqrt (227.), + -11. / 3632. * std::sqrt (227.) }; + inv_mask_coeffs[1] + = { 1. / 42. * std::sqrt (7.), + 1. / 14. * std::sqrt (2.) * std::sqrt (7.), + 0., + -1. / 14. * std::sqrt (7.) * std::sqrt (3.), + 0., + 0., + 1. / 42. * std::sqrt (21.), + 2. / 35. * std::sqrt (2.) * std::sqrt (21.), + 1. / 70. * std::sqrt (2.) * std::sqrt (7.), + -1. / 10. * std::sqrt (7.), + -4. / 105. * std::sqrt (21.), + 0., + 1. / 42. * std::sqrt (35.), + 1. / 35. * std::sqrt (2.) * std::sqrt (35.), + 1. / 70. * std::sqrt (2.) * std::sqrt (35.) * std::sqrt (3.), + 1. / 90. * std::sqrt (35.) * std::sqrt (3.), + -1. / 15. * std::sqrt (35.), + -1. / 63. * std::sqrt (7.) * std::sqrt (3.), + -1. / 6., + 1. / 10. * std::sqrt (2.), + -1. / 5. * std::sqrt (2.) * std::sqrt (3.), + 1. / 90. * std::sqrt (3.), + -1. / 15., + 1. / 9. * std::sqrt (15.), + -2. / 13503. * std::sqrt (49511.), + -1. / 49511. * std::sqrt (99022.), + 0., + 107. / 99022. * std::sqrt (49511.) * std::sqrt (3.), + 0., + 0., + 1. / 357. * std::sqrt (357.), + 0., + 0., + -5. / 119. * std::sqrt (119.), + -5. / 714. * std::sqrt (357.), + 0., + -17165. / 1239124305279. * std::sqrt (22717278930115.), + 37141. / 22717278930115. * std::sqrt (45434557860230.), + 1. / 3211830755. * std::sqrt (45434557860230.) * std::sqrt (3.), + 388103. / 29207930053005. * std::sqrt (22717278930115.) * std::sqrt (3.), + 118. / 1376498895. * std::sqrt (22717278930115.), + 101. / 11562590718. * std::sqrt (4543455786023.) * std::sqrt (3.), + 181. / 4261866. * std::sqrt (3551555.), + -1. / 835660. * std::sqrt (2.) * std::sqrt (3551555.), + -3. / 835660. * std::sqrt (2.) * std::sqrt (3551555.) * std::sqrt (3.), + 2452. / 31963995. * std::sqrt (3551555.) * std::sqrt (3.), + -3014. / 10654665. * std::sqrt (3551555.), + -7. / 53721. * std::sqrt (710311.) * std::sqrt (3.), + 7282357. / 16004299559901506. * std::sqrt (40010748899753765.), + -207283. / 22863285085573580. * std::sqrt (2.) * std::sqrt (40010748899753765.), + -637051. / 160042995599015060. * std::sqrt (2.) * std::sqrt (40010748899753765.) * std::sqrt (3.), + -19924813. / 120032246699261295. * std::sqrt (40010748899753765.) * std::sqrt (3.), + 32621817. / 40010748899753765. * std::sqrt (40010748899753765.), + -14038732. / 3429492762836037. * std::sqrt (8002149779950753.) * std::sqrt (3.), + -3146919. / 13069903182840364. * std::sqrt (16337378978550455.) * std::sqrt (2.), + -9374109. / 9335645130600260. * std::sqrt (16337378978550455.), + 9113. / 3267475795710091. * std::sqrt (16337378978550455.) * std::sqrt (3.), + -102871829. / 98024273871302730. * std::sqrt (16337378978550455.) * std::sqrt (6.), + 1202058. / 3267475795710091. * std::sqrt (16337378978550455.) * std::sqrt (2.), + 101660. / 1400346769590039. * std::sqrt (3267475795710091.) * std::sqrt (6.), + 4607. / 813765708. * std::sqrt (3.) * std::sqrt (22604603.) * std::sqrt (2.), + 47767. / 1627531416. * std::sqrt (3.) * std::sqrt (22604603.), + 3097. / 1356276180. * std::sqrt (22604603.), + 44573. / 813765708. * std::sqrt (22604603.) * std::sqrt (2.), + 60479. / 8137657080. * std::sqrt (3.) * std::sqrt (22604603.) * std::sqrt (2.), + 35. / 116252244. * std::sqrt (22604603.) * std::sqrt (10.), + 10139599. / 12544349722107978. * std::sqrt (10453624768423315.), + 236915369. / 125443497221079780. * std::sqrt (2.) * std::sqrt (10453624768423315.), + 22632821. / 125443497221079780. * std::sqrt (2.) * std::sqrt (10453624768423315.) * std::sqrt (3.), + 100445987. / 94082622915809835. * std::sqrt (10453624768423315.) * std::sqrt (3.), + 59438607. / 20907249536846630. * std::sqrt (10453624768423315.), + 6114122. / 18816524583161967. * std::sqrt (2090724953684663.) * std::sqrt (3.), + -7561. / 134754580332. * std::sqrt (4031407861599.) * std::sqrt (2.), + -34349. / 269509160664. * std::sqrt (4031407861599.), + -24191. / 224590967220. * std::sqrt (1343802620533.), + -6583. / 134754580332. * std::sqrt (1343802620533.) * std::sqrt (2.), + -138637. / 1347545803320. * std::sqrt (4031407861599.) * std::sqrt (2.), + -2203. / 134754580332. * std::sqrt (1343802620533.) * std::sqrt (10.), + 321983. / 73809317445517536. * std::sqrt (404363576778211096835.) * std::sqrt (2.), + -477679. / 184523293613793840. * std::sqrt (404363576778211096835.), + 284125. / 36904658722758768. * std::sqrt (404363576778211096835.) * std::sqrt (3.), + -3699613. / 1107139761682763040. * std::sqrt (404363576778211096835.) * std::sqrt (6.), + 41839. / 6150776453793128. * std::sqrt (404363576778211096835.) * std::sqrt (2.), + 1232917. / 110713976168276304. * std::sqrt (80872715355642219367.) * std::sqrt (6.), + -253. / 2959022236. * std::sqrt (1083741893935.) * std::sqrt (2.), + 109. / 739755559. * std::sqrt (1083741893935.), + -1751. / 8877066708. * std::sqrt (1083741893935.) * std::sqrt (3.), + -71. / 2219266677. * std::sqrt (1083741893935.) * std::sqrt (6.), + 201. / 1479511118. * std::sqrt (1083741893935.) * std::sqrt (2.), + -2003. / 4438533354. * std::sqrt (216748378787.) * std::sqrt (6.), + -415. / 2854752. * std::sqrt (5.) * std::sqrt (29737.) * std::sqrt (2.), + 91. / 1427376. * std::sqrt (5.) * std::sqrt (29737.), + -1369. / 7136880. * std::sqrt (5.) * std::sqrt (29737.) * std::sqrt (3.), + -311. / 8564256. * std::sqrt (5.) * std::sqrt (29737.) * std::sqrt (6.), + 61. / 1189480. * std::sqrt (5.) * std::sqrt (29737.) * std::sqrt (2.), + -1085. / 4282128. * std::sqrt (29737.) * std::sqrt (6.), + 5. / 1048. * std::sqrt (5.) * std::sqrt (131.), + 3. / 10480. * std::sqrt (2.) * std::sqrt (5.) * std::sqrt (131.), + 17. / 6288. * std::sqrt (2.) * std::sqrt (5.) * std::sqrt (131.) * std::sqrt (3.), + -4. / 1965. * std::sqrt (5.) * std::sqrt (131.) * std::sqrt (3.), + 5. / 1048. * std::sqrt (5.) * std::sqrt (131.), + 2. / 393. * std::sqrt (131.) * std::sqrt (3.), + 5. / 1816. * std::sqrt (3.) * std::sqrt (5.) * std::sqrt (227.), + 41. / 18160. * std::sqrt (2.) * std::sqrt (3.) * std::sqrt (5.) * std::sqrt (227.), + 47. / 18160. * std::sqrt (2.) * std::sqrt (5.) * std::sqrt (227.), + 2. / 1135. * std::sqrt (5.) * std::sqrt (227.), + 3. / 1135. * std::sqrt (3.) * std::sqrt (5.) * std::sqrt (227.), + 7. / 1816. * std::sqrt (227.) }; + inv_mask_coeffs[2] + = { -2. / 21. * std::sqrt (7.), + -1. / 14. * std::sqrt (2.) * std::sqrt (7.), + -1. / 14. * std::sqrt (2.) * std::sqrt (7.) * std::sqrt (3.), + 2. / 63. * std::sqrt (7.) * std::sqrt (3.), + 2. / 21. * std::sqrt (7.), + 2. / 63. * std::sqrt (7.) * std::sqrt (15.), + 0., + 1. / 14. * std::sqrt (2.) * std::sqrt (21.), + -1. / 14. * std::sqrt (2.) * std::sqrt (7.), + -2. / 21. * std::sqrt (7.), + -1. / 21. * std::sqrt (21.), + 1. / 21. * std::sqrt (7.) * std::sqrt (5.), + 0., + 0., + 0., + 2. / 63. * std::sqrt (35.) * std::sqrt (3.), + -1. / 21. * std::sqrt (35.), + 1. / 63. * std::sqrt (7.) * std::sqrt (3.), + 0., + 0., + 0., + 0., + 0., + 0., + -101. / 148533. * std::sqrt (49511.), + 1. / 49511. * std::sqrt (99022.), + 1. / 49511. * std::sqrt (99022.) * std::sqrt (3.), + 47. / 81018. * std::sqrt (49511.) * std::sqrt (3.), + 47. / 27006. * std::sqrt (49511.), + 47. / 81018. * std::sqrt (49511.) * std::sqrt (15.), + 0., + 0., + 0., + 25. / 714. * std::sqrt (119.), + 25. / 1428. * std::sqrt (357.), + -25. / 1428. * std::sqrt (119.) * std::sqrt (5.), + 900. / 649065112289. * std::sqrt (22717278930115.), + -657. / 649065112289. * std::sqrt (45434557860230.), + -657. / 649065112289. * std::sqrt (45434557860230.) * std::sqrt (3.), + 464141. / 7434745831674. * std::sqrt (22717278930115.) * std::sqrt (3.), + -504965. / 4956497221116. * std::sqrt (22717278930115.), + 341669. / 14869491663348. * std::sqrt (4543455786023.) * std::sqrt (3.), + 0., + -3. / 835660. * std::sqrt (2.) * std::sqrt (3551555.), + 1. / 835660. * std::sqrt (2.) * std::sqrt (3551555.) * std::sqrt (3.), + -1. / 3551555. * std::sqrt (3551555.) * std::sqrt (3.), + -3. / 7103110. * std::sqrt (3551555.), + 1. / 1420622. * std::sqrt (710311.) * std::sqrt (3.), + -12115. / 8002149779950753. * std::sqrt (40010748899753765.), + 176879. / 160042995599015060. * std::sqrt (2.) * std::sqrt (40010748899753765.), + 176879. / 160042995599015060. * std::sqrt (2.) * std::sqrt (40010748899753765.) * std::sqrt (3.), + -2780108. / 120032246699261295. * std::sqrt (40010748899753765.) * std::sqrt (3.), + 27557. / 630090533854390. * std::sqrt (40010748899753765.), + -17749. / 6858985525672074. * std::sqrt (8002149779950753.) * std::sqrt (3.), + 4310246. / 3267475795710091. * std::sqrt (16337378978550455.) * std::sqrt (2.), + 219167687. / 130699031828403640. * std::sqrt (16337378978550455.), + 219167687. / 130699031828403640. * std::sqrt (16337378978550455.) * std::sqrt (3.), + 83593273. / 98024273871302730. * std::sqrt (16337378978550455.) * std::sqrt (6.), + 332749. / 257281558717330. * std::sqrt (16337378978550455.) * std::sqrt (2.), + 8398969. / 2800693539180078. * std::sqrt (3267475795710091.) * std::sqrt (6.), + 0., + 17917. / 361673648. * std::sqrt (3.) * std::sqrt (22604603.), + -17917. / 361673648. * std::sqrt (22604603.), + 11387. / 232504488. * std::sqrt (22604603.) * std::sqrt (2.), + 11387. / 465008976. * std::sqrt (3.) * std::sqrt (22604603.) * std::sqrt (2.), + -11387. / 465008976. * std::sqrt (22604603.) * std::sqrt (10.), + 1834325. / 6272174861053989. * std::sqrt (10453624768423315.), + 807103. / 6272174861053989. * std::sqrt (2.) * std::sqrt (10453624768423315.), + 807103. / 6272174861053989. * std::sqrt (2.) * std::sqrt (10453624768423315.) * std::sqrt (3.), + -77301811. / 37633049166323934. * std::sqrt (10453624768423315.) * std::sqrt (3.), + 27772799. / 8362899814738652. * std::sqrt (10453624768423315.), + -59252053. / 7526609833. * std::sqrt (2090724953684663.) * std::sqrt (3.), + 0., + 4585. / 59890924592. * std::sqrt (4031407861599.), + -4585. / 59890924592. * std::sqrt (1343802620533.), + 12785. / 269509160664. * std::sqrt (1343802620533.) * std::sqrt (2.), + 12785. / 539018321328. * std::sqrt (4031407861599.) * std::sqrt (2.), + -12785. / 539018321328. * std::sqrt (1343802620533.) * std::sqrt (10.), + -283585. / 73809317445517536. * std::sqrt (404363576778211096835.) * std::sqrt (2.), + -623887. / 184523293613793840. * std::sqrt (404363576778211096835.), + -623887. / 184523293613793840. * std::sqrt (404363576778211096835.) * std::sqrt (3.), + -828829. / 1107139761682763040. * std::sqrt (404363576778211096835.) * std::sqrt (6.), + -62357. / 30753882268965640. * std::sqrt (404363576778211096835.) * std::sqrt (2.), + -390251. / 110713976168276304. * std::sqrt (80872715355642219367.) * std::sqrt (6.), + 0., + 85. / 5918044472. * std::sqrt (1083741893935.), + -85. / 17754133416. * std::sqrt (1083741893935.) * std::sqrt (3.), + 2. / 739755559. * std::sqrt (1083741893935.) * std::sqrt (6.), + 3. / 739755559. * std::sqrt (1083741893935.) * std::sqrt (2.), + -5. / 739755559. * std::sqrt (216748378787.) * std::sqrt (6.), + -5. / 12576. * std::sqrt (5.) * std::sqrt (29737.) * std::sqrt (2.), + -11. / 31440. * std::sqrt (5.) * std::sqrt (29737.), + -11. / 31440. * std::sqrt (5.) * std::sqrt (29737.) * std::sqrt (3.), + -259. / 42821280. * std::sqrt (5.) * std::sqrt (29737.) * std::sqrt (6.), + -377. / 1189480. * std::sqrt (5.) * std::sqrt (29737.) * std::sqrt (2.), + -1409. / 4282128. * std::sqrt (29737.) * std::sqrt (6.), + 0., + 19. / 5240. * std::sqrt (2.) * std::sqrt (5.) * std::sqrt (131.), + -19. / 15720. * std::sqrt (2.) * std::sqrt (5.) * std::sqrt (131.) * std::sqrt (3.), + 23. / 15720. * std::sqrt (5.) * std::sqrt (131.) * std::sqrt (3.), + 23. / 10480. * std::sqrt (5.) * std::sqrt (131.), + -23. / 6288. * std::sqrt (131.) * std::sqrt (3.), + 0., + 0., + 0., + 5. / 1816. * std::sqrt (5.) * std::sqrt (227.), + -5. / 3632. * std::sqrt (3.) * std::sqrt (5.) * std::sqrt (227.), + 5. / 3632. * std::sqrt (227.) }; + inv_mask_coeffs[3] + = { 1. / 42. * std::sqrt (7.), + -1. / 28. * std::sqrt (2.) * std::sqrt (7.), + 1. / 28. * std::sqrt (2.) * std::sqrt (7.) * std::sqrt (3.), + -1. / 42. * std::sqrt (7.) * std::sqrt (3.), + 1. / 14. * std::sqrt (7.), + -1. / 42. * std::sqrt (7.) * std::sqrt (15.), + -1. / 42. * std::sqrt (21.), + 3. / 140. * std::sqrt (2.) * std::sqrt (21.), + -13. / 140. * std::sqrt (2.) * std::sqrt (7.), + -1. / 210. * std::sqrt (7.), + -1. / 70. * std::sqrt (21.), + 11. / 210. * std::sqrt (7.) * std::sqrt (5.), + 1. / 42. * std::sqrt (35.), + 1. / 140. * std::sqrt (2.) * std::sqrt (35.), + 3. / 140. * std::sqrt (2.) * std::sqrt (35.) * std::sqrt (3.), + 13. / 630. * std::sqrt (35.) * std::sqrt (3.), + -11. / 210. * std::sqrt (35.), + -5. / 126. * std::sqrt (7.) * std::sqrt (3.), + 1. / 6., + 7. / 20. * std::sqrt (2.), + 1. / 20. * std::sqrt (2.) * std::sqrt (3.), + -19. / 90. * std::sqrt (3.), + -7. / 30., + -1. / 90. * std::sqrt (15.), + -2. / 13503. * std::sqrt (49511.), + 1. / 99022. * std::sqrt (99022.), + -1. / 99022. * std::sqrt (99022.) * std::sqrt (3.), + 107. / 297066. * std::sqrt (49511.) * std::sqrt (3.), + -107. / 99022. * std::sqrt (49511.), + 107. / 297066. * std::sqrt (49511.) * std::sqrt (15.), + -1. / 357. * std::sqrt (357.), + 0., + 0., + 5. / 714. * std::sqrt (119.), + -5. / 476. * std::sqrt (357.), + 25. / 1428. * std::sqrt (119.) * std::sqrt (5.), + -17165. / 1239124305279. * std::sqrt (22717278930115.), + -7961. / 22717278930115. * std::sqrt (45434557860230.), + 22107. / 22717278930115. * std::sqrt (45434557860230.) * std::sqrt (3.), + -8682827. / 408911020742070. * std::sqrt (22717278930115.) * std::sqrt (3.), + 9252923. / 272607347161380. * std::sqrt (22717278930115.), + 15545015. / 163564408296828. * std::sqrt (4543455786023.) * std::sqrt (3.), + -181. / 4261866. * std::sqrt (3551555.), + 1. / 208915. * std::sqrt (2.) * std::sqrt (3551555.), + 1. / 417830. * std::sqrt (2.) * std::sqrt (3551555.) * std::sqrt (3.), + -349. / 4566285. * std::sqrt (3551555.) * std::sqrt (3.), + 6037. / 21309330. * std::sqrt (3551555.), + 1657. / 12785598. * std::sqrt (710311.) * std::sqrt (3.), + 7282357. / 16004299559901506. * std::sqrt (40010748899753765.), + -115043. / 80021497799507530. * std::sqrt (2.) * std::sqrt (40010748899753765.), + -261004. / 40010748899753765. * std::sqrt (2.) * std::sqrt (40010748899753765.) * std::sqrt (3.), + -67682876. / 40010748899753765. * std::sqrt (40010748899753765.) * std::sqrt (3.), + -117880181. / 80021497799507530. * std::sqrt (40010748899753765.), + -638973. / 2286328508557358. * std::sqrt (8002149779950753.) * std::sqrt (3.), + -3146919. / 13069903182840364. * std::sqrt (16337378978550455.) * std::sqrt (2.), + 66165543. / 130699031828403640. * std::sqrt (16337378978550455.), + -65436503. / 130699031828403640. * std::sqrt (16337378978550455.) * std::sqrt (3.), + -43939123. / 98024273871302730. * std::sqrt (16337378978550455.) * std::sqrt (6.), + 41486933. / 32674757957100910. * std::sqrt (16337378978550455.) * std::sqrt (2.), + -4006159. / 2800693539180078. * std::sqrt (3267475795710091.) * std::sqrt (6.), + -4607. / 813765708. * std::sqrt (3.) * std::sqrt (22604603.) * std::sqrt (2.), + 220253. / 16275314160. * std::sqrt (3.) * std::sqrt (22604603.), + -245029. / 5425104720. * std::sqrt (22604603.), + -30727. / 2712552360. * std::sqrt (22604603.) * std::sqrt (2.), + 232591. / 16275314160. * std::sqrt (3.) * std::sqrt (22604603.) * std::sqrt (2.), + -17069. / 775014960. * std::sqrt (22604603.) * std::sqrt (10.), + 10139599. / 12544349722107978. * std::sqrt (10453624768423315.), + -84508453. / 125443497221079780. * std::sqrt (2.) * std::sqrt (10453624768423315.), + 25954819. / 25088699444215956. * std::sqrt (2.) * std::sqrt (10453624768423315.) * std::sqrt (3.), + -90971423. / 188165245831619670. * std::sqrt (10453624768423315.) * std::sqrt (3.), + 4317883. / 8362899814738652. * std::sqrt (10453624768423315.), + 316319885. / 75266098332647868. * std::sqrt (2090724953684663.) * std::sqrt (3.), + 7561. / 134754580332. * std::sqrt (4031407861599.) * std::sqrt (2.), + -26599. / 2695091606640. * std::sqrt (4031407861599.), + 220127. / 898363868880. * std::sqrt (1343802620533.), + -26659. / 449181934440. * std::sqrt (1343802620533.) * std::sqrt (2.), + 131467. / 2695091606640. * std::sqrt (4031407861599.) * std::sqrt (2.), + 63289. / 898363868880. * std::sqrt (1343802620533.) * std::sqrt (10.), + 321983. / 73809317445517536. * std::sqrt (404363576778211096835.) * std::sqrt (2.), + 2369777. / 184523293613793840. * std::sqrt (404363576778211096835.), + 471473. / 184523293613793840. * std::sqrt (404363576778211096835.) * std::sqrt (3.), + 366179. / 1107139761682763040. * std::sqrt (404363576778211096835.) * std::sqrt (6.), + 378603. / 30753882268965640. * std::sqrt (404363576778211096835.) * std::sqrt (2.), + 216469. / 110713976168276304. * std::sqrt (80872715355642219367.) * std::sqrt (6.), + 253. / 2959022236. * std::sqrt (1083741893935.) * std::sqrt (2.), + 2187. / 5918044472. * std::sqrt (1083741893935.), + 443. / 17754133416. * std::sqrt (1083741893935.) * std::sqrt (3.), + 458. / 2219266677. * std::sqrt (1083741893935.) * std::sqrt (6.), + 93. / 739755559. * std::sqrt (1083741893935.) * std::sqrt (2.), + 34. / 2219266677. * std::sqrt (216748378787.) * std::sqrt (6.), + -415. / 2854752. * std::sqrt (5.) * std::sqrt (29737.) * std::sqrt (2.), + -2281. / 7136880. * std::sqrt (5.) * std::sqrt (29737.), + -457. / 7136880. * std::sqrt (5.) * std::sqrt (29737.) * std::sqrt (3.), + -4867. / 42821280. * std::sqrt (5.) * std::sqrt (29737.) * std::sqrt (6.), + -77. / 1189480. * std::sqrt (5.) * std::sqrt (29737.) * std::sqrt (2.), + -257. / 4282128. * std::sqrt (29737.) * std::sqrt (6.), + -5. / 1048. * std::sqrt (5.) * std::sqrt (131.), + -41. / 10480. * std::sqrt (2.) * std::sqrt (5.) * std::sqrt (131.), + -47. / 31440. * std::sqrt (2.) * std::sqrt (5.) * std::sqrt (131.) * std::sqrt (3.), + 3. / 5240. * std::sqrt (5.) * std::sqrt (131.) * std::sqrt (3.), + -73. / 10480. * std::sqrt (5.) * std::sqrt (131.), + -3. / 2096. * std::sqrt (131.) * std::sqrt (3.), + 5. / 1816. * std::sqrt (3.) * std::sqrt (5.) * std::sqrt (227.), + 3. / 18160. * std::sqrt (2.) * std::sqrt (3.) * std::sqrt (5.) * std::sqrt (227.), + 17. / 3632. * std::sqrt (2.) * std::sqrt (5.) * std::sqrt (227.), + -7. / 9080. * std::sqrt (5.) * std::sqrt (227.), + 5. / 3632. * std::sqrt (3.) * std::sqrt (5.) * std::sqrt (227.), + 37. / 3632. * std::sqrt (227.) }; + break; + + case 4: + mask_coeffs[0] = { 1. / 2., + 0., + 0., + 0., + 0., + 0., + 0., + 0., + 0., + 0., + 0., + 1. / 8., + -(1. / 8.) * std::sqrt (3.), + 0., + 0., + 0., + 0., + 0., + 0., + 0., + 0., + (1. / 8.) * std::sqrt (3.), + 1. / 8., + 0., + 0., + 0., + 0., + 0., + 0., + 0., + -(5. / 24.) * std::sqrt (3.), + -(1. / 24.) * std::sqrt (6.), + (1. / 8.) * std::sqrt (2.), + 1. / 24., + -(1. / 24.) * std::sqrt (3.), + (1. / 24.) * std::sqrt (5.), + 0., + 0., + 0., + 0., + 0., + (3. / 16.) * std::sqrt (2.), + (1. / 16.) * std::sqrt (2.) * std::sqrt (3.), + (1. / 24.) * std::sqrt (3.), + -1. / 16., + -(1. / 48.) * std::sqrt (15.), + 0., + 0., + 0., + 0., + -(1. / 12.) * std::sqrt (15.), + (1. / 48.) * std::sqrt (30.), + -(1. / 16.) * std::sqrt (10.), + (1. / 24.) * std::sqrt (5.), + (1. / 48.) * std::sqrt (15.), + 1. / 48., + 0., + 0., + 0., + 0., + 1. / 8., + -(1. / 16.) * std::sqrt (2.), + (1. / 16.) * std::sqrt (2.) * std::sqrt (3.), + -(1. / 24.) * std::sqrt (3.), + 1. / 8., + -(1. / 24.) * std::sqrt (15.), + 1. / 64., + -(1. / 64.) * std::sqrt (3.), + (1. / 64.) * std::sqrt (5.), + -(1. / 64.) * std::sqrt (7.), + 0., + -(3. / 80.) * std::sqrt (6.), + -(3. / 80.) * std::sqrt (2.), + 1. / 20., + -(1. / 40.) * std::sqrt (3.), + -(1. / 40.) * std::sqrt (5.), + (1. / 64.) * std::sqrt (3.), + -11. / 320., + (1. / 320.) * std::sqrt (5.) * std::sqrt (3.), + (3. / 320.) * std::sqrt (7.) * std::sqrt (3.), + -(1. / 8.) * std::sqrt (5.), + -(1. / 80.) * std::sqrt (10.), + (1. / 80.) * std::sqrt (3.) * std::sqrt (10.), + (3. / 40.) * std::sqrt (5.) * std::sqrt (3.), + (1. / 40.) * std::sqrt (5.), + (1. / 8.) * std::sqrt (3.), + (1. / 64.) * std::sqrt (5.), + -(1. / 320.) * std::sqrt (5.) * std::sqrt (3.), + -3. / 64., + -(1. / 320.) * std::sqrt (7.) * std::sqrt (5.), + 0., + -(3. / 80.) * std::sqrt (14.), + -(1. / 80.) * std::sqrt (3.) * std::sqrt (14.), + (1. / 60.) * std::sqrt (7.) * std::sqrt (3.), + -(1. / 40.) * std::sqrt (7.), + -(1. / 120.) * std::sqrt (15.) * std::sqrt (7.), + (1. / 64.) * std::sqrt (7.), + (3. / 320.) * std::sqrt (7.) * std::sqrt (3.), + (1. / 320.) * std::sqrt (7.) * std::sqrt (5.), + 1. / 320. }; + mask_coeffs[1] = { 1. / 2., + 0., + 0., + 0., + 0., + 0., + 0., + 0., + 0., + 0., + -(1. / 4.) * std::sqrt (2.), + 1. / 4., + 0., + 0., + 0., + 0., + 0., + 0., + 0., + 0., + -(1. / 4.) * std::sqrt (2.) * std::sqrt (3.), + 0., + 1. / 4., + 0., + 0., + 0., + 0., + 0., + 0., + 0., + (1. / 24.) * std::sqrt (3.), + -(1. / 6.) * std::sqrt (6.), + 0., + 1. / 8., + 0., + 0., + 0., + 0., + 0., + 0., + 1. / 8., + -(5. / 16.) * std::sqrt (2.), + -(1. / 16.) * std::sqrt (2.) * std::sqrt (3.), + 0., + 1. / 8., + 0., + 0., + 0., + 0., + 0., + (1. / 24.) * std::sqrt (15.), + (1. / 48.) * std::sqrt (30.), + -(3. / 16.) * std::sqrt (10.), + 0., + 0., + 1. / 8., + 0., + 0., + 0., + 0., + 1. / 16., + (3. / 16.) * std::sqrt (2.), + 0., + -(3. / 16.) * std::sqrt (3.), + 0., + 0., + 1. / 16., + 0., + 0., + 0., + (1. / 16.) * std::sqrt (3.), + (3. / 20.) * std::sqrt (6.), + (3. / 80.) * std::sqrt (2.), + -21. / 80., + -(1. / 10.) * std::sqrt (3.), + 0., + 0., + 1. / 16., + 0., + 0., + (1. / 16.) * std::sqrt (5.), + (3. / 40.) * std::sqrt (10.), + (3. / 80.) * std::sqrt (3.) * std::sqrt (10.), + (7. / 240.) * std::sqrt (5.) * std::sqrt (3.), + -(7. / 40.) * std::sqrt (5.), + -(1. / 24.) * std::sqrt (3.), + 0., + 0., + 1. / 16., + 0., + (1. / 16.) * std::sqrt (7.), + -(3. / 80.) * std::sqrt (14.), + (3. / 40.) * std::sqrt (3.) * std::sqrt (14.), + -(1. / 240.) * std::sqrt (7.) * std::sqrt (3.), + (1. / 40.) * std::sqrt (7.), + -(1. / 24.) * std::sqrt (15.) * std::sqrt (7.), + 0., + 0., + 0., + 1. / 16. }; + mask_coeffs[2] = { 1. / 2., + 0., + 0., + 0., + 0., + 0., + 0., + 0., + 0., + 0., + (1. / 2.) * std::sqrt (2.), + -1. / 8., + -(1. / 8.) * std::sqrt (3.), + 0., + 0., + 0., + 0., + 0., + 0., + 0., + 0., + (1. / 8.) * std::sqrt (3.), + -1. / 8., + 0., + 0., + 0., + 0., + 0., + 0., + 0., + (1. / 8.) * std::sqrt (3.), + -(1. / 8.) * std::sqrt (6.), + -(3. / 8.) * std::sqrt (2.), + 1. / 24., + (1. / 24.) * std::sqrt (3.), + (1. / 24.) * std::sqrt (5.), + 0., + 0., + 0., + 0., + 0., + (3. / 8.) * std::sqrt (2.), + -(1. / 8.) * std::sqrt (2.) * std::sqrt (3.), + -(1. / 24.) * std::sqrt (3.), + -1. / 16., + (1. / 48.) * std::sqrt (15.), + 0., + 0., + 0., + 0., + 0., + 0., + 0., + (1. / 24.) * std::sqrt (5.), + -(1. / 48.) * std::sqrt (15.), + 1. / 48., + 0., + 0., + 0., + 0., + -1. / 4., + -(3. / 16.) * std::sqrt (2.), + -(3. / 16.) * std::sqrt (2.) * std::sqrt (3.), + (1. / 12.) * std::sqrt (3.), + 1. / 4., + (1. / 12.) * std::sqrt (15.), + -1. / 64., + -(1. / 64.) * std::sqrt (3.), + -(1. / 64.) * std::sqrt (5.), + -(1. / 64.) * std::sqrt (7.), + 0., + (3. / 16.) * std::sqrt (6.), + -(3. / 16.) * std::sqrt (2.), + -1. / 4., + -(1. / 8.) * std::sqrt (3.), + (1. / 8.) * std::sqrt (5.), + (1. / 64.) * std::sqrt (3.), + 11. / 320., + (1. / 320.) * std::sqrt (5.) * std::sqrt (3.), + -(3. / 320.) * std::sqrt (7.) * std::sqrt (3.), + 0., + 0., + 0., + (1. / 12.) * std::sqrt (5.) * std::sqrt (3.), + -(1. / 8.) * std::sqrt (5.), + (1. / 24.) * std::sqrt (3.), + -(1. / 64.) * std::sqrt (5.), + -(1. / 320.) * std::sqrt (5.) * std::sqrt (3.), + 3. / 64., + -(1. / 320.) * std::sqrt (7.) * std::sqrt (5.), + 0., + 0., + 0., + 0., + 0., + 0., + (1. / 64.) * std::sqrt (7.), + -(3. / 320.) * std::sqrt (7.) * std::sqrt (3.), + (1. / 320.) * std::sqrt (7.) * std::sqrt (5.), + -1. / 320. }; + mask_coeffs[3] = { 1. / 2., + 0., + 0., + 0., + 0., + 0., + 0., + 0., + 0., + 0., + -(1. / 4.) * std::sqrt (2.), + -1. / 8., + (1. / 8.) * std::sqrt (3.), + 0., + 0., + 0., + 0., + 0., + 0., + 0., + (1. / 4.) * std::sqrt (2.) * std::sqrt (3.), + -(1. / 8.) * std::sqrt (3.), + -1. / 8., + 0., + 0., + 0., + 0., + 0., + 0., + 0., + (1. / 24.) * std::sqrt (3.), + (1. / 12.) * std::sqrt (6.), + -(1. / 4.) * std::sqrt (2.), + 1. / 24., + -(1. / 24.) * std::sqrt (3.), + (1. / 24.) * std::sqrt (5.), + 0., + 0., + 0., + 0., + -1. / 8., + -(1. / 16.) * std::sqrt (2.), + (3. / 16.) * std::sqrt (2.) * std::sqrt (3.), + (1. / 24.) * std::sqrt (3.), + -1. / 16., + -(1. / 48.) * std::sqrt (15.), + 0., + 0., + 0., + 0., + (1. / 24.) * std::sqrt (15.), + -(5. / 48.) * std::sqrt (30.), + -(1. / 16.) * std::sqrt (10.), + (1. / 24.) * std::sqrt (5.), + (1. / 48.) * std::sqrt (15.), + 1. / 48., + 0., + 0., + 0., + 0., + 1. / 16., + -(3. / 32.) * std::sqrt (2.), + (3. / 32.) * std::sqrt (2.) * std::sqrt (3.), + -(1. / 16.) * std::sqrt (3.), + 3. / 16., + -(1. / 16.) * std::sqrt (15.), + -1. / 64., + (1. / 64.) * std::sqrt (3.), + -(1. / 64.) * std::sqrt (5.), + (1. / 64.) * std::sqrt (7.), + -(1. / 16.) * std::sqrt (3.), + (9. / 160.) * std::sqrt (6.), + -(39. / 160.) * std::sqrt (2.), + -1. / 80., + -(3. / 80.) * std::sqrt (3.), + (11. / 80.) * std::sqrt (5.), + -(1. / 64.) * std::sqrt (3.), + 11. / 320., + -(1. / 320.) * std::sqrt (5.) * std::sqrt (3.), + -(3. / 320.) * std::sqrt (7.) * std::sqrt (3.), + (1. / 16.) * std::sqrt (5.), + (3. / 160.) * std::sqrt (10.), + (9. / 160.) * std::sqrt (3.) * std::sqrt (10.), + (13. / 240.) * std::sqrt (5.) * std::sqrt (3.), + -(11. / 80.) * std::sqrt (5.), + -(5. / 48.) * std::sqrt (3.), + -(1. / 64.) * std::sqrt (5.), + (1. / 320.) * std::sqrt (5.) * std::sqrt (3.), + 3. / 64., + (1. / 320.) * std::sqrt (7.) * std::sqrt (5.), + -(1. / 16.) * std::sqrt (7.), + -(21. / 160.) * std::sqrt (14.), + -(3. / 160.) * std::sqrt (3.) * std::sqrt (14.), + (19. / 240.) * std::sqrt (7.) * std::sqrt (3.), + (7. / 80.) * std::sqrt (7.), + (1. / 240.) * std::sqrt (15.) * std::sqrt (7.), + -(1. / 64.) * std::sqrt (7.), + -(3. / 320.) * std::sqrt (7.) * std::sqrt (3.), + -(1. / 320.) * std::sqrt (7.) * std::sqrt (5.), + -1. / 320. }; + + inv_mask_coeffs[0] = { + -(7. / 255.) * std::sqrt (51.), + -(4. / 255.) * std::sqrt (2.) * std::sqrt (51.), + (4. / 85.) * std::sqrt (2.) * std::sqrt (17.), + -(1. / 510.) * std::sqrt (17.), + (1. / 510.) * std::sqrt (51.), + -(1. / 510.) * std::sqrt (17.) * std::sqrt (5.), + (1. / 170.) * std::sqrt (51.), + -(3. / 170.) * std::sqrt (17.), + (1. / 170.) * std::sqrt (51.) * std::sqrt (5.), + -(1. / 170.) * std::sqrt (51.) * std::sqrt (7.), + 0., + -(3. / 85.) * std::sqrt (2.) * std::sqrt (17.), + -(1. / 85.) * std::sqrt (2.) * std::sqrt (17.) * std::sqrt (3.), + -(7. / 510.) * std::sqrt (17.) * std::sqrt (3.), + (7. / 340.) * std::sqrt (17.), + (7. / 1020.) * std::sqrt (17.) * std::sqrt (15.), + 0., + 0., + 0., + 0., + -(2. / 255.) * std::sqrt (255.), + (1. / 255.) * std::sqrt (2.) * std::sqrt (255.), + -(1. / 85.) * std::sqrt (2.) * std::sqrt (85.), + -(9. / 1190.) * std::sqrt (85.), + -(71. / 7140.) * std::sqrt (255.), + (1. / 68.) * std::sqrt (17.), + -(4. / 595.) * std::sqrt (255.), + (6. / 595.) * std::sqrt (85.), + 0., + (2. / 595.) * std::sqrt (255.) * std::sqrt (7.), + 0., + (1. / 85.) * std::sqrt (2.) * std::sqrt (3.) * std::sqrt (119.), + (1. / 85.) * std::sqrt (2.) * std::sqrt (119.), + -(13. / 1785.) * std::sqrt (119.), + (13. / 3570.) * std::sqrt (3.) * std::sqrt (119.), + (13. / 3570.) * std::sqrt (119.) * std::sqrt (5.), + -(5. / 476.) * std::sqrt (3.) * std::sqrt (119.), + -(3. / 476.) * std::sqrt (119.), + -(1. / 476.) * std::sqrt (3.) * std::sqrt (119.) * std::sqrt (5.), + -(1. / 68.) * std::sqrt (17.) * std::sqrt (3.), + -(2. / 85.) * std::sqrt (17.) * std::sqrt (3.), + (1. / 85.) * std::sqrt (2.) * std::sqrt (17.) * std::sqrt (3.), + -(3. / 85.) * std::sqrt (2.) * std::sqrt (17.), + (4. / 595.) * std::sqrt (17.), + (1. / 70.) * std::sqrt (17.) * std::sqrt (3.), + -(1. / 170.) * std::sqrt (17.) * std::sqrt (5.), + -(13. / 2380.) * std::sqrt (17.) * std::sqrt (3.), + (9. / 2380.) * std::sqrt (17.), + (1. / 340.) * std::sqrt (3.) * std::sqrt (17.) * std::sqrt (5.), + (3. / 2380.) * std::sqrt (3.) * std::sqrt (17.) * std::sqrt (7.), + (94. / 151215.) * std::sqrt (30243.), + (29. / 1209720.) * std::sqrt (60486.), + -(29. / 403240.) * std::sqrt (20162.), + -(139. / 151215.) * std::sqrt (10081.), + (139. / 151215.) * std::sqrt (30243.), + -(139. / 151215.) * std::sqrt (10081.) * std::sqrt (5.), + -(39. / 201620.) * std::sqrt (30243.), + (117. / 201620.) * std::sqrt (10081.), + -(39. / 201620.) * std::sqrt (30243.) * std::sqrt (5.), + (39. / 201620.) * std::sqrt (30243.) * std::sqrt (7.), + 0., + -(197. / 374680.) * std::sqrt (28101.), + -(197. / 374680.) * std::sqrt (9367.), + (157. / 562020.) * std::sqrt (9367.) * std::sqrt (2.), + -(157. / 1124040.) * std::sqrt (28101.) * std::sqrt (2.), + -(157. / 1124040.) * std::sqrt (9367.) * std::sqrt (10.), + (1. / 4408.) * std::sqrt (28101.) * std::sqrt (2.), + -(11. / 22040.) * std::sqrt (9367.) * std::sqrt (2.), + (1. / 22040.) * std::sqrt (28101.) * std::sqrt (10.), + (3. / 22040.) * std::sqrt (28101.) * std::sqrt (14.), + (266678. / 4523204162845605.) * std::sqrt (4446309692077229715.), + (13037. / 435971485575480.) * std::sqrt (8892619384154459430.), + -(13037. / 145323828525160.) * std::sqrt (2964206461384819810.), + -(465899. / 3015469441897070.) * std::sqrt (1482103230692409905.), + -(249661. / 18092816651382420.) * std::sqrt (4446309692077229715.), + -(3889. / 14532382852516.) * std::sqrt (296420646138481981.), + -(146223. / 6030938883794140.) * std::sqrt (4446309692077229715.), + (395973. / 6030938883794140.) * std::sqrt (1482103230692409905.), + -(6927. / 70952222162284.) * std::sqrt (889261938415445943.), + (131991. / 6030938883794140.) * std::sqrt (4446309692077229715.) * std::sqrt (7.), + 0., + (1331061. / 33795221994742280.) * std::sqrt (3.) * std::sqrt (5813623063645540717.), + (1331061. / 33795221994742280.) * std::sqrt (5813623063645540717.), + (8528963. / 354849830944793940.) * std::sqrt (5813623063645540717.) * std::sqrt (2.), + -(8528963. / 709699661889587880.) * std::sqrt (3.) * std::sqrt (5813623063645540717.) * std::sqrt (2.), + -(8528963. / 709699661889587880.) * std::sqrt (5813623063645540717.) * std::sqrt (10.), + -(1809547. / 47313310792639192.) * std::sqrt (3.) * std::sqrt (5813623063645540717.) * std::sqrt (2.), + -(6939703. / 236566553963195960.) * std::sqrt (5813623063645540717.) * std::sqrt (2.), + -(1809547. / 236566553963195960.) * std::sqrt (3.) * std::sqrt (5813623063645540717.) * std::sqrt (10.), + -(1593681. / 33795221994742280.) * std::sqrt (3.) * std::sqrt (830517580520791531.) * std::sqrt (2.), + -(94. / 4932503680355.) * std::sqrt (75359839878746133473.), + (1963. / 340081608381595.) * std::sqrt (150719679757492266946.), + -(1963. / 340081608381595.) * std::sqrt (150719679757492266946.) * std::sqrt (3.), + (40174523699. / 2260795196362384004190.) * std::sqrt (75359839878746133473.) * std::sqrt (3.), + (442333. / 46491156345813340.) * std::sqrt (75359839878746133473.), + (283561721. / 54476992683430939860.) * std::sqrt (75359839878746133473.) * std::sqrt (15.), + -(5588564901. / 376799199393730667365.) * std::sqrt (75359839878746133473.), + (2579301. / 395174828939413390.) * std::sqrt (75359839878746133473.) * std::sqrt (3.), + (669837894. / 376799199393730667365.) * std::sqrt (75359839878746133473.) * std::sqrt (5.), + (2579301. / 56453546991344770.) * std::sqrt (10765691411249447639.), + 0., + -(87344. / 5006748678989665.) * std::sqrt (3.) * std::sqrt (4601202035991502135.), + -(87344. / 5006748678989665.) * std::sqrt (4601202035991502135.), + (267897. / 10013497357979330.) * std::sqrt (4601202035991502135.) * std::sqrt (2.), + -(267897. / 20026994715958660.) * std::sqrt (3.) * std::sqrt (4601202035991502135.) * std::sqrt (2.), + -(267897. / 4005398943191732.) * std::sqrt (920240407198300427.) * std::sqrt (2.), + -(1477. / 2002699471595866.) * std::sqrt (3.) * std::sqrt (4601202035991502135.) * std::sqrt (2.), + (373811137. / 18404808143966008540.) * std::sqrt (4601202035991502135.) * std::sqrt (2.), + -(1477. / 2002699471595866.) * std::sqrt (3.) * std::sqrt (920240407198300427.) * std::sqrt (2.), + -(57279771. / 18404808143966008540.) * std::sqrt (3.) * std::sqrt (4601202035991502135.) * std::sqrt (14.), + -(5723. / 663924570947208.) * std::sqrt (3.) * std::sqrt (100630761362055825401.), + -(3443717. / 633229751622794916.) * std::sqrt (2.) * std::sqrt (3.) * std::sqrt (100630761362055825401.), + (3443717. / 211076583874264972.) * std::sqrt (2.) * std::sqrt (100630761362055825401.), + -(81275881577. / 7245414818068019428872.) * std::sqrt (100630761362055825401.), + (37031431. / 3799378509736769496.) * std::sqrt (3.) * std::sqrt (100630761362055825401.), + -(74881715981. / 7245414818068019428872.) * std::sqrt (100630761362055825401.) * std::sqrt (5.), + (3379671293. / 1811353704517004857218.) * std::sqrt (3.) * std::sqrt (100630761362055825401.), + -(1766083. / 316614875811397458.) * std::sqrt (100630761362055825401.), + (3356169269. / 1811353704517004857218.) * std::sqrt (3.) * std::sqrt (100630761362055825401.) * std::sqrt (5.), + -(1766083. / 949844627434192374.) * std::sqrt (3.) * std::sqrt (100630761362055825401.) * std::sqrt (7.), + 0., + -(35634798. / 26420650763090388293945651.) * std::sqrt (2.) * std::sqrt (10381525354495561779357901615959985.), + -(11878266. / 26420650763090388293945651.) * std::sqrt (2.) * std::sqrt (10381525354495561779357901615959985.) + * std::sqrt (3.), + -(20320891. / 26420650763090388293945651.) * std::sqrt (10381525354495561779357901615959985.) * std::sqrt (3.), + (60962673. / 52841301526180776587891302.) * std::sqrt (10381525354495561779357901615959985.), + (101604455. / 52841301526180776587891302.) * std::sqrt (2076305070899112355871580323191997.) * std::sqrt (3.), + -(14864355. / 52841301526180776587891302.) * std::sqrt (10381525354495561779357901615959985.), + (25368661932. / 121402890256400334210680266345.) * std::sqrt (10381525354495561779357901615959985.) + * std::sqrt (3.), + -(14864355. / 52841301526180776587891302.) * std::sqrt (2076305070899112355871580323191997.), + -(20629671003. / 121402890256400334210680266345.) * std::sqrt (10381525354495561779357901615959985.) + * std::sqrt (7.), + (18787831687. / 1800761375403625296168373725677556.) * std::sqrt (34560885477513793515221944312683818679790383.) + * std::sqrt (2.), + -(668109227. / 150063447950302108014031143806463.) * std::sqrt (34560885477513793515221944312683818679790383.), + (668109227. / 50021149316767369338010381268821.) * std::sqrt (11520295159171264505073981437561272893263461.), + (77196499445. / 12605329627825377073178616079742892.) * std::sqrt (11520295159171264505073981437561272893263461.) + * std::sqrt (2.), + (115796597710. / 3151332406956344268294654019935723.) * std::sqrt (34560885477513793515221944312683818679790383.) + * std::sqrt (2.), + -(123516617363. / 6302664813912688536589308039871446.) * std::sqrt (11520295159171264505073981437561272893263461.) + * std::sqrt (10.), + (45350093203. / 3151332406956344268294654019935723.) * std::sqrt (34560885477513793515221944312683818679790383.) + * std::sqrt (2.), + -(53838840385. / 4201776542608459024392872026580964.) * std::sqrt (11520295159171264505073981437561272893263461.) + * std::sqrt (2.), + -(36861346021. / 6302664813912688536589308039871446.) * std::sqrt (34560885477513793515221944312683818679790383.) + * std::sqrt (10.), + -(53838840385. / 1800761375403625296168373725677556.) * std::sqrt (4937269353930541930745992044669116954255769.) + * std::sqrt (2.), + 0., + -(3721507211145. / 4253813477052476837810939317540404681518.) + * std::sqrt (38697087397201085046824763022410609242891822943821145.), + -(3721507211145. / 4253813477052476837810939317540404681518.) + * std::sqrt (12899029132400361682274921007470203080963940981273715.), + (24982247342777. / 44665041509051006797014862834174249155939.) + * std::sqrt (12899029132400361682274921007470203080963940981273715.) * std::sqrt (2.), + -(24982247342777. / 89330083018102013594029725668348498311878.) + * std::sqrt (38697087397201085046824763022410609242891822943821145.) * std::sqrt (2.), + -(124911236713885. / 89330083018102013594029725668348498311878.) + * std::sqrt (2579805826480072336454984201494040616192788196254743.) * std::sqrt (2.), + (92576089466905. / 357320332072408054376118902673393993247512.) + * std::sqrt (38697087397201085046824763022410609242891822943821145.) * std::sqrt (2.), + -(457237098592223. / 595533886787346757293531504455656655412520.) + * std::sqrt (12899029132400361682274921007470203080963940981273715.) * std::sqrt (2.), + (92576089466905. / 357320332072408054376118902673393993247512.) + * std::sqrt (7739417479440217009364952604482121848578364588764229.) * std::sqrt (2.), + (328210312920817. / 255228808623148610268656359052424280891080.) + * std::sqrt (5528155342457297863832109003201515606127403277688735.) * std::sqrt (2.), + (8350465757. / 8356878024197038297510645591805844.) * std::sqrt (4587144914088926523642597305294551825737974887.) + * std::sqrt (2.), + -(12769142503. / 23677821068558275176280162510116558.) + * std::sqrt (4587144914088926523642597305294551825737974887.), + (12769142503. / 23677821068558275176280162510116558.) + * std::sqrt (4587144914088926523642597305294551825737974887.) * std::sqrt (3.), + (9201923168435. / 2983405454638342672211300476274686308.) + * std::sqrt (4587144914088926523642597305294551825737974887.) * std::sqrt (6.), + (766387478705. / 497234242439723778701883412712447718.) + * std::sqrt (4587144914088926523642597305294551825737974887.) * std::sqrt (2.), + (40604474896. / 43873609627034451061930889356980681.) + * std::sqrt (4587144914088926523642597305294551825737974887.) * std::sqrt (30.), + -(816293405917. / 1988936969758895114807533650849790872.) + * std::sqrt (4587144914088926523642597305294551825737974887.) * std::sqrt (2.), + -(408860067185. / 1988936969758895114807533650849790872.) + * std::sqrt (4587144914088926523642597305294551825737974887.) * std::sqrt (6.), + (1634013540287. / 1988936969758895114807533650849790872.) + * std::sqrt (4587144914088926523642597305294551825737974887.) * std::sqrt (10.), + -(408860067185. / 284133852822699302115361950121398696.) + * std::sqrt (655306416298418074806085329327793117962567841.) * std::sqrt (2.), + 0., + -(89565777. / 719808635052720933803909396708.) * std::sqrt (1977693186713807260499140827732350041750787.), + -(29855259. / 719808635052720933803909396708.) * std::sqrt (1977693186713807260499140827732350041750787.) + * std::sqrt (3.), + (1839768913. / 30231962672214279219764194661736.) * std::sqrt (1977693186713807260499140827732350041750787.) + * std::sqrt (6.), + -(1839768913. / 20154641781476186146509463107824.) * std::sqrt (1977693186713807260499140827732350041750787.) + * std::sqrt (2.), + -(1839768913. / 60463925344428558439528389323472.) * std::sqrt (1977693186713807260499140827732350041750787.) + * std::sqrt (30.), + (81180230. / 1259665111342261634156841444239.) * std::sqrt (1977693186713807260499140827732350041750787.) + * std::sqrt (2.), + (1244361635056. / 31004137385467085601502338467054507.) * std::sqrt (1977693186713807260499140827732350041750787.) + * std::sqrt (6.), + (16236046. / 1259665111342261634156841444239.) * std::sqrt (1977693186713807260499140827732350041750787.) + * std::sqrt (10.), + (37584728116. / 4429162483638155085928905495293501.) * std::sqrt (282527598101972465785591546818907148821541.) + * std::sqrt (2.), + -(2251. / 169742459505050280.) * std::sqrt (5.) * std::sqrt (9023182675486518006136327.) * std::sqrt (2.), + (17193353. / 1021020397229942913876.) * std::sqrt (5.) * std::sqrt (9023182675486518006136327.), + -(17193353. / 1021020397229942913876.) * std::sqrt (5.) * std::sqrt (9023182675486518006136327.) * std::sqrt (3.), + -(12610477289. / 5632969531517595055853892.) * std::sqrt (5.) * std::sqrt (9023182675486518006136327.) + * std::sqrt (6.), + (156304241. / 12252244766759314966512.) * std::sqrt (5.) * std::sqrt (9023182675486518006136327.) + * std::sqrt (2.), + -(22842783383. / 1325404595651198836671504.) * std::sqrt (9023182675486518006136327.) * std::sqrt (6.), + -(16279991593. / 4694141276264662546544910.) * std::sqrt (5.) * std::sqrt (9023182675486518006136327.) + * std::sqrt (2.), + -(161419. / 1531530595844914370814.) * std::sqrt (5.) * std::sqrt (9023182675486518006136327.) * std::sqrt (6.), + (17269490063. / 938828255252932509308982.) * std::sqrt (9023182675486518006136327.) * std::sqrt (2.), + -(161419. / 1531530595844914370814.) * std::sqrt (5.) * std::sqrt (9023182675486518006136327.) * std::sqrt (14.), + -(373. / 480099976916990.) * std::sqrt (32801803268852750932905.), + -(1135347. / 8153947833475398352.) * std::sqrt (2.) * std::sqrt (32801803268852750932905.), + (3406041. / 8153947833475398352.) * std::sqrt (2.) * std::sqrt (10933934422950916977635.), + -(65640638071. / 52482885230164401492648.) * std::sqrt (10933934422950916977635.), + -(18382799. / 57077634834327788464.) * std::sqrt (32801803268852750932905.), + -(161144650201. / 104965770460328802985296.) * std::sqrt (2186786884590183395527.), + (241428878713. / 262414426150822007463240.) * std::sqrt (32801803268852750932905.), + -(65168697. / 28538817417163894232.) * std::sqrt (10933934422950916977635.), + (158055233897. / 52482885230164401492648.) * std::sqrt (6560360653770550186581.), + -(21722899. / 4076973916737699176.) * std::sqrt (4685971895550392990415.), + 0., + (7017635. / 145801627650810369817648.) * std::sqrt (2.) * std::sqrt (3810436754575291399844659645971.), + (7017635. / 145801627650810369817648.) * std::sqrt (2.) * std::sqrt (1270145584858430466614886548657.), + (6187581. / 510305696777836294361768.) * std::sqrt (1270145584858430466614886548657.), + -(6187581. / 1020611393555672588723536.) * std::sqrt (3810436754575291399844659645971.), + -(6187581. / 1020611393555672588723536.) * std::sqrt (1270145584858430466614886548657.) * std::sqrt (5.), + -(26679495. / 510305696777836294361768.) * std::sqrt (3810436754575291399844659645971.), + (1418562467701. / 12560154114792884713126195784.) * std::sqrt (1270145584858430466614886548657.), + -(5335899. / 510305696777836294361768.) * std::sqrt (3810436754575291399844659645971.) * std::sqrt (5.), + -(390269612653. / 1794307730684697816160885112.) * std::sqrt (544348107796470199977808520853.), + -(9171027465. / 1630424008205457167101018540673.) * std::sqrt (338531287325211031860850338067150911335.), + -(88455586713. / 32608480164109143342020370813460.) * std::sqrt (2.) + * std::sqrt (338531287325211031860850338067150911335.), + (88455586713. / 32608480164109143342020370813460.) * std::sqrt (2.) + * std::sqrt (338531287325211031860850338067150911335.) * std::sqrt (3.), + -(81801246623. / 1027167125169438015273641680623990.) * std::sqrt (338531287325211031860850338067150911335.) + * std::sqrt (3.), + (2486332315627. / 228259361148764003394142595694220.) * std::sqrt (338531287325211031860850338067150911335.), + -(4540839165427. / 410866850067775206109456672249596.) * std::sqrt (67706257465042206372170067613430182267.) + * std::sqrt (3.), + (923652270883. / 136955616689258402036485557416532.) * std::sqrt (338531287325211031860850338067150911335.), + -(1269299198773. / 228259361148764003394142595694220.) * std::sqrt (338531287325211031860850338067150911335.) + * std::sqrt (3.), + (2997533838223. / 136955616689258402036485557416532.) * std::sqrt (67706257465042206372170067613430182267.), + -(1269299198773. / 32608480164109143342020370813460.) * std::sqrt (48361612475030147408692905438164415905.), + 0., + (12023525118263. / 160217911392840687741864510155458435940.) * std::sqrt (2.) * std::sqrt (3.) + * std::sqrt (2206416960936351241282951190627144110360471628935.), + (12023525118263. / 160217911392840687741864510155458435940.) * std::sqrt (2.) + * std::sqrt (2206416960936351241282951190627144110360471628935.), + (12094494868803. / 80108955696420343870932255077729217970.) + * std::sqrt (2206416960936351241282951190627144110360471628935.), + -(12094494868803. / 160217911392840687741864510155458435940.) * std::sqrt (3.) + * std::sqrt (2206416960936351241282951190627144110360471628935.), + -(12094494868803. / 32043582278568137548372902031091687188.) + * std::sqrt (441283392187270248256590238125428822072094325787.), + -(4831085856893. / 32043582278568137548372902031091687188.) * std::sqrt (3.) + * std::sqrt (2206416960936351241282951190627144110360471628935.), + -(20488202062537. / 160217911392840687741864510155458435940.) + * std::sqrt (2206416960936351241282951190627144110360471628935.), + -(4831085856893. / 32043582278568137548372902031091687188.) * std::sqrt (3.) + * std::sqrt (441283392187270248256590238125428822072094325787.), + -(27822656506393. / 160217911392840687741864510155458435940.) * std::sqrt (3.) + * std::sqrt (315202422990907320183278741518163444337210232705.), + (382806051013. / 98117893498107663442262690982978596536.) + * std::sqrt (2170343179656283287883941488614423306932736027111121.) * std::sqrt (2.), + (407998884011. / 196235786996215326884525381965957193072.) + * std::sqrt (2170343179656283287883941488614423306932736027111121.), + -(407998884011. / 196235786996215326884525381965957193072.) + * std::sqrt (2170343179656283287883941488614423306932736027111121.) * std::sqrt (3.), + (40139427862927. / 12362854580761565593725099063855303163536.) + * std::sqrt (2170343179656283287883941488614423306932736027111121.) * std::sqrt (6.), + (2671462346691. / 392471573992430653769050763931914386144.) + * std::sqrt (2170343179656283287883941488614423306932736027111121.) * std::sqrt (2.), + -(1548883277965. / 24725709161523131187450198127710606327072.) + * std::sqrt (2170343179656283287883941488614423306932736027111121.) * std::sqrt (30.), + (28563276219353. / 8241903053841043729150066042570202109024.) + * std::sqrt (2170343179656283287883941488614423306932736027111121.) * std::sqrt (2.), + -(403877025253. / 392471573992430653769050763931914386144.) + * std::sqrt (2170343179656283287883941488614423306932736027111121.) * std::sqrt (6.), + -(11600441158727. / 8241903053841043729150066042570202109024.) + * std::sqrt (2170343179656283287883941488614423306932736027111121.) * std::sqrt (10.), + -(2827139176771. / 392471573992430653769050763931914386144.) + * std::sqrt (310049025665183326840563069802060472418962289587303.) * std::sqrt (2.), + 0., + (2899091076177. / 12617220303184348439653502251837102088720.) * std::sqrt (3.) + * std::sqrt (115935593470518835817766866162978250334523437784363455.), + (2899091076177. / 12617220303184348439653502251837102088720.) + * std::sqrt (115935593470518835817766866162978250334523437784363455.), + (197131084021567. / 264961626366871317232723547288579143863120.) + * std::sqrt (115935593470518835817766866162978250334523437784363455.) * std::sqrt (2.), + -(197131084021567. / 529923252733742634465447094577158287726240.) * std::sqrt (3.) + * std::sqrt (115935593470518835817766866162978250334523437784363455.) * std::sqrt (2.), + -(197131084021567. / 105984650546748526893089418915431657545248.) + * std::sqrt (23187118694103767163553373232595650066904687556872691.) * std::sqrt (2.), + (47797679863537. / 105984650546748526893089418915431657545248.) * std::sqrt (3.) + * std::sqrt (115935593470518835817766866162978250334523437784363455.) * std::sqrt (2.), + (49120096363207. / 176641084244580878155149031525719429242080.) + * std::sqrt (115935593470518835817766866162978250334523437784363455.) * std::sqrt (2.), + (47797679863537. / 105984650546748526893089418915431657545248.) * std::sqrt (3.) + * std::sqrt (23187118694103767163553373232595650066904687556872691.) * std::sqrt (2.), + (47230929935107. / 529923252733742634465447094577158287726240.) * std::sqrt (3.) + * std::sqrt (115935593470518835817766866162978250334523437784363455.) * std::sqrt (14.), + -(20515. / 2286773113391511064.) * std::sqrt (5.) * std::sqrt (77136233821044229928744879.) * std::sqrt (2.), + (3243379. / 426387269065530351920.) * std::sqrt (5.) * std::sqrt (77136233821044229928744879.), + -(3243379. / 426387269065530351920.) * std::sqrt (5.) * std::sqrt (77136233821044229928744879.) * std::sqrt (3.), + -(13885376191. / 20173660861297437540390960.) * std::sqrt (5.) * std::sqrt (77136233821044229928744879.) + * std::sqrt (6.), + (88626309. / 5969421766917424926880.) * std::sqrt (5.) * std::sqrt (77136233821044229928744879.) * std::sqrt (2.), + -(130913345459. / 8069464344518975016156384.) * std::sqrt (77136233821044229928744879.) * std::sqrt (6.), + -(18963554641. / 2689821448172991672052128.) * std::sqrt (5.) * std::sqrt (77136233821044229928744879.) + * std::sqrt (2.), + (10834321. / 5969421766917424926880.) * std::sqrt (5.) * std::sqrt (77136233821044229928744879.) * std::sqrt (6.), + (45998322779. / 2689821448172991672052128.) * std::sqrt (77136233821044229928744879.) * std::sqrt (2.), + (10834321. / 852774538131060703840.) * std::sqrt (5.) * std::sqrt (11019461974434889989820697.) * std::sqrt (2.), + 0., + (47379. / 2602262296426928432.) * std::sqrt (9902847527964529535108767.), + (15793. / 2602262296426928432.) * std::sqrt (9902847527964529535108767.) * std::sqrt (3.), + -(175059. / 18215836074988499024.) * std::sqrt (9902847527964529535108767.) * std::sqrt (6.), + (525177. / 36431672149976998048.) * std::sqrt (9902847527964529535108767.) * std::sqrt (2.), + (175059. / 36431672149976998048.) * std::sqrt (9902847527964529535108767.) * std::sqrt (30.), + (427985. / 36431672149976998048.) * std::sqrt (9902847527964529535108767.) * std::sqrt (2.), + -(23211713163. / 1741980403851150161665120.) * std::sqrt (9902847527964529535108767.) * std::sqrt (6.), + (85597. / 36431672149976998048.) * std::sqrt (9902847527964529535108767.) * std::sqrt (10.), + (15794763577. / 1741980403851150161665120.) * std::sqrt (9902847527964529535108767.) * std::sqrt (14.), + (7. / 509624628712.) * std::sqrt (5.) * std::sqrt (14155690870266160523.), + (27. / 1338360810160.) * std::sqrt (2.) * std::sqrt (5.) * std::sqrt (14155690870266160523.), + -(27. / 1338360810160.) * std::sqrt (2.) * std::sqrt (5.) * std::sqrt (14155690870266160523.) * std::sqrt (3.), + -(11872673. / 2247926207894052840.) * std::sqrt (5.) * std::sqrt (14155690870266160523.) * std::sqrt (3.), + -(9909. / 332582661324760.) * std::sqrt (5.) * std::sqrt (14155690870266160523.), + (1235131. / 64226463082687224.) * std::sqrt (14155690870266160523.) * std::sqrt (3.), + (12002545. / 1198893977543494848.) * std::sqrt (5.) * std::sqrt (14155690870266160523.), + -(1431. / 85827783567680.) * std::sqrt (5.) * std::sqrt (14155690870266160523.) * std::sqrt (3.), + (19982563. / 171270568220499264.) * std::sqrt (14155690870266160523.), + -(1431. / 12261111938240.) * std::sqrt (5.) * std::sqrt (2022241552895165789.), + 0., + -(71. / 33562341360905874.) * std::sqrt (7.) * std::sqrt (2111308236157200531699814841.), + -(71. / 100687024082717622.) * std::sqrt (7.) * std::sqrt (2111308236157200531699814841.) * std::sqrt (3.), + (367. / 1057213752868535031.) * std::sqrt (7.) * std::sqrt (2111308236157200531699814841.) * std::sqrt (6.), + -(367. / 704809168579023354.) * std::sqrt (7.) * std::sqrt (2111308236157200531699814841.) * std::sqrt (2.), + -(367. / 2114427505737070062.) * std::sqrt (7.) * std::sqrt (2111308236157200531699814841.) * std::sqrt (30.), + -(8215. / 5638473348632186832.) * std::sqrt (7.) * std::sqrt (2111308236157200531699814841.) * std::sqrt (2.), + (338305669. / 179735735443232008914720.) * std::sqrt (7.) * std::sqrt (2111308236157200531699814841.) + * std::sqrt (6.), + -(1643. / 5638473348632186832.) * std::sqrt (7.) * std::sqrt (2111308236157200531699814841.) * std::sqrt (10.), + -(659421703. / 77029600904242289534880.) * std::sqrt (2111308236157200531699814841.) * std::sqrt (2.), + -(63. / 299336.) * std::sqrt (7.) * std::sqrt (187085.), + (3. / 42160.) * std::sqrt (2.) * std::sqrt (7.) * std::sqrt (187085.), + -(3. / 42160.) * std::sqrt (2.) * std::sqrt (7.) * std::sqrt (187085.) * std::sqrt (3.), + -(197. / 4490040.) * std::sqrt (7.) * std::sqrt (187085.) * std::sqrt (3.), + -(1101. / 10476760.) * std::sqrt (7.) * std::sqrt (187085.), + (109. / 6286056.) * std::sqrt (7.) * std::sqrt (37417.) * std::sqrt (3.), + (887. / 2394688.) * std::sqrt (7.) * std::sqrt (187085.), + -(159. / 2703680.) * std::sqrt (7.) * std::sqrt (187085.) * std::sqrt (3.), + -(21187. / 16762816.) * std::sqrt (7.) * std::sqrt (37417.), + -(159. / 386240.) * std::sqrt (187085.), + 0., + -(781. / 632764920.) * std::sqrt (7.) * std::sqrt (13736271805.), + -(781. / 1898294760.) * std::sqrt (7.) * std::sqrt (13736271805.) * std::sqrt (3.), + (4037. / 19932094980.) * std::sqrt (7.) * std::sqrt (13736271805.) * std::sqrt (6.), + -(4037. / 13288063320.) * std::sqrt (7.) * std::sqrt (13736271805.) * std::sqrt (2.), + -(4037. / 7972837992.) * std::sqrt (7.) * std::sqrt (2747254361.) * std::sqrt (6.), + -(18073. / 21260901312.) * std::sqrt (7.) * std::sqrt (13736271805.) * std::sqrt (2.), + -(5263. / 7086967104.) * std::sqrt (7.) * std::sqrt (13736271805.) * std::sqrt (6.), + -(18073. / 21260901312.) * std::sqrt (7.) * std::sqrt (2747254361.) * std::sqrt (2.), + (1603. / 3037271616.) * std::sqrt (13736271805.) * std::sqrt (2.) + }; + inv_mask_coeffs[1] = { + (1. / 255.) * std::sqrt (51.), + 0., + 0., + -(3. / 34.) * std::sqrt (17.), + 0., + 0., + (8. / 255.) * std::sqrt (51.), + 0., + 0., + 0., + -(1. / 85.) * std::sqrt (17.), + 0., + 0., + (1. / 17.) * std::sqrt (17.) * std::sqrt (3.), + (1. / 34.) * std::sqrt (17.), + 0., + -(3. / 85.) * std::sqrt (17.), + -(1. / 51.) * std::sqrt (17.) * std::sqrt (3.), + 0., + 0., + (1. / 255.) * std::sqrt (255.), + 0., + 0., + -(5. / 357.) * std::sqrt (85.), + -(8. / 357.) * std::sqrt (255.), + -(5. / 714.) * std::sqrt (17.), + -(3. / 595.) * std::sqrt (255.), + (3. / 119.) * std::sqrt (85.), + (4. / 357.) * std::sqrt (51.), + 0., + (1. / 255.) * std::sqrt (3.) * std::sqrt (119.), + 0., + 0., + (13. / 714.) * std::sqrt (119.), + -(1. / 42.) * std::sqrt (3.) * std::sqrt (119.), + -(5. / 714.) * std::sqrt (119.) * std::sqrt (5.), + (3. / 2380.) * std::sqrt (3.) * std::sqrt (119.), + -(3. / 476.) * std::sqrt (119.), + (3. / 476.) * std::sqrt (3.) * std::sqrt (119.) * std::sqrt (5.), + (1. / 204.) * std::sqrt (17.) * std::sqrt (3.), + (1. / 85.) * std::sqrt (17.) * std::sqrt (3.), + 0., + 0., + -(3. / 238.) * std::sqrt (17.), + (5. / 238.) * std::sqrt (17.) * std::sqrt (3.), + -(15. / 238.) * std::sqrt (17.) * std::sqrt (5.), + -(1. / 2380.) * std::sqrt (17.) * std::sqrt (3.), + (1. / 476.) * std::sqrt (17.), + -(1. / 476.) * std::sqrt (3.) * std::sqrt (17.) * std::sqrt (5.), + (1. / 68.) * std::sqrt (3.) * std::sqrt (17.) * std::sqrt (7.), + (23. / 151215.) * std::sqrt (30243.), + (1. / 2372.) * std::sqrt (60486.), + 0., + (33. / 20162.) * std::sqrt (10081.), + 0., + 0., + -(326. / 151215.) * std::sqrt (30243.), + 0., + 0., + 0., + (9. / 46835.) * std::sqrt (28101.) * std::sqrt (2.), + (8. / 8265.) * std::sqrt (28101.), + (1. / 11020.) * std::sqrt (9367.), + (293. / 187340.) * std::sqrt (9367.) * std::sqrt (2.), + (1. / 6460.) * std::sqrt (28101.) * std::sqrt (2.), + 0., + -(433. / 281010.) * std::sqrt (28101.) * std::sqrt (2.), + -(65. / 56202.) * std::sqrt (9367.) * std::sqrt (2.), + 0., + 0., + -(127474. / 4523204162845605.) * std::sqrt (4446309692077229715.), + -(20209. / 354761110811420.) * std::sqrt (8892619384154459430.), + -(3. / 149562019735.) * std::sqrt (2964206461384819810.), + -(984289. / 9046408325691210.) * std::sqrt (1482103230692409905.), + -(869. / 15255326012970.) * std::sqrt (4446309692077229715.), + -(16. / 1525532601297.) * std::sqrt (296420646138481981.), + (12921. / 1507734720948535.) * std::sqrt (4446309692077229715.), + (162. / 508510867099.) * std::sqrt (1482103230692409905.), + (97. / 1525532601297.) * std::sqrt (889261938415445943.), + 0., + (153974. / 12673208248028355.) * std::sqrt (3.) * std::sqrt (5813623063645540717.) * std::sqrt (2.), + (23786. / 745482838119315.) * std::sqrt (3.) * std::sqrt (5813623063645540717.), + (33967. / 993977117492420.) * std::sqrt (5813623063645540717.), + -(1042249. / 354849830944793940.) * std::sqrt (5813623063645540717.) * std::sqrt (2.), + (24971. / 719776533356580.) * std::sqrt (3.) * std::sqrt (5813623063645540717.) * std::sqrt (2.), + (283. / 64401058247694.) * std::sqrt (5813623063645540717.) * std::sqrt (10.), + (5334053. / 177424915472396970.) * std::sqrt (3.) * std::sqrt (5813623063645540717.) * std::sqrt (2.), + -(3196337. / 35484983094479394.) * std::sqrt (5813623063645540717.) * std::sqrt (2.), + -(215. / 10733509707949.) * std::sqrt (3.) * std::sqrt (5813623063645540717.) * std::sqrt (10.), + -(32. / 4600075589121.) * std::sqrt (3.) * std::sqrt (830517580520791531.) * std::sqrt (2.), + (511320053. / 53828457056247238195.) * std::sqrt (75359839878746133473.), + (9375967. / 2533103861470458268.) * std::sqrt (150719679757492266946.), + (18643987. / 2533103861470458268.) * std::sqrt (150719679757492266946.) * std::sqrt (3.), + -(1606374893. / 226079519636238400419.) * std::sqrt (75359839878746133473.) * std::sqrt (3.), + (1676163449. / 75359839878746133473.) * std::sqrt (75359839878746133473.), + (1514493269. / 452159039272476800838.) * std::sqrt (75359839878746133473.) * std::sqrt (15.), + -(15174623133. / 1507196797574922669460.) * std::sqrt (75359839878746133473.), + (4230197143. / 301439359514984533892.) * std::sqrt (75359839878746133473.) * std::sqrt (3.), + -(7526450769. / 301439359514984533892.) * std::sqrt (75359839878746133473.) * std::sqrt (5.), + -(53. / 1415155355084.) * std::sqrt (10765691411249447639.), + (250819. / 15020246036968995.) * std::sqrt (3.) * std::sqrt (4601202035991502135.) * std::sqrt (2.), + -(1435130777. / 55214424431898025620.) * std::sqrt (3.) * std::sqrt (4601202035991502135.), + (2165452619. / 18404808143966008540.) * std::sqrt (4601202035991502135.), + (173311837. / 13803606107974506405.) * std::sqrt (4601202035991502135.) * std::sqrt (2.), + -(517390183. / 27607212215949012810.) * std::sqrt (3.) * std::sqrt (4601202035991502135.) * std::sqrt (2.), + (601660837. / 2760721221594901281.) * std::sqrt (920240407198300427.) * std::sqrt (2.), + (322980269. / 110428848863796051240.) * std::sqrt (3.) * std::sqrt (4601202035991502135.) * std::sqrt (2.), + -(1421033857. / 110428848863796051240.) * std::sqrt (4601202035991502135.) * std::sqrt (2.), + (355566707. / 7361923257586403416.) * std::sqrt (3.) * std::sqrt (920240407198300427.) * std::sqrt (2.), + -(4281870479. / 110428848863796051240.) * std::sqrt (3.) * std::sqrt (4601202035991502135.) * std::sqrt (14.), + (6189762347. / 7245414818068019428872.) * std::sqrt (3.) * std::sqrt (100630761362055825401.), + (253307003. / 402523045448223301604.) * std::sqrt (2.) * std::sqrt (3.) * std::sqrt (100630761362055825401.), + -(21974693. / 100630761362055825401.) * std::sqrt (2.) * std::sqrt (100630761362055825401.), + -(19928784305. / 2415138272689339809624.) * std::sqrt (100630761362055825401.), + -(50387337. / 100630761362055825401.) * std::sqrt (3.) * std::sqrt (100630761362055825401.), + -(13789958. / 301892284086167476203.) * std::sqrt (100630761362055825401.) * std::sqrt (5.), + -(20563480799. / 905676852258502428609.) * std::sqrt (3.) * std::sqrt (100630761362055825401.), + (232348571. / 100630761362055825401.) * std::sqrt (100630761362055825401.), + (51569245. / 301892284086167476203.) * std::sqrt (3.) * std::sqrt (100630761362055825401.) * std::sqrt (5.), + (4767191. / 301892284086167476203.) * std::sqrt (3.) * std::sqrt (100630761362055825401.) * std::sqrt (7.), + -(8048826. / 26420650763090388293945651.) * std::sqrt (10381525354495561779357901615959985.), + -(114250710039. / 485611561025601336842721065380.) * std::sqrt (2.) + * std::sqrt (10381525354495561779357901615959985.), + -(8162038241. / 1456834683076804010528163196140.) * std::sqrt (2.) + * std::sqrt (10381525354495561779357901615959985.) * std::sqrt (3.), + (268482057928. / 364208670769201002632040799035.) * std::sqrt (10381525354495561779357901615959985.) + * std::sqrt (3.), + (30816338733. / 242805780512800668421360532690.) * std::sqrt (10381525354495561779357901615959985.), + -(7158211. / 11206420639052338542524332278.) * std::sqrt (2076305070899112355871580323191997.) * std::sqrt (3.), + (409441277441. / 97122312205120267368544213076.) * std::sqrt (10381525354495561779357901615959985.), + (318008231329. / 485611561025601336842721065380.) * std::sqrt (10381525354495561779357901615959985.) + * std::sqrt (3.), + (52517993. / 7470947092701559028349554852.) * std::sqrt (2076305070899112355871580323191997.), + (5006013. / 37354735463507795141747774260.) * std::sqrt (10381525354495561779357901615959985.) * std::sqrt (7.), + -(9718477427. / 1800761375403625296168373725677556.) * std::sqrt (34560885477513793515221944312683818679790383.) + * std::sqrt (2.), + -(4599920803. / 600253791801208432056124575225852.) * std::sqrt (34560885477513793515221944312683818679790383.), + -(545076871. / 200084597267069477352041525075284.) * std::sqrt (11520295159171264505073981437561272893263461.), + (101963879233. / 4201776542608459024392872026580964.) * std::sqrt (11520295159171264505073981437561272893263461.) + * std::sqrt (2.), + (5279879750. / 1050444135652114756098218006645241.) * std::sqrt (34560885477513793515221944312683818679790383.) + * std::sqrt (2.), + -(20031233. / 700296090434743170732145337763494.) * std::sqrt (11520295159171264505073981437561272893263461.) + * std::sqrt (10.), + (431396358845. / 25210659255650754146357232159485784.) * std::sqrt (34560885477513793515221944312683818679790383.) + * std::sqrt (2.), + (211748147575. / 2801184361738972682928581351053976.) * std::sqrt (11520295159171264505073981437561272893263461.) + * std::sqrt (2.), + (6376206841. / 2801184361738972682928581351053976.) * std::sqrt (34560885477513793515221944312683818679790383.) + * std::sqrt (10.), + (518455699. / 1200507583602416864112249150451704.) * std::sqrt (4937269353930541930745992044669116954255769.) + * std::sqrt (2.), + -(4191737066993. / 25522880862314861026865635905242428089108.) + * std::sqrt (38697087397201085046824763022410609242891822943821145.) * std::sqrt (2.), + -(21698614438913. / 127614404311574305134328179526212140445540.) + * std::sqrt (38697087397201085046824763022410609242891822943821145.), + -(3317655634739. / 42538134770524768378109393175404046815180.) + * std::sqrt (12899029132400361682274921007470203080963940981273715.), + (74078482457633. / 297766943393673378646765752227828327706260.) + * std::sqrt (12899029132400361682274921007470203080963940981273715.) * std::sqrt (2.), + (83956918856843. / 223325207545255033985074314170871245779695.) + * std::sqrt (38697087397201085046824763022410609242891822943821145.) * std::sqrt (2.), + (295519271611. / 2290514949182102912667428863290987136202.) + * std::sqrt (2579805826480072336454984201494040616192788196254743.) * std::sqrt (2.), + -(138036952528357. / 357320332072408054376118902673393993247512.) + * std::sqrt (38697087397201085046824763022410609242891822943821145.) * std::sqrt (2.), + (1541004269138889. / 595533886787346757293531504455656655412520.) + * std::sqrt (12899029132400361682274921007470203080963940981273715.) * std::sqrt (2.), + (32450167149109. / 27486179390185234952009146359491845634424.) + * std::sqrt (7739417479440217009364952604482121848578364588764229.) * std::sqrt (2.), + (246712773001. / 6544328426234579750478368180831391817720.) + * std::sqrt (5528155342457297863832109003201515606127403277688735.) * std::sqrt (2.), + -(33277657927. / 71033463205674825528840487530349674.) + * std::sqrt (4587144914088926523642597305294551825737974887.) * std::sqrt (2.), + -(8018575811. / 47355642137116550352560325020233116.) + * std::sqrt (4587144914088926523642597305294551825737974887.), + -(34451999. / 15785214045705516784186775006744372.) * std::sqrt (4587144914088926523642597305294551825737974887.) + * std::sqrt (3.), + -(45393743092. / 82872373739953963116980568785407953.) + * std::sqrt (4587144914088926523642597305294551825737974887.) * std::sqrt (6.), + (180643383910. / 82872373739953963116980568785407953.) + * std::sqrt (4587144914088926523642597305294551825737974887.) * std::sqrt (2.), + (30493809896. / 248617121219861889350941706356223859.) + * std::sqrt (4587144914088926523642597305294551825737974887.) * std::sqrt (30.), + -(3252866603635. / 1988936969758895114807533650849790872.) + * std::sqrt (4587144914088926523642597305294551825737974887.) * std::sqrt (2.), + (674188168057. / 662978989919631704935844550283263624.) + * std::sqrt (4587144914088926523642597305294551825737974887.) * std::sqrt (6.), + (1188336998305. / 662978989919631704935844550283263624.) + * std::sqrt (4587144914088926523642597305294551825737974887.) * std::sqrt (10.), + (117736564615. / 94711284274233100705120650040466232.) + * std::sqrt (655306416298418074806085329327793117962567841.) * std::sqrt (2.), + -(41048131. / 1439617270105441867607818793416.) * std::sqrt (1977693186713807260499140827732350041750787.) + * std::sqrt (2.), + (68018605097. / 35433299869105240687431243962348008.) * std::sqrt (1977693186713807260499140827732350041750787.), + -(517957494301. / 35433299869105240687431243962348008.) * std::sqrt (1977693186713807260499140827732350041750787.) + * std::sqrt (3.), + -(3724728319187. / 186024824312802513609014030802327042.) + * std::sqrt (1977693186713807260499140827732350041750787.) * std::sqrt (6.), + (12542538732513. / 248033099083736684812018707736436056.) + * std::sqrt (1977693186713807260499140827732350041750787.) * std::sqrt (2.), + (309921001591. / 26574974901828930515573432971761006.) * std::sqrt (1977693186713807260499140827732350041750787.) + * std::sqrt (30.), + (9072405014777. / 124016549541868342406009353868218028.) + * std::sqrt (1977693186713807260499140827732350041750787.) * std::sqrt (2.), + -(10691339142787. / 124016549541868342406009353868218028.) + * std::sqrt (1977693186713807260499140827732350041750787.) * std::sqrt (6.), + (1867258485609. / 17716649934552620343715621981174004.) * std::sqrt (1977693186713807260499140827732350041750787.) + * std::sqrt (10.), + (4537812639663. / 17716649934552620343715621981174004.) * std::sqrt (282527598101972465785591546818907148821541.) + * std::sqrt (2.), + (31062823019. / 4694141276264662546544910.) * std::sqrt (5.) * std::sqrt (9023182675486518006136327.) + * std::sqrt (2.), + -(4634055305. / 1251771007003910012411976.) * std::sqrt (5.) * std::sqrt (9023182675486518006136327.), + (1962584999. / 417257002334636670803992.) * std::sqrt (5.) * std::sqrt (9023182675486518006136327.) + * std::sqrt (3.), + -(10187602871. / 6258855035019550062059880.) * std::sqrt (5.) * std::sqrt (9023182675486518006136327.) + * std::sqrt (6.), + (11422843913. / 1564713758754887515514970.) * std::sqrt (5.) * std::sqrt (9023182675486518006136327.) + * std::sqrt (2.), + -(94854218441. / 3755313021011730037235928.) * std::sqrt (9023182675486518006136327.) * std::sqrt (6.), + (5635365389. / 1877656510505865018617964.) * std::sqrt (5.) * std::sqrt (9023182675486518006136327.) + * std::sqrt (2.), + -(12702606877. / 3129427517509775031029940.) * std::sqrt (5.) * std::sqrt (9023182675486518006136327.) + * std::sqrt (6.), + (25562403581. / 625885503501955006205988.) * std::sqrt (9023182675486518006136327.) * std::sqrt (2.), + -(75102359527. / 3129427517509775031029940.) * std::sqrt (5.) * std::sqrt (9023182675486518006136327.) + * std::sqrt (14.), + -(3774560557. / 37487775164403143923320.) * std::sqrt (32801803268852750932905.), + -(1680989081. / 4998370021920419189776.) * std::sqrt (2.) * std::sqrt (32801803268852750932905.), + -(186987211. / 4998370021920419189776.) * std::sqrt (2.) * std::sqrt (10933934422950916977635.), + -(147387757597. / 131207213075411003731620.) * std::sqrt (10933934422950916977635.), + -(1728483959. / 4685971895550392990415.) * std::sqrt (32801803268852750932905.), + -(2718845969. / 7497555032880628784664.) * std::sqrt (2186786884590183395527.), + -(31227737353. / 26241442615082200746324.) * std::sqrt (32801803268852750932905.), + (2390149057. / 6247962527400523987220.) * std::sqrt (10933934422950916977635.), + -(4219853141. / 3748777516440314392332.) * std::sqrt (6560360653770550186581.), + -(5158205801. / 18743887582201571961660.) * std::sqrt (4685971895550392990415.), + -(1546701. / 72900813825405184908824.) * std::sqrt (3810436754575291399844659645971.), + -(168180502375. / 3588615461369395632321770224.) * std::sqrt (2.) * std::sqrt (3810436754575291399844659645971.), + -(15321775471. / 10765846384108186896965310672.) * std::sqrt (2.) * std::sqrt (1270145584858430466614886548657.), + -(2334013807429. / 9420115586094663534844646838.) * std::sqrt (1270145584858430466614886548657.), + (61462552931. / 37680462344378654139378587352.) * std::sqrt (3810436754575291399844659645971.), + (1330663552. / 672865399006761681060331917.) * std::sqrt (1270145584858430466614886548657.) * std::sqrt (5.), + -(2821342734059. / 18840231172189327069689293676.) * std::sqrt (3810436754575291399844659645971.), + -(897994103569. / 18840231172189327069689293676.) * std::sqrt (1270145584858430466614886548657.), + (17586111443. / 2691461596027046724241327668.) * std::sqrt (3810436754575291399844659645971.) * std::sqrt (5.), + (7506033509. / 897153865342348908080442556.) * std::sqrt (544348107796470199977808520853.), + (29033791627. / 9782544049232743002606111244038.) * std::sqrt (338531287325211031860850338067150911335.), + (571997989609. / 97825440492327430026061112440380.) * std::sqrt (2.) + * std::sqrt (338531287325211031860850338067150911335.), + (15445557757. / 97825440492327430026061112440380.) * std::sqrt (2.) + * std::sqrt (338531287325211031860850338067150911335.) * std::sqrt (3.), + (4962615991294. / 513583562584719007636820840311995.) * std::sqrt (338531287325211031860850338067150911335.) + * std::sqrt (3.), + (31724240899. / 57064840287191000848535648923555.) * std::sqrt (338531287325211031860850338067150911335.), + -(24740566081. / 29347632147698229007818333732114.) * std::sqrt (67706257465042206372170067613430182267.) + * std::sqrt (3.), + (687660039299. / 68477808344629201018242778708266.) * std::sqrt (338531287325211031860850338067150911335.), + (207462820181. / 34238904172314600509121389354133.) * std::sqrt (338531287325211031860850338067150911335.) + * std::sqrt (3.), + -(14001662081. / 1630424008205457167101018540673.) * std::sqrt (67706257465042206372170067613430182267.), + -(21129732215. / 9782544049232743002606111244038.) * std::sqrt (48361612475030147408692905438164415905.), + (380882463935. / 16021791139284068774186451015545843594.) * std::sqrt (3.) + * std::sqrt (2206416960936351241282951190627144110360471628935.), + (8047101277511. / 160217911392840687741864510155458435940.) * std::sqrt (2.) * std::sqrt (3.) + * std::sqrt (2206416960936351241282951190627144110360471628935.), + (7996749319427. / 480653734178522063225593530466375307820.) * std::sqrt (2.) + * std::sqrt (2206416960936351241282951190627144110360471628935.), + (87580586621218. / 841144034812413610644788678316156788685.) + * std::sqrt (2206416960936351241282951190627144110360471628935.), + (140216775842933. / 1682288069624827221289577356632313577370.) * std::sqrt (3.) + * std::sqrt (2206416960936351241282951190627144110360471628935.), + (6410679726415. / 168228806962482722128957735663231357737.) + * std::sqrt (441283392187270248256590238125428822072094325787.), + (1462012559648. / 168228806962482722128957735663231357737.) * std::sqrt (3.) + * std::sqrt (2206416960936351241282951190627144110360471628935.), + (264686913258637. / 1682288069624827221289577356632313577370.) + * std::sqrt (2206416960936351241282951190627144110360471628935.), + (40537497879073. / 336457613924965444257915471326462715474.) * std::sqrt (3.) + * std::sqrt (441283392187270248256590238125428822072094325787.), + (725006979794. / 40054477848210171935466127538864608985.) * std::sqrt (3.) + * std::sqrt (315202422990907320183278741518163444337210232705.), + -(957643077667. / 588707360988645980653576145897871579216.) + * std::sqrt (2170343179656283287883941488614423306932736027111121.) * std::sqrt (2.), + -(2730479074427. / 588707360988645980653576145897871579216.) + * std::sqrt (2170343179656283287883941488614423306932736027111121.), + -(495706085759. / 588707360988645980653576145897871579216.) + * std::sqrt (2170343179656283287883941488614423306932736027111121.) * std::sqrt (3.), + -(440793649411. / 441530520741484485490182109423403684412.) + * std::sqrt (2170343179656283287883941488614423306932736027111121.) * std::sqrt (6.), + -(1276483125567. / 343412627243376822047919418440425087876.) + * std::sqrt (2170343179656283287883941488614423306932736027111121.) * std::sqrt (2.), + -(292084042297. / 1766122082965937941960728437693614737648.) + * std::sqrt (2170343179656283287883941488614423306932736027111121.) * std::sqrt (30.), + -(52091814613. / 294353680494322990326788072948935789608.) + * std::sqrt (2170343179656283287883941488614423306932736027111121.) * std::sqrt (2.), + -(2918670140159. / 2060475763460260932287516510642550527256.) + * std::sqrt (2170343179656283287883941488614423306932736027111121.) * std::sqrt (6.), + -(106887841407. / 98117893498107663442262690982978596536.) + * std::sqrt (2170343179656283287883941488614423306932736027111121.) * std::sqrt (10.), + -(132007635397. / 147176840247161495163394036474467894804.) + * std::sqrt (310049025665183326840563069802060472418962289587303.) * std::sqrt (2.), + -(470853666287. / 7570332181910609063792101351102261253232.) * std::sqrt (3.) + * std::sqrt (115935593470518835817766866162978250334523437784363455.) * std::sqrt (2.), + (1349368536607. / 37851660909553045318960506755511306266160.) * std::sqrt (3.) + * std::sqrt (115935593470518835817766866162978250334523437784363455.), + -(4464919479629. / 12617220303184348439653502251837102088720.) + * std::sqrt (115935593470518835817766866162978250334523437784363455.), + (7565400655297. / 22080135530572609769393628940714928655260.) + * std::sqrt (115935593470518835817766866162978250334523437784363455.) * std::sqrt (2.), + -(8614641795799. / 37851660909553045318960506755511306266160.) * std::sqrt (3.) + * std::sqrt (115935593470518835817766866162978250334523437784363455.) * std::sqrt (2.), + -(2311500363317. / 4416027106114521953878725788142985731052.) + * std::sqrt (23187118694103767163553373232595650066904687556872691.) * std::sqrt (2.), + (2632981589375. / 26496162636687131723272354728857914386312.) * std::sqrt (3.) + * std::sqrt (115935593470518835817766866162978250334523437784363455.) * std::sqrt (2.), + (134507199129. / 6308610151592174219826751125918551044360.) + * std::sqrt (115935593470518835817766866162978250334523437784363455.) * std::sqrt (2.), + -(7547606989465. / 13248081318343565861636177364428957193156.) * std::sqrt (3.) + * std::sqrt (23187118694103767163553373232595650066904687556872691.) * std::sqrt (2.), + -(1182590560499. / 44160271061145219538787257881429857310520.) * std::sqrt (3.) + * std::sqrt (115935593470518835817766866162978250334523437784363455.) * std::sqrt (14.), + (617869031. / 192130103440927976575152.) * std::sqrt (5.) * std::sqrt (77136233821044229928744879.) + * std::sqrt (2.), + -(3742345477. / 960650517204639882875760.) * std::sqrt (5.) * std::sqrt (77136233821044229928744879.), + (5898629039. / 960650517204639882875760.) * std::sqrt (5.) * std::sqrt (77136233821044229928744879.) + * std::sqrt (3.), + -(4403543891. / 5043415215324359385097740.) * std::sqrt (5.) * std::sqrt (77136233821044229928744879.) + * std::sqrt (6.), + (57598589. / 560379468369373265010860.) * std::sqrt (5.) * std::sqrt (77136233821044229928744879.) + * std::sqrt (2.), + (7117002769. / 576390310322783929725456.) * std::sqrt (77136233821044229928744879.) * std::sqrt (6.), + (1998274151. / 672455362043247918013032.) * std::sqrt (5.) * std::sqrt (77136233821044229928744879.) + * std::sqrt (2.), + -(1975932947. / 672455362043247918013032.) * std::sqrt (5.) * std::sqrt (77136233821044229928744879.) + * std::sqrt (6.), + (349110411. / 32021683906821329429192.) * std::sqrt (77136233821044229928744879.) * std::sqrt (2.), + (216972896. / 12008131465057998535947.) * std::sqrt (5.) * std::sqrt (11019461974434889989820697.) + * std::sqrt (2.), + -(49955. / 2602262296426928432.) * std::sqrt (9902847527964529535108767.) * std::sqrt (2.), + (905083053. / 24885434340730716595216.) * std::sqrt (9902847527964529535108767.), + -(3253373333. / 74656303022192149785648.) * std::sqrt (9902847527964529535108767.) * std::sqrt (3.), + -(1588665131. / 130648530288836262124884.) * std::sqrt (9902847527964529535108767.) * std::sqrt (6.), + (1127740739. / 24885434340730716595216.) * std::sqrt (9902847527964529535108767.) * std::sqrt (2.), + -(2368607. / 102630424421709553908.) * std::sqrt (9902847527964529535108767.) * std::sqrt (30.), + (685180611. / 87099020192557508083256.) * std::sqrt (9902847527964529535108767.) * std::sqrt (2.), + -(376756511. / 37328151511096074892824.) * std::sqrt (9902847527964529535108767.) * std::sqrt (6.), + (893882452. / 54436887620348442552035.) * std::sqrt (9902847527964529535108767.) * std::sqrt (10.), + -(267617085. / 7918092744777955280296.) * std::sqrt (9902847527964529535108767.) * std::sqrt (14.), + (229775. / 42817642055124816.) * std::sqrt (5.) * std::sqrt (14155690870266160523.), + -(21403. / 12593424133860240.) * std::sqrt (2.) * std::sqrt (5.) * std::sqrt (14155690870266160523.), + (208123. / 53522052568906020.) * std::sqrt (2.) * std::sqrt (5.) * std::sqrt (14155690870266160523.) + * std::sqrt (3.), + (2688691. / 4495852415788105680.) * std::sqrt (5.) * std::sqrt (14155690870266160523.) * std::sqrt (3.), + -(47384. / 31221197331861845.) * std::sqrt (5.) * std::sqrt (14155690870266160523.), + (206441. / 16056615770671806.) * std::sqrt (14155690870266160523.) * std::sqrt (3.), + -(512879. / 299723494385873712.) * std::sqrt (5.) * std::sqrt (14155690870266160523.), + (23414. / 18732718399117107.) * std::sqrt (5.) * std::sqrt (14155690870266160523.) * std::sqrt (3.), + -(1435. / 892034209481767.) * std::sqrt (14155690870266160523.), + (43691. / 2676102628445301.) * std::sqrt (5.) * std::sqrt (2022241552895165789.), + -(35. / 67124682721811748.) * std::sqrt (7.) * std::sqrt (2111308236157200531699814841.) * std::sqrt (2.), + (841217. / 7702960090424228953488.) * std::sqrt (7.) * std::sqrt (2111308236157200531699814841.), + -(15759497. / 23108880271272686860464.) * std::sqrt (7.) * std::sqrt (2111308236157200531699814841.) + * std::sqrt (3.), + (3160319. / 26960360316484801337208.) * std::sqrt (7.) * std::sqrt (2111308236157200531699814841.) + * std::sqrt (6.), + -(764393. / 2567653363474742984496.) * std::sqrt (7.) * std::sqrt (2111308236157200531699814841.) + * std::sqrt (2.), + -(8297. / 42357203953628910192.) * std::sqrt (7.) * std::sqrt (2111308236157200531699814841.) * std::sqrt (30.), + -(1652011. / 6740090079121200334302.) * std::sqrt (7.) * std::sqrt (2111308236157200531699814841.) + * std::sqrt (2.), + (9460915. / 46217760542545373720928.) * std::sqrt (7.) * std::sqrt (2111308236157200531699814841.) + * std::sqrt (6.), + -(2208257. / 11233483465202000557170.) * std::sqrt (7.) * std::sqrt (2111308236157200531699814841.) + * std::sqrt (10.), + -(16327. / 16098140209872996768.) * std::sqrt (2111308236157200531699814841.) * std::sqrt (2.), + (63. / 598672.) * std::sqrt (7.) * std::sqrt (187085.), + (3. / 88040.) * std::sqrt (2.) * std::sqrt (7.) * std::sqrt (187085.), + (161. / 2993360.) * std::sqrt (2.) * std::sqrt (7.) * std::sqrt (187085.) * std::sqrt (3.), + -(2377. / 62860560.) * std::sqrt (7.) * std::sqrt (187085.) * std::sqrt (3.), + (1577. / 10476760.) * std::sqrt (7.) * std::sqrt (187085.), + (109. / 898008.) * std::sqrt (7.) * std::sqrt (37417.) * std::sqrt (3.), + -(10. / 261919.) * std::sqrt (7.) * std::sqrt (187085.), + (3. / 261919.) * std::sqrt (7.) * std::sqrt (187085.) * std::sqrt (3.), + (135. / 598672.) * std::sqrt (7.) * std::sqrt (37417.), + (4. / 37417.) * std::sqrt (187085.), + -(77. / 253105968.) * std::sqrt (7.) * std::sqrt (13736271805.) * std::sqrt (2.), + -(2137. / 3796589520.) * std::sqrt (7.) * std::sqrt (13736271805.), + -(2153. / 11389768560.) * std::sqrt (7.) * std::sqrt (13736271805.) * std::sqrt (3.), + -(2483. / 26576126640.) * std::sqrt (7.) * std::sqrt (13736271805.) * std::sqrt (6.), + -(527. / 1265529840.) * std::sqrt (7.) * std::sqrt (13736271805.) * std::sqrt (2.), + -(887. / 5315225328.) * std::sqrt (7.) * std::sqrt (2747254361.) * std::sqrt (6.), + -(5. / 664403166.) * std::sqrt (7.) * std::sqrt (13736271805.) * std::sqrt (2.), + -(2873. / 22779537120.) * std::sqrt (7.) * std::sqrt (13736271805.) * std::sqrt (6.), + -(97. / 221467722.) * std::sqrt (7.) * std::sqrt (2747254361.) * std::sqrt (2.), + -(11. / 87277920.) * std::sqrt (13736271805.) * std::sqrt (2.) + }; + inv_mask_coeffs[2] = { + (1. / 51.) * std::sqrt (51.), + 0., + 0., + -(5. / 102.) * std::sqrt (17.), + -(5. / 102.) * std::sqrt (51.), + -(5. / 102.) * std::sqrt (17.) * std::sqrt (5.), + (1. / 102.) * std::sqrt (51.), + (1. / 34.) * std::sqrt (17.), + (1. / 102.) * std::sqrt (51.) * std::sqrt (5.), + (1. / 102.) * std::sqrt (51.) * std::sqrt (7.), + 0., + 0., + 0., + -(5. / 102.) * std::sqrt (17.) * std::sqrt (3.), + -(5. / 68.) * std::sqrt (17.), + (5. / 204.) * std::sqrt (17.) * std::sqrt (15.), + (1. / 34.) * std::sqrt (17.), + (11. / 510.) * std::sqrt (17.) * std::sqrt (3.), + (1. / 170.) * std::sqrt (17.) * std::sqrt (5.), + -(3. / 170.) * std::sqrt (17.) * std::sqrt (7.), + 0., + 0., + 0., + -(5. / 102.) * std::sqrt (85.), + (5. / 204.) * std::sqrt (255.), + -(5. / 204.) * std::sqrt (17.), + (1. / 102.) * std::sqrt (255.), + (1. / 170.) * std::sqrt (85.), + -(1. / 34.) * std::sqrt (51.), + (1. / 510.) * std::sqrt (255.) * std::sqrt (7.), + 0., + 0., + 0., + 0., + 0., + 0., + -(1. / 102.) * std::sqrt (3.) * std::sqrt (119.), + (3. / 170.) * std::sqrt (119.), + -(1. / 510.) * std::sqrt (3.) * std::sqrt (119.) * std::sqrt (5.), + (1. / 510.) * std::sqrt (17.) * std::sqrt (3.), + 0., + 0., + 0., + 0., + 0., + 0., + 0., + 0., + 0., + 0., + -(28. / 30243.) * std::sqrt (30243.), + -(3. / 4744.) * std::sqrt (60486.), + -(9. / 4744.) * std::sqrt (20162.), + -(32. / 30243.) * std::sqrt (10081.), + -(32. / 30243.) * std::sqrt (30243.), + -(32. / 30243.) * std::sqrt (10081.) * std::sqrt (5.), + (97. / 120972.) * std::sqrt (30243.), + (97. / 40324.) * std::sqrt (10081.), + (97. / 120972.) * std::sqrt (30243.) * std::sqrt (5.), + (97. / 120972.) * std::sqrt (30243.) * std::sqrt (7.), + 0., + (7. / 4408.) * std::sqrt (28101.), + -(7. / 4408.) * std::sqrt (9367.), + (151. / 112404.) * std::sqrt (9367.) * std::sqrt (2.), + (151. / 224808.) * std::sqrt (28101.) * std::sqrt (2.), + -(151. / 224808.) * std::sqrt (9367.) * std::sqrt (10.), + -(227. / 224808.) * std::sqrt (28101.) * std::sqrt (2.), + -(2497. / 1124040.) * std::sqrt (9367.) * std::sqrt (2.), + -(227. / 1124040.) * std::sqrt (28101.) * std::sqrt (10.), + (227. / 374680.) * std::sqrt (28101.) * std::sqrt (14.), + -(46. / 17738055540571.) * std::sqrt (4446309692077229715.), + -(209. / 141904444324568.) * std::sqrt (8892619384154459430.), + -(627. / 141904444324568.) * std::sqrt (2964206461384819810.), + (358655. / 1809281665138242.) * std::sqrt (1482103230692409905.), + -(366101. / 3618563330276484.) * std::sqrt (4446309692077229715.), + (336317. / 3618563330276484.) * std::sqrt (296420646138481981.), + -(533663. / 3618563330276484.) * std::sqrt (4446309692077229715.), + -(101959. / 1206187776758828.) * std::sqrt (1482103230692409905.), + (549575. / 1206187776758828.) * std::sqrt (889261938415445943.), + -(101959. / 3618563330276484.) * std::sqrt (4446309692077229715.) * std::sqrt (7.), + 0., + -(263. / 397590846996968.) * std::sqrt (3.) * std::sqrt (5813623063645540717.), + (263. / 397590846996968.) * std::sqrt (5813623063645540717.), + (83. / 596386270495452.) * std::sqrt (5813623063645540717.) * std::sqrt (2.), + (83. / 1192772540990904.) * std::sqrt (3.) * std::sqrt (5813623063645540717.) * std::sqrt (2.), + -(83. / 1192772540990904.) * std::sqrt (5813623063645540717.) * std::sqrt (10.), + -(1285849. / 20277133196845368.) * std::sqrt (3.) * std::sqrt (5813623063645540717.) * std::sqrt (2.), + (11686541. / 101385665984226840.) * std::sqrt (5813623063645540717.) * std::sqrt (2.), + -(1285849. / 101385665984226840.) * std::sqrt (3.) * std::sqrt (5813623063645540717.) * std::sqrt (10.), + (1171949. / 101385665984226840.) * std::sqrt (3.) * std::sqrt (830517580520791531.) * std::sqrt (2.), + (37444. / 633275965367614567.) * std::sqrt (75359839878746133473.), + (85063. / 2533103861470458268.) * std::sqrt (150719679757492266946.), + (85063. / 2533103861470458268.) * std::sqrt (150719679757492266946.) * std::sqrt (3.), + -(4867909. / 26597590545439811814.) * std::sqrt (75359839878746133473.) * std::sqrt (3.), + (5699817. / 17731727030293207876.) * std::sqrt (75359839878746133473.), + -(474437. / 53195181090879623628.) * std::sqrt (75359839878746133473.) * std::sqrt (15.), + (2940969. / 17731727030293207876.) * std::sqrt (75359839878746133473.), + (54861. / 17731727030293207876.) * std::sqrt (75359839878746133473.) * std::sqrt (3.), + -(2831247. / 17731727030293207876.) * std::sqrt (75359839878746133473.) * std::sqrt (5.), + (54861. / 2533103861470458268.) * std::sqrt (10765691411249447639.), + 0., + -(354787. / 18404808143966008540.) * std::sqrt (3.) * std::sqrt (4601202035991502135.), + (354787. / 18404808143966008540.) * std::sqrt (4601202035991502135.), + (111967. / 27607212215949012810.) * std::sqrt (4601202035991502135.) * std::sqrt (2.), + (111967. / 55214424431898025620.) * std::sqrt (3.) * std::sqrt (4601202035991502135.) * std::sqrt (2.), + -(111967. / 11042884886379605124.) * std::sqrt (920240407198300427.) * std::sqrt (2.), + -(2449961. / 7361923257586403416.) * std::sqrt (3.) * std::sqrt (4601202035991502135.) * std::sqrt (2.), + (69764267. / 110428848863796051240.) * std::sqrt (4601202035991502135.) * std::sqrt (2.), + -(2449961. / 7361923257586403416.) * std::sqrt (3.) * std::sqrt (920240407198300427.) * std::sqrt (2.), + (533509. / 110428848863796051240.) * std::sqrt (3.) * std::sqrt (4601202035991502135.) * std::sqrt (14.), + (16691875271. / 2415138272689339809624.) * std::sqrt (3.) * std::sqrt (100630761362055825401.), + (4521189803. / 3622707409034009714436.) * std::sqrt (2.) * std::sqrt (3.) * std::sqrt (100630761362055825401.), + (4521189803. / 1207569136344669904812.) * std::sqrt (2.) * std::sqrt (100630761362055825401.), + -(41625431257. / 7245414818068019428872.) * std::sqrt (100630761362055825401.), + -(58212405037. / 7245414818068019428872.) * std::sqrt (3.) * std::sqrt (100630761362055825401.), + -(51577615525. / 7245414818068019428872.) * std::sqrt (100630761362055825401.) * std::sqrt (5.), + -(18535653679. / 1811353704517004857218.) * std::sqrt (3.) * std::sqrt (100630761362055825401.), + -(16884295711. / 603784568172334952406.) * std::sqrt (100630761362055825401.), + -(15232937743. / 1811353704517004857218.) * std::sqrt (3.) * std::sqrt (100630761362055825401.) * std::sqrt (5.), + -(16884295711. / 1811353704517004857218.) * std::sqrt (3.) * std::sqrt (100630761362055825401.) * std::sqrt (7.), + 0., + (219317359841. / 485611561025601336842721065380.) * std::sqrt (2.) + * std::sqrt (10381525354495561779357901615959985.), + -(219317359841. / 1456834683076804010528163196140.) * std::sqrt (2.) + * std::sqrt (10381525354495561779357901615959985.) * std::sqrt (3.), + -(249900927982. / 364208670769201002632040799035.) * std::sqrt (10381525354495561779357901615959985.) + * std::sqrt (3.), + -(124950463991. / 121402890256400334210680266345.) * std::sqrt (10381525354495561779357901615959985.), + (124950463991. / 72841734153840200526408159807.) * std::sqrt (2076305070899112355871580323191997.) + * std::sqrt (3.), + -(262608600927. / 97122312205120267368544213076.) * std::sqrt (10381525354495561779357901615959985.), + -(965825773499. / 485611561025601336842721065380.) * std::sqrt (10381525354495561779357901615959985.) + * std::sqrt (3.), + -(262608600927. / 97122312205120267368544213076.) * std::sqrt (2076305070899112355871580323191997.), + (789080475681. / 485611561025601336842721065380.) * std::sqrt (10381525354495561779357901615959985.) + * std::sqrt (7.), + (216374389. / 600253791801208432056124575225852.) * std::sqrt (34560885477513793515221944312683818679790383.) + * std::sqrt (2.), + -(1081871945. / 1800761375403625296168373725677556.) * std::sqrt (34560885477513793515221944312683818679790383.), + -(1081871945. / 600253791801208432056124575225852.) * std::sqrt (11520295159171264505073981437561272893263461.), + (458877615349. / 12605329627825377073178616079742892.) * std::sqrt (11520295159171264505073981437561272893263461.) + * std::sqrt (2.), + -(66204005069. / 3151332406956344268294654019935723.) * std::sqrt (34560885477513793515221944312683818679790383.) + * std::sqrt (2.), + (12330716987. / 6302664813912688536589308039871446.) * std::sqrt (11520295159171264505073981437561272893263461.) + * std::sqrt (10.), + (1048465284425. / 25210659255650754146357232159485784.) + * std::sqrt (34560885477513793515221944312683818679790383.) * std::sqrt (2.), + (187190120429. / 8403553085216918048785744053161928.) * std::sqrt (11520295159171264505073981437561272893263461.) + * std::sqrt (2.), + -(674085043567. / 25210659255650754146357232159485784.) + * std::sqrt (34560885477513793515221944312683818679790383.) * std::sqrt (10.), + (187190120429. / 3601522750807250592336747451355112.) * std::sqrt (4937269353930541930745992044669116954255769.) + * std::sqrt (2.), + 0., + -(1384642923741. / 42538134770524768378109393175404046815180.) + * std::sqrt (38697087397201085046824763022410609242891822943821145.), + (1384642923741. / 42538134770524768378109393175404046815180.) + * std::sqrt (12899029132400361682274921007470203080963940981273715.), + (987783658468. / 31903601077893576283582044881553035111385.) + * std::sqrt (12899029132400361682274921007470203080963940981273715.) * std::sqrt (2.), + (493891829234. / 31903601077893576283582044881553035111385.) + * std::sqrt (38697087397201085046824763022410609242891822943821145.) * std::sqrt (2.), + -(493891829234. / 6380720215578715256716408976310607022277.) + * std::sqrt (2579805826480072336454984201494040616192788196254743.) * std::sqrt (2.), + -(79882324431527. / 51045761724629722053731271810484856178216.) + * std::sqrt (38697087397201085046824763022410609242891822943821145.) * std::sqrt (2.), + (251303206132013. / 85076269541049536756218786350808093630360.) + * std::sqrt (12899029132400361682274921007470203080963940981273715.) * std::sqrt (2.), + -(79882324431527. / 51045761724629722053731271810484856178216.) + * std::sqrt (7739417479440217009364952604482121848578364588764229.) * std::sqrt (2.), + (44913625919231. / 255228808623148610268656359052424280891080.) + * std::sqrt (5528155342457297863832109003201515606127403277688735.) * std::sqrt (2.), + -(2949095387. / 47355642137116550352560325020233116.) + * std::sqrt (4587144914088926523642597305294551825737974887.) * std::sqrt (2.), + (14745476935. / 142066926411349651057680975060699348.) + * std::sqrt (4587144914088926523642597305294551825737974887.), + (14745476935. / 142066926411349651057680975060699348.) + * std::sqrt (4587144914088926523642597305294551825737974887.) * std::sqrt (3.), + -(1153684664267. / 2983405454638342672211300476274686308.) + * std::sqrt (4587144914088926523642597305294551825737974887.) * std::sqrt (6.), + (264754856977. / 248617121219861889350941706356223859.) + * std::sqrt (4587144914088926523642597305294551825737974887.) * std::sqrt (2.), + (86968895519. / 1491702727319171336105650238137343154.) + * std::sqrt (4587144914088926523642597305294551825737974887.) * std::sqrt (30.), + -(778427753245. / 1988936969758895114807533650849790872.) + * std::sqrt (4587144914088926523642597305294551825737974887.) * std::sqrt (2.), + (151020369599. / 1988936969758895114807533650849790872.) + * std::sqrt (4587144914088926523642597305294551825737974887.) * std::sqrt (6.), + (1080468492443. / 1988936969758895114807533650849790872.) + * std::sqrt (4587144914088926523642597305294551825737974887.) * std::sqrt (10.), + (151020369599. / 284133852822699302115361950121398696.) + * std::sqrt (655306416298418074806085329327793117962567841.) * std::sqrt (2.), + 0., + -(99068468313. / 35433299869105240687431243962348008.) * std::sqrt (1977693186713807260499140827732350041750787.), + (33022822771. / 35433299869105240687431243962348008.) * std::sqrt (1977693186713807260499140827732350041750787.) + * std::sqrt (3.), + (219892593187. / 744099297251210054436056123209308168.) * std::sqrt (1977693186713807260499140827732350041750787.) + * std::sqrt (6.), + (219892593187. / 496066198167473369624037415472872112.) * std::sqrt (1977693186713807260499140827732350041750787.) + * std::sqrt (2.), + -(219892593187. / 1488198594502420108872112246418616336.) + * std::sqrt (1977693186713807260499140827732350041750787.) * std::sqrt (30.), + -(653016818285. / 62008274770934171203004676934109014.) * std::sqrt (1977693186713807260499140827732350041750787.) + * std::sqrt (2.), + (257899062724. / 31004137385467085601502338467054507.) * std::sqrt (1977693186713807260499140827732350041750787.) + * std::sqrt (6.), + -(130603363657. / 62008274770934171203004676934109014.) * std::sqrt (1977693186713807260499140827732350041750787.) + * std::sqrt (10.), + -(17240052841. / 4429162483638155085928905495293501.) * std::sqrt (282527598101972465785591546818907148821541.) + * std::sqrt (2.), + (166150187. / 6258855035019550062059880.) * std::sqrt (5.) * std::sqrt (9023182675486518006136327.) + * std::sqrt (2.), + -(166150187. / 3755313021011730037235928.) * std::sqrt (5.) * std::sqrt (9023182675486518006136327.), + -(166150187. / 3755313021011730037235928.) * std::sqrt (5.) * std::sqrt (9023182675486518006136327.) + * std::sqrt (3.), + (6401649883. / 28164847657587975279269460.) * std::sqrt (5.) * std::sqrt (9023182675486518006136327.) + * std::sqrt (6.), + -(20564887073. / 37553130210117300372359280.) * std::sqrt (5.) * std::sqrt (9023182675486518006136327.) + * std::sqrt (2.), + -(2096292431. / 22531878126070380223415568.) * std::sqrt (9023182675486518006136327.) * std::sqrt (6.), + (150561685. / 938828255252932509308982.) * std::sqrt (5.) * std::sqrt (9023182675486518006136327.) + * std::sqrt (2.), + -(79001474. / 2347070638132331273272455.) * std::sqrt (5.) * std::sqrt (9023182675486518006136327.) + * std::sqrt (6.), + -(1068814321. / 938828255252932509308982.) * std::sqrt (9023182675486518006136327.) * std::sqrt (2.), + -(79001474. / 2347070638132331273272455.) * std::sqrt (5.) * std::sqrt (9023182675486518006136327.) + * std::sqrt (14.), + (18337090039. / 18743887582201571961660.) * std::sqrt (32801803268852750932905.), + (1508104313. / 2499185010960209594888.) * std::sqrt (2.) * std::sqrt (32801803268852750932905.), + (4524312939. / 2499185010960209594888.) * std::sqrt (2.) * std::sqrt (10933934422950916977635.), + (556040014021. / 262414426150822007463240.) * std::sqrt (10933934422950916977635.), + (552078974437. / 524828852301644014926480.) * std::sqrt (32801803268852750932905.), + (776079395879. / 104965770460328802985296.) * std::sqrt (2186786884590183395527.), + (40189709773. / 52482885230164401492648.) * std::sqrt (32801803268852750932905.), + (1581266713. / 866054211718884513080.) * std::sqrt (10933934422950916977635.), + (118467327161. / 52482885230164401492648.) * std::sqrt (6560360653770550186581.), + (1581266713. / 371166090736664791320.) * std::sqrt (4685971895550392990415.), + 0., + -(74255906633. / 672865399006761681060331917.) * std::sqrt (2.) * std::sqrt (3810436754575291399844659645971.), + (74255906633. / 672865399006761681060331917.) * std::sqrt (2.) * std::sqrt (1270145584858430466614886548657.), + -(3088358318609. / 12560154114792884713126195784.) * std::sqrt (1270145584858430466614886548657.), + -(3088358318609. / 25120308229585769426252391568.) * std::sqrt (3810436754575291399844659645971.), + (3088358318609. / 25120308229585769426252391568.) * std::sqrt (1270145584858430466614886548657.) * std::sqrt (5.), + -(4869940864235. / 37680462344378654139378587352.) * std::sqrt (3810436754575291399844659645971.), + -(5662256210753. / 37680462344378654139378587352.) * std::sqrt (1270145584858430466614886548657.), + -(973988172847. / 37680462344378654139378587352.) * std::sqrt (3810436754575291399844659645971.) * std::sqrt (5.), + (733435139963. / 1794307730684697816160885112.) * std::sqrt (544348107796470199977808520853.), + -(1520709232. / 4891272024616371501303055622019.) * std::sqrt (338531287325211031860850338067150911335.), + -(706043572. / 4891272024616371501303055622019.) * std::sqrt (2.) + * std::sqrt (338531287325211031860850338067150911335.), + -(706043572. / 4891272024616371501303055622019.) * std::sqrt (2.) + * std::sqrt (338531287325211031860850338067150911335.) * std::sqrt (3.), + -(2120016097259. / 205433425033887603054728336124798.) * std::sqrt (338531287325211031860850338067150911335.) + * std::sqrt (3.), + (690885622297. / 45651872229752800678828519138844.) * std::sqrt (338531287325211031860850338067150911335.), + -(2262093788363. / 410866850067775206109456672249596.) * std::sqrt (67706257465042206372170067613430182267.) + * std::sqrt (3.), + -(423495254960. / 34238904172314600509121389354133.) * std::sqrt (338531287325211031860850338067150911335.), + -(4275295492. / 1694995256055178243025811354165.) * std::sqrt (338531287325211031860850338067150911335.) + * std::sqrt (3.), + (417955528472. / 11412968057438200169707129784711.) * std::sqrt (67706257465042206372170067613430182267.), + -(4275295492. / 242142179436454034717973050595.) * std::sqrt (48361612475030147408692905438164415905.), + 0., + -(4893951460033. / 240326867089261031612796765233187653910.) * std::sqrt (2.) * std::sqrt (3.) + * std::sqrt (2206416960936351241282951190627144110360471628935.), + (4893951460033. / 240326867089261031612796765233187653910.) * std::sqrt (2.) + * std::sqrt (2206416960936351241282951190627144110360471628935.), + -(18530948270789. / 560762689874942407096525785544104525790.) + * std::sqrt (2206416960936351241282951190627144110360471628935.), + -(18530948270789. / 1121525379749884814193051571088209051580.) * std::sqrt (3.) + * std::sqrt (2206416960936351241282951190627144110360471628935.), + (18530948270789. / 224305075949976962838610314217641810316.) + * std::sqrt (441283392187270248256590238125428822072094325787.), + (13501422129904. / 168228806962482722128957735663231357737.) * std::sqrt (3.) + * std::sqrt (2206416960936351241282951190627144110360471628935.), + -(151316135239408. / 841144034812413610644788678316156788685.) + * std::sqrt (2206416960936351241282951190627144110360471628935.), + (13501422129904. / 168228806962482722128957735663231357737.) * std::sqrt (3.) + * std::sqrt (441283392187270248256590238125428822072094325787.), + (776281616208. / 40054477848210171935466127538864608985.) * std::sqrt (3.) + * std::sqrt (315202422990907320183278741518163444337210232705.), + -(47693768843. / 73588420123580747581697018237233947402.) + * std::sqrt (2170343179656283287883941488614423306932736027111121.) * std::sqrt (2.), + -(88574142137. / 147176840247161495163394036474467894804.) + * std::sqrt (2170343179656283287883941488614423306932736027111121.), + -(88574142137. / 147176840247161495163394036474467894804.) + * std::sqrt (2170343179656283287883941488614423306932736027111121.) * std::sqrt (3.), + -(13440607051201. / 12362854580761565593725099063855303163536.) + * std::sqrt (2170343179656283287883941488614423306932736027111121.) * std::sqrt (6.), + (357110768213. / 392471573992430653769050763931914386144.) + * std::sqrt (2170343179656283287883941488614423306932736027111121.) * std::sqrt (2.), + -(6252889961477. / 24725709161523131187450198127710606327072.) + * std::sqrt (2170343179656283287883941488614423306932736027111121.) * std::sqrt (30.), + -(7037618389747. / 8241903053841043729150066042570202109024.) + * std::sqrt (2170343179656283287883941488614423306932736027111121.) * std::sqrt (2.), + -(320211899021. / 1177414721977291961307152291795743158432.) + * std::sqrt (2170343179656283287883941488614423306932736027111121.) * std::sqrt (6.), + (851550601151. / 2747301017947014576383355347523400703008.) + * std::sqrt (2170343179656283287883941488614423306932736027111121.) * std::sqrt (10.), + -(2241483293147. / 1177414721977291961307152291795743158432.) + * std::sqrt (310049025665183326840563069802060472418962289587303.) * std::sqrt (2.), + 0., + -(1571354129709. / 6308610151592174219826751125918551044360.) * std::sqrt (3.) + * std::sqrt (115935593470518835817766866162978250334523437784363455.), + (1571354129709. / 6308610151592174219826751125918551044360.) + * std::sqrt (115935593470518835817766866162978250334523437784363455.), + -(52123916060423. / 264961626366871317232723547288579143863120.) + * std::sqrt (115935593470518835817766866162978250334523437784363455.) * std::sqrt (2.), + -(52123916060423. / 529923252733742634465447094577158287726240.) * std::sqrt (3.) + * std::sqrt (115935593470518835817766866162978250334523437784363455.) * std::sqrt (2.), + (52123916060423. / 105984650546748526893089418915431657545248.) + * std::sqrt (23187118694103767163553373232595650066904687556872691.) * std::sqrt (2.), + -(4112532819029. / 105984650546748526893089418915431657545248.) * std::sqrt (3.) + * std::sqrt (115935593470518835817766866162978250334523437784363455.) * std::sqrt (2.), + -(23772796635541. / 176641084244580878155149031525719429242080.) + * std::sqrt (115935593470518835817766866162978250334523437784363455.) * std::sqrt (2.), + -(4112532819029. / 105984650546748526893089418915431657545248.) * std::sqrt (3.) + * std::sqrt (23187118694103767163553373232595650066904687556872691.) * std::sqrt (2.), + (16063388299559. / 529923252733742634465447094577158287726240.) * std::sqrt (3.) + * std::sqrt (115935593470518835817766866162978250334523437784363455.) * std::sqrt (14.), + (60986401. / 24016262930115997071894.) * std::sqrt (5.) * std::sqrt (77136233821044229928744879.) + * std::sqrt (2.), + (113260459. / 48032525860231994143788.) * std::sqrt (5.) * std::sqrt (77136233821044229928744879.), + (113260459. / 48032525860231994143788.) * std::sqrt (5.) * std::sqrt (77136233821044229928744879.) + * std::sqrt (3.), + (1726922093. / 4034732172259487508078192.) * std::sqrt (5.) * std::sqrt (77136233821044229928744879.) + * std::sqrt (6.), + (177886091. / 81509740853727020365216.) * std::sqrt (5.) * std::sqrt (77136233821044229928744879.) + * std::sqrt (2.), + (24518411381. / 8069464344518975016156384.) * std::sqrt (77136233821044229928744879.) * std::sqrt (6.), + (751420643. / 2689821448172991672052128.) * std::sqrt (5.) * std::sqrt (77136233821044229928744879.) + * std::sqrt (2.), + (6083374559. / 13449107240864958360260640.) * std::sqrt (5.) * std::sqrt (77136233821044229928744879.) + * std::sqrt (6.), + (2803215301. / 896607149390997224017376.) * std::sqrt (77136233821044229928744879.) * std::sqrt (2.), + (6083374559. / 1921301034409279765751520.) * std::sqrt (5.) * std::sqrt (11019461974434889989820697.) + * std::sqrt (2.), + 0., + -(33015017. / 12442717170365358297608.) * std::sqrt (9902847527964529535108767.), + (33015017. / 37328151511096074892824.) * std::sqrt (9902847527964529535108767.) * std::sqrt (3.), + -(110637271. / 174198040385115016166512.) * std::sqrt (9902847527964529535108767.) * std::sqrt (6.), + -(331911813. / 348396080770230032333024.) * std::sqrt (9902847527964529535108767.) * std::sqrt (2.), + (110637271. / 348396080770230032333024.) * std::sqrt (9902847527964529535108767.) * std::sqrt (30.), + (59400689. / 348396080770230032333024.) * std::sqrt (9902847527964529535108767.) * std::sqrt (2.), + -(714507041. / 1045188242310690096999072.) * std::sqrt (9902847527964529535108767.) * std::sqrt (6.), + (59400689. / 1741980403851150161665120.) * std::sqrt (9902847527964529535108767.) * std::sqrt (10.), + (85100809. / 348396080770230032333024.) * std::sqrt (9902847527964529535108767.) * std::sqrt (14.), + -(7. / 286084146612.) * std::sqrt (5.) * std::sqrt (14155690870266160523.), + -(13. / 1144336586448.) * std::sqrt (2.) * std::sqrt (5.) * std::sqrt (14155690870266160523.), + -(13. / 1144336586448.) * std::sqrt (2.) * std::sqrt (5.) * std::sqrt (14155690870266160523.) * std::sqrt (3.), + (171947. / 224792620789405284.) * std::sqrt (5.) * std::sqrt (14155690870266160523.) * std::sqrt (3.), + -(2300. / 81094019043797.) * std::sqrt (5.) * std::sqrt (14155690870266160523.), + -(3015853. / 112396310394702642.) * std::sqrt (14155690870266160523.) * std::sqrt (3.), + (1172527. / 1198893977543494848.) * std::sqrt (5.) * std::sqrt (14155690870266160523.), + -(3103811. / 856352841102496320.) * std::sqrt (5.) * std::sqrt (14155690870266160523.) * std::sqrt (3.), + -(16438663. / 399631325847831616.) * std::sqrt (14155690870266160523.), + -(21726677. / 856352841102496320.) * std::sqrt (5.) * std::sqrt (2022241552895165789.), + 0., + (7956089. / 7702960090424228953488.) * std::sqrt (7.) * std::sqrt (2111308236157200531699814841.), + -(7956089. / 23108880271272686860464.) * std::sqrt (7.) * std::sqrt (2111308236157200531699814841.) + * std::sqrt (3.), + (21664219. / 80881080949454404011624.) * std::sqrt (7.) * std::sqrt (2111308236157200531699814841.) + * std::sqrt (6.), + (21664219. / 53920720632969602674416.) * std::sqrt (7.) * std::sqrt (2111308236157200531699814841.) + * std::sqrt (2.), + -(21664219. / 161762161898908808023248.) * std::sqrt (7.) * std::sqrt (2111308236157200531699814841.) + * std::sqrt (30.), + -(500471. / 17973573544323200891472.) * std::sqrt (7.) * std::sqrt (2111308236157200531699814841.) + * std::sqrt (2.), + (93825223. / 323524323797817616046496.) * std::sqrt (7.) * std::sqrt (2111308236157200531699814841.) + * std::sqrt (6.), + -(500471. / 89867867721616004457360.) * std::sqrt (7.) * std::sqrt (2111308236157200531699814841.) + * std::sqrt (10.), + -(12545653. / 15405920180848457906976.) * std::sqrt (2111308236157200531699814841.) * std::sqrt (2.), + 0., + 0., + 0., + -(39. / 1047676.) * std::sqrt (7.) * std::sqrt (187085.) * std::sqrt (3.), + (117. / 2095352.) * std::sqrt (7.) * std::sqrt (187085.), + -(39. / 2095352.) * std::sqrt (7.) * std::sqrt (37417.) * std::sqrt (3.), + -(481. / 16762816.) * std::sqrt (7.) * std::sqrt (187085.), + -(481. / 83814080.) * std::sqrt (7.) * std::sqrt (187085.) * std::sqrt (3.), + (1443. / 16762816.) * std::sqrt (7.) * std::sqrt (37417.), + -(481. / 11973440.) * std::sqrt (187085.), + 0., + -(91. / 3796589520.) * std::sqrt (7.) * std::sqrt (13736271805.), + (91. / 11389768560.) * std::sqrt (7.) * std::sqrt (13736271805.) * std::sqrt (3.), + -(221. / 39864189960.) * std::sqrt (7.) * std::sqrt (13736271805.) * std::sqrt (6.), + -(221. / 26576126640.) * std::sqrt (7.) * std::sqrt (13736271805.) * std::sqrt (2.), + (221. / 15945675984.) * std::sqrt (7.) * std::sqrt (2747254361.) * std::sqrt (6.), + (845. / 7086967104.) * std::sqrt (7.) * std::sqrt (13736271805.) * std::sqrt (2.), + -(24349. / 318913519680.) * std::sqrt (7.) * std::sqrt (13736271805.) * std::sqrt (6.), + (845. / 7086967104.) * std::sqrt (7.) * std::sqrt (2747254361.) * std::sqrt (2.), + -(143. / 15186358080.) * std::sqrt (13736271805.) * std::sqrt (2.) + }; + inv_mask_coeffs[3] = { + (1. / 255.) * std::sqrt (51.), + 0., + 0., + -(1. / 34.) * std::sqrt (17.), + (1. / 34.) * std::sqrt (51.), + -(1. / 34.) * std::sqrt (17.) * std::sqrt (5.), + -(2. / 255.) * std::sqrt (51.), + (2. / 85.) * std::sqrt (17.), + -(2. / 255.) * std::sqrt (51.) * std::sqrt (5.), + (2. / 255.) * std::sqrt (51.) * std::sqrt (7.), + (1. / 85.) * std::sqrt (17.), + 0., + 0., + -(1. / 102.) * std::sqrt (17.) * std::sqrt (3.), + (3. / 68.) * std::sqrt (17.), + -(5. / 204.) * std::sqrt (17.) * std::sqrt (15.), + (1. / 170.) * std::sqrt (17.), + -(1. / 510.) * std::sqrt (17.) * std::sqrt (3.), + -(1. / 170.) * std::sqrt (17.) * std::sqrt (5.), + (3. / 170.) * std::sqrt (17.) * std::sqrt (7.), + (1. / 255.) * std::sqrt (255.), + 0., + 0., + (11. / 714.) * std::sqrt (85.), + -(11. / 1428.) * std::sqrt (255.), + -(115. / 1428.) * std::sqrt (17.), + (1. / 210.) * std::sqrt (255.), + -(19. / 1190.) * std::sqrt (85.), + (5. / 238.) * std::sqrt (51.), + (11. / 3570.) * std::sqrt (255.) * std::sqrt (7.), + -(1. / 255.) * std::sqrt (3.) * std::sqrt (119.), + 0., + 0., + -(13. / 714.) * std::sqrt (119.), + (1. / 42.) * std::sqrt (3.) * std::sqrt (119.), + (5. / 714.) * std::sqrt (119.) * std::sqrt (5.), + (61. / 7140.) * std::sqrt (3.) * std::sqrt (119.), + -(27. / 2380.) * std::sqrt (119.), + -(31. / 7140.) * std::sqrt (3.) * std::sqrt (119.) * std::sqrt (5.), + -(7. / 1020.) * std::sqrt (17.) * std::sqrt (3.), + (1. / 85.) * std::sqrt (17.) * std::sqrt (3.), + 0., + 0., + -(31. / 238.) * std::sqrt (17.), + -(9. / 238.) * std::sqrt (17.) * std::sqrt (3.), + -(1. / 238.) * std::sqrt (17.) * std::sqrt (5.), + (69. / 2380.) * std::sqrt (17.) * std::sqrt (3.), + (103. / 2380.) * std::sqrt (17.), + (9. / 2380.) * std::sqrt (3.) * std::sqrt (17.) * std::sqrt (5.), + (1. / 2380.) * std::sqrt (3.) * std::sqrt (17.) * std::sqrt (7.), + (23. / 151215.) * std::sqrt (30243.), + -(1. / 4744.) * std::sqrt (60486.), + (3. / 4744.) * std::sqrt (20162.), + (11. / 20162.) * std::sqrt (10081.), + -(11. / 20162.) * std::sqrt (30243.), + (11. / 20162.) * std::sqrt (10081.) * std::sqrt (5.), + (163. / 302430.) * std::sqrt (30243.), + -(163. / 100810.) * std::sqrt (10081.), + (163. / 302430.) * std::sqrt (30243.) * std::sqrt (5.), + -(163. / 302430.) * std::sqrt (30243.) * std::sqrt (7.), + -(9. / 46835.) * std::sqrt (28101.) * std::sqrt (2.), + (1. / 2280.) * std::sqrt (28101.), + -(33. / 22040.) * std::sqrt (9367.), + -(103. / 281010.) * std::sqrt (9367.) * std::sqrt (2.), + (499. / 1124040.) * std::sqrt (28101.) * std::sqrt (2.), + -(673. / 1124040.) * std::sqrt (9367.) * std::sqrt (10.), + -(9. / 93670.) * std::sqrt (28101.) * std::sqrt (2.), + (73. / 140505.) * std::sqrt (9367.) * std::sqrt (2.), + -(46. / 140505.) * std::sqrt (28101.) * std::sqrt (10.), + (157. / 281010.) * std::sqrt (28101.) * std::sqrt (14.), + -(127474. / 4523204162845605.) * std::sqrt (4446309692077229715.), + (13093. / 709522221622840.) * std::sqrt (8892619384154459430.), + -(67743. / 709522221622840.) * std::sqrt (2964206461384819810.), + (77797. / 4523204162845605.) * std::sqrt (1482103230692409905.), + (109249. / 18092816651382420.) * std::sqrt (4446309692077229715.), + -(1177835. / 3618563330276484.) * std::sqrt (296420646138481981.), + (32783. / 532141666217130.) * std::sqrt (4446309692077229715.), + -(480221. / 3015469441897070.) * std::sqrt (1482103230692409905.), + (70333. / 603093888379414.) * std::sqrt (889261938415445943.), + (480439. / 9046408325691210.) * std::sqrt (4446309692077229715.) * std::sqrt (7.), + -(153974. / 12673208248028355.) * std::sqrt (3.) * std::sqrt (5813623063645540717.) * std::sqrt (2.), + -(233. / 205650438101880.) * std::sqrt (3.) * std::sqrt (5813623063645540717.), + -(129111. / 1987954234984840.) * std::sqrt (5813623063645540717.), + (2514809. / 88712457736198485.) * std::sqrt (5813623063645540717.) * std::sqrt (2.), + -(305969. / 13915679644893880.) * std::sqrt (3.) * std::sqrt (5813623063645540717.) * std::sqrt (2.), + -(12135647. / 709699661889587880.) * std::sqrt (5813623063645540717.) * std::sqrt (10.), + (1195057. / 177424915472396970.) * std::sqrt (3.) * std::sqrt (5813623063645540717.) * std::sqrt (2.), + -(4784798. / 88712457736198485.) * std::sqrt (5813623063645540717.) * std::sqrt (2.), + (809962. / 29570819245399495.) * std::sqrt (3.) * std::sqrt (5813623063645540717.) * std::sqrt (10.), + (653681. / 8448805498685570.) * std::sqrt (3.) * std::sqrt (830517580520791531.) * std::sqrt (2.), + (511320053. / 53828457056247238195.) * std::sqrt (75359839878746133473.), + (23277997. / 2533103861470458268.) * std::sqrt (150719679757492266946.), + (14009977. / 2533103861470458268.) * std::sqrt (150719679757492266946.) * std::sqrt (3.), + -(1899088045. / 452159039272476800838.) * std::sqrt (75359839878746133473.) * std::sqrt (3.), + (8018315537. / 301439359514984533892.) * std::sqrt (75359839878746133473.), + (1715324797. / 904318078544953601676.) * std::sqrt (75359839878746133473.) * std::sqrt (15.), + (52585434827. / 1507196797574922669460.) * std::sqrt (75359839878746133473.), + -(33301827917. / 1507196797574922669460.) * std::sqrt (75359839878746133473.) * std::sqrt (3.), + -(24080242253. / 1507196797574922669460.) * std::sqrt (75359839878746133473.) * std::sqrt (5.), + -(4086969317. / 215313828224988952780.) * std::sqrt (10765691411249447639.), + -(250819. / 15020246036968995.) * std::sqrt (3.) * std::sqrt (4601202035991502135.) * std::sqrt (2.), + -(3965744317. / 55214424431898025620.) * std::sqrt (3.) * std::sqrt (4601202035991502135.), + -(365160921. / 18404808143966008540.) * std::sqrt (4601202035991502135.), + -(879489177. / 9202404071983004270.) * std::sqrt (4601202035991502135.) * std::sqrt (2.), + -(1257063491. / 55214424431898025620.) * std::sqrt (3.) * std::sqrt (4601202035991502135.) * std::sqrt (2.), + -(38266497. / 3680961628793201708.) * std::sqrt (920240407198300427.) * std::sqrt (2.), + (9262652021. / 110428848863796051240.) * std::sqrt (3.) * std::sqrt (4601202035991502135.) * std::sqrt (2.), + (2332812619. / 22085769772759210248.) * std::sqrt (4601202035991502135.) * std::sqrt (2.), + (850426337. / 22085769772759210248.) * std::sqrt (3.) * std::sqrt (920240407198300427.) * std::sqrt (2.), + (15965701. / 22085769772759210248.) * std::sqrt (3.) * std::sqrt (4601202035991502135.) * std::sqrt (14.), + (6189762347. / 7245414818068019428872.) * std::sqrt (3.) * std::sqrt (100630761362055825401.), + -(341205775. / 805046090896446603208.) * std::sqrt (2.) * std::sqrt (3.) * std::sqrt (100630761362055825401.), + (672022237. / 805046090896446603208.) * std::sqrt (2.) * std::sqrt (100630761362055825401.), + -(16852494361. / 7245414818068019428872.) * std::sqrt (100630761362055825401.), + (17839041013. / 7245414818068019428872.) * std::sqrt (3.) * std::sqrt (100630761362055825401.), + -(21797888269. / 7245414818068019428872.) * std::sqrt (100630761362055825401.) * std::sqrt (5.), + (10990595137. / 1811353704517004857218.) * std::sqrt (3.) * std::sqrt (100630761362055825401.), + -(10941103513. / 603784568172334952406.) * std::sqrt (100630761362055825401.), + (10732926817. / 1811353704517004857218.) * std::sqrt (3.) * std::sqrt (100630761362055825401.) * std::sqrt (5.), + -(9575615233. / 1811353704517004857218.) * std::sqrt (3.) * std::sqrt (100630761362055825401.) * std::sqrt (7.), + (8048826. / 26420650763090388293945651.) * std::sqrt (10381525354495561779357901615959985.), + -(53044335899. / 485611561025601336842721065380.) * std::sqrt (2.) + * std::sqrt (10381525354495561779357901615959985.), + (13496698783. / 112064206390523385425243322780.) * std::sqrt (2.) + * std::sqrt (10381525354495561779357901615959985.) * std::sqrt (3.), + -(465461023. / 2290620570875478003975099365.) * std::sqrt (10381525354495561779357901615959985.) * std::sqrt (3.), + (81828708269. / 121402890256400334210680266345.) * std::sqrt (10381525354495561779357901615959985.), + -(32396783088. / 24280578051280066842136053269.) * std::sqrt (2076305070899112355871580323191997.) + * std::sqrt (3.), + (54806990779. / 97122312205120267368544213076.) * std::sqrt (10381525354495561779357901615959985.), + -(67427217025. / 97122312205120267368544213076.) * std::sqrt (10381525354495561779357901615959985.) + * std::sqrt (3.), + (463565534311. / 97122312205120267368544213076.) * std::sqrt (2076305070899112355871580323191997.), + -(131015847657. / 97122312205120267368544213076.) * std::sqrt (10381525354495561779357901615959985.) + * std::sqrt (7.), + -(9718477427. / 1800761375403625296168373725677556.) * std::sqrt (34560885477513793515221944312683818679790383.) + * std::sqrt (2.), + (1482345095. / 600253791801208432056124575225852.) * std::sqrt (34560885477513793515221944312683818679790383.), + -(2572498837. / 200084597267069477352041525075284.) * std::sqrt (11520295159171264505073981437561272893263461.), + (38004385243. / 12605329627825377073178616079742892.) * std::sqrt (11520295159171264505073981437561272893263461.) + * std::sqrt (2.), + -(17646267307. / 3151332406956344268294654019935723.) * std::sqrt (34560885477513793515221944312683818679790383.) + * std::sqrt (2.), + (9541647431. / 900380687701812648084186862838778.) * std::sqrt (11520295159171264505073981437561272893263461.) + * std::sqrt (10.), + (299573807791. / 25210659255650754146357232159485784.) * std::sqrt (34560885477513793515221944312683818679790383.) + * std::sqrt (2.), + -(1127778764717. / 42017765426084590243928720265809640.) + * std::sqrt (11520295159171264505073981437561272893263461.) * std::sqrt (2.), + (22158108113. / 18007613754036252961683737256775560.) * std::sqrt (34560885477513793515221944312683818679790383.) + * std::sqrt (10.), + (2040666613423. / 18007613754036252961683737256775560.) * std::sqrt (4937269353930541930745992044669116954255769.) + * std::sqrt (2.), + (4191737066993. / 25522880862314861026865635905242428089108.) + * std::sqrt (38697087397201085046824763022410609242891822943821145.) * std::sqrt (2.), + -(1468205941837. / 31903601077893576283582044881553035111385.) + * std::sqrt (38697087397201085046824763022410609242891822943821145.), + (481082116801. / 1636082106558644937619592045207847954430.) + * std::sqrt (12899029132400361682274921007470203080963940981273715.), + (4213805427553. / 16854732644924908225288627484594056285260.) + * std::sqrt (12899029132400361682274921007470203080963940981273715.) * std::sqrt (2.), + -(28261026977692. / 223325207545255033985074314170871245779695.) + * std::sqrt (38697087397201085046824763022410609242891822943821145.) * std::sqrt (2.), + -(17559576478733. / 12761440431157430513432817952621214044554.) + * std::sqrt (2579805826480072336454984201494040616192788196254743.) * std::sqrt (2.), + -(27260744103297. / 59553388678734675729353150445565665541252.) + * std::sqrt (38697087397201085046824763022410609242891822943821145.) * std::sqrt (2.), + (22613315184946. / 14888347169683668932338287611391416385313.) + * std::sqrt (12899029132400361682274921007470203080963940981273715.) * std::sqrt (2.), + -(25837628217377. / 12761440431157430513432817952621214044554.) + * std::sqrt (7739417479440217009364952604482121848578364588764229.) * std::sqrt (2.), + -(62684986359401. / 25522880862314861026865635905242428089108.) + * std::sqrt (5528155342457297863832109003201515606127403277688735.) * std::sqrt (2.), + -(33277657927. / 71033463205674825528840487530349674.) + * std::sqrt (4587144914088926523642597305294551825737974887.) * std::sqrt (2.), + (1927126955. / 23677821068558275176280162510116558.) + * std::sqrt (4587144914088926523642597305294551825737974887.), + -(1015241476. / 11838910534279137588140081255058279.) + * std::sqrt (4587144914088926523642597305294551825737974887.) * std::sqrt (3.), + -(525642331526. / 745851363659585668052825119068671577.) + * std::sqrt (4587144914088926523642597305294551825737974887.) * std::sqrt (6.), + (483380829881. / 248617121219861889350941706356223859.) + * std::sqrt (4587144914088926523642597305294551825737974887.) * std::sqrt (2.), + (150030751537. / 745851363659585668052825119068671577.) + * std::sqrt (4587144914088926523642597305294551825737974887.) * std::sqrt (30.), + -(44353139717. / 58498146169379268082574519142640908.) + * std::sqrt (4587144914088926523642597305294551825737974887.) * std::sqrt (2.), + -(829380251171. / 2486171212198618893509417063562238590.) + * std::sqrt (4587144914088926523642597305294551825737974887.) * std::sqrt (6.), + (2446239353479. / 1243085606099309446754708531781119295.) + * std::sqrt (4587144914088926523642597305294551825737974887.) * std::sqrt (10.), + (2514626523473. / 710334632056748255288404875303496740.) + * std::sqrt (655306416298418074806085329327793117962567841.) * std::sqrt (2.), + (41048131. / 1439617270105441867607818793416.) * std::sqrt (1977693186713807260499140827732350041750787.) + * std::sqrt (2.), + (101368193000. / 4429162483638155085928905495293501.) * std::sqrt (1977693186713807260499140827732350041750787.), + (112484722301. / 17716649934552620343715621981174004.) * std::sqrt (1977693186713807260499140827732350041750787.) + * std::sqrt (3.), + (3045863083849. / 744099297251210054436056123209308168.) + * std::sqrt (1977693186713807260499140827732350041750787.) * std::sqrt (6.), + -(36938127657925. / 496066198167473369624037415472872112.) + * std::sqrt (1977693186713807260499140827732350041750787.) * std::sqrt (2.), + -(5502525896197. / 1488198594502420108872112246418616336.) + * std::sqrt (1977693186713807260499140827732350041750787.) * std::sqrt (30.), + (18683945240453. / 124016549541868342406009353868218028.) + * std::sqrt (1977693186713807260499140827732350041750787.) * std::sqrt (2.), + -(16180743403689. / 124016549541868342406009353868218028.) + * std::sqrt (1977693186713807260499140827732350041750787.) * std::sqrt (6.), + -(7519539348217. / 124016549541868342406009353868218028.) + * std::sqrt (1977693186713807260499140827732350041750787.) * std::sqrt (10.), + -(951591621239. / 17716649934552620343715621981174004.) * std::sqrt (282527598101972465785591546818907148821541.) + * std::sqrt (2.), + (31062823019. / 4694141276264662546544910.) * std::sqrt (5.) * std::sqrt (9023182675486518006136327.) + * std::sqrt (2.), + (2787165037. / 312942751750977503102994.) * std::sqrt (5.) * std::sqrt (9023182675486518006136327.), + (313424923. / 625885503501955006205988.) * std::sqrt (5.) * std::sqrt (9023182675486518006136327.) + * std::sqrt (3.), + -(320954013887. / 28164847657587975279269460.) * std::sqrt (5.) * std::sqrt (9023182675486518006136327.) + * std::sqrt (6.), + -(276071348023. / 37553130210117300372359280.) * std::sqrt (5.) * std::sqrt (9023182675486518006136327.) + * std::sqrt (2.), + -(18905708711. / 22531878126070380223415568.) * std::sqrt (9023182675486518006136327.) * std::sqrt (6.), + -(6185546743. / 110450382970933236389292.) * std::sqrt (5.) * std::sqrt (9023182675486518006136327.) + * std::sqrt (2.), + -(189397121741. / 9388282552529325093089820.) * std::sqrt (5.) * std::sqrt (9023182675486518006136327.) + * std::sqrt (6.), + -(34102449277. / 1877656510505865018617964.) * std::sqrt (9023182675486518006136327.) * std::sqrt (2.), + -(2197863791. / 9388282552529325093089820.) * std::sqrt (5.) * std::sqrt (9023182675486518006136327.) + * std::sqrt (14.), + -(3774560557. / 37487775164403143923320.) * std::sqrt (32801803268852750932905.), + (747000935. / 4998370021920419189776.) * std::sqrt (2.) * std::sqrt (32801803268852750932905.), + -(2614977227. / 4998370021920419189776.) * std::sqrt (2.) * std::sqrt (10933934422950916977635.), + -(33183272999. / 262414426150822007463240.) * std::sqrt (10933934422950916977635.), + (68002038787. / 524828852301644014926480.) * std::sqrt (32801803268852750932905.), + -(299656085761. / 104965770460328802985296.) * std::sqrt (2186786884590183395527.), + (31791923629. / 52482885230164401492648.) * std::sqrt (32801803268852750932905.), + -(122075209297. / 87471475383607335821080.) * std::sqrt (10933934422950916977635.), + (35169454361. / 52482885230164401492648.) * std::sqrt (6560360653770550186581.), + -(78296710897. / 37487775164403143923320.) * std::sqrt (4685971895550392990415.), + (1546701. / 72900813825405184908824.) * std::sqrt (3810436754575291399844659645971.), + -(244609865827. / 10765846384108186896965310672.) * std::sqrt (2.) * std::sqrt (3810436754575291399844659645971.), + (764473148423. / 10765846384108186896965310672.) * std::sqrt (2.) * std::sqrt (1270145584858430466614886548657.), + (435612242569. / 5382923192054093448482655336.) * std::sqrt (1270145584858430466614886548657.), + -(6409694637595. / 75360924688757308278757174704.) * std::sqrt (3810436754575291399844659645971.), + (6137735213909. / 75360924688757308278757174704.) * std::sqrt (1270145584858430466614886548657.) * std::sqrt (5.), + -(34891938897. / 1794307730684697816160885112.) * std::sqrt (3810436754575291399844659645971.), + (2917702383691. / 37680462344378654139378587352.) * std::sqrt (1270145584858430466614886548657.), + -(507096265731. / 12560154114792884713126195784.) * std::sqrt (3810436754575291399844659645971.) * std::sqrt (5.), + (538755465999. / 1794307730684697816160885112.) * std::sqrt (544348107796470199977808520853.), + (29033791627. / 9782544049232743002606111244038.) * std::sqrt (338531287325211031860850338067150911335.), + -(262830658169. / 97825440492327430026061112440380.) * std::sqrt (2.) + * std::sqrt (338531287325211031860850338067150911335.), + (293721773683. / 97825440492327430026061112440380.) * std::sqrt (2.) + * std::sqrt (338531287325211031860850338067150911335.) * std::sqrt (3.), + (2829425277857. / 1027167125169438015273641680623990.) * std::sqrt (338531287325211031860850338067150911335.) + * std::sqrt (3.), + -(2238371937981. / 228259361148764003394142595694220.) * std::sqrt (338531287325211031860850338067150911335.), + (6749438779597. / 410866850067775206109456672249596.) * std::sqrt (67706257465042206372170067613430182267.) + * std::sqrt (3.), + (249319640921. / 68477808344629201018242778708266.) * std::sqrt (338531287325211031860850338067150911335.), + -(539419007851. / 342389041723146005091213893541330.) * std::sqrt (338531287325211031860850338067150911335.) + * std::sqrt (3.), + -(171068367630. / 11412968057438200169707129784711.) * std::sqrt (67706257465042206372170067613430182267.), + (820428927517. / 24456360123081857506515278110095.) * std::sqrt (48361612475030147408692905438164415905.), + -(380882463935. / 16021791139284068774186451015545843594.) * std::sqrt (3.) + * std::sqrt (2206416960936351241282951190627144110360471628935.), + (8072277256553. / 480653734178522063225593530466375307820.) * std::sqrt (2.) * std::sqrt (3.) + * std::sqrt (2206416960936351241282951190627144110360471628935.), + -(40210330408513. / 480653734178522063225593530466375307820.) * std::sqrt (2.) + * std::sqrt (2206416960936351241282951190627144110360471628935.), + (60460785674071. / 1682288069624827221289577356632313577370.) + * std::sqrt (2206416960936351241282951190627144110360471628935.), + -(44811592769359. / 3364576139249654442579154713264627154740.) * std::sqrt (3.) + * std::sqrt (2206416960936351241282951190627144110360471628935.), + -(261264677822167. / 672915227849930888515830942652925430948.) + * std::sqrt (441283392187270248256590238125428822072094325787.), + -(1297159856969. / 112152537974988481419305157108820905158.) * std::sqrt (3.) + * std::sqrt (2206416960936351241282951190627144110360471628935.), + (90506952767479. / 1682288069624827221289577356632313577370.) + * std::sqrt (2206416960936351241282951190627144110360471628935.), + -(6917492055114. / 56076268987494240709652578554410452579.) * std::sqrt (3.) + * std::sqrt (441283392187270248256590238125428822072094325787.), + -(8951657491937. / 40054477848210171935466127538864608985.) * std::sqrt (3.) + * std::sqrt (315202422990907320183278741518163444337210232705.), + -(957643077667. / 588707360988645980653576145897871579216.) + * std::sqrt (2170343179656283287883941488614423306932736027111121.) * std::sqrt (2.), + (621680408575. / 588707360988645980653576145897871579216.) + * std::sqrt (2170343179656283287883941488614423306932736027111121.), + -(1613092580093. / 588707360988645980653576145897871579216.) + * std::sqrt (2170343179656283287883941488614423306932736027111121.) * std::sqrt (3.), + (7796076285503. / 12362854580761565593725099063855303163536.) + * std::sqrt (2170343179656283287883941488614423306932736027111121.) * std::sqrt (6.), + -(3499098848199. / 2747301017947014576383355347523400703008.) + * std::sqrt (2170343179656283287883941488614423306932736027111121.) * std::sqrt (2.), + -(24227475061169. / 24725709161523131187450198127710606327072.) + * std::sqrt (2170343179656283287883941488614423306932736027111121.) * std::sqrt (30.), + (983748733991. / 8241903053841043729150066042570202109024.) + * std::sqrt (2170343179656283287883941488614423306932736027111121.) * std::sqrt (2.), + (2702922799177. / 8241903053841043729150066042570202109024.) + * std::sqrt (2170343179656283287883941488614423306932736027111121.) * std::sqrt (6.), + -(2830038256519. / 2747301017947014576383355347523400703008.) + * std::sqrt (2170343179656283287883941488614423306932736027111121.) * std::sqrt (10.), + -(7915696678283. / 1177414721977291961307152291795743158432.) + * std::sqrt (310049025665183326840563069802060472418962289587303.) * std::sqrt (2.), + (470853666287. / 7570332181910609063792101351102261253232.) * std::sqrt (3.) + * std::sqrt (115935593470518835817766866162978250334523437784363455.) * std::sqrt (2.), + (7372063487747. / 37851660909553045318960506755511306266160.) * std::sqrt (3.) + * std::sqrt (115935593470518835817766866162978250334523437784363455.), + (1557775471511. / 12617220303184348439653502251837102088720.) + * std::sqrt (115935593470518835817766866162978250334523437784363455.), + -(44334087925441. / 264961626366871317232723547288579143863120.) + * std::sqrt (115935593470518835817766866162978250334523437784363455.) * std::sqrt (2.), + (167055705079309. / 529923252733742634465447094577158287726240.) * std::sqrt (3.) + * std::sqrt (115935593470518835817766866162978250334523437784363455.) * std::sqrt (2.), + (9025288781485. / 105984650546748526893089418915431657545248.) + * std::sqrt (23187118694103767163553373232595650066904687556872691.) * std::sqrt (2.), + -(2686760757267. / 35328216848916175631029806305143885848416.) * std::sqrt (3.) + * std::sqrt (115935593470518835817766866162978250334523437784363455.) * std::sqrt (2.), + (18902357960929. / 176641084244580878155149031525719429242080.) + * std::sqrt (115935593470518835817766866162978250334523437784363455.) * std::sqrt (2.), + (62852500001419. / 105984650546748526893089418915431657545248.) * std::sqrt (3.) + * std::sqrt (23187118694103767163553373232595650066904687556872691.) * std::sqrt (2.), + (945069659329. / 529923252733742634465447094577158287726240.) * std::sqrt (3.) + * std::sqrt (115935593470518835817766866162978250334523437784363455.) * std::sqrt (14.), + (617869031. / 192130103440927976575152.) * std::sqrt (5.) * std::sqrt (77136233821044229928744879.) + * std::sqrt (2.), + (10719116297. / 960650517204639882875760.) * std::sqrt (5.) * std::sqrt (77136233821044229928744879.), + (1078141781. / 960650517204639882875760.) * std::sqrt (5.) * std::sqrt (77136233821044229928744879.) + * std::sqrt (3.), + (76469124049. / 20173660861297437540390960.) * std::sqrt (5.) * std::sqrt (77136233821044229928744879.) + * std::sqrt (6.), + (31821888583. / 4483035746954986120086880.) * std::sqrt (5.) * std::sqrt (77136233821044229928744879.) + * std::sqrt (2.), + (5554739153. / 8069464344518975016156384.) * std::sqrt (77136233821044229928744879.) * std::sqrt (6.), + -(3106909447. / 2689821448172991672052128.) * std::sqrt (5.) * std::sqrt (77136233821044229928744879.) + * std::sqrt (2.), + (75509398331. / 13449107240864958360260640.) * std::sqrt (5.) * std::sqrt (77136233821044229928744879.) + * std::sqrt (6.), + (6075089491. / 896607149390997224017376.) * std::sqrt (77136233821044229928744879.) * std::sqrt (2.), + (1275076031. / 1921301034409279765751520.) * std::sqrt (5.) * std::sqrt (11019461974434889989820697.) + * std::sqrt (2.), + (49955. / 2602262296426928432.) * std::sqrt (9902847527964529535108767.) * std::sqrt (2.), + (2079228193. / 24885434340730716595216.) * std::sqrt (9902847527964529535108767.), + (269062087. / 74656303022192149785648.) * std::sqrt (9902847527964529535108767.) * std::sqrt (3.), + (30113983421. / 522594121155345048499536.) * std::sqrt (9902847527964529535108767.) * std::sqrt (6.), + (7970952551. / 348396080770230032333024.) * std::sqrt (9902847527964529535108767.) * std::sqrt (2.), + (32960981. / 95017112937335463363552.) * std::sqrt (9902847527964529535108767.) * std::sqrt (30.), + (31080051349. / 348396080770230032333024.) * std::sqrt (9902847527964529535108767.) * std::sqrt (2.), + (2267590919. / 95017112937335463363552.) * std::sqrt (9902847527964529535108767.) * std::sqrt (6.), + (274554491. / 91683179150060534824480.) * std::sqrt (9902847527964529535108767.) * std::sqrt (10.), + (55742399. / 348396080770230032333024.) * std::sqrt (9902847527964529535108767.) * std::sqrt (14.), + (229775. / 42817642055124816.) * std::sqrt (5.) * std::sqrt (14155690870266160523.), + (2861327. / 428176420551248160.) * std::sqrt (2.) * std::sqrt (5.) * std::sqrt (14155690870266160523.), + (468641. / 428176420551248160.) * std::sqrt (2.) * std::sqrt (5.) * std::sqrt (14155690870266160523.) + * std::sqrt (3.), + (22438489. / 4495852415788105680.) * std::sqrt (5.) * std::sqrt (14155690870266160523.) * std::sqrt (3.), + (361927. / 71362736758541360.) * std::sqrt (5.) * std::sqrt (14155690870266160523.), + (1685797. / 899170483157621136.) * std::sqrt (14155690870266160523.) * std::sqrt (3.), + (7012303. / 1198893977543494848.) * std::sqrt (5.) * std::sqrt (14155690870266160523.), + (34621. / 27624285196854720.) * std::sqrt (5.) * std::sqrt (14155690870266160523.) * std::sqrt (3.), + (2378393. / 399631325847831616.) * std::sqrt (14155690870266160523.), + (1024117. / 856352841102496320.) * std::sqrt (5.) * std::sqrt (2022241552895165789.), + (35. / 67124682721811748.) * std::sqrt (7.) * std::sqrt (2111308236157200531699814841.) * std::sqrt (2.), + (8300357. / 7702960090424228953488.) * std::sqrt (7.) * std::sqrt (2111308236157200531699814841.), + (6617923. / 23108880271272686860464.) * std::sqrt (7.) * std::sqrt (2111308236157200531699814841.) + * std::sqrt (3.), + (1690973. / 8986786772161600445736.) * std::sqrt (7.) * std::sqrt (2111308236157200531699814841.) + * std::sqrt (6.), + (13583989. / 17973573544323200891472.) * std::sqrt (7.) * std::sqrt (2111308236157200531699814841.) + * std::sqrt (2.), + (70571. / 1633961231302109171952.) * std::sqrt (7.) * std::sqrt (2111308236157200531699814841.) * std::sqrt (30.), + -(11160043. / 53920720632969602674416.) * std::sqrt (7.) * std::sqrt (2111308236157200531699814841.) + * std::sqrt (2.), + (11033333. / 29411302163437965095136.) * std::sqrt (7.) * std::sqrt (2111308236157200531699814841.) + * std::sqrt (6.), + (502141. / 4729887774821894971440.) * std::sqrt (7.) * std::sqrt (2111308236157200531699814841.) + * std::sqrt (10.), + (2755147. / 15405920180848457906976.) * std::sqrt (2111308236157200531699814841.) * std::sqrt (2.), + (63. / 598672.) * std::sqrt (7.) * std::sqrt (187085.), + (381. / 5986720.) * std::sqrt (2.) * std::sqrt (7.) * std::sqrt (187085.), + (263. / 5986720.) * std::sqrt (2.) * std::sqrt (7.) * std::sqrt (187085.) * std::sqrt (3.), + -(1403. / 62860560.) * std::sqrt (7.) * std::sqrt (187085.) * std::sqrt (3.), + (3641. / 20953520.) * std::sqrt (7.) * std::sqrt (187085.), + (1039. / 12572112.) * std::sqrt (7.) * std::sqrt (37417.) * std::sqrt (3.), + -(193. / 16762816.) * std::sqrt (7.) * std::sqrt (187085.), + (31. / 2703680.) * std::sqrt (7.) * std::sqrt (187085.) * std::sqrt (3.), + (4227. / 16762816.) * std::sqrt (7.) * std::sqrt (37417.), + (641. / 11973440.) * std::sqrt (187085.), + (77. / 253105968.) * std::sqrt (7.) * std::sqrt (13736271805.) * std::sqrt (2.), + (1. / 474573690.) * std::sqrt (7.) * std::sqrt (13736271805.), + (2141. / 5694884280.) * std::sqrt (7.) * std::sqrt (13736271805.) * std::sqrt (3.), + -(461. / 8858708880.) * std::sqrt (7.) * std::sqrt (13736271805.) * std::sqrt (6.), + (439. / 2214677220.) * std::sqrt (7.) * std::sqrt (13736271805.) * std::sqrt (2.), + (235. / 442935444.) * std::sqrt (7.) * std::sqrt (2747254361.) * std::sqrt (6.), + (313. / 21260901312.) * std::sqrt (7.) * std::sqrt (13736271805.) * std::sqrt (2.), + -(8509. / 318913519680.) * std::sqrt (7.) * std::sqrt (13736271805.) * std::sqrt (6.), + (3155. / 7086967104.) * std::sqrt (7.) * std::sqrt (2747254361.) * std::sqrt (2.), + (8657. / 15186358080.) * std::sqrt (13736271805.) * std::sqrt (2.) + }; + break; + } +} +} // namespace t8_mra + +#endif diff --git a/src/t8_mra/num/mat.hxx b/src/t8_mra/num/mat.hxx new file mode 100644 index 0000000000..f293a75bfc --- /dev/null +++ b/src/t8_mra/num/mat.hxx @@ -0,0 +1,205 @@ +#pragma once + +#ifdef T8_ENABLE_MRA + +#include +#include +#include + +#include + +namespace t8_mra +{ + +class mat { + std::vector data; + size_t num_rows = 0u; + size_t num_cols = 0u; + + public: + mat () = default; + mat (size_t _rows, size_t _cols): data (_rows * _cols, {}), num_rows (_rows), num_cols (_cols) + { + } + + mat (size_t _rows, size_t _cols, std::initializer_list l): data (l), num_rows (_rows), num_cols (_cols) + { + if (l.size () != _rows * _cols) + throw std::out_of_range ("number elements in t8_mra::util::mat does not fit to number columns " + "and number rows"); + } + + mat (const mat &) = default; + mat & + operator= (const mat &) + = default; + mat (mat &&) = default; + mat & + operator= (mat &&) + = default; + + mat & + operator= (const std::initializer_list &l) + { + if (l.size () != num_rows * num_cols) + throw std::out_of_range ("number elements in t8_mra::util::mat does not fit to number columns " + "and number rows"); + + std::copy_n (l.begin (), l.size (), data.begin ()); + + return *this; + } + + double & + operator() (size_t i, size_t j); + double + operator() (size_t i, size_t j) const; + + mat & + operator= (double v); + + void + resize (size_t _m, size_t _n); + + size_t + rows () const noexcept; + size_t + cols () const noexcept; +}; + +inline double & +mat::operator() (size_t i, size_t j) +{ + if (i >= num_rows || j >= num_cols) + throw std::out_of_range ("indices in t8_mra::util::mat::operator() is out of range"); + + return data[num_rows * j + i]; +} + +inline double +mat::operator() (size_t i, size_t j) const +{ + if (i >= num_rows || j >= num_cols) + throw std::out_of_range ("indices in t8_mra::util::mat::operator() is out of range"); + + return data[num_rows * j + i]; +} + +inline void +mat::resize (size_t _rows, size_t _cols) +{ + data.clear (); + num_rows = _rows; + num_cols = _cols; + data.resize (_rows * _cols); +} + +inline size_t +mat::rows () const noexcept +{ + return num_rows; +} +inline size_t +mat::cols () const noexcept +{ + return num_cols; +} + +/// Matrix is saved as A = (L - E_n) + U +/// below diagonal: L +/// Remaining matrix: U +inline void +lu_factors (mat &A, std::vector &p) +{ + if (A.rows () != A.cols ()) + throw std::logic_error ("Matrix in t8_mra::util::lr_factor is not a square matrix"); + + const auto n = A.rows (); + p.resize (n); + + for (auto i = 0u; i < n; ++i) + p[i] = i; + + for (auto j = 0u; j < n; j++) { + auto Aj_max = 0.0; + auto piv = j; + + for (auto k = j; k < n; k++) { + const auto Ap = std::abs (A (k, j)); + + if (Ap > Aj_max) { + Aj_max = Ap; + piv = k; + } + } + + if (piv != j) { + std::swap (p[piv], p[j]); + for (auto k = 0u; k < n; k++) + std::swap (A (piv, k), A (j, k)); + } + + for (auto i = j + 1; i < n; i++) { + A (i, j) /= A (j, j); + + for (auto k = j + 1; k < n; k++) + A (i, k) -= A (i, j) * A (j, k); + } + } +} + +inline void +lu_solve (const mat &A, const std::vector &p, vec &x) +{ + if (A.rows () != A.cols ()) + throw std::logic_error ("Matrix in t8_mra::util::lr_solve is not a square matrix"); + if (A.rows () != p.size ()) + throw std::logic_error ("Permutation vector in t8_mra::util::lr_solve does not fit"); + if (A.rows () != x.size ()) + throw std::logic_error ("Solution vector in t8_mra::util::lr_solve does not fit"); + + const auto n = A.rows (); + + const auto b = x; + for (auto i = 0u; i < n; ++i) { + x (i) = b (p[i]); + for (auto k = 0u; k < i; ++k) + x (i) -= A (i, k) * x (k); + } + + for (int i = n - 1; i >= 0; --i) { + for (auto k = static_cast (i + 1); k < n; ++k) + x (i) -= A (i, k) * x (k); + x (i) /= A (i, i); + } +} + +inline void +lu_solve (const mat &A, const std::vector &p, std::vector &x) +{ + if (A.rows () != A.cols ()) + throw std::logic_error ("Matrix in t8_mra::util::lr_solve is not a square matrix"); + if (A.rows () != p.size ()) + throw std::logic_error ("Permutation vector in t8_mra::util::lr_solve does not fit"); + if (A.rows () != x.size ()) + throw std::logic_error ("Solution vector in t8_mra::util::lr_solve does not fit"); + + const auto n = A.rows (); + + const auto b = x; + for (auto i = 0u; i < n; ++i) { + x[i] = b[p[i]]; + for (auto k = 0u; k < i; ++k) + x[i] -= A (i, k) * x[k]; + } + + for (int i = n - 1; i >= 0; --i) { + for (auto k = static_cast (i + 1); k < n; ++k) + x[i] -= A (i, k) * x[k]; + x[i] /= A (i, i); + } +} + +} // namespace t8_mra + +#endif diff --git a/src/t8_mra/num/multiindex.hxx b/src/t8_mra/num/multiindex.hxx new file mode 100644 index 0000000000..e2962bc053 --- /dev/null +++ b/src/t8_mra/num/multiindex.hxx @@ -0,0 +1,178 @@ +#pragma once + +#ifdef T8_ENABLE_MRA + +#include +#include + +namespace t8_mra +{ + +/** + * @brief Multiindex type for D-dimensional indices + * + * Used to represent tensor product basis indices. + * For example, in 2D with P=3: multiindex {1,2} represents phi_1(x) * phi_2(y) + * + * @tparam DIM Spatial dimension + */ +template +using multiindex = std::array; + +/** + * @brief Computes the number of basis functions for tensor-structured basis + * + * For a tensor basis of polynomial degree P-1 in D dimensions, + * the number of basis functions is P^D. + * + * @param P Polynomial order (degree is P-1) + * @param D Spatial dimension + * @return constexpr int Number of basis functions + */ +constexpr int +tensor_basis_size (int P, int D) +{ + int result = 1; + for (int i = 0; i < D; ++i) + result *= P; + + return result; +} + +/** + * @brief Generates the multiindex set (pset) for tensor-structured basis + * + * Creates all possible combinations of 1D basis indices for a tensor product basis. + * For D dimensions and polynomial order P, generates P^D multiindices. + * + * Example for D=2, P=3: + * pset[0] = {0,0}, pset[1] = {0,1}, pset[2] = {0,2}, + * pset[3] = {1,0}, pset[4] = {1,1}, pset[5] = {1,2}, + * pset[6] = {2,0}, pset[7] = {2,1}, pset[8] = {2,2} + * + * The ordering is lexicographic with the last dimension varying fastest. + * + * @tparam DIM Spatial dimension + * @param P Polynomial order (degree is P-1) + * @return std::vector> Vector of all multiindices + */ +template +std::vector> +generate_tensor_pset (int P) +{ + const int num_basis = tensor_basis_size (P, DIM); + std::vector> pset (num_basis); + + if constexpr (DIM == 1) { + // 1D case: simple enumeration + for (int i = 0; i < P; ++i) { + pset[i][0] = i; + } + } + else if constexpr (DIM == 2) { + int idx = 0; + for (int j = 0; j < P; ++j) { + for (int i = 0; i < P; ++i) { + pset[idx][0] = i; + pset[idx][1] = j; + ++idx; + } + } + + /// TODO + // multiindex buffer; + // for (unsigned i = 0; i < idx; ++i) { + // if (pset[i][0] == 1 && pset[i][1] == 0) { + // buffer = pset[1]; + // pset[1] = pset[i]; + // pset[i] = buffer; + // } + // if (pset[i][0] == 0 && pset[i][1] == 1) { + // buffer = pset[2]; + // pset[2] = pset[i]; + // pset[i] = buffer; + // } + // } + + } + else if constexpr (DIM == 3) { + // 3D case: ix-fast, then iy, then iz + // Loop order: k (outer), j (middle), i (inner) so i varies fastest + int idx = 0; + for (int k = 0; k < P; ++k) { + for (int j = 0; j < P; ++j) { + for (int i = 0; i < P; ++i) { + pset[idx][0] = i; + pset[idx][1] = j; + pset[idx][2] = k; + ++idx; + } + } + } + } + + return pset; +} + +/** + * @brief Evaluates a single tensor basis function at a point + * + * Computes phi_p(x) = product_{d=0}^{DIM-1} phi_1d(x[d], pset[p][d]) + * where phi_1d is the 1D Legendre basis function. + * + * @tparam DIM Spatial dimension + * @param x Point in [0,1]^DIM where to evaluate + * @param p Basis function index + * @param pset Multiindex set mapping p to 1D indices + * @param phi_1d Function pointer to 1D basis evaluation + * @return double Value of the p-th basis function at x + */ +template +inline double +eval_tensor_basis (const std::array &x, int p, const std::vector> &pset, + double (*phi_1d) (double, int)) +{ + double result = 1.0; + for (unsigned int d = 0; d < DIM; ++d) + result *= phi_1d (x[d], pset[p][d]); + + return result; +} + +/** + * @brief Evaluates the gradient of a tensor basis function at a point + * + * Computes grad_phi_p(x)[dir] using the product rule: + * d/dx_dir [phi_p(x)] = phi_1d'(x[dir], pset[p][dir]) * product_{d!=dir} phi_1d(x[d], pset[p][d]) + * + * @tparam DIM Spatial dimension + * @param x Point in [0,1]^DIM where to evaluate + * @param p Basis function index + * @param dir Derivative direction (0, 1, ..., DIM-1) + * @param pset Multiindex set mapping p to 1D indices + * @param phi_1d Function pointer to 1D basis evaluation + * @param phi_prime_1d Function pointer to 1D basis derivative evaluation + * @return double Value of the dir-th partial derivative of phi_p at x + */ +template +inline double +eval_tensor_basis_gradient (const std::array &x, int p, int dir, const std::vector> &pset, + double (*phi_1d) (double, int), double (*phi_prime_1d) (double, int)) +{ + double result = 1.0; + for (unsigned int d = 0; d < DIM; ++d) { + if (d == static_cast (dir)) { + // Use derivative in this direction + result *= phi_prime_1d (x[d], pset[p][d]); + } + else { + // Use function value in other directions + result *= phi_1d (x[d], pset[p][d]); + } + } + return result; +} + +} // namespace t8_mra + +#endif // T8_ENABLE_MRA diff --git a/src/t8_mra/num/quadrature.hxx b/src/t8_mra/num/quadrature.hxx new file mode 100644 index 0000000000..5164696271 --- /dev/null +++ b/src/t8_mra/num/quadrature.hxx @@ -0,0 +1,95 @@ +#pragma once + +#ifdef T8_ENABLE_MRA + +#include +#include +#include + +namespace t8_mra +{ + +/** + * @brief Generates 1D Gauss-Legendre quadrature points and weights on [0,1] + * + * Uses GSL to generate Gauss-Legendre quadrature nodes and weights. + * The quadrature rule integrates polynomials up to degree 2*n-1 exactly, + * where n is the number of quadrature points. + * + * @param num_points Number of quadrature points + * @param points Output vector for quadrature points (will be resized) + * @param weights Output vector for quadrature weights (will be resized) + */ +inline void +gauss_legendre_1d (int num_points, std::vector &points, std::vector &weights) +{ + if (num_points <= 0) + throw std::invalid_argument ("Number of quadrature points must be positive"); + + // Allocate GSL workspace for Gauss-Legendre quadrature on [0,1] + gsl_integration_fixed_workspace *workspace + = gsl_integration_fixed_alloc (gsl_integration_fixed_legendre, num_points, 0.0, 1.0, 0.0, 0.0); + + if (!workspace) + throw std::runtime_error ("Failed to allocate GSL integration workspace"); + + // Get pointers to nodes and weights + double *nodes_ptr = gsl_integration_fixed_nodes (workspace); + double *weights_ptr = gsl_integration_fixed_weights (workspace); + + // Copy to output vectors + points.resize (num_points); + weights.resize (num_points); + + for (int i = 0; i < num_points; ++i) { + points[i] = nodes_ptr[i]; + weights[i] = weights_ptr[i]; + } + + // Free workspace + gsl_integration_fixed_free (workspace); +} + +/** + * @brief Generates 1D Gauss-Legendre quadrature on arbitrary interval [a,b] + * + * @param num_points Number of quadrature points + * @param a Left endpoint of interval + * @param b Right endpoint of interval + * @param points Output vector for quadrature points (will be resized) + * @param weights Output vector for quadrature weights (will be resized) + */ +inline void +gauss_legendre_1d_interval (int num_points, double a, double b, std::vector &points, + std::vector &weights) +{ + if (a >= b) + throw std::invalid_argument ("Interval endpoints must satisfy a < b"); + + // Allocate GSL workspace for Gauss-Legendre quadrature on [a,b] + gsl_integration_fixed_workspace *workspace + = gsl_integration_fixed_alloc (gsl_integration_fixed_legendre, num_points, a, b, 0.0, 0.0); + + if (!workspace) + throw std::runtime_error ("Failed to allocate GSL integration workspace"); + + // Get pointers to nodes and weights + double *nodes_ptr = gsl_integration_fixed_nodes (workspace); + double *weights_ptr = gsl_integration_fixed_weights (workspace); + + // Copy to output vectors + points.resize (num_points); + weights.resize (num_points); + + for (int i = 0; i < num_points; ++i) { + points[i] = nodes_ptr[i]; + weights[i] = weights_ptr[i]; + } + + // Free workspace + gsl_integration_fixed_free (workspace); +} + +} // namespace t8_mra + +#endif // T8_ENABLE_MRA diff --git a/src/t8_mra/num/vec.hxx b/src/t8_mra/num/vec.hxx new file mode 100644 index 0000000000..b52d41cd96 --- /dev/null +++ b/src/t8_mra/num/vec.hxx @@ -0,0 +1,160 @@ +#pragma once + +// Matrixklasse mit LR-Zerlegung, entnommen aus +// http://www.igpm.rwth-aachen.de/Download/ss13/na2/na2-base.h Numerische +// Analysis II, SS 2013, Prof. Dr. Wolfgang Dahmen, Dr. Markus Bachmayr + +#ifdef T8_ENABLE_MRA + +#include +#include +#include +#include +#include + +namespace t8_mra +{ + +class vec { + std::vector data; + + public: + vec () = default; + vec (size_t n): data (n, 0.0) + { + } + vec (std::initializer_list l): data (l) + { + } + + vec (const vec&) = default; + vec& + operator= (const vec&) + = default; + vec (vec&&) = default; + vec& + operator= (vec&&) + = default; + + vec& + operator= (const std::initializer_list& l) + { + if (l.size () != data.size ()) + throw std::out_of_range ("number elements in t8_mra::util::vec does not fit to number columns " + "and number rows"); + + std::copy_n (l.begin (), l.size (), data.begin ()); + + return *this; + } + + double& + operator() (size_t i) + { + if (i >= data.size ()) + throw std::out_of_range ("index in t8_mra::util::vec::operator() is out of range"); + + return data[i]; + } + + double + operator() (size_t i) const + { + if (i >= data.size ()) + throw std::out_of_range ("index in t8_mra::util::vec::operator() is out of range"); + + return data[i]; + } + + size_t + size () const noexcept + { + return data.size (); + } + + vec& + operator+= (const vec& y) + { + if (y.size () < data.size ()) + throw std::logic_error ("lengths in t8_mra::util::vec::operator+= does not fit"); + + for (auto i = 0u; i < data.size (); i++) + data[i] += y (i); + + return *this; + } + + vec& + operator-= (const vec& y) + { + if (y.size () < data.size ()) + throw std::logic_error ("lengths in t8_mra::util::vec::operator+= does not fit"); + for (auto i = 0u; i < data.size (); i++) + data[i] -= y (i); + + return *this; + } + + vec& + operator*= (double v) + { + for (auto i = 0u; i < data.size (); i++) + data[i] *= v; + + return *this; + } + + void + resize (size_t n) + { + data.clear (); + data.resize (n); + } +}; + +inline double +inner (const vec& v1, const vec& v2) +{ + if (v1.size () != v2.size ()) + throw std::logic_error ("lengths in t8_mra::util::inner does not fit"); + + auto res = 0.0; + for (auto i = 0u; i < v1.size (); i++) + res += v1 (i) * v2 (i); + + return res; +} + +inline double +l1norm (const vec& v) +{ + auto res = 0.0; + for (auto i = 0u; i < v.size (); i++) + res += std::abs (v (i)); + + return res; +} + +inline double +l2norm (const vec& v) +{ + return std::sqrt (inner (v, v)); +} + +inline double +linftynorm (const vec& v) +{ + auto res = 0.0; + for (auto i = 0u; i < v.size (); i++) { + const auto tmp = std::abs (v (i)); + + if (tmp > res) + res = tmp; + } + + return res; +} + +} // namespace t8_mra + +#endif diff --git a/src/t8_mra/shapes/cartesian.hxx b/src/t8_mra/shapes/cartesian.hxx new file mode 100644 index 0000000000..96784c789f --- /dev/null +++ b/src/t8_mra/shapes/cartesian.hxx @@ -0,0 +1,310 @@ +#pragma once + +#ifdef T8_ENABLE_MRA + +#include "t8_mra/core/base.hxx" +#include "t8_mra/core/adaptation.hxx" +#include "t8_mra/num/mask_coefficients_compute.hxx" + +namespace t8_mra +{ + +/** + * @brief Cartesian multiscale implementation (partial specialization) + * + * Generic implementation for all cartesian elements (LINE, QUAD, HEX). + * Inherits all common MRA functionality from multiscale_base and + * implements cartesian-specific: + * - Detail norm computation (uses sqrt(volume) scaling) + * - Projection (uses Gauss-Legendre tensor product quadrature) + * - No vertex ordering needed (cartesian elements have standard ordering) + * + * @tparam TShape Element shape (T8_ECLASS_LINE, T8_ECLASS_QUAD, or T8_ECLASS_HEX) + * @tparam U Number of solution components + * @tparam P Polynomial order + */ +template + requires is_cartesian +class multiscale: + public multiscale_base, + public multiscale_adaptation> { + + public: + using Base = multiscale_base; + using Adaptation = multiscale_adaptation>; + using element_t = typename Base::element_t; + using levelmultiindex = typename Base::levelmultiindex; + using index_set = typename Base::index_set; + + // Make adaptation methods accessible + using Adaptation::coarsen; + using Adaptation::refine; + using Adaptation::initialize_data_adaptive; + using Adaptation::balance; + + //============================================================================= + // Constructor + //============================================================================= + + /** + * @brief Constructor for cartesian multiscale + * + * @param _max_level Maximum refinement level + * @param _comm MPI communicator + */ + multiscale (int _max_level, sc_MPI_Comm _comm): Base (_max_level, _comm) + { + // Initialize mask coefficients via computation + t8_mra::initialize_mask_coefficients_computed (Base::P_DIM, Base::DOF, Base::mask_coefficients, + Base::inverse_mask_coefficients); + } + + //============================================================================= + // Element-Specific Implementation: Detail Norm + //============================================================================= + + /** + * @brief Compute local detail norm for cartesian elements + * + * Cartesian elements use sqrt(volume) scaling factor (no factor of 2). + * + * @param lmi Level multi-index + * @return Array of detail norms (one per solution component) + */ + std::array + local_detail_norm (const levelmultiindex &lmi) override + { + std::array detail_norm = {}; + const auto vol = Base::d_map.get (lmi).vol; + const auto &details = Base::d_map.get (lmi).d_coeffs; + + // Cartesian-specific scaling: sqrt(volume) without factor of 2 + const auto scaling_factor = std::sqrt (vol); + + for (auto u = 0u; u < Base::U_DIM; ++u) { + double norm_sq = 0.0; + for (auto k = 0u; k < levelmultiindex::NUM_CHILDREN; ++k) { + for (auto i = 0u; i < Base::DOF; ++i) { + const auto d = details[element_t::wavelet_idx (k, u, i)]; + norm_sq += d * d; + } + } + detail_norm[u] = std::sqrt (norm_sq) / scaling_factor; + } + + return detail_norm; + } + + //============================================================================= + // Element-Specific Implementation: Projection + //============================================================================= + + /** + * @brief Project a function onto the DG basis for a cartesian element + * + * Uses Gauss-Legendre tensor product quadrature on reference element [0,1]^DIM. + * Direct mapping from physical to reference coordinates (no transformation matrix). + * + * @param dg_coeffs Output vector for DG coefficients + * @param tree_idx Tree index in forest + * @param element Pointer to element + * @param func Function to project + */ + template + void + project_impl (std::vector &dg_coeffs, int tree_idx, const t8_element_t *element, Func &&func) + { + // Extract element vertices (cartesian: axis-aligned box) + constexpr int num_vertices = (Base::DIM == 1 ? 2 : (Base::DIM == 2 ? 4 : 8)); + double vertices[8][3] = {}; + + if constexpr (Base::DIM == 2 && TShape == T8_ECLASS_QUAD) { + // t8code QUAD vertex ordering: 0-1-2-3 as (0,0)-(1,0)-(0,1)-(1,1) + // We need: 0-1-2-3 as (0,0)-(1,0)-(1,1)-(0,1) for standard quad + // So we swap vertices 2 and 3 + const int vertex_perm[4] = { 0, 1, 3, 2 }; + for (int i = 0; i < num_vertices; ++i) + t8_forest_element_coordinate (Base::forest, tree_idx, element, vertex_perm[i], vertices[i]); + } + else { + for (int i = 0; i < num_vertices; ++i) + t8_forest_element_coordinate (Base::forest, tree_idx, element, i, vertices[i]); + } + + // Get physical quadrature points via direct mapping + const auto phys_quad_points = Base::basis.deref_quad_points (vertices); + + + // Note: For orthonormal Legendre basis, NO volume/Jacobian scaling in projection! + // The basis functions are normalized on reference element [0,1]^DIM + + // Precompute basis values at all quadrature points + std::vector> basis_at_quad (Base::basis.num_quad_points); + for (auto q = 0u; q < Base::basis.num_quad_points; ++q) { + // Extract reference quadrature point + std::vector x_ref (Base::DIM); + for (unsigned int d = 0; d < Base::DIM; ++d) + x_ref[d] = Base::basis.ref_quad_points[Base::DIM * q + d]; + + const auto basis_vals = Base::basis.basis_value (x_ref); + for (auto i = 0u; i < Base::DOF; ++i) + basis_at_quad[q][i] = basis_vals[i]; + } + + // Project onto each basis function + for (auto i = 0u; i < Base::DOF; ++i) { + std::array sum = {}; + + for (auto q = 0u; q < Base::basis.num_quad_points; ++q) { + // Extract physical coordinates + std::array x_phys; + for (unsigned int d = 0; d < Base::DIM; ++d) + x_phys[d] = phys_quad_points[Base::DIM * q + d]; + + // Evaluate function at physical point + // Supports two calling conventions: + // func(x, y, z) -> std::array (return value) + // func(x, y, z, double* out) (output pointer) + std::array f_val; + if constexpr (Base::DIM == 1) { + if constexpr (std::is_invocable_v) + f_val = func (x_phys[0]); + else + func (x_phys[0], f_val.data ()); + } + else if constexpr (Base::DIM == 2) { + if constexpr (std::is_invocable_v) + f_val = func (x_phys[0], x_phys[1]); + else + func (x_phys[0], x_phys[1], f_val.data ()); + } + else { + if constexpr (std::is_invocable_v) + f_val = func (x_phys[0], x_phys[1], x_phys[2]); + else + func (x_phys[0], x_phys[1], x_phys[2], f_val.data ()); + } + + // Accumulate quadrature sum: integral(f * phi_i) + // Note: For orthonormal basis, the volume scaling cancels out + for (auto u = 0u; u < Base::U_DIM; ++u) + sum[u] += Base::basis.quad_weights[q] * f_val[u] * basis_at_quad[q][i]; + } + + // Store coefficients directly (already includes volume scaling from quadrature) + for (auto u = 0u; u < Base::U_DIM; ++u) + dg_coeffs[element_t::dg_idx (u, i)] = sum[u]; + } + } + + /** + * @brief Project a function onto a single forest leaf + * + * Builds the complete element data (volume, DG coefficients) for the + * given leaf. + * + * @param tree_idx Tree index in forest + * @param element Pointer to element + * @param func Function to project + * @return Element data of the leaf + */ + template + element_t + project_leaf (int tree_idx, const t8_element_t *element, Func &&func) + { + element_t data; + + data.vol = t8_forest_element_volume (Base::forest, tree_idx, element); + project_impl (data.u_coeffs, tree_idx, element, func); + + return data; + } + + //============================================================================= + // Initialization + //============================================================================= + + /** + * @brief Initialize data on uniform mesh with function projection + * + * @param mesh Coarse mesh + * @param scheme Element scheme + * @param level Uniform refinement level + * @param func Function to project onto the mesh + */ + template + void + initialize_data (t8_cmesh_t mesh, const t8_scheme *scheme, int level, Func &&func) + { + Base::forest = t8_forest_new_uniform (mesh, scheme, level, 0, Base::comm); + + levelmultiindex *elem_data; + t8_mra::forest_data *user_data; + + user_data = T8_ALLOC (t8_mra::forest_data, 1); + elem_data = T8_ALLOC (levelmultiindex, 1); + + T8_ASSERT (t8_forest_is_committed (Base::forest)); + + const auto num_local_elements = t8_forest_get_global_num_leaf_elements (Base::forest); + const auto num_ghost_elements = t8_forest_get_num_ghosts (Base::forest); + + user_data->lmi_map = new t8_mra::levelindex_map (Base::maximum_level); + user_data->lmi_idx = sc_array_new_count (sizeof (levelmultiindex), num_local_elements + num_ghost_elements); + user_data->mra_instance = this; + + const auto num_local_trees = t8_forest_get_num_local_trees (Base::forest); + auto current_idx = 0u; + + for (auto tree_idx = 0u; tree_idx < num_local_trees; ++tree_idx) { + const auto num_elements_in_tree = t8_forest_get_tree_num_leaf_elements (Base::forest, tree_idx); + const auto base_element = t8_forest_global_tree_id (Base::forest, tree_idx); + + for (auto ele_idx = 0u; ele_idx < num_elements_in_tree; ++ele_idx, ++current_idx) { + const auto *element = t8_forest_get_leaf_element_in_tree (Base::forest, tree_idx, ele_idx); + const auto lmi = levelmultiindex (base_element, element, scheme); + + user_data->lmi_map->insert (lmi, project_leaf (tree_idx, element, func)); + + // Insert lmi into forest + t8_mra::set_lmi_forest_data (user_data, current_idx, lmi); + } + } + + T8_FREE (elem_data); + t8_forest_set_user_data (Base::forest, user_data); + } + + //============================================================================= + // Cleanup + //============================================================================= + + /** + * @brief Clean up forest data structures + */ + void + cleanup () + { + Base::cleanup (); + + auto *user_data = Base::get_user_data (); + if (user_data) { + if (user_data->lmi_map) { + delete user_data->lmi_map; + user_data->lmi_map = nullptr; + } + if (user_data->lmi_idx) { + sc_array_destroy (user_data->lmi_idx); + user_data->lmi_idx = nullptr; + } + T8_FREE (user_data); + } + + if (Base::forest != nullptr) + t8_forest_unref (&this->forest); + } +}; + +} // namespace t8_mra + +#endif // T8_ENABLE_MRA diff --git a/src/t8_mra/shapes/triangle.hxx b/src/t8_mra/shapes/triangle.hxx new file mode 100644 index 0000000000..8f408bce31 --- /dev/null +++ b/src/t8_mra/shapes/triangle.hxx @@ -0,0 +1,311 @@ +#pragma once + +#ifdef T8_ENABLE_MRA + +#include "t8_mra/core/base.hxx" +#include "t8_mra/core/adaptation.hxx" +#include "t8_mra/data/triangle_order.hxx" +#include "t8_mra/num/mask_coefficients.hxx" + +namespace t8_mra +{ + +/** + * @brief Triangle-specific multiscale implementation (partial specialization) + * + * Inherits all common MRA functionality from multiscale_base and + * implements triangle-specific: + * - Detail norm computation (uses sqrt(2*volume) scaling) + * - Projection (uses Dunavant quadrature + transformation matrix) + * - Vertex ordering (via triangle_order) + */ +template +class multiscale: + public multiscale_base, + public multiscale_adaptation> { + + public: + static constexpr t8_eclass TShape = T8_ECLASS_TRIANGLE; + using Base = multiscale_base; + using Adaptation = multiscale_adaptation>; + using element_t = typename Base::element_t; + using levelmultiindex = typename Base::levelmultiindex; + using index_set = typename Base::index_set; + + // Make adaptation methods accessible + using Adaptation::coarsen; + using Adaptation::refine; + using Adaptation::initialize_data_adaptive; + using Adaptation::balance; + + //============================================================================= + // Constructor + //============================================================================= + + /** + * @brief Constructor for triangle multiscale + * + * @param _max_level Maximum refinement level + * @param _comm MPI communicator + */ + multiscale (int _max_level, sc_MPI_Comm _comm): Base (_max_level, _comm) + { + // Initialize mask coefficients from hardcoded values + t8_mra::initialize_mask_coefficients (Base::P_DIM, Base::DOF, Base::mask_coefficients, + Base::inverse_mask_coefficients); + } + + //============================================================================= + // Element-Specific Implementation: Detail Norm + //============================================================================= + + /** + * @brief Compute local detail norm for triangles + * + * Triangles use sqrt(2*volume) scaling factor in the detail norm computation. + * + * @param lmi Level multi-index + * @return Array of detail norms (one per solution component) + */ + std::array + local_detail_norm (const levelmultiindex &lmi) override + { + std::array detail_norm = {}; + const auto vol = Base::d_map.get (lmi).vol; + const auto &details = Base::d_map.get (lmi).d_coeffs; + + for (auto u = 0u; u < Base::U_DIM; ++u) { + double norm_sq = 0.0; + for (auto k = 0u; k < levelmultiindex::NUM_CHILDREN; ++k) { + for (auto i = 0u; i < Base::DOF; ++i) { + const auto d = details[element_t::wavelet_idx (k, u, i)]; + norm_sq += d * d; + } + } + // Match old implementation: sqrt(sum / vol) + detail_norm[u] = std::sqrt (norm_sq / vol); + } + + return detail_norm; + } + + //============================================================================= + // Element-Specific Implementation: Projection + //============================================================================= + + /** + * @brief Project a function onto the DG basis for a triangle + * + * Uses Dunavant quadrature and requires transformation to reference element + * due to arbitrary triangle geometry. + * + * @param dg_coeffs Output vector for DG coefficients + * @param tree_idx Tree index in forest + * @param element Pointer to element + * @param func Function to project (signature: std::array(double x, double y)) + */ + template + void + project_impl (std::vector &dg_coeffs, int tree_idx, const t8_element_t *element, + const std::array &point_order, Func &&func) + { + // ONE-TO-ONE implementation of old t8_mra.hpp::project() + // Takes point_order as parameter (already computed in initialize_data) + + // Get triangle vertices from t8code and reorder them + // OLD IMPLEMENTATION: vertices[order[i]] = t8code_vertex[i] + // This stores t8code vertex i at position order[i] in the array + double vertices[3][3]; + for (auto i = 0; i < 3; ++i) + t8_forest_element_coordinate (Base::forest, tree_idx, element, i, vertices[point_order[i]]); + + // Compute transformation to reference element + auto [trafo_mat, perm] = Base::basis.trafo_matrix_to_ref_element (vertices); + const auto deref_quad_points = Base::basis.deref_quad_points (vertices); + const auto volume = t8_forest_element_volume (Base::forest, tree_idx, element); + + // Triangle-specific scaling factor + const auto scaling_factor = std::sqrt (1.0 / (2.0 * volume)); + + // Project function onto DG basis using Dunavant quadrature + for (auto i = 0u; i < Base::DOF; ++i) { + std::array sum = {}; + + for (auto j = 0u; j < Base::basis.num_quad_points; ++j) { + const auto x_deref = deref_quad_points[2 * j]; + const auto y_deref = deref_quad_points[1 + 2 * j]; + + // Transform to reference coordinates + const auto ref = Base::basis.ref_point (trafo_mat, perm, { x_deref, y_deref, 1.0 }); + + // Evaluate function at physical point + const auto f_val = func (x_deref, y_deref); + + // Evaluate basis at reference point + const auto basis_val = Base::basis.basis_value (ref); + + // Accumulate: ∫ f(x) φᵢ(x) dx ≈ Σ w_j f(x_j) φᵢ(x_j) * scaling + for (auto k = 0u; k < Base::U_DIM; ++k) + sum[k] += Base::basis.quad_weights[j] * f_val[k] * scaling_factor * basis_val[i]; + } + + for (auto k = 0u; k < Base::U_DIM; ++k) + dg_coeffs[element_t::dg_idx (k, i)] = sum[k] * volume; + } + } + + /** + * @brief Project a function onto a single forest leaf + * + * Builds the complete element data (vertex order, volume, DG coefficients) + * for the given leaf. + * + * @param tree_idx Tree index in forest + * @param element Pointer to element + * @param func Function to project + * @return Element data of the leaf + */ + template + element_t + project_leaf (int tree_idx, const t8_element_t *element, Func &&func) + { + element_t data; + + const auto gtreeid = t8_forest_global_tree_id (Base::forest, tree_idx); + const auto *scheme = t8_forest_get_scheme (Base::forest); + triangle_order::get_point_order_at_level (gtreeid, element, scheme, data.order); + + data.vol = t8_forest_element_volume (Base::forest, tree_idx, element); + project_impl (data.u_coeffs, tree_idx, element, data.order, func); + + return data; + } + + //============================================================================= + // Initialization + //============================================================================= + + /** + * @brief Initialize data on uniform mesh with function projection + * + * @param mesh Coarse mesh + * @param scheme Element scheme + * @param level Uniform refinement level + * @param func Function to project onto the mesh + */ + void + initialize_data (t8_cmesh_t mesh, const t8_scheme *scheme, int level, auto &&func) + { + Base::forest = t8_forest_new_uniform (mesh, scheme, level, 0, Base::comm); + + levelmultiindex *elem_data; + t8_mra::forest_data *user_data; + + user_data = T8_ALLOC (t8_mra::forest_data, 1); + elem_data = T8_ALLOC (levelmultiindex, 1); + + T8_ASSERT (t8_forest_is_committed (Base::forest)); + + const auto num_local_elements = t8_forest_get_global_num_leaf_elements (Base::forest); + const auto num_ghost_elements = t8_forest_get_num_ghosts (Base::forest); + + user_data->lmi_map = new t8_mra::levelindex_map (Base::maximum_level); + user_data->lmi_idx = sc_array_new_count (sizeof (levelmultiindex), num_local_elements + num_ghost_elements); + user_data->mra_instance = this; + + const auto num_local_trees = t8_forest_get_num_local_trees (Base::forest); + auto current_idx = 0u; + + for (auto tree_idx = 0u; tree_idx < num_local_trees; ++tree_idx) { + const auto num_elements_in_tree = t8_forest_get_tree_num_leaf_elements (Base::forest, tree_idx); + const auto base_element = t8_forest_global_tree_id (Base::forest, tree_idx); + + for (auto ele_idx = 0u; ele_idx < num_elements_in_tree; ++ele_idx, ++current_idx) { + const auto *element = t8_forest_get_leaf_element_in_tree (Base::forest, tree_idx, ele_idx); + const auto lmi = levelmultiindex (base_element, element, scheme); + + user_data->lmi_map->insert (lmi, project_leaf (tree_idx, element, func)); + + // Insert lmi into forest + t8_mra::set_lmi_forest_data (user_data, current_idx, lmi); + } + } + + T8_FREE (elem_data); + t8_forest_set_user_data (Base::forest, user_data); + } + + //============================================================================= + // Post-Adaptation Hook + //============================================================================= + + /** + * @brief Update vertex orders after forest adaptation + * + * Triangle vertex order depends on ancestor.type which is not stored in LMI. + * This function updates the vertex orders in lmi_map to match the actual + * t8code elements after adaptation. + */ + void + post_adaptation_hook () override + { + if (Base::forest == nullptr) + return; + + auto *forest_data = Base::get_user_data (); + const auto *forest_scheme = t8_forest_get_scheme (Base::forest); + t8_locidx_t elem_offset = 0; + + for (t8_locidx_t tree_idx = 0; tree_idx < t8_forest_get_num_local_trees (Base::forest); ++tree_idx) { + const auto num_elems = t8_forest_get_tree_num_leaf_elements (Base::forest, tree_idx); + const auto base_element = tree_idx; + + for (t8_locidx_t elem_idx = 0; elem_idx < num_elems; ++elem_idx) { + const auto *elem = t8_forest_get_leaf_element_in_tree (Base::forest, tree_idx, elem_idx); + const auto lmi = t8_mra::get_lmi_from_forest_data (forest_data, elem_offset + elem_idx); + + if (forest_data->lmi_map->contains (lmi)) { + std::array correct_order; + t8_mra::triangle_order::get_point_order_at_level (base_element, elem, forest_scheme, correct_order); + + auto &elem_data = forest_data->lmi_map->get (lmi); + elem_data.order = correct_order; + } + } + elem_offset += num_elems; + } + } + + //============================================================================= + // Cleanup + //============================================================================= + + /** + * @brief Clean up forest data structures + */ + void + cleanup () + { + Base::cleanup (); + + auto *user_data = Base::get_user_data (); + if (user_data) { + if (user_data->lmi_map) { + delete user_data->lmi_map; + user_data->lmi_map = nullptr; + } + if (user_data->lmi_idx) { + sc_array_destroy (user_data->lmi_idx); + user_data->lmi_idx = nullptr; + } + T8_FREE (user_data); + } + + if (Base::forest != nullptr) + t8_forest_unref (&this->forest); + } +}; + +} // namespace t8_mra + +#endif // T8_ENABLE_MRA diff --git a/src/t8_mra/t8_mra.hxx b/src/t8_mra/t8_mra.hxx new file mode 100644 index 0000000000..7d71f591c5 --- /dev/null +++ b/src/t8_mra/t8_mra.hxx @@ -0,0 +1,54 @@ +#pragma once + +/** \file t8_mra.hxx + * Main header of the t8code multiresolution analysis (MRA) module. + * + * The module maintains element-local DG data together with an adaptive + * forest. A multiscale transformation decomposes the data into coarse + * approximations and detail coefficients; adaptation criteria act on those + * details: coarsening removes cells that carry no essential information, + * refinement adds resolution where a criterion demands it. The criteria are + * exchangeable (see criteria/), defaults implement Harten-style hard + * thresholding and prediction. + * + * Central class: t8_mra::multiscale with element shape TShape, + * U solution components and polynomial order P. + * + * Basic usage: + * \code + * #include + * + * t8_mra::multiscale mra (max_level, comm); + * + * // Project a function onto a uniform forest of level max_level ... + * mra.initialize_data (cmesh, scheme, max_level, func); + * + * // ... or build the grid bottom-up: refine towards max_level only where + * // the coarsening criterion finds significant details (never builds the + * // uniform fine grid) + * mra.initialize_data_adaptive (cmesh, scheme, max_level, func); + * + * // Adapt: defaults are hard thresholding / Harten's prediction ... + * mra.coarsen (min_level, max_level); + * mra.refine (min_level, max_level); + * + * // ... with adjustable parameters, or any type satisfying the + * // coarsening/refinement_criterion concept + * mra.coarsen (min_level, max_level, t8_mra::hard_thresholding { .c_thresh = 0.1 }); + * mra.refine (min_level, max_level, my_criterion {}); + * + * t8_mra::write_forest_lagrange_vtk (mra, "solution", P - 1); + * + * mra.cleanup (); + * \endcode + */ + +#ifdef T8_ENABLE_MRA + +#include +#include +#include +#include +#include + +#endif // T8_ENABLE_MRA diff --git a/src/t8_schemes/t8_standalone/t8_standalone_implementation.hxx b/src/t8_schemes/t8_standalone/t8_standalone_implementation.hxx index 0a36d2c1ff..8d01a78381 100644 --- a/src/t8_schemes/t8_standalone/t8_standalone_implementation.hxx +++ b/src/t8_schemes/t8_standalone/t8_standalone_implementation.hxx @@ -28,6 +28,7 @@ #include #include #include +#include template struct t8_standalone_scheme diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 48e6119e00..f91d87564f 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -18,7 +18,11 @@ # along with t8code; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -add_library( gtest ${CMAKE_CURRENT_LIST_DIR}/../thirdparty/googletest-mpi/gtest/gtest-all.cc ) +# Always build the vendored gtest statically: with BUILD_SHARED_LIBS=ON a +# shared libgtest.so is not found via rpath at runtime and the loader may pick +# up an ABI-incompatible system libgtest.so instead. +add_library( gtest STATIC ${CMAKE_CURRENT_LIST_DIR}/../thirdparty/googletest-mpi/gtest/gtest-all.cc ) +set_target_properties( gtest PROPERTIES POSITION_INDEPENDENT_CODE ON ) target_include_directories( gtest SYSTEM PUBLIC ${CMAKE_CURRENT_LIST_DIR}/../thirdparty/googletest-mpi ${CMAKE_CURRENT_LIST_DIR}/.. ) # Function to add a test executable and register it with CTest. @@ -197,6 +201,10 @@ endif() add_t8_cpp_test( NAME t8_gtest_vector_split_serial SOURCES t8_vector_helper/t8_gtest_vector_split.cxx ) +if( T8CODE_ENABLE_MRA ) + add_t8_cpp_test( NAME t8_gtest_mra_adaptation_serial SOURCES t8_mra/t8_gtest_mra_adaptation.cxx ) +endif() + copy_test_file( test_cube_unstructured_1.inp ) copy_test_file( test_cube_unstructured_2.inp ) copy_test_file( test_vtk_tri.vtu ) diff --git a/test/t8_mra/t8_gtest_mra_adaptation.cxx b/test/t8_mra/t8_gtest_mra_adaptation.cxx new file mode 100644 index 0000000000..b193514bae --- /dev/null +++ b/test/t8_mra/t8_gtest_mra_adaptation.cxx @@ -0,0 +1,428 @@ +/* + This file is part of t8code. + t8code is a C library to manage a collection (a forest) of multiple + connected adaptive space-trees of general element classes in parallel. + + Copyright (C) 2026 the developers + + t8code is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + t8code is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with t8code; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +/** \file t8_gtest_mra_adaptation.cxx + * Regression tests for the MRA path (multiscale_base + mst + + * multiscale_adaptation), typed over (element shape, U components, order P): + * - forward/inverse multiscale transformation round-trip is the identity + * - coarsen -> refine -> coarsen returns to the coarsened grid and data + * - lmi_map mirrors the forest leaves exactly after every adaptation step + * + * Shapes: TRIANGLE (hardcoded masks, P = 1..4), QUAD and HEX (computed + * masks, arbitrary P). + */ + +#include + +#ifdef T8_ENABLE_MRA + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +namespace +{ + +template +struct Config +{ + static constexpr t8_eclass Shape = TShape; + static constexpr int U = U_; + static constexpr int P = P_; + static constexpr int DIM = t8_mra::t8_eclass_dim (); +}; + +/* Smooth test function for the MST round-trip; components differ to catch + * component-indexing bugs. */ +template +auto +smooth_func () +{ + if constexpr (DIM == 2) + return [] (double x, double y) { + std::array res; + for (auto u = 0; u < U; ++u) + res[u] = std::sin (2.0 * M_PI * (u + 1) * x) * std::sin (2.0 * M_PI * y); + return res; + }; + else + return [] (double x, double y, double z) { + std::array res; + for (auto u = 0; u < U; ++u) + res[u] = std::sin (2.0 * M_PI * (u + 1) * x) * std::sin (2.0 * M_PI * y) * std::sin (2.0 * M_PI * z); + return res; + }; +} + +/* Discontinuous test function: jump along a circle/sphere segment. + * Guarantees significant details at the jump, hence coarsening keeps a fine + * band there and refinement must trigger via the neighbour prediction. */ +template +auto +jump_func () +{ + if constexpr (DIM == 2) + return [] (double x, double y) { + std::array res; + const double r = x * x + y * y; + for (auto u = 0; u < U; ++u) + res[u] = (u + 1) * ((r < 0.25) ? (x * y + x + 3.0) : (x * x * y - 2.0 * x * y * y + 3.0 * x)); + return res; + }; + else + return [] (double x, double y, double z) { + std::array res; + const double r = x * x + y * y + z * z; + for (auto u = 0; u < U; ++u) + res[u] = (u + 1) * ((r < 0.25) ? (x * y + z + 3.0) : (x * x * y - 2.0 * y * z + 3.0 * x)); + return res; + }; +} + +template +t8_mra::multiscale +make_mra (int max_level) +{ + return t8_mra::multiscale (max_level, sc_MPI_COMM_WORLD); +} + +/* Every forest leaf must have its lmi stored in lmi_idx with the element's + * level, and exactly the leaves must be present in lmi_map. */ +template +void +expect_forest_map_consistent (MRA &mra) +{ + auto *forest = mra.get_forest (); + auto *user_data = mra.get_user_data (); + auto *lmi_map = mra.get_lmi_map (); + const auto *scheme = t8_forest_get_scheme (forest); + + ASSERT_EQ (static_cast (t8_forest_get_local_num_leaf_elements (forest)), lmi_map->size ()); + + auto current_idx = t8_locidx_t { 0 }; + const auto num_trees = t8_forest_get_num_local_trees (forest); + for (t8_locidx_t tree_idx = 0; tree_idx < num_trees; ++tree_idx) { + const auto tree_class = t8_forest_get_tree_class (forest, tree_idx); + const auto num_elements = t8_forest_get_tree_num_leaf_elements (forest, tree_idx); + + for (t8_locidx_t ele_idx = 0; ele_idx < num_elements; ++ele_idx, ++current_idx) { + const auto *element = t8_forest_get_leaf_element_in_tree (forest, tree_idx, ele_idx); + const auto lmi = t8_mra::get_lmi_from_forest_data (user_data, current_idx); + + EXPECT_EQ (lmi.level (), static_cast (scheme->element_get_level (tree_class, element))); + EXPECT_TRUE (lmi_map->contains (lmi)); + } + } +} + +template +void +expect_maps_equal (const MapT &expected, const MapT &actual, unsigned int max_level, double tol) +{ + for (auto l = 0u; l <= max_level; ++l) { + ASSERT_EQ (expected[l].size (), actual[l].size ()) << "entry count differs on level " << l; + + for (const auto &[lmi, data] : expected[l]) { + ASSERT_TRUE (actual.contains (lmi)) << "missing lmi on level " << l; + + const auto &other = actual.get (lmi); + ASSERT_EQ (data.u_coeffs.size (), other.u_coeffs.size ()); + for (auto i = 0u; i < data.u_coeffs.size (); ++i) + EXPECT_NEAR (data.u_coeffs[i], other.u_coeffs[i], tol) << "coeff " << i << " on level " << l; + } + } +} + +/* Constant initial data: all detail coefficients vanish exactly, so the + * bottom-up initialization must never keep a refined level. */ +template +auto +constant_func () +{ + if constexpr (DIM == 2) + return [] (double, double) { + std::array res; + for (auto u = 0; u < U; ++u) + res[u] = 3.0 * (u + 1); + return res; + }; + else + return [] (double, double, double) { + std::array res; + for (auto u = 0; u < U; ++u) + res[u] = 3.0 * (u + 1); + return res; + }; +} + +/* Custom coarsening criterion without prepare(): nothing is significant + * -> coarsening must collapse the grid completely. Exercises the + * coarsening_criterion extension point. */ +struct collapse_criterion +{ + template + bool + significant (MRA &, const typename MRA::levelmultiindex &) + { + return false; + } +}; + +template +class mra_adaptation: public ::testing::Test { +}; + +using Configs = ::testing::Types< + /* Triangle: hardcoded masks, P = 1..4 */ + Config, Config, Config, + Config, Config, + /* Quad: computed masks */ + Config, Config, Config, + Config, Config, + /* Hex (3D): computed masks */ + Config, Config, Config>; + +struct ConfigNames +{ + template + static std::string + GetName (int) + { + return std::string (t8_eclass_to_string[T::Shape]) + "_U" + std::to_string (T::U) + "_P" + std::to_string (T::P); + } +}; + +TYPED_TEST_SUITE (mra_adaptation, Configs, ConfigNames); + +/* Forward then inverse MST over the full level range must reproduce the + * single-scale data up to round-off. */ +TYPED_TEST (mra_adaptation, mst_roundtrip) +{ + constexpr auto Shape = TypeParam::Shape; + constexpr auto U = TypeParam::U; + constexpr auto P = TypeParam::P; + constexpr auto DIM = TypeParam::DIM; + + const int max_level = (DIM == 3) ? 3 : 4; + auto mra = make_mra (max_level); + + t8_cmesh_t cmesh = t8_cmesh_new_hypercube (Shape, sc_MPI_COMM_WORLD, 0, 0, 0); + auto *scheme = t8_scheme_new_default (); + /* The forest takes ownership of cmesh and scheme; keep our own references */ + t8_cmesh_ref (cmesh); + t8_scheme_ref (const_cast (scheme)); + + mra.initialize_data (cmesh, scheme, max_level, smooth_func ()); + expect_forest_map_consistent (mra); + + const auto snapshot = *mra.get_lmi_map (); + + mra.multiscale_transformation (0, max_level); + /* All single-scale data restricted to level 0 */ + EXPECT_EQ (mra.get_lmi_map ()->size (), mra.get_lmi_map ()->size (0)); + + mra.inverse_multiscale_transformation (0, max_level); + expect_maps_equal (snapshot, *mra.get_lmi_map (), max_level, 1e-9); + + mra.cleanup (); + t8_cmesh_destroy (&cmesh); + t8_scheme_unref (const_cast (&scheme)); +} + +/* Coarsening discards only non-significant details; refinement adds only + * zero-detail children (plus Harten grading). A second coarsening therefore + * must return exactly to the first coarsened grid and data. */ +TYPED_TEST (mra_adaptation, coarsen_refine_roundtrip) +{ + constexpr auto Shape = TypeParam::Shape; + constexpr auto U = TypeParam::U; + constexpr auto P = TypeParam::P; + constexpr auto DIM = TypeParam::DIM; + + if constexpr (P == 1) { + /* With piecewise constants the approximation error of the smooth pieces + * dominates the details on all levels; whether coarsening triggers is a + * threshold tuning question, not an algorithmic invariant. */ + GTEST_SKIP () << "coarsening behaviour for P=1 is threshold-dependent"; + } + else { + const int max_level = (DIM == 3) ? 4 : 5; + auto mra = make_mra (max_level); + + t8_cmesh_t cmesh = t8_cmesh_new_hypercube (Shape, sc_MPI_COMM_WORLD, 0, 0, 0); + auto *scheme = t8_scheme_new_default (); + t8_cmesh_ref (cmesh); + t8_scheme_ref (const_cast (scheme)); + + mra.initialize_data (cmesh, scheme, max_level, jump_func ()); + const auto num_uniform = t8_forest_get_global_num_leaf_elements (mra.get_forest ()); + + mra.coarsen (0, max_level); + expect_forest_map_consistent (mra); + const auto num_coarse = t8_forest_get_global_num_leaf_elements (mra.get_forest ()); + EXPECT_LT (num_coarse, num_uniform) << "smooth regions must coarsen"; + + const auto snapshot = *mra.get_lmi_map (); + + mra.refine (0, max_level); + expect_forest_map_consistent (mra); + const auto num_refined = t8_forest_get_global_num_leaf_elements (mra.get_forest ()); + EXPECT_GT (num_refined, num_coarse) << "neighbour prediction must refine around the jump"; + + mra.coarsen (0, max_level); + expect_forest_map_consistent (mra); + const auto num_recoarse = t8_forest_get_global_num_leaf_elements (mra.get_forest ()); + EXPECT_EQ (num_recoarse, num_coarse) << "zero-detail children must coarsen away again"; + + expect_maps_equal (snapshot, *mra.get_lmi_map (), max_level, 1e-8); + + mra.cleanup (); + t8_cmesh_destroy (&cmesh); + t8_scheme_unref (const_cast (&scheme)); + } +} + +/* A criterion that finds nothing significant must collapse the grid to the + * base cells, regardless of the data. */ +TYPED_TEST (mra_adaptation, custom_criterion_collapse) +{ + constexpr auto Shape = TypeParam::Shape; + constexpr auto U = TypeParam::U; + constexpr auto P = TypeParam::P; + constexpr auto DIM = TypeParam::DIM; + + const int max_level = (DIM == 3) ? 3 : 4; + auto mra = make_mra (max_level); + + t8_cmesh_t cmesh = t8_cmesh_new_hypercube (Shape, sc_MPI_COMM_WORLD, 0, 0, 0); + auto *scheme = t8_scheme_new_default (); + t8_cmesh_ref (cmesh); + t8_scheme_ref (const_cast (scheme)); + + mra.initialize_data (cmesh, scheme, max_level, jump_func ()); + + mra.coarsen (0, max_level, collapse_criterion {}); + expect_forest_map_consistent (mra); + + EXPECT_EQ (t8_forest_get_global_num_leaf_elements (mra.get_forest ()), + t8_forest_get_num_global_trees (mra.get_forest ())); + + mra.cleanup (); + t8_cmesh_destroy (&cmesh); + t8_scheme_unref (const_cast (&scheme)); +} + +/* Bottom-up initialization on constant data: every detail is exactly zero, + * so nothing is significant and the result must be the level-1 grid with + * the level-1 projection. */ +TYPED_TEST (mra_adaptation, bottom_up_constant_collapses) +{ + constexpr auto Shape = TypeParam::Shape; + constexpr auto U = TypeParam::U; + constexpr auto P = TypeParam::P; + constexpr auto DIM = TypeParam::DIM; + + const int max_level = (DIM == 3) ? 3 : 4; + auto mra = make_mra (max_level); + auto reference = make_mra (max_level); + + t8_cmesh_t cmesh = t8_cmesh_new_hypercube (Shape, sc_MPI_COMM_WORLD, 0, 0, 0); + auto *scheme = t8_scheme_new_default (); + /* Two forests consume one reference each; keep our own on top */ + t8_cmesh_ref (cmesh); + t8_cmesh_ref (cmesh); + t8_scheme_ref (const_cast (scheme)); + t8_scheme_ref (const_cast (scheme)); + + mra.initialize_data_adaptive (cmesh, scheme, max_level, constant_func ()); + expect_forest_map_consistent (mra); + + reference.initialize_data (cmesh, scheme, 1, constant_func ()); + + EXPECT_EQ (t8_forest_get_global_num_leaf_elements (mra.get_forest ()), + t8_forest_get_global_num_leaf_elements (reference.get_forest ())); + expect_maps_equal (*reference.get_lmi_map (), *mra.get_lmi_map (), max_level, 1e-9); + + mra.cleanup (); + reference.cleanup (); + t8_cmesh_destroy (&cmesh); + t8_scheme_unref (const_cast (&scheme)); +} + +/* Bottom-up initialization on discontinuous data must produce an adaptive + * grid: strictly coarser than uniform, refined to max_level at the jump, + * and a valid starting point for the regular adaptation cycle. */ +TYPED_TEST (mra_adaptation, bottom_up_adaptive_grid) +{ + constexpr auto Shape = TypeParam::Shape; + constexpr auto U = TypeParam::U; + constexpr auto P = TypeParam::P; + constexpr auto DIM = TypeParam::DIM; + + if constexpr (P == 1) { + GTEST_SKIP () << "coarsening behaviour for P=1 is threshold-dependent"; + } + else { + using levelmultiindex = typename t8_mra::multiscale::levelmultiindex; + + const int max_level = (DIM == 3) ? 4 : 5; + auto mra = make_mra (max_level); + + t8_cmesh_t cmesh = t8_cmesh_new_hypercube (Shape, sc_MPI_COMM_WORLD, 0, 0, 0); + auto *scheme = t8_scheme_new_default (); + t8_cmesh_ref (cmesh); + t8_scheme_ref (const_cast (scheme)); + + mra.initialize_data_adaptive (cmesh, scheme, max_level, jump_func ()); + expect_forest_map_consistent (mra); + + const auto num_adaptive = t8_forest_get_global_num_leaf_elements (mra.get_forest ()); + const auto num_trees = t8_forest_get_num_global_trees (mra.get_forest ()); + const auto num_uniform + = num_trees * static_cast (std::pow (levelmultiindex::NUM_CHILDREN, max_level)); + + EXPECT_LT (num_adaptive, num_uniform) << "smooth regions must stay coarse"; + EXPECT_GT (mra.get_lmi_map ()->size (max_level), 0u) << "the jump must reach max_level"; + EXPECT_EQ (mra.get_lmi_map ()->size (0), 0u) << "level 1 is the minimum level"; + + /* The result must be a valid input for the regular adaptation cycle */ + mra.coarsen (1, max_level); + expect_forest_map_consistent (mra); + EXPECT_LE (t8_forest_get_global_num_leaf_elements (mra.get_forest ()), num_adaptive); + + mra.cleanup (); + t8_cmesh_destroy (&cmesh); + t8_scheme_unref (const_cast (&scheme)); + } +} + +} // namespace + +#endif /* T8_ENABLE_MRA */ diff --git a/test/t8_types/t8_gtest_type.cxx b/test/t8_types/t8_gtest_type.cxx index 44aeddb432..492dcdb255 100644 --- a/test/t8_types/t8_gtest_type.cxx +++ b/test/t8_types/t8_gtest_type.cxx @@ -27,6 +27,7 @@ along with t8code; if not, write to the Free Software Foundation, Inc., #include #include #include +#include /* Tags to differencce between strong types */ struct dummy_int diff --git a/thirdparty/unordered_dense b/thirdparty/unordered_dense new file mode 160000 index 0000000000..73f3cbb237 --- /dev/null +++ b/thirdparty/unordered_dense @@ -0,0 +1 @@ +Subproject commit 73f3cbb237e84d483afafc743f1f14ec53e12314 diff --git a/tutorials/CMakeLists.txt b/tutorials/CMakeLists.txt index 5d3a1d1d87..3af70d04fa 100644 --- a/tutorials/CMakeLists.txt +++ b/tutorials/CMakeLists.txt @@ -64,7 +64,8 @@ add_t8_tutorial( NAME t8_step7_interpolation SOURCES general/t8_step7 add_t8_tutorial( NAME t8_tutorial_build_cmesh SOURCES general/t8_tutorial_build_cmesh.cxx general/t8_tutorial_build_cmesh_main.cxx) add_t8_tutorial( NAME t8_tutorial_search SOURCES general/t8_tutorial_search.cxx general/t8_step3_adapt_forest.cxx ) add_t8_tutorial( NAME t8_features_curved_meshes SOURCES features/t8_features_curved_meshes.cxx) -add_t8_tutorial( NAME t8_tutorial_build_scheme SOURCES general/t8_tutorial_build_scheme.cxx) +add_t8_tutorial( NAME t8_tutorial_build_scheme SOURCES general/t8_tutorial_build_scheme.cxx) +add_t8_tutorial( NAME t8_mra_example SOURCES general/t8_mra_example.cxx ) copy_tutorial_file (features/t8_features_curved_meshes_generate_cmesh_hex.geo) copy_tutorial_file (features/t8_features_curved_meshes_generate_cmesh_quad.geo) copy_tutorial_file (features/t8_features_curved_meshes_generate_cmesh_tet.geo) diff --git a/tutorials/general/t8_mra_example.cxx b/tutorials/general/t8_mra_example.cxx new file mode 100644 index 0000000000..ec84101904 --- /dev/null +++ b/tutorials/general/t8_mra_example.cxx @@ -0,0 +1,478 @@ +/** + * @file t8_mra_example.cxx + * @brief MRA tutorial: adaptive coarsening/refinement with VTK output + * + * Demonstrates: + * 1. Full adaptation cycle (top-down): initialize -> coarsen -> refine -> coarsen + * 2. Bottom-up initialization: adaptive grid without building the uniform grid + * 3. Custom adaptation criteria + * 4. Triangle vs quad comparison on the same data + * 5. 3D (hex) adaptation + * 6. Two state variables (U = 2) with different jump locations + */ + +#ifdef T8_ENABLE_MRA + +#include "t8_mra/t8_mra.hxx" +#include "t8_cmesh.h" +#include "t8_cmesh/t8_cmesh_examples.h" + +#include +#include +#include + +//============================================================================= +// Test Functions +//============================================================================= + +/** + * @brief Gaussian bump function + */ +template +auto +gaussian_bump () +{ + return [] (double x, double y) -> std::array { + const double cx = 0.5, cy = 0.5; + const double sigma = 0.15; + const double r2 = (x - cx) * (x - cx) + (y - cy) * (y - cy); + return { std::exp (-r2 / (2 * sigma * sigma)) }; + }; +} + +/** + * @brief Sine wave function + */ +template +auto +sine_wave () +{ + return + [] (double x, double y) -> std::array { return { std::sin (4 * M_PI * x) * std::sin (4 * M_PI * y) }; }; +} + +/** + * @brief Step function (discontinuous) + */ +template +auto +step_function () +{ + return [] (double x, double y) -> std::array { return { (x > 0.5 && y > 0.5) ? 1.0 : 0.0 }; }; +} + +/** + * @brief Two polynomials separated by a jump along a quarter circle + */ +template +auto +quarter_circle () +{ + return [] (double x, double y) -> std::array { + double r = x * x + y * y; + return { (r < 0.25) ? (x * y + x + 3.) : (x * x * y - 2. * x * y * y + 3. * x) }; + }; +} + +/** + * @brief Two components with quarter-circle jumps around the bottom-left + * (u0) and top-right (u1) corner + */ +template +auto +two_quarter_circles () +{ + return [] (double x, double y) -> std::array { + const double xm = 1. - x; + const double ym = 1. - y; + const double r0 = x * x + y * y; + const double r1 = xm * xm + ym * ym; + return { (r0 < 0.25) ? (x * y + x + 3.) : (x * x * y - 2. * x * y * y + 3. * x), + (r1 < 0.25) ? (xm * ym + xm + 3.) : (xm * xm * ym - 2. * xm * ym * ym + 3. * xm) }; + }; +} + +/** + * @brief 3D Gaussian bump function + */ +template +auto +gaussian_bump_3d () +{ + return [] (double x, double y, double z) -> std::array { + const double cx = 0.5, cy = 0.5, cz = 0.5; + const double sigma = 0.15; + const double r2 = (x - cx) * (x - cx) + (y - cy) * (y - cy) + (z - cz) * (z - cz); + return { std::exp (-r2 / (2 * sigma * sigma)) }; + }; +} + +//============================================================================= +// Helpers +//============================================================================= + +/** + * @brief Write high-order Lagrange VTK output (polynomial degree P-1) + */ +template +void +write_vtk_output (MRA &mra, const std::string &filename) +{ + std::cout << " Writing VTK: " << filename << ".vtu\n"; + + t8_mra::write_forest_lagrange_vtk (mra, filename.c_str (), MRA::P_DIM - 1); +} + +/** + * @brief Print element and DOF count of the current grid + */ +template +t8_gloidx_t +print_grid_stats (MRA &mra, const std::string &label) +{ + const auto num_elements = t8_forest_get_global_num_leaf_elements (mra.get_forest ()); + std::cout << " " << label << ": " << num_elements << " elements, " << (num_elements * MRA::DOF * MRA::U_DIM) + << " DOF\n"; + return num_elements; +} + +//============================================================================= +// Example 1: Full Adaptation Cycle (top-down) +//============================================================================= + +/** + * Initialize on the uniform max_level grid, then coarsen away the + * non-significant details, refine via Harten's prediction (grading band at + * the jump) and coarsen again: the zero-detail children created by the + * refinement carry no information, so the grid returns to the coarsened one. + */ +void +example_adaptation_cycle () +{ + std::cout << "\n=== 1. Triangle: full adaptation cycle (top-down) ===\n"; + + constexpr int U = 1; + constexpr int P = 3; + const int min_level = 0; + const int max_level = 7; + const double c_thresh = 1.0; + + t8_mra::multiscale mra (max_level, sc_MPI_COMM_WORLD); + + t8_cmesh_t cmesh = t8_cmesh_new_hypercube (T8_ECLASS_TRIANGLE, sc_MPI_COMM_WORLD, 0, 0, 0); + auto *scheme = t8_scheme_new_default (); + /* The forest takes ownership of cmesh and scheme; keep our own references + * since we destroy/unref them explicitly below. */ + t8_cmesh_ref (cmesh); + t8_scheme_ref (const_cast (scheme)); + + mra.initialize_data (cmesh, scheme, max_level, quarter_circle ()); + print_grid_stats (mra, "Uniform level " + std::to_string (max_level)); + write_vtk_output (mra, "mra_output/01_cycle_step0_uniform"); + + mra.coarsen (min_level, max_level, t8_mra::hard_thresholding { .c_thresh = c_thresh }); + const auto num_coarse = print_grid_stats (mra, "After coarsening"); + write_vtk_output (mra, "mra_output/01_cycle_step1_coarsened"); + + mra.refine (min_level, max_level, t8_mra::harten_prediction { .c_thresh = c_thresh }); + print_grid_stats (mra, "After refinement"); + write_vtk_output (mra, "mra_output/01_cycle_step2_refined"); + + mra.coarsen (min_level, max_level, t8_mra::hard_thresholding { .c_thresh = c_thresh }); + const auto num_recoarse = print_grid_stats (mra, "After second coarsening"); + write_vtk_output (mra, "mra_output/01_cycle_step3_coarsened"); + + std::cout << " Round-trip: " << num_coarse << " -> " << num_recoarse + << (num_coarse == num_recoarse ? " (exact)\n" : "\n"); + + mra.balance (); + const auto num_balanced = print_grid_stats (mra, "After balancing"); + write_vtk_output (mra, "mra_output/01_cycle_step4_balance"); + + mra.cleanup (); + t8_cmesh_destroy (&cmesh); + t8_scheme_unref (const_cast (&scheme)); +} + +//============================================================================= +// Example 2: Bottom-Up Initialization +//============================================================================= + +/** + * Build the adaptive grid directly from the initial data: project on level 1, + * then refine level by level only where the details are significant. The + * uniform max_level grid of example 1 is never built. + */ +void +example_bottom_up () +{ + std::cout << "\n=== 2. Triangle: bottom-up initialization ===\n"; + + constexpr int U = 1; + constexpr int P = 3; + const int max_level = 7; + const double c_thresh = 1.0; + + t8_mra::multiscale mra (max_level, sc_MPI_COMM_WORLD); + + t8_cmesh_t cmesh = t8_cmesh_new_hypercube (T8_ECLASS_TRIANGLE, sc_MPI_COMM_WORLD, 0, 0, 0); + auto *scheme = t8_scheme_new_default (); + t8_cmesh_ref (cmesh); + t8_scheme_ref (const_cast (scheme)); + + mra.initialize_data_adaptive (cmesh, scheme, max_level, quarter_circle (), + t8_mra::hard_thresholding { .c_thresh = c_thresh }); + + const auto num_adaptive = print_grid_stats (mra, "Adaptive grid"); + const auto num_trees = t8_forest_get_num_global_trees (mra.get_forest ()); + const auto num_uniform = num_trees * static_cast (std::pow (4, max_level)); + std::cout << " Uniform level " << max_level << " grid (never built): " << num_uniform << " elements\n"; + std::cout << " Compression: " << (100.0 * (1.0 - static_cast (num_adaptive) / num_uniform)) << " %\n"; + + write_vtk_output (mra, "mra_output/02_bottom_up"); + + mra.cleanup (); + t8_cmesh_destroy (&cmesh); + t8_scheme_unref (const_cast (&scheme)); +} + +//============================================================================= +// Example 3: Custom Adaptation Criterion +//============================================================================= + +/** + * @brief Hard thresholding with an enforced minimum refinement level + * + * Any type satisfying the coarsening_criterion concept can be passed to + * coarsen(). This one composes the default thresholding with a level floor: + * families below floor_level are always kept refined. + */ +struct thresholding_with_floor +{ + t8_mra::hard_thresholding thresholding; + unsigned int floor_level = 4; + + template + void + prepare (MRA &mra) + { + thresholding.prepare (mra); + } + + template + bool + significant (MRA &mra, const typename MRA::levelmultiindex &lmi) + { + return lmi.level () < floor_level || thresholding.significant (mra, lmi); + } +}; + +void +example_custom_criterion () +{ + std::cout << "\n=== 3. Quad: custom coarsening criterion (level floor) ===\n"; + + constexpr int U = 1; + constexpr int P = 3; + const int min_level = 0; + const int max_level = 6; + const double c_thresh = 1.0; + const unsigned int floor_level = 4; + + t8_mra::multiscale mra_plain (max_level, sc_MPI_COMM_WORLD); + t8_mra::multiscale mra_floor (max_level, sc_MPI_COMM_WORLD); + + t8_cmesh_t cmesh = t8_cmesh_new_hypercube (T8_ECLASS_QUAD, sc_MPI_COMM_WORLD, 0, 0, 0); + auto *scheme = t8_scheme_new_default (); + /* Two forests consume one reference each; keep our own on top */ + t8_cmesh_ref (cmesh); + t8_cmesh_ref (cmesh); + t8_scheme_ref (const_cast (scheme)); + t8_scheme_ref (const_cast (scheme)); + + auto func = gaussian_bump (); + + mra_plain.initialize_data (cmesh, scheme, max_level, func); + mra_plain.coarsen (min_level, max_level, t8_mra::hard_thresholding { .c_thresh = c_thresh }); + print_grid_stats (mra_plain, "Plain thresholding"); + write_vtk_output (mra_plain, "mra_output/03_criterion_plain"); + + mra_floor.initialize_data (cmesh, scheme, max_level, func); + mra_floor.coarsen (min_level, max_level, thresholding_with_floor { { .c_thresh = c_thresh }, floor_level }); + print_grid_stats (mra_floor, "With level floor " + std::to_string (floor_level)); + write_vtk_output (mra_floor, "mra_output/03_criterion_floor"); + + mra_plain.cleanup (); + mra_floor.cleanup (); + t8_cmesh_destroy (&cmesh); + t8_scheme_unref (const_cast (&scheme)); +} + +//============================================================================= +// Example 4: Triangle vs Quad +//============================================================================= + +template +t8_gloidx_t +run_shape (const std::string &name, auto &&func, int max_level, double c_thresh) +{ + constexpr int U = 1; + constexpr int P = 3; + + t8_mra::multiscale mra (max_level, sc_MPI_COMM_WORLD); + + t8_cmesh_t cmesh = t8_cmesh_new_hypercube (Shape, sc_MPI_COMM_WORLD, 0, 0, 0); + auto *scheme = t8_scheme_new_default (); + t8_cmesh_ref (cmesh); + t8_scheme_ref (const_cast (scheme)); + + mra.initialize_data (cmesh, scheme, max_level, func); + const auto num_uniform = t8_forest_get_global_num_leaf_elements (mra.get_forest ()); + + mra.coarsen (0, max_level, t8_mra::hard_thresholding { .c_thresh = c_thresh }); + + const auto num_adapted = print_grid_stats (mra, name + " adapted"); + std::cout << " " << num_uniform << " -> " << num_adapted + << " elements (compression: " << (100.0 * (1.0 - static_cast (num_adapted) / num_uniform)) + << " %)\n"; + write_vtk_output (mra, "mra_output/04_compare_" + name); + + mra.cleanup (); + t8_cmesh_destroy (&cmesh); + t8_scheme_unref (const_cast (&scheme)); + + return num_adapted; +} + +/** + * Same data, same threshold, same domain: compare how triangle and quad + * grids adapt. The triangle hypercube consists of two base trees, the quad + * of one; DOF per element differ (P(P+1)/2 vs P^2), so compare total DOF. + */ +void +example_triangle_vs_quad () +{ + std::cout << "\n=== 4. Triangle vs quad on the same data ===\n"; + + const int max_level = 6; + const double c_thresh = 1.0; + auto func = quarter_circle<1> (); + + run_shape ("triangle", func, max_level, c_thresh); + run_shape ("quad", func, max_level, c_thresh); +} + +//============================================================================= +// Example 5: Hex (3D) +//============================================================================= + +void +example_hex_3d () +{ + std::cout << "\n=== 5. Hex: 3D adaptation ===\n"; + + constexpr int U = 1; + constexpr int P = 3; + const int min_level = 0; + const int max_level = 4; + const double c_thresh = 1.0; + + t8_mra::multiscale mra (max_level, sc_MPI_COMM_WORLD); + + t8_cmesh_t cmesh = t8_cmesh_new_hypercube (T8_ECLASS_HEX, sc_MPI_COMM_WORLD, 0, 0, 0); + auto *scheme = t8_scheme_new_default (); + t8_cmesh_ref (cmesh); + t8_scheme_ref (const_cast (scheme)); + + mra.initialize_data (cmesh, scheme, max_level, gaussian_bump_3d ()); + print_grid_stats (mra, "Uniform level " + std::to_string (max_level)); + write_vtk_output (mra, "mra_output/05_hex_step0_uniform"); + + mra.coarsen (min_level, max_level, t8_mra::hard_thresholding { .c_thresh = c_thresh }); + print_grid_stats (mra, "After coarsening"); + write_vtk_output (mra, "mra_output/05_hex_step1_coarsened"); + + mra.refine (min_level, max_level, t8_mra::harten_prediction { .c_thresh = c_thresh }); + print_grid_stats (mra, "After refinement"); + write_vtk_output (mra, "mra_output/05_hex_step2_refined"); + + mra.cleanup (); + t8_cmesh_destroy (&cmesh); + t8_scheme_unref (const_cast (&scheme)); + + std::cout << " Use ParaView 'Clip' / 'Slice' filters to see the internal structure.\n"; +} + +//============================================================================= +// Example 6: Two State Variables +//============================================================================= + +/** + * U = 2: each component carries its own quarter-circle jump (bottom-left vs + * top-right corner). Significance is the maximum over the components, so + * the grid refines along both arcs. + */ +void +example_two_components () +{ + std::cout << "\n=== 6. Triangle: two state variables ===\n"; + + constexpr int U = 2; + constexpr int P = 3; + const int min_level = 0; + const int max_level = 7; + const double c_thresh = 0.2; + + t8_mra::multiscale mra (max_level, sc_MPI_COMM_WORLD); + + t8_cmesh_t cmesh = t8_cmesh_new_hypercube (T8_ECLASS_TRIANGLE, sc_MPI_COMM_WORLD, 0, 0, 0); + auto *scheme = t8_scheme_new_default (); + t8_cmesh_ref (cmesh); + t8_scheme_ref (const_cast (scheme)); + + mra.initialize_data_adaptive (cmesh, scheme, max_level, two_quarter_circles ()); + print_grid_stats (mra, "Uniform level " + std::to_string (max_level)); + write_vtk_output (mra, "mra_output/06_two_components_step0_initial"); + + mra.coarsen (min_level, max_level, t8_mra::hard_thresholding { .c_thresh = c_thresh }); + print_grid_stats (mra, "After coarsening"); + write_vtk_output (mra, "mra_output/06_two_components_step1_coarsened"); + + mra.refine (min_level, max_level, t8_mra::harten_prediction { .c_thresh = c_thresh }); + print_grid_stats (mra, "After refinement"); + write_vtk_output (mra, "mra_output/06_two_components_step2_refined"); + + std::cout << " Color by u0 / u1 in ParaView: the grid follows both jumps.\n"; + + mra.cleanup (); + t8_cmesh_destroy (&cmesh); + t8_scheme_unref (const_cast (&scheme)); +} + +//============================================================================= +// Main +//============================================================================= + +int +main (int argc, char **argv) +{ + int mpiret = sc_MPI_Init (&argc, &argv); + SC_CHECK_MPI (mpiret); + + sc_init (sc_MPI_COMM_WORLD, 1, 1, nullptr, SC_LP_ESSENTIAL); + t8_init (SC_LP_PRODUCTION); + + example_adaptation_cycle (); + example_bottom_up (); + example_custom_criterion (); + example_triangle_vs_quad (); + example_hex_3d (); + example_two_components (); + + std::cout << "\nAll examples completed. Output in mra_output/ (open in ParaView).\n"; + + sc_finalize (); + return 0; +} + +#endif // T8_ENABLE_MRA