diff --git a/CMakeLists.txt b/CMakeLists.txt index 2cc0e1268f32904b68135746f6dab20b53ed06e6..bb86e28beb514b219c04b40a6678a36c48696ef7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -233,10 +233,12 @@ endif() include_directories(contrib/sgr) include_directories(${CMAKE_CURRENT_SOURCE_DIR}) +set(COMMON_SOURCES gmsh_io.cpp reference_element.cpp physical_element.cpp entity.cpp kernels_cpu.cpp connectivity.cpp) + add_executable(gmsh_gpu_dg main.cpp gmsh_io.cpp reference_element.cpp physical_element.cpp entity.cpp kernels_cpu.cpp) target_link_libraries(gmsh_gpu_dg ${LINK_LIBS}) -add_executable(test test.cpp test_basics.cpp test_mass.cpp test_differentiation.cpp gmsh_io.cpp reference_element.cpp physical_element.cpp entity.cpp kernels_cpu.cpp) +add_executable(test test.cpp test_basics.cpp test_mass.cpp test_differentiation.cpp test_lifting.cpp ${COMMON_SOURCES}) target_link_libraries(test ${LINK_LIBS}) add_executable(dump_orient dump_orient.cpp) diff --git a/connectivity.cpp b/connectivity.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b42f125fb4b973026fd5c77cdbeea410e93f63af --- /dev/null +++ b/connectivity.cpp @@ -0,0 +1,53 @@ +#include <iostream> + +#include "connectivity.h" + +/***************************************************************************/ +neighbour_descriptor::neighbour_descriptor() +{} + +neighbour_descriptor::neighbour_descriptor(size_t p_iE, size_t p_iT, size_t p_via_iF) + : iE(p_iE), iT(p_iT), via_iF(p_via_iF) +{} + +bool +neighbour_descriptor::operator<(const neighbour_descriptor& other) const +{ + std::array<size_t, 3> arr_me{iE, iT, via_iF}; + std::array<size_t, 3> arr_other{other.iE, other.iT, other.via_iF}; + return arr_me < arr_other; +} + +std::ostream& operator<<(std::ostream& os, const neighbour_descriptor& nd) +{ + os << "(" << nd.iE << ", " << nd.iT << ", " << nd.via_iF << ")"; + return os; +} + +/***************************************************************************/ +cell_descriptor::cell_descriptor() +{} + +cell_descriptor::cell_descriptor(size_t p_iE, size_t p_iT) + : iE(p_iE), iT(p_iT) +{} + +bool +cell_descriptor::operator<(const cell_descriptor& other) const +{ + if (iE < other.iE) + return true; + + if (iE == other.iE) + return (iT < other.iT); + + return false; +} + +std::ostream& operator<<(std::ostream& os, const cell_descriptor& cd) +{ + os << "(" << cd.iE << "," << cd.iT << ")"; + return os; +} + +/***************************************************************************/ diff --git a/connectivity.h b/connectivity.h new file mode 100644 index 0000000000000000000000000000000000000000..6df1b85b43c9d5632a9635a80babc984e2edbad6 --- /dev/null +++ b/connectivity.h @@ -0,0 +1,227 @@ +#pragma once + +#include <algorithm> +#include <cassert> +#include <array> +#include <map> +#include <set> + +#include "sgr.hpp" + +/***************************************************************************/ +struct neighbour_descriptor +{ + size_t iE; /* Entity offset in data structures */ + size_t iT; /* Element offset in entity in data structures */ + size_t via_iF; /* Element face index (i.e. 0,1,2 for triangles) */ + + neighbour_descriptor(); + neighbour_descriptor(size_t, size_t, size_t); + bool operator<(const neighbour_descriptor&) const; +}; + +std::ostream& operator<<(std::ostream&, const neighbour_descriptor&); + +/***************************************************************************/ +struct cell_descriptor +{ + size_t iE; + size_t iT; + + cell_descriptor(); + cell_descriptor(size_t, size_t); + bool operator<(const cell_descriptor&) const; +}; + +std::ostream& operator<<(std::ostream&, const cell_descriptor&); + +/***************************************************************************/ +template<typename NT> +class neighbour_pair +{ + NT n_first; + NT n_second; + size_t n_used; + +public: + neighbour_pair() + : n_used(0) + {} + + void + add_neighbour(const NT& n) + { + assert(n_used == 0 or n_used == 1); + if (n_used == 0) + { + n_first = n; + n_used = 1; + return; + } + + if (n_used == 1) + { + n_second = n; + n_used = 2; + return; + } + + throw std::out_of_range("Pair has already two neighbours"); + } + + NT first() const + { + assert(n_used == 1 or n_used == 2); + if (n_used < 1) + throw std::out_of_range("Pair does not have n_first"); + + return n_first; + } + + NT second() const + { + assert(n_used == 2); + if (n_used < 2) + throw std::out_of_range("Pair does not have n_second"); + + return n_second; + } + + size_t num_neighbours() const + { + assert(n_used < 3); + return n_used; + } +}; + +/***************************************************************************/ +template<typename FaceKey> +class element_connectivity +{ +public: + using ConnData = std::pair<FaceKey, neighbour_descriptor>; + using NeighPair = neighbour_pair<neighbour_descriptor>; + +private: + std::map<FaceKey, NeighPair> face_cell_adj; + std::map<cell_descriptor, std::set<ConnData>> cell_cell_adj; + +public: + element_connectivity() + {} + + void + connect(const FaceKey& fk, const neighbour_descriptor& nd) + { + /* Insert the information that one the two neighbours around + * face `e' is `nd'. */ + auto& nr = face_cell_adj[fk]; + nr.add_neighbour(nd); + + /* If we've got both neighbours around `e`, add a connection + * between them in the cell-cell adjacency list */ + if ( nr.num_neighbours() == 2 ) + { + auto fst = nr.first(); + auto snd = nr.second(); + cell_cell_adj[{fst.iE, fst.iT}].insert({fk, snd}); + cell_cell_adj[{snd.iE, snd.iT}].insert({fk, fst}); + } + } + + /* Find the neighbour of the element (iE,iT) via face fk */ + std::pair<neighbour_descriptor, bool> + neighbour_via(size_t iE, size_t iT, const FaceKey& fk) const + { + auto nr = face_cell_adj.at(fk); + + if ( nr.num_neighbours() == 1 ) + { +#ifndef _NDEBUG + auto fst = nr.first(); + assert(fst.iE == iE); + assert(fst.iT == iT); +#endif + return std::make_pair(neighbour_descriptor(), false); + } + + if ( nr.num_neighbours() == 2 ) + { + auto fst = nr.first(); + auto snd = nr.second(); + + assert( (fst.iE == iE and fst.iT == iT) or + (snd.iE == iE and snd.iT == iT) + ); + + if ( fst.iE == iE and fst.iT == iT) + return std::make_pair(snd, true); + + return std::make_pair(fst, true); + } + + assert(true && "Shouldn't have arrived here"); + return std::make_pair(neighbour_descriptor(), false); /* Silence the compiler */ + } + + std::set<ConnData> + neighbours(size_t iE, size_t iT) const + { + return cell_cell_adj.at({iE, iT}); + } + + std::set<ConnData> + neighbours(const cell_descriptor& cd) const + { + return cell_cell_adj.at(cd); + } + + std::set<cell_descriptor> + neighbours_as_cell_descriptors(const cell_descriptor& cd) const + { + auto filter = [](const ConnData& connd) -> auto { + cell_descriptor ret; + ret.iE = connd.second.iE; + ret.iT = connd.second.iT; + return ret; + }; + + auto neighs = cell_cell_adj.at(cd); + std::set<cell_descriptor> ret; + std::transform(neighs.begin(), neighs.end(), + std::inserter(ret, ret.begin()), filter); + return ret; + } + + bool + are_connected(const cell_descriptor& a, const cell_descriptor& b) const + { + auto neighs = neighbours(a); + for (const auto& [fk, nd] : neighs) + if (nd.iE == b.iE and nd.iT == b.iT) + return true; + + return false; + } + + std::set<cell_descriptor> + element_keys() const + { + using PT = std::pair<cell_descriptor, std::set<ConnData>>; + auto get_node = [](const PT& adjpair) -> auto { + return adjpair.first; + }; + + auto cc_begin = cell_cell_adj.begin(); + auto cc_end = cell_cell_adj.end(); + std::set<cell_descriptor> ret; + std::transform(cc_begin, cc_end, std::inserter(ret, ret.begin()), get_node); + return ret; + } + + void clear(void) + { + face_cell_adj.clear(); + } +}; + diff --git a/entity.cpp b/entity.cpp index 32b509b847aea858b7ff0457db3eb070fd51ebfe..15154488c1dd44f6a67599ba96e4348c85ecf610 100644 --- a/entity.cpp +++ b/entity.cpp @@ -138,14 +138,14 @@ entity::num_cells_per_orientation(void) const } const physical_element& -entity::cell(size_t iT) +entity::cell(size_t iT) const { assert(iT < physical_cells.size()); return physical_cells[iT]; } const physical_element& -entity::face(size_t iF) +entity::face(size_t iF) const { assert(iF < physical_faces.size()); return physical_faces[iF]; @@ -153,7 +153,7 @@ entity::face(size_t iF) matxd -entity::mass_matrix(size_t iT) +entity::mass_matrix(size_t iT) const { const auto& pe = physical_cells[iT]; assert( pe.orientation() < reference_cells.size() ); @@ -177,6 +177,9 @@ entity::mass_matrix(size_t iT) void entity::sort_by_orientation(void) { + if (cur_elem_ordering == entity_ordering::BY_ORIENTATION) + return; + /* Sort by orientation and keep GMSH ordering within the same orientation */ auto comp = [](const physical_element& e1, const physical_element& e2) -> bool { auto oe1 = e1.orientation(); @@ -191,6 +194,40 @@ entity::sort_by_orientation(void) }; std::sort(physical_cells.begin(), physical_cells.end(), comp); + + std::vector<physical_element> new_pf; + new_pf.resize( physical_faces.size() ); + std::vector<size_t> new_ft; + new_ft.resize( faceTags.size() ); + std::vector<size_t> new_fnt; + new_fnt.resize( faceNodesTags.size() ); + + size_t nodes_per_face = faceNodesTags.size()/faceTags.size(); + assert(nodes_per_face * faceTags.size() == faceNodesTags.size()); + + for (size_t new_iT = 0; new_iT < physical_cells.size(); new_iT++) + { + auto old_iT = physical_cells[new_iT].original_position(); + for (size_t iF = 0; iF < NUM_TET_FACES; iF++) + { + auto old_ofs = NUM_TET_FACES*old_iT + iF; + auto new_ofs = NUM_TET_FACES*new_iT + iF; + new_pf[new_ofs] = physical_faces[old_ofs]; + new_ft[new_ofs] = faceTags[old_ofs]; + + for (size_t iN = 0; iN < nodes_per_face; iN++) + { + auto old_ofs_nt = nodes_per_face*old_ofs + iN; + auto new_ofs_nt = nodes_per_face*new_ofs + iN; + new_fnt[new_ofs_nt] = faceNodesTags[old_ofs_nt]; + } + } + } + + std::swap(new_pf, physical_faces); + std::swap(new_ft, faceTags); + std::swap(new_fnt, faceNodesTags); + cur_elem_ordering = entity_ordering::BY_ORIENTATION; } @@ -210,6 +247,7 @@ entity::sort_by_gmsh(void) std::sort(physical_cells.begin(), physical_cells.end(), comp); cur_elem_ordering = entity_ordering::GMSH; + throw std::logic_error("NOT SORTED"); } int @@ -343,8 +381,13 @@ entity::populate_lifting_matrices_planar(entity_data_cpu& ed, for (size_t iO = 0; iO < ed.num_orientations; iO++) { + if (dofs_f2c.find(iO) == dofs_f2c.end()) + continue; + auto& f2c = dofs_f2c.at(iO); assert(f2c.size() == NUM_TET_FACES*ed.num_fluxes); + matxd lm = matxd::Zero(ed.num_bf, lm_cols); + for (size_t iF = 0; iF < NUM_TET_FACES; iF++) { auto fO = FACE_ORIENTATION(iO, iF); @@ -358,22 +401,33 @@ entity::populate_lifting_matrices_planar(entity_data_cpu& ed, for (size_t i = 0; i < mm.rows(); i++) { assert(iF*ed.num_fluxes + i < f2c.size()); - auto lm_row = iO*ed.num_bf + f2c[iF*ed.num_fluxes + i]; - assert(lm_row < lm_rows); + auto lm_row = f2c[iF*ed.num_fluxes + i]; + assert(lm_row < ed.num_bf); for (size_t j = 0; j < mm.cols(); j++) { auto lm_col = iF*ed.num_fluxes + j; assert(lm_col < lm_cols); - ed.lifting_matrices(lm_row, lm_col) = mm(i, j); + lm(lm_row, lm_col) = mm(i, j); } } } + + auto& re = reference_cells[iO]; + matxd mass = re.mass_matrix(); + Eigen::LDLT<matxd> mass_ldlt(mass); + if (mass_ldlt.info() != Eigen::Success ) + throw std::invalid_argument("LDLT failed"); + + size_t base_row = iO * ed.num_bf; + ed.lifting_matrices.block(base_row, 0, ed.num_bf, lm_cols) = + mass_ldlt.solve(lm); } } void entity::populate_lifting_matrices_curved(entity_data_cpu&, const dofs_f2c_t&) const -{} +{ +} void entity::populate_lifting_matrices(entity_data_cpu& ed) const @@ -387,26 +441,38 @@ entity::populate_lifting_matrices(entity_data_cpu& ed) const { auto& pe = physical_cells[iT]; auto orient = pe.orientation(); - auto cnt = pe.node_tags(); + auto cnt = pe.bf_keys(); +#ifndef EXPENSIVE_ASSERTS if ( dofs_f2c.find(orient) != dofs_f2c.end() ) continue; +#endif - std::vector<size_t> f2c( NUM_TET_FACES*num_face_bf ); + std::vector<size_t> f2c; + f2c.reserve( NUM_TET_FACES*num_face_bf ); for (size_t iF = 0; iF < NUM_TET_FACES; iF++) { - auto origpos = pe.original_position(); - auto face_ofs = NUM_TET_FACES*origpos+iF; + auto face_ofs = NUM_TET_FACES*iT+iF; auto& pf = physical_faces[face_ofs]; - auto fnt = pf.node_tags(); + auto fnt = pf.bf_keys(); for (size_t i = 0; i < fnt.size(); i++) for (size_t j = 0; j < cnt.size(); j++) - if (cnt[i] == fnt[j]) - f2c[iF*num_face_bf + i] = j; + if (cnt[j] == fnt[i]) + { + assert(f2c.size() == iF*num_face_bf + i); + f2c.push_back(j); + } - dofs_f2c[orient] = f2c; } + assert(f2c.size() == NUM_TET_FACES*num_face_bf); + +#ifdef EXPENSIVE_ASSERTS + if ( dofs_f2c.find(orient) != dofs_f2c.end() ) + assert(dofs_f2c[orient] == f2c); +#endif + + dofs_f2c[orient] = f2c; } if (g_order == 1) @@ -423,10 +489,20 @@ entity::populate_jacobians(entity_data_cpu& ed) const if (g_order == 1) { ed.jacobians = matxd::Zero(3*num_elems, 3); + ed.cell_determinants = vecxd::Zero(num_elems); for (size_t iT = 0; iT < num_elems; iT++) { auto& pe = physical_cells[iT]; ed.jacobians.block(3*iT, 0, 3, 3) = pe.jacobian().inverse().transpose(); + ed.cell_determinants(iT) = pe.determinant(); + } + + assert(num_faces() == 4*num_elems); + ed.face_determinants = vecxd(4*num_elems); + for (size_t iF = 0; iF < num_faces(); iF++) + { + auto& pf = physical_faces[iF]; + ed.face_determinants(iF) = pf.determinant(); } } else @@ -483,8 +559,10 @@ entity::populate_normals(entity_data_cpu& ed) const void entity::populate_entity_data(entity_data_cpu& ed) const { + assert(cur_elem_ordering == entity_ordering::BY_ORIENTATION); assert(reference_faces.size() > 0); assert(reference_cells.size() > 0); + ed.ordering = cur_elem_ordering; ed.g_order = g_order; ed.a_order = a_order; ed.num_elems = num_cells_per_orientation(); @@ -493,7 +571,8 @@ entity::populate_entity_data(entity_data_cpu& ed) const ed.num_fluxes = reference_faces[0].num_basis_functions(); ed.num_qp = (g_order == 1) ? 1 : reference_cells[0].num_integration_points(); ed.num_face_qp = (g_order == 1) ? 1 : reference_faces[0].num_integration_points(); - ed.dofs_base = 0; + ed.dof_base = 0; + ed.flux_base = 0; populate_differentiation_matrices(ed); populate_jacobians(ed); @@ -503,8 +582,9 @@ entity::populate_entity_data(entity_data_cpu& ed) const ed.invmass_matrices = matxd::Zero(ed.num_bf*num_cells(), ed.num_bf); for (size_t iT = 0; iT < num_cells(); iT++) { + matxd mass = mass_matrix(iT); ed.invmass_matrices.block(iT*ed.num_bf, 0, ed.num_bf, ed.num_bf) = - mass_matrix(iT).inverse(); + mass.inverse(); } } diff --git a/entity.h b/entity.h index 7e23a26a7e6c453eb76c436d22cd577b4a11f936..fc9219b97b32f734339db41f9ddabe8e1f3ca5c3 100644 --- a/entity.h +++ b/entity.h @@ -4,16 +4,11 @@ #include "reference_element.h" #include "physical_element.h" - +#include "connectivity.h" #include "entity_data.h" using scalar_function = std::function<double(const point_3d&)>; -enum class entity_ordering { - GMSH, - BY_ORIENTATION -}; - class entity { int dim; @@ -29,10 +24,10 @@ class entity std::vector<physical_element> physical_cells; std::vector<reference_element> reference_faces; - std::vector<physical_element> physical_faces; /* Always ordered by GMSH */ + std::vector<physical_element> physical_faces; - std::vector<size_t> faceTags; /* Always ordered by GMSH */ - std::vector<size_t> faceNodesTags; /* Always ordered by GMSH */ + std::vector<size_t> faceTags; + std::vector<size_t> faceNodesTags; using dofs_f2c_t = std::map<size_t, std::vector<size_t>>; void populate_lifting_matrices_planar(entity_data_cpu&, const dofs_f2c_t&) const; @@ -66,19 +61,19 @@ public: size_t num_cell_orientations(void) const; std::vector<size_t> num_cells_per_orientation(void) const; + const physical_element& cell(size_t) const; + const reference_element& cell_refelem(size_t) const; + const reference_element& cell_refelem(const physical_element&) const; + size_t num_faces(void) const; std::vector<size_t> face_tags() const; - const reference_element& cell_refelem(const physical_element&) const; + const physical_element& face(size_t) const; + const reference_element& face_refelem(size_t) const; const reference_element& face_refelem(const physical_element&) const; - const physical_element& cell(size_t); - const physical_element& cell_bytag(size_t); - const physical_element& face(size_t); - const physical_element& face_bytag(size_t); - - matxd mass_matrix(size_t); + matxd mass_matrix(size_t) const; void sort_by_orientation(void); void sort_by_gmsh(void); @@ -89,6 +84,8 @@ public: void populate_entity_data(entity_data_cpu&) const; + constexpr size_t num_faces_per_elem(void) const { return 4; } + std::vector<physical_element>::const_iterator begin() const { return physical_cells.begin(); } std::vector<physical_element>::const_iterator end() const { return physical_cells.end(); } @@ -97,4 +94,3 @@ public: std::pair<std::vector<point_3d>, std::vector<quadrature_point_3d>> integrate(const entity& e, int order); - diff --git a/entity_data.h b/entity_data.h index c447bdf0d216870835e76cfded32d35c9e8fb04c..d0cec3424dab1207bd0d3f1c7fcdcaf6cc9e24cd 100644 --- a/entity_data.h +++ b/entity_data.h @@ -2,6 +2,11 @@ #include "eigen.h" +enum class entity_ordering { + GMSH, + BY_ORIENTATION +}; + struct entity_data_cpu { int g_order; @@ -13,7 +18,12 @@ struct entity_data_cpu size_t num_fluxes; /* No. of flux dofs per elem */ size_t num_face_qp; /* No. of integration pts per face */ size_t num_faces; /* No. of faces */ - size_t dofs_base; + entity_ordering ordering; + + size_t dof_base; /* Offset in the global dof vector */ + size_t flux_base; /* Offset in the global flux vector */ + std::vector<size_t> fluxdofs_mine; + std::vector<size_t> fluxdofs_neighbour; /* Inverse of mass matrices. Populated only if geometric order > 1. * num_bf*num_elems rows, num_bf columns. Matrix at offset elem*num_bf @@ -39,4 +49,5 @@ struct entity_data_cpu matxd normals; vecxd cell_determinants; + vecxd face_determinants; }; diff --git a/gmsh_io.cpp b/gmsh_io.cpp index dbde114eeedeb164b7463c44f9438e759d440d89..6b13afd9f291dbd5e229b8086311e7b564ae7a3d 100644 --- a/gmsh_io.cpp +++ b/gmsh_io.cpp @@ -117,12 +117,28 @@ model::entity_at(size_t pos) return entities.at(pos); } +void +model::update_connectivity(const entity& e, size_t entity_index) +{ + for (size_t iT = 0; iT < e.num_cells(); iT++) + { + for (size_t iF = 0; iF < e.num_faces_per_elem(); iF++) + { + size_t findex = e.num_faces_per_elem() * iT + iF; + neighbour_descriptor cd(entity_index, iT, iF); + element_key fk( e.face(findex) ); + conn.connect( fk, cd ); + } + } +} + void model::import_gmsh_entities(void) { gvp_t gmsh_entities; gm::getEntities(gmsh_entities, DIMENSION(3)); + size_t entity_index = 0; for (auto [dim, tag] : gmsh_entities) { std::vector<int> elemTypes; @@ -136,7 +152,12 @@ model::import_gmsh_entities(void) { entity e({.dim = dim, .tag = tag, .etype = elemType, .gorder = geometric_order, .aorder = approximation_order}); + + e.sort_by_orientation(); + update_connectivity(e, entity_index); + entities.push_back( std::move(e) ); + entity_index++; } } } diff --git a/gmsh_io.h b/gmsh_io.h index 6bb94845742d9a4ffeb2e7ebbc7a788d28974cc0..190515764b364e82bae4e35a25d73e428d82af91 100644 --- a/gmsh_io.h +++ b/gmsh_io.h @@ -5,6 +5,7 @@ #include <gmsh.h> #include "entity.h" +#include "physical_element.h" namespace g = gmsh; namespace go = gmsh::option; @@ -32,11 +33,15 @@ class model std::vector<entity> entities; - std::map<size_t, std::vector<element_key>> boundary_map; + std::map<size_t, std::vector<element_key>> boundary_map; + + element_connectivity<element_key> conn; void map_boundaries(void); void import_gmsh_entities(void); + void update_connectivity(const entity&, size_t); + public: model(); model(int, int); diff --git a/kernels_cpu.cpp b/kernels_cpu.cpp index 0dc5b6ee9e2157eb17073f260877f26d1f815ab8..71becb6da678ff70168af3f30e07f4b43a020bba 100644 --- a/kernels_cpu.cpp +++ b/kernels_cpu.cpp @@ -53,4 +53,57 @@ void compute_field_derivatives(const entity_data_cpu& ed, default: std::cout << "compute_field_derivatives: invalid order" << std::endl; } +} + +/* Field derivatives kernel, case for elements with planar faces */ +void compute_flux_lifting(const entity_data_cpu& ed, + const vecxd& flux, vecxd& field) +{ + switch (ed.a_order) + { + case 1: + if (ed.g_order == 1) + compute_lifting_kernel_planar<1>(ed, flux, field); + else + compute_lifting_kernel_curved<1>(ed, flux, field); + break; + + case 2: + if (ed.g_order == 1) + compute_lifting_kernel_planar<2>(ed, flux, field); + else + compute_lifting_kernel_curved<2>(ed, flux, field); + break; + + case 3: + if (ed.g_order == 1) + compute_lifting_kernel_planar<3>(ed, flux, field); + else + compute_lifting_kernel_curved<3>(ed, flux, field); + break; + + case 4: + if (ed.g_order == 1) + compute_lifting_kernel_planar<4>(ed, flux, field); + else + compute_lifting_kernel_curved<4>(ed, flux, field); + break; + + case 5: + if (ed.g_order == 1) + compute_lifting_kernel_planar<5>(ed, flux, field); + else + compute_lifting_kernel_curved<5>(ed, flux, field); + break; + + case 6: + if (ed.g_order == 1) + compute_lifting_kernel_planar<6>(ed, flux, field); + else + compute_lifting_kernel_curved<6>(ed, flux, field); + break; + + default: + std::cout << "compute_flux_lifting: invalid order" << std::endl; + } } \ No newline at end of file diff --git a/kernels_cpu.h b/kernels_cpu.h index b3d33a481c229097932dbc3bd5ebb94c5ed366f0..b71bd44a1aded5bf83254e9b84c73d89af6a9325 100644 --- a/kernels_cpu.h +++ b/kernels_cpu.h @@ -45,7 +45,7 @@ void compute_field_derivatives_kernel_planar(const entity_data_cpu& ed, for (size_t iT = 0; iT < num_elems; iT++) { size_t ent_iT = orient_elem_base + iT; - size_t dofofs = ed.dofs_base + orient_base + iT*ed.num_bf; + size_t dofofs = ed.dof_base + orient_base + iT*ed.num_bf; for (size_t odof = 0; odof < KS::num_bf; odof++) { @@ -111,7 +111,7 @@ void compute_field_derivatives_kernel_curved(const entity_data_cpu& ed, { //printf( "Thread %d works with idx %lu\n", omp_get_thread_num(), iT); size_t ent_iT = orient_elem_base + iT; - size_t dofofs = ed.dofs_base + orient_base + iT*ed.num_bf; + size_t dofofs = ed.dof_base + orient_base + iT*ed.num_bf; df_dx.segment(dofofs, ed.num_bf) = vecxd::Zero(ed.num_bf); df_dy.segment(dofofs, ed.num_bf) = vecxd::Zero(ed.num_bf); @@ -173,4 +173,56 @@ void compute_field_derivatives_kernel_curved(const entity_data_cpu& ed, void compute_field_derivatives(const entity_data_cpu& ed, - const vecxd& f, vecxd& df_dx, vecxd& df_dy, vecxd& df_dz); \ No newline at end of file + const vecxd& f, vecxd& df_dx, vecxd& df_dy, vecxd& df_dz); + + +template<size_t AK> +void compute_lifting_kernel_planar(const entity_data_cpu& ed, + const vecxd& flux, vecxd& field) +{ + /* Flop count: 21*ed.num_bf*ed.num_bf*num_total_elems */ + /* The elements must be ordered by orientation -> entity::sort_by_orientation() */ + + assert(ed.g_order == 1); + + using KS = kernel_cpu_sizes<AK>; + assert(ed.num_bf == KS::num_bf); + assert(ed.num_fluxes == KS::num_fluxes); + + size_t orient_elem_base = 0; + for (size_t iO = 0; iO < ed.num_orientations; iO++) + { + size_t num_elems = ed.num_elems[iO]; + size_t dof_base = ed.dof_base + orient_elem_base * ed.num_bf; + size_t flux_base = ed.flux_base + orient_elem_base * 4*ed.num_fluxes; + + for (size_t iT = 0; iT < num_elems; iT++) + { + size_t dof_ofs = dof_base + iT*ed.num_bf; + size_t flux_ofs = flux_base + 4*iT*KS::num_fluxes; + auto inv_det = 1./ed.cell_determinants[orient_elem_base+iT]; + + for (size_t i = 0; i < KS::num_bf; i++) + { + auto facc = field(dof_ofs+i); + for (size_t iF = 0; iF < 4; iF++) + { + auto face_det = ed.face_determinants[4*(orient_elem_base+iT)+iF]; + for (size_t k = 0; k < KS::num_fluxes; k++) + facc += face_det * inv_det * ed.lifting_matrices(iO*KS::num_bf+i, KS::num_fluxes*iF + k) * flux(flux_ofs+KS::num_fluxes*iF + k); + } + field(dof_ofs+i) = facc; + } + } + + orient_elem_base += num_elems; + } +} + +template<size_t AK> +void compute_lifting_kernel_curved(const entity_data_cpu& ed, + const vecxd& flux, vecxd& field) +{} + +void compute_flux_lifting(const entity_data_cpu& ed, + const vecxd& flux, vecxd& field); \ No newline at end of file diff --git a/physical_element.cpp b/physical_element.cpp index d806625292a9b953bc88421c3f39b030b89beb4f..651a94bd97bcb8b6a1e55586948a8de57e77026b 100644 --- a/physical_element.cpp +++ b/physical_element.cpp @@ -127,6 +127,13 @@ physical_elements_factory::physical_elements_factory(const entity_params& ep) gmm::getBasisFunctionsOrientationForElements(elemType, basis_func_name(approx_order), cellOrientations, tag); assert(cellOrientations.size() == cellTags.size()); + + std::vector<double> coord; + gmm::getKeysForElements(elemType, basis_func_name(approx_order), + keypairs, coord, tag, false); + + keys_per_elem = gmm::getNumberOfKeysForElements(elemType, basis_func_name(approx_order)); + assert(keys_per_elem*cellTags.size() == keypairs.size()); } std::vector<physical_element> @@ -155,6 +162,13 @@ physical_elements_factory::get_elements() new_pe.m_measure = 0.0; new_pe.m_gmsh_elemtype = elemType; + new_pe.m_bf_keys.resize(keys_per_elem); + for (size_t i = 0; i < keys_per_elem; i++) + { + auto [vi, vu] = keypairs[keys_per_elem*elem + i]; + new_pe.m_bf_keys[i] = bf_key(vi,vu); + } + new_pe.m_node_tags.resize(num_nodes); for (size_t i = 0; i < num_nodes; i++) new_pe.m_node_tags[i] = cellNodesTags[num_nodes*elem+i]; diff --git a/physical_element.h b/physical_element.h index 819fa91d69c68845f6a3f102a63c044a6589f1b4..1ee451e02a53da5299248817eaa16331f08878be 100644 --- a/physical_element.h +++ b/physical_element.h @@ -1,8 +1,33 @@ #pragma once +#include "gmsh.h" #include "point.hpp" #include "types.h" +/* This is a key for a basis function. Did this + * class to protect the code from the GMSH api, + * which is apparently changing */ +class bf_key +{ + int wtf_int; // No idea of what this integer represents + size_t wtf_uint; // This is some kind of bf numbering + +public: + bf_key() = default; + bf_key(const bf_key&) = default; + bf_key(bf_key&&) = default; + bf_key(int pi, size_t pu) + : wtf_int(pi), wtf_uint(pu) + {} + + bf_key& operator=(const bf_key&) = default; + + bool operator==(const bf_key& other) const + { + return (wtf_int == other.wtf_int) and (wtf_uint == other.wtf_uint); + } +}; + class physical_element { size_t m_original_position; /* Position in GMSH ordering (relative to entity) */ @@ -19,6 +44,7 @@ class physical_element double m_measure; /* Element measure */ int m_geometric_order; int m_approximation_order; + std::vector<bf_key> m_bf_keys; public: physical_element(); @@ -31,6 +57,8 @@ public: int element_type(void) const; vec_size_t node_tags(void) const; + const std::vector<bf_key> bf_keys(void) const { return m_bf_keys; } + double determinant(void) const; double determinant(size_t) const; vec_double determinants(void) const; @@ -64,6 +92,8 @@ class physical_elements_factory std::vector<double> cellJacs; std::vector<double> cellDets; std::vector<int> cellOrientations; + gmsh::vectorpair keypairs; + int keys_per_elem; public: physical_elements_factory(const entity_params& ep); diff --git a/test.cpp b/test.cpp index a18a62bd50d49e6900c1816bb594c010d9de2376..ed5506a90bb036eb6d1bc4bdb0c6f3d495b99d88 100644 --- a/test.cpp +++ b/test.cpp @@ -28,7 +28,6 @@ int main(void) int failed_tests = 0; - #if 0 std::cout << Bmagentafg << " *** TESTING: BASIC OPERATIONS ***" << reset << std::endl; for (size_t go = 1; go < 5; go++) @@ -39,6 +38,9 @@ int main(void) } } + std::cout << Bmagentafg << " *** TESTING: NORMAL COMPUTATION ***" << reset << std::endl; + for (size_t i = 1; i < 5; i++) + test_normals(i,i); std::cout << Bmagentafg << " *** TESTING: PROJECTIONS ***" << reset << std::endl; for (size_t go = 1; go < 5; go++) @@ -46,19 +48,17 @@ int main(void) for (size_t ao = go; ao < 5; ao++) failed_tests += test_mass_convergence(go, ao); } -#endif std::cout << Bmagentafg << " *** TESTING: DIFFERENTIATION ***" << reset << std::endl; for (size_t go = 1; go < 5; go++) for (size_t ao = go; ao < 5; ao++) failed_tests += test_differentiation_convergence(go, ao); +#endif + + std::cout << Bmagentafg << " *** TESTING: LIFTING ***" << reset << std::endl; + for (size_t ao = 1; ao < 2; ao++) + test_lifting(1, ao); - /*****************************************/ - - std::cout << Bmagentafg << " *** TESTING: NORMAL COMPUTATION ***" << reset << std::endl; - for (size_t i = 1; i < 5; i++) - test_normals(i,i); - gmsh::finalize(); return failed_tests; diff --git a/test.h b/test.h index b7e7ee54895010f74f8f65d2394917e20d481d92..8e376469e1fa0bfbd76eff5b8bcd34f3d5ca6dd0 100644 --- a/test.h +++ b/test.h @@ -28,4 +28,4 @@ int test_basics(int, int); int test_normals(int, int); int test_mass_convergence(int, int); int test_differentiation_convergence(int, int); - +int test_lifting(int, int); diff --git a/test_basics.cpp b/test_basics.cpp index 4f1d63c149509a35c8e56fd5875bb2c27c475e60..806303d3b28679d3332bed19389018a610fa86d0 100644 --- a/test_basics.cpp +++ b/test_basics.cpp @@ -1,13 +1,15 @@ #include <iostream> - #include <unistd.h> +#ifdef _OPENMP +#include <omp.h> +#endif + #include "test.h" #include "gmsh_io.h" -#include "entity_data.h" - #include "sgr.hpp" + using namespace sgr; static void @@ -189,6 +191,7 @@ test_normals(int geometric_order, int approximation_order) std::sort(faceTags.begin(), faceTags.end()); auto& e0 = mod.entity_at(0); + entity_data_cpu ed; e0.populate_entity_data(ed); auto ft = e0.face_tags(); @@ -231,4 +234,4 @@ test_normals(int geometric_order, int approximation_order) } return 0; -} \ No newline at end of file +} diff --git a/test_lifting.cpp b/test_lifting.cpp new file mode 100644 index 0000000000000000000000000000000000000000..61158f1765b756159ece0369b50655374d754525 --- /dev/null +++ b/test_lifting.cpp @@ -0,0 +1,144 @@ +#include <iostream> +#include <iomanip> + +#include <unistd.h> + +#include "test.h" +#include "gmsh_io.h" +#include "sgr.hpp" +#include "entity_data.h" +#include "kernels_cpu.h" +#include "timecounter.h" + +using namespace sgr; + +static void +make_geometry(int order, double mesh_h) +{ + gm::add("difftest"); + gmo::addBox(0.0, 0.0, 0.0, 1.0, 1.0, 1.0); + gmo::synchronize(); + + gvp_t vp; + gm::getEntities(vp); + gmm::setSize(vp, mesh_h); +} + + +int test_lifting(int geometric_order, int approximation_order) +{ + std::vector<double> sizes({ 0.32, 0.16, 0.08, 0.04 }); + std::vector<double> errors; + + std::cout << cyanfg << "Testing geometric order " << geometric_order; + std::cout << ", approximation order = " << approximation_order << nofg; + std::cout << std::endl; + + auto F = [](const point_3d& pt) -> vecxd { + vec3d Fret; + Fret(0) = std::sin(M_PI*pt.y()); + Fret(1) = std::sin(M_PI*pt.z()); + Fret(2) = std::sin(M_PI*pt.x()); + return Fret; + }; + + auto div_F = [](const point_3d& pt) -> double { + return 0; + auto dFx_dx = M_PI*std::cos(M_PI*pt.x()); + auto dFy_dy = M_PI*std::cos(M_PI*pt.y()); + auto dFz_dz = M_PI*std::cos(M_PI*pt.z()); + return dFx_dx + dFy_dy + dFz_dz; + }; + + for (size_t i = 0; i < sizes.size(); i++) + { + double h = sizes[i]; + make_geometry(0,h); + model mod(geometric_order, approximation_order); + + auto& e0 = mod.entity_at(0); + e0.sort_by_orientation(); + + entity_data_cpu ed; + e0.populate_entity_data(ed); + + vecxd exp_field = e0.project(div_F); + vecxd lift_field = vecxd::Zero( e0.num_cells() * ed.num_bf ); + vecxd flux = vecxd::Zero( e0.num_cells() * 4*ed.num_fluxes ); + + for (size_t iT = 0; iT < e0.num_cells(); iT++) + { + for (size_t iF = 0; iF < 4; iF++) + { + auto& pf = e0.face(4*iT+iF); + auto& rf = e0.face_refelem(pf); + auto num_fluxes = rf.num_basis_functions(); + const auto pqps = pf.integration_points(); + + matxd mass = matxd::Zero(num_fluxes, num_fluxes); + vecxd rhs = vecxd::Zero(num_fluxes); + for (size_t iQp = 0; iQp < pqps.size(); iQp++) + { + vec3d n; + if (ed.g_order == 1) + n = ed.normals.row(4*iT+iF); + else + n = ed.normals.row( (4*iT+iF)*pqps.size() + iQp ); + + const auto& pqp = pqps[iQp]; + const vecxd phi = rf.basis_functions(iQp); + mass += pqp.weight() * phi * phi.transpose(); + rhs += pqp.weight() * F(pqp.point()).dot(n) * phi; + } + auto fluxofs = (4*iT+iF)*num_fluxes; + flux.segment(fluxofs, num_fluxes) = mass.ldlt().solve(rhs); + } + } + + timecounter tc; + tc.tic(); + compute_flux_lifting(ed, flux, lift_field); + double time = tc.toc(); + + double err = 0.0; + for (size_t iT = 0; iT < e0.num_cells(); iT++) + { + auto& pe = e0.cell(iT); + auto& re = e0.cell_refelem(pe); + auto num_bf = re.num_basis_functions(); + matxd mass = matxd::Zero(num_bf, num_bf); + vecxd vol = vecxd::Zero(num_bf); + + const auto pqps = pe.integration_points(); + for(size_t iQp = 0; iQp < pqps.size(); iQp++) + { + const auto& pqp = pqps[iQp]; + const vecxd phi = re.basis_functions(iQp); + const matxd dphi = re.basis_gradients(iQp); + mass += pqp.weight() * phi * phi.transpose(); + vol += pqp.weight() * dphi * F(pqp.point()); + } + + vecxd comp_field = lift_field.segment(iT*num_bf, num_bf) - mass.ldlt().solve(vol); + vecxd diff = comp_field - exp_field.segment(iT*num_bf, num_bf); + err += diff.dot(mass*diff); + } + + std::cout << std::sqrt(err) << std::endl; + + if (geometric_order == 1) + { + std::cout << "Kernel runtime: " << time << " seconds. Estimated performance: "; + double flops = 3*(ed.num_bf)*4*ed.num_fluxes*e0.num_cells(); + std::cout << flops/(1e9*time) << " GFlops/s" << std::endl; + } + else + { + std::cout << "Kernel runtime: " << time << " seconds. Estimated performance: "; + double flops = ((21*ed.num_bf+6)*ed.num_bf*ed.num_qp + 3*(2*ed.num_bf-1)*ed.num_bf)*e0.num_cells(); + std::cout << flops/(1e9*time) << " GFlops/s" << std::endl; + } + } + + return 0; +} \ No newline at end of file diff --git a/types.h b/types.h index 100670e37e55a9ebd0880dee4c684ab05dd1fc49..cb89d6cccb39cfb2cf3ae102278b8fc2be7faaf1 100644 --- a/types.h +++ b/types.h @@ -8,11 +8,13 @@ using vec_size_t = std::vector<size_t>; using vec_double = std::vector<double>; using vec_point_3d = std::vector<point_3d>; using vec_quadpt_3d = std::vector<quadrature_point_3d>; +using vec3d = Eigen::Matrix<double, 3, 1>; using mat3d = Eigen::Matrix<double, 3, 3, Eigen::RowMajor>; using vecxd = Eigen::Matrix<double, Eigen::Dynamic, 1>; using matxd = Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>; using vec_mat3d = eigen_compatible_stdvector<mat3d>; using vec_matxd = eigen_compatible_stdvector<matxd>; + struct entity_params { int dim;