From 55c3aa57d69ed561a0518da57ae7abd52fc9ee9d Mon Sep 17 00:00:00 2001 From: Matthew McCall Date: Fri, 21 Mar 2025 13:06:42 -0400 Subject: [PATCH 01/27] Refactor GridPointSearch to support dimensional template specialization. Updated `GridPointSearch` to use a templated design (`GridPointSearch`) and introduced `GridPointSearch2D` as a specific case for 2D point localization. This change ensures clearer extensibility for other dimensions and improves code maintainability by centralizing shared logic in a new base class (`PointLocalizationSearch`). Adjusted test cases, headers, and related implementations accordingly. --- src/pcms/adapter/omega_h/omega_h_field.h | 7 +- src/pcms/interpolator/adj_search.hpp | 4 +- src/pcms/point_search.cpp | 219 ++++++++++------------- src/pcms/point_search.h | 38 ++-- test/test_point_search.cpp | 75 ++++---- 5 files changed, 158 insertions(+), 185 deletions(-) diff --git a/src/pcms/adapter/omega_h/omega_h_field.h b/src/pcms/adapter/omega_h/omega_h_field.h index 7a7283aa..0769efc4 100644 --- a/src/pcms/adapter/omega_h/omega_h_field.h +++ b/src/pcms/adapter/omega_h/omega_h_field.h @@ -154,7 +154,7 @@ class OmegaHField void ConstructSearch(int nx, int ny) { PCMS_FUNCTION_TIMER; - search_ = GridPointSearch(mesh_, nx, ny); + search_ = GridPointSearch2D(mesh_, nx, ny); } // pass through to search function [[nodiscard]] auto Search(Kokkos::View points) const @@ -220,9 +220,8 @@ class OmegaHField private: std::string name_; Omega_h::Mesh& mesh_; - // TODO make this a pointer and introduce base class to Search for alternative - // search methods - std::optional search_; + // TODO make this a pointer and introduce base class to Search for alternative search methods + std::optional search_; // bitmask array that specifies a filter on the field Omega_h::Read mask_; LO size_; diff --git a/src/pcms/interpolator/adj_search.hpp b/src/pcms/interpolator/adj_search.hpp index 6f90d823..25047b8f 100644 --- a/src/pcms/interpolator/adj_search.hpp +++ b/src/pcms/interpolator/adj_search.hpp @@ -26,7 +26,7 @@ Omega_h::Real calculateDistance(const Omega_h::Real* p1, } inline void checkTargetPoints( - const Kokkos::View& results) + const Kokkos::View& results) { Kokkos::fence(); pcms::printInfo("INFO: Checking target points...\n"); @@ -119,7 +119,7 @@ inline void FindSupports::adjBasedSearch( }); Kokkos::fence(); - pcms::GridPointSearch search_cell(source_mesh, 10, 10); + pcms::GridPointSearch2D search_cell(source_mesh, 10, 10); auto results = search_cell(target_points); checkTargetPoints(results); diff --git a/src/pcms/point_search.cpp b/src/pcms/point_search.cpp index ee7b091b..56bcadca 100644 --- a/src/pcms/point_search.cpp +++ b/src/pcms/point_search.cpp @@ -2,35 +2,29 @@ #include #include -// From -// https://en.wikipedia.org/wiki/Distance_from_a_point_to_a_line#Line_defined_by_two_points +// From https://en.wikipedia.org/wiki/Distance_from_a_point_to_a_line#Line_defined_by_two_points KOKKOS_INLINE_FUNCTION -double distance_from_line(const double x0, const double y0, const double x1, - const double y1, const double x2, const double y2) +double distance_from_line(const double x0, const double y0, const double x1, const double y1, const double x2, const double y2) { - const Omega_h::Vector<2> p1 = {x1, y1}; - const Omega_h::Vector<2> p2 = {x2, y2}; + const Omega_h::Vector<2> p1 = { x1, y1 }; + const Omega_h::Vector<2> p2 = { x2, y2 }; auto disp = p2 - p1; - return std::abs(disp[1] * x0 - disp[0] * y0 + x2 * y1 - y2 * x1) / - Omega_h::norm(disp); + return std::abs(disp[1]*x0 - disp[0]*y0 + x2*y1 - y2*x1) / Omega_h::norm(disp); } -// Law of Cosines, where a, b, c and gamma are defined here: -// https://en.wikipedia.org/wiki/Law_of_cosines#Use_in_solving_triangles +// Law of Cosines, where a, b, c and gamma are defined here: https://en.wikipedia.org/wiki/Law_of_cosines#Use_in_solving_triangles KOKKOS_INLINE_FUNCTION double angle_from_side_lengths(const double a, const double b, const double c) { - return std::acos((a * a + b * b - c * c) / 2 * a * b); + return std::acos((a*a + b*b - c*c) / 2*a*b); } KOKKOS_INLINE_FUNCTION -bool normal_intersects_segment(const Omega_h::Few a, - const Omega_h::Few b, - const Omega_h::Few c) +bool normal_intersects_segment(const Omega_h::Few a, const Omega_h::Few b, const Omega_h::Few c) { const auto ab_len = Omega_h::norm(a - b); - const auto bc_len = Omega_h::norm(b - c); + const auto bc_len = Omega_h::norm(b -c); const auto ac_len = Omega_h::norm(a - c); const double angle1 = angle_from_side_lengths(bc_len, ac_len, ab_len); @@ -115,15 +109,13 @@ bool line_intersects_bbox(const Omega_h::Vector<2>& p0, return false; } -[[nodiscard]] KOKKOS_INLINE_FUNCTION bool within_bbox( - const Omega_h::Vector<2> coord, const AABBox<2>& bbox) noexcept -{ +[[nodiscard]] KOKKOS_INLINE_FUNCTION +bool within_bbox(const Omega_h::Vector<2> coord, const AABBox<2> & bbox) noexcept { auto left = bbox.center[0] - bbox.half_width[0]; auto right = bbox.center[0] + bbox.half_width[0]; auto bot = bbox.center[1] - bbox.half_width[1]; auto top = bbox.center[1] + bbox.half_width[1]; - return (coord[0] >= left) && (coord[0] <= right) && (coord[1] >= bot) && - (coord[1] <= top); + return (coord[0]>=left) && (coord[0]<=right) && (coord[1] >= bot) && (coord[1]<=top); } [[nodiscard]] KOKKOS_INLINE_FUNCTION bool bbox_verts_within_triangle( @@ -133,22 +125,14 @@ bool line_intersects_bbox(const Omega_h::Vector<2>& p0, auto right = bbox.center[0] + bbox.half_width[0]; auto bot = bbox.center[1] - bbox.half_width[1]; auto top = bbox.center[1] + bbox.half_width[1]; - auto xi = barycentric_from_global({left, bot}, coords); - if (Omega_h::is_barycentric_inside(xi, fuzz)) { - return true; - } - xi = barycentric_from_global({left, top}, coords); - if (Omega_h::is_barycentric_inside(xi, fuzz)) { - return true; - } - xi = barycentric_from_global({right, top}, coords); - if (Omega_h::is_barycentric_inside(xi, fuzz)) { - return true; - } - xi = barycentric_from_global({right, bot}, coords); - if (Omega_h::is_barycentric_inside(xi, fuzz)) { - return true; - } + auto xi = barycentric_from_global({left,bot}, coords); + if(Omega_h::is_barycentric_inside(xi, fuzz)){ return true;} + xi = barycentric_from_global({left,top}, coords); + if(Omega_h::is_barycentric_inside(xi, fuzz)){ return true;} + xi = barycentric_from_global({right,top}, coords); + if(Omega_h::is_barycentric_inside(xi, fuzz)){ return true;} + xi = barycentric_from_global({right,bot}, coords); + if(Omega_h::is_barycentric_inside(xi, fuzz)){ return true;} return false; } @@ -163,7 +147,8 @@ KOKKOS_FUNCTION bool triangle_intersects_bbox( // triangle and grid cell bounding box intersect if (intersects(triangle_bbox(coords), bbox)) { // if any of the triangle verts inside of bbox - if (within_bbox(coords[0], bbox) || within_bbox(coords[1], bbox) || + if (within_bbox(coords[0], bbox) || + within_bbox(coords[1], bbox) || within_bbox(coords[2], bbox)) { return true; } @@ -220,8 +205,7 @@ struct GridTriIntersectionFunctor LO num_intersections = 0; // hierarchical parallel may make be very beneficial here... for (LO elem_idx = 0; elem_idx < nelems_; ++elem_idx) { - const auto elem_tri2verts = - Omega_h::gather_verts<3>(tris2verts_, elem_idx); + const auto elem_tri2verts = Omega_h::gather_verts<3>(tris2verts_, elem_idx); // 2d mesh with 2d coords, but 3 triangles const auto vertex_coords = Omega_h::gather_vectors<3, 2>(coords_, elem_tri2verts); @@ -246,8 +230,8 @@ struct GridTriIntersectionFunctor LO nelems_; }; -// num_grid_cells should be result of grid.GetNumCells(), take as argument to -// avoid extra copy of grid from gpu to cpu +// num_grid_cells should be result of grid.GetNumCells(), take as argument to avoid extra copy +// of grid from gpu to cpu Kokkos::Crs construct_intersection_map(Omega_h::Mesh& mesh, Kokkos::View grid, @@ -272,22 +256,16 @@ Omega_h::Vector<3> barycentric_from_global( return {1 - xi[0] - xi[1], xi[0], xi[1]}; } -template -OMEGA_H_INLINE double myreduce(const Omega_h::Vector& x, - Op op) OMEGA_H_NOEXCEPT -{ +template +OMEGA_H_INLINE double myreduce(const Omega_h::Vector & x, Op op) OMEGA_H_NOEXCEPT { auto out = x[0]; - for (int i = 1; i < n; ++i) - out = op(out, x[i]); + for (int i = 1; i < n; ++i) out = op(out, x[i]); return out; } -Kokkos::View GridPointSearch::operator()( - Kokkos::View points) const +Kokkos::View GridPointSearch2D::operator()(Kokkos::View points) const { - static_assert(dim == 2, "point search assumes dim==2"); - Kokkos::View results("point search result", - points.extent(0)); + Kokkos::View results("point search result", points.extent(0)); auto num_rows = candidate_map_.numRows(); // needed so that we don't capture this ptr which will be memory error on cuda auto grid = grid_; @@ -308,100 +286,89 @@ Kokkos::View GridPointSearch::operator()( auto candidates_end = candidate_map.row_map(cell_id + 1); bool found = false; - auto nearest_triangle = candidates_begin; - auto dimensionality = GridPointSearch::Result::Dimensionality::EDGE; - Omega_h::Real distance_to_nearest{INFINITY}; - Omega_h::Vector<3> parametric_coords_to_nearest; - // create array that's size of number of candidates x num coords to store - // parametric inversion - for (auto i = candidates_begin; i < candidates_end; ++i) { - const int triangleID = candidate_map.entries(i); - const auto elem_tri2verts = - Omega_h::gather_verts<3>(tris2verts, triangleID); - // 2d mesh with 2d coords, but 3 triangles - auto vertex_coords = - Omega_h::gather_vectors<3, 2>(coords, elem_tri2verts); - auto parametric_coords = barycentric_from_global(point, vertex_coords); - - if (Omega_h::is_barycentric_inside(parametric_coords, fuzz)) { - results(p) = GridPointSearch::Result{ - GridPointSearch::Result::Dimensionality::FACE, triangleID, - parametric_coords}; - found = true; - break; - } + auto nearest_triangle = candidates_begin; + auto dimensionality = GridPointSearch::Result::Dimensionality::EDGE; + Omega_h::Real distance_to_nearest { INFINITY }; + Omega_h::Vector<3> parametric_coords_to_nearest; + // create array that's size of number of candidates x num coords to store + // parametric inversion + for (auto i = candidates_begin; i < candidates_end; ++i) { + const int triangleID = candidate_map.entries(i); + const auto elem_tri2verts = Omega_h::gather_verts<3>(tris2verts, triangleID); + // 2d mesh with 2d coords, but 3 triangles + auto vertex_coords = Omega_h::gather_vectors<3, 2>(coords, elem_tri2verts); + auto parametric_coords = barycentric_from_global(point, vertex_coords); - for (int j = 0; j < 3; ++j) { - // Every triangle (face) is connected to 3 edges - const int edgeID = tris2edges_adj.ab2b[triangleID * 3 + j]; + if (Omega_h::is_barycentric_inside(parametric_coords, fuzz)) { + results(p) = GridPointSearch::Result{GridPointSearch::Result::Dimensionality::FACE, triangleID, parametric_coords}; + found = true; + break; + } - auto vertex_a_id = edges2verts_adj.ab2b[edgeID * 2]; - auto vertex_b_id = edges2verts_adj.ab2b[edgeID * 2 + 1]; + for (int j = 0; j < 3; ++j) { + // Every triangle (face) is connected to 3 edges + const int edgeID = tris2edges_adj.ab2b[triangleID * 3 + j]; - auto vertex_a = Omega_h::get_vector<2>(coords, vertex_a_id); - auto vertex_b = Omega_h::get_vector<2>(coords, vertex_b_id); + auto vertex_a_id = edges2verts_adj.ab2b[edgeID * 2]; + auto vertex_b_id = edges2verts_adj.ab2b[edgeID * 2 + 1]; - if (!normal_intersects_segment(point, vertex_a, vertex_b)) - continue; + auto vertex_a = Omega_h::get_vector<2>(coords, vertex_a_id); + auto vertex_b = Omega_h::get_vector<2>(coords, vertex_b_id); - const auto xa = vertex_a[0]; - const auto ya = vertex_a[1]; - const auto xb = vertex_b[0]; - const auto yb = vertex_b[1]; + if (!normal_intersects_segment(point, vertex_a, vertex_b)) continue; - const auto xp = point[0]; - const auto yp = point[1]; + const auto xa = vertex_a[0]; + const auto ya = vertex_a[1]; + const auto xb = vertex_b[0]; + const auto yb = vertex_b[1]; - const auto distance_to_ab = - distance_from_line(xp, yp, xa, ya, xb, yb); + const auto xp = point[0]; + const auto yp = point[1]; - if (distance_to_ab >= distance_to_nearest) { - continue; - } + const auto distance_to_ab = distance_from_line(xp, yp, xa, ya, xb, yb); - dimensionality = GridPointSearch::Result::Dimensionality::EDGE; - nearest_triangle = i; - distance_to_nearest = distance_to_ab; - parametric_coords_to_nearest = parametric_coords; - } + if (distance_to_ab >= distance_to_nearest) { continue; } - // Every triangle (face) is connected to 3 vertices - for (int j = 0; j < 3; ++j) { - // Get the vertex ID from the connectivity array - const int vertexID = tris2verts_adj.ab2b[triangleID * 3 + j]; - // Get the vertex coordinates from the mesh using vertexID - const Omega_h::Few vertex = - Omega_h::get_vector<2>(coords, vertexID); - - if (const auto distance = Omega_h::norm(point - vertex); - distance < distance_to_nearest) { - dimensionality = GridPointSearch::Result::Dimensionality::VERTEX; - nearest_triangle = i; - distance_to_nearest = distance; - parametric_coords_to_nearest = parametric_coords; - } - } + dimensionality = GridPointSearch::Result::Dimensionality::EDGE; + nearest_triangle = i; + distance_to_nearest = distance_to_ab; + parametric_coords_to_nearest = parametric_coords; } - if (!found) { - results(p) = GridPointSearch::Result{ - dimensionality, -1 * candidate_map.entries(nearest_triangle), - parametric_coords_to_nearest}; + + // Every triangle (face) is connected to 3 vertices + for (int j = 0; j < 3; ++j) { + // Get the vertex ID from the connectivity array + const int vertexID = tris2verts_adj.ab2b[triangleID * 3 + j]; + // Get the vertex coordinates from the mesh using vertexID + const Omega_h::Few vertex = + Omega_h::get_vector<2>(coords, vertexID); + + if (const auto distance = Omega_h::norm(point - vertex);distance < distance_to_nearest) { + dimensionality = GridPointSearch::Result::Dimensionality::VERTEX; + nearest_triangle = i; + distance_to_nearest = distance; + parametric_coords_to_nearest = parametric_coords; + } } - }); + } + if(!found) + { + results(p) = GridPointSearch::Result{dimensionality, -1 * candidate_map.entries(nearest_triangle), parametric_coords_to_nearest}; + } + }); return results; } -GridPointSearch::GridPointSearch(Omega_h::Mesh& mesh, LO Nx, LO Ny, Real fuzz) +GridPointSearch<2>::GridPointSearch(Omega_h::Mesh& mesh, LO Nx, LO Ny, Real fuzz) : fuzz_(fuzz) { auto mesh_bbox = Omega_h::get_bounding_box<2>(&mesh); auto grid_h = Kokkos::create_mirror_view(grid_); - grid_h(0) = - Uniform2DGrid{.edge_length = {mesh_bbox.max[0] - mesh_bbox.min[0], - mesh_bbox.max[1] - mesh_bbox.min[1]}, - .bot_left = {mesh_bbox.min[0], mesh_bbox.min[1]}, - .divisions = {Nx, Ny}}; + grid_h(0) = Uniform2DGrid{.edge_length = {mesh_bbox.max[0] - mesh_bbox.min[0], + mesh_bbox.max[1] - mesh_bbox.min[1]}, + .bot_left = {mesh_bbox.min[0], mesh_bbox.min[1]}, + .divisions = {Nx, Ny}}; Kokkos::deep_copy(grid_, grid_h); candidate_map_ = detail::construct_intersection_map( mesh, grid_, grid_h(0).GetNumCells(), fuzz_); diff --git a/src/pcms/point_search.h b/src/pcms/point_search.h index 151978b7..2c86b865 100644 --- a/src/pcms/point_search.h +++ b/src/pcms/point_search.h @@ -15,8 +15,7 @@ namespace pcms // TODO take a bounding box as we may want a bbox that's bigger than the mesh! // this function is in the public header for testing, but should not be directly // used -namespace detail -{ +namespace detail { Kokkos::Crs construct_intersection_map(Omega_h::Mesh& mesh, Kokkos::View grid, @@ -30,15 +29,11 @@ Omega_h::Vector<3> barycentric_from_global( const Omega_h::Matrix<2, 3>& coords, const AABBox<2>& bbox, Real fuzz = 1E-12); -class GridPointSearch +template +class PointLocalizationSearch { - using CandidateMapT = - Kokkos::Crs; - public: - static constexpr auto dim = 2; - struct Result - { + struct Result { enum class Dimensionality { VERTEX = 0, @@ -51,6 +46,25 @@ class GridPointSearch Omega_h::Vector parametric_coords; }; + static constexpr auto DIM = 2; + + virtual Kokkos::View operator()(Kokkos::View point) const = 0; +}; + +template +class GridPointSearch : public PointLocalizationSearch +{ + static_assert(false, "Not implemented"); +}; + +template <> +class GridPointSearch<2> : public PointLocalizationSearch<2> +{ + using CandidateMapT = Kokkos::Crs; + +public: + using Result = PointLocalizationSearch::Result; + GridPointSearch(Omega_h::Mesh& mesh, LO Nx, LO Ny, Real fuzz = 1E-12); /** * given a point in global coordinates give the id of the triangle that the @@ -59,7 +73,7 @@ class GridPointSearch * id will be a negative number and (TODO) will return a negative id of the * closest element */ - Kokkos::View operator()(Kokkos::View point) const; + Kokkos::View operator()(Kokkos::View point) const override; private: Omega_h::Mesh mesh_; @@ -73,5 +87,7 @@ class GridPointSearch Omega_h::Reals coords_; }; -} // namespace pcms +using GridPointSearch2D = GridPointSearch<2>; + +} // namespace detail #endif // PCMS_COUPLING_POINT_SEARCH_H diff --git a/test/test_point_search.cpp b/test/test_point_search.cpp index 06bacf48..7590efe3 100644 --- a/test/test_point_search.cpp +++ b/test/test_point_search.cpp @@ -112,8 +112,7 @@ bool num_candidates_within_range(const T& intersection_map, pcms::LO min, Kokkos::parallel_reduce( intersection_map.numRows(), KOKKOS_LAMBDA(const int i, result_type& update) { - auto num_candidates = - intersection_map.row_map(i + 1) - intersection_map.row_map(i); + auto num_candidates = intersection_map.row_map(i + 1) - intersection_map.row_map(i); if (num_candidates > update.max_val) update.max_val = num_candidates; if (num_candidates < update.min_val) @@ -125,11 +124,11 @@ bool num_candidates_within_range(const T& intersection_map, pcms::LO min, std::cerr << result.min_val << ' ' << result.max_val << '\n'; return within_range; } -// extern Omega_h::Library omega_h_library; +//extern Omega_h::Library omega_h_library; TEST_CASE("construct intersection map") { - // auto world = omega_h_library.world(); + //auto world = omega_h_library.world(); auto lib = Omega_h::Library{}; auto world = lib.world(); auto mesh = @@ -139,11 +138,9 @@ TEST_CASE("construct intersection map") { Kokkos::View grid_d("uniform grid"); auto grid_h = Kokkos::create_mirror_view(grid_d); - grid_h(0) = Uniform2DGrid{ - .edge_length{1, 1}, .bot_left = {0, 0}, .divisions = {10, 10}}; + grid_h(0) = Uniform2DGrid{.edge_length{1, 1}, .bot_left = {0, 0}, .divisions = {10, 10}}; Kokkos::deep_copy(grid_d, grid_h); - auto intersection_map = pcms::detail::construct_intersection_map( - mesh, grid_d, grid_h(0).GetNumCells()); + auto intersection_map = pcms::detail::construct_intersection_map(mesh, grid_d, grid_h(0).GetNumCells()); // assert(cudaSuccess == cudaDeviceSynchronize()); REQUIRE(intersection_map.numRows() == 100); REQUIRE(num_candidates_within_range(intersection_map, 2, 16)); @@ -152,38 +149,35 @@ TEST_CASE("construct intersection map") { Kokkos::View grid_d("uniform grid"); auto grid_h = Kokkos::create_mirror_view(grid_d); - grid_h(0) = Uniform2DGrid{ - .edge_length{1, 1}, .bot_left = {0, 0}, .divisions = {60, 60}}; + grid_h(0) = Uniform2DGrid{.edge_length{1, 1}, .bot_left = {0, 0}, .divisions = {60, 60}}; Kokkos::deep_copy(grid_d, grid_h); // require number of candidates is >=1 and <=6 - auto intersection_map = pcms::detail::construct_intersection_map( - mesh, grid_d, grid_h(0).GetNumCells()); + auto intersection_map = pcms::detail::construct_intersection_map(mesh, grid_d, grid_h(0).GetNumCells()); REQUIRE(intersection_map.numRows() == 3600); REQUIRE(num_candidates_within_range(intersection_map, 1, 6)); } } -TEST_CASE("uniform grid search") -{ - using pcms::GridPointSearch; +TEST_CASE("uniform grid search") { + using pcms::GridPointSearch2D; auto lib = Omega_h::Library{}; auto world = lib.world(); auto mesh = Omega_h::build_box(world, OMEGA_H_SIMPLEX, 1, 1, 1, 10, 10, 0, false); - GridPointSearch search{mesh, 10, 10}; - Kokkos::View points("test_points", 7); - // Kokkos::View points("test_points", 1); + GridPointSearch2D search{mesh,10,10}; + Kokkos::View points("test_points", 7); + //Kokkos::View points("test_points", 1); auto points_h = Kokkos::create_mirror_view(points); - points_h(0, 0) = 0; - points_h(0, 1) = 0; - points_h(1, 0) = 0.55; - points_h(1, 1) = 0.54; - points_h(2, 0) = 100; - points_h(2, 1) = 100; - points_h(3, 0) = 1; - points_h(3, 1) = 1; - points_h(4, 0) = -1; - points_h(4, 1) = -1; + points_h(0,0) = 0; + points_h(0,1) = 0; + points_h(1,0) = 0.55; + points_h(1,1) = 0.54; + points_h(2,0) = 100; + points_h(2,1) = 100; + points_h(3,0) = 1; + points_h(3,1) = 1; + points_h(4,0) = -1; + points_h(4,1) = -1; points_h(5, 0) = 1.01; points_h(5, 1) = 0.95; points_h(6, 0) = 0.05; @@ -195,16 +189,16 @@ TEST_CASE("uniform grid search") SECTION("global coordinate within mesh") { { - auto [dim, idx, coords] = results_h(0); - REQUIRE(dim == GridPointSearch::Result::Dimensionality::FACE); + auto [dim, idx,coords] = results_h(0); + REQUIRE(dim == GridPointSearch2D::Result::Dimensionality::FACE); REQUIRE(idx == 0); REQUIRE(coords[0] == Catch::Approx(1)); REQUIRE(coords[1] == Catch::Approx(0)); REQUIRE(coords[2] == Catch::Approx(0)); } { - auto [dim, idx, coords] = results_h(1); - REQUIRE(dim == GridPointSearch::Result::Dimensionality::FACE); + auto [dim, idx,coords] = results_h(1); + REQUIRE(dim == GridPointSearch2D::Result::Dimensionality::FACE); REQUIRE(idx == 91); REQUIRE(coords[0] == Catch::Approx(0.5)); REQUIRE(coords[1] == Catch::Approx(0.1)); @@ -212,28 +206,25 @@ TEST_CASE("uniform grid search") } } // feature needs to be added - SECTION("Global coordinate outside mesh", "[!mayfail]") - { + SECTION("Global coordinate outside mesh", "[!mayfail]") { auto out_of_bounds = results_h(2); auto top_right = results_h(3); - REQUIRE(out_of_bounds.dimensionality == - GridPointSearch::Result::Dimensionality::VERTEX); - REQUIRE(-1 * out_of_bounds.tri_id == top_right.tri_id); + REQUIRE(out_of_bounds.dimensionality == GridPointSearch2D::Result::Dimensionality::VERTEX); + REQUIRE(-1*out_of_bounds.tri_id == top_right.tri_id); out_of_bounds = results_h(4); auto bot_left = results_h(0); - REQUIRE(out_of_bounds.dimensionality == - GridPointSearch::Result::Dimensionality::VERTEX); - REQUIRE(-1 * out_of_bounds.tri_id == bot_left.tri_id); + REQUIRE(out_of_bounds.dimensionality == GridPointSearch2D::Result::Dimensionality::VERTEX); + REQUIRE(-1*out_of_bounds.tri_id == bot_left.tri_id); out_of_bounds = results_h(5); REQUIRE(out_of_bounds.dimensionality == - GridPointSearch::Result::Dimensionality::EDGE); + GridPointSearch2D::Result::Dimensionality::EDGE); REQUIRE(-1 * out_of_bounds.tri_id == top_right.tri_id); out_of_bounds = results_h(6); REQUIRE(out_of_bounds.dimensionality == - GridPointSearch::Result::Dimensionality::EDGE); + GridPointSearch2D::Result::Dimensionality::EDGE); REQUIRE(-1 * out_of_bounds.tri_id == bot_left.tri_id); } } From 40c9ecb502299374916b7c0373773596bfba20b2 Mon Sep 17 00:00:00 2001 From: Matthew McCall Date: Fri, 21 Mar 2025 13:14:53 -0400 Subject: [PATCH 02/27] Switch `optional` to `unique_ptr` Replaced `std::optional` with `std::unique_ptr` for `search_` to better handle polymorphism and align with future extensions. Updated type aliasing and constructors accordingly to enhance code clarity and maintainability. --- src/pcms/adapter/omega_h/omega_h_field.h | 7 +++---- src/pcms/point_search.h | 6 ++++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/pcms/adapter/omega_h/omega_h_field.h b/src/pcms/adapter/omega_h/omega_h_field.h index 0769efc4..14d2945a 100644 --- a/src/pcms/adapter/omega_h/omega_h_field.h +++ b/src/pcms/adapter/omega_h/omega_h_field.h @@ -154,14 +154,13 @@ class OmegaHField void ConstructSearch(int nx, int ny) { PCMS_FUNCTION_TIMER; - search_ = GridPointSearch2D(mesh_, nx, ny); + search_ = std::make_unique(mesh_, nx, ny); } // pass through to search function [[nodiscard]] auto Search(Kokkos::View points) const { PCMS_FUNCTION_TIMER; - PCMS_ALWAYS_ASSERT(search_.has_value() && - "search data structure must be constructed before use"); + PCMS_ALWAYS_ASSERT(search_ != nullptr && "search data structure must be constructed before use"); return (*search_)(points); } @@ -221,7 +220,7 @@ class OmegaHField std::string name_; Omega_h::Mesh& mesh_; // TODO make this a pointer and introduce base class to Search for alternative search methods - std::optional search_; + std::unique_ptr search_; // bitmask array that specifies a filter on the field Omega_h::Read mask_; LO size_; diff --git a/src/pcms/point_search.h b/src/pcms/point_search.h index 2c86b865..11fad310 100644 --- a/src/pcms/point_search.h +++ b/src/pcms/point_search.h @@ -51,6 +51,8 @@ class PointLocalizationSearch virtual Kokkos::View operator()(Kokkos::View point) const = 0; }; +using PointLocalizationSearch2D = PointLocalizationSearch<2>; + template class GridPointSearch : public PointLocalizationSearch { @@ -58,12 +60,12 @@ class GridPointSearch : public PointLocalizationSearch }; template <> -class GridPointSearch<2> : public PointLocalizationSearch<2> +class GridPointSearch<2> : public PointLocalizationSearch2D { using CandidateMapT = Kokkos::Crs; public: - using Result = PointLocalizationSearch::Result; + using Result = PointLocalizationSearch2D::Result; GridPointSearch(Omega_h::Mesh& mesh, LO Nx, LO Ny, Real fuzz = 1E-12); /** From 40a86e8a72147b99e0818b1a6be51e2ad7e3b363 Mon Sep 17 00:00:00 2001 From: Matthew McCall Date: Sat, 5 Apr 2025 13:14:14 -0400 Subject: [PATCH 03/27] Add 3D grid point search functionality and refactor 2D implementation Introduced support for 3D point localization and grid intersection with a new `GridTriIntersectionFunctor3D`. Refactored 2D implementations for consistency, generalizing methods to handle dimensions dynamically. Added templates and utilities to support both 2D and 3D use cases effectively. --- src/pcms/point_search.cpp | 122 +++++++++++++++++++++++++++++++------- src/pcms/point_search.h | 15 +---- src/pcms/uniform_grid.h | 5 +- 3 files changed, 108 insertions(+), 34 deletions(-) diff --git a/src/pcms/point_search.cpp b/src/pcms/point_search.cpp index 56bcadca..5077d22f 100644 --- a/src/pcms/point_search.cpp +++ b/src/pcms/point_search.cpp @@ -49,6 +49,31 @@ AABBox<2> triangle_bbox(const Omega_h::Matrix<2, 3>& coords) return {.center = {(max[0] + min[0]) / 2.0, (max[1] + min[1]) / 2.0}, .half_width = {(max[0] - min[0]) / 2.0, (max[1] - min[1]) / 2.0}}; } + +template +AABBox simplex_bbox(const Omega_h::Matrix& coords) +{ + std::array max{coords(0, 0), coords(1, 0)}; + std::array min{coords(0, 0), coords(1, 0)}; + + for (int i = 1; i < dim + 1; ++i) { + for (int j = 0; j < dim; ++j) { + max[j] = std::fmax(max[j], coords(j, i)); + min[j] = std::fmin(min[j], coords(j, i)); + } + } + + std::array center; + std::array half_width; + + for (int j = 0; j < dim; ++j) { + center[j] = (max[j] + min[j]) / 2.0; + half_width[j] = (max[j] - min[j]) / 2.0; + } + + return {.center = center, .half_width = half_width}; +} + // Liang, You-Dong, and B. A. Barsky. “A New Concept and Method for Line // Clipping.” ACM Transactions on Graphics 3, no. 1 (January 1984): 1–22. // https://doi.org/10.1145/357332.357333. @@ -109,13 +134,14 @@ bool line_intersects_bbox(const Omega_h::Vector<2>& p0, return false; } +template [[nodiscard]] KOKKOS_INLINE_FUNCTION -bool within_bbox(const Omega_h::Vector<2> coord, const AABBox<2> & bbox) noexcept { - auto left = bbox.center[0] - bbox.half_width[0]; - auto right = bbox.center[0] + bbox.half_width[0]; - auto bot = bbox.center[1] - bbox.half_width[1]; - auto top = bbox.center[1] + bbox.half_width[1]; - return (coord[0]>=left) && (coord[0]<=right) && (coord[1] >= bot) && (coord[1]<=top); +bool within_bbox(const Omega_h::Vector coord, const AABBox & bbox) noexcept { + for (int i = 0; i < dim; ++i) { + if (coord[i] < bbox.center[i] - bbox.half_width[i]) return false; + if (coord[i] > bbox.center[i] + bbox.half_width[i]) return false; + } + return true; } [[nodiscard]] KOKKOS_INLINE_FUNCTION bool bbox_verts_within_triangle( @@ -147,9 +173,9 @@ KOKKOS_FUNCTION bool triangle_intersects_bbox( // triangle and grid cell bounding box intersect if (intersects(triangle_bbox(coords), bbox)) { // if any of the triangle verts inside of bbox - if (within_bbox(coords[0], bbox) || - within_bbox(coords[1], bbox) || - within_bbox(coords[2], bbox)) { + if (within_bbox<2>(coords[0], bbox) || + within_bbox<2>(coords[1], bbox) || + within_bbox<2>(coords[2], bbox)) { return true; } // if any of the bbox verts are within the triangle @@ -166,6 +192,15 @@ KOKKOS_FUNCTION bool triangle_intersects_bbox( return false; } +template +[[nodiscard]] +KOKKOS_FUNCTION +bool simplex_intersects_bbox(const Omega_h::Matrix& coords, const AABBox& bbox) +{ + return intersects(simplex_bbox(coords), bbox); + // TODO: Add refined cases from triangle_intersects_bbox +} + namespace detail { /** @@ -178,9 +213,9 @@ namespace detail * \Warning since this uses Omega_h data which is only available in the * "Default" Execution space, the should not be used in an alternative EXE space */ -struct GridTriIntersectionFunctor +struct GridTriIntersectionFunctor2D { - GridTriIntersectionFunctor(Omega_h::Mesh& mesh, + GridTriIntersectionFunctor2D(Omega_h::Mesh& mesh, Kokkos::View grid, Real fuzz) : mesh_(mesh), tris2verts_(mesh_.ask_elem_verts()), @@ -190,7 +225,7 @@ struct GridTriIntersectionFunctor nelems_(mesh_.nelems()) { if (mesh_.dim() != 2) { - std::cerr << "GridTriIntersection currently only developed for 2D " + std::cerr << "GridTriIntersection2D currently only developed for 2D " "triangular meshes\n"; std::terminate(); } @@ -230,6 +265,53 @@ struct GridTriIntersectionFunctor LO nelems_; }; +struct GridTriIntersectionFunctor3D +{ + GridTriIntersectionFunctor3D(Omega_h::Mesh& mesh, Kokkos::View grid) + : mesh_(mesh), + tets2verts_(mesh_.ask_elem_verts()), + coords_(mesh_.coords()), + grid_(grid), + nelems_(mesh_.nelems()) + { + if (mesh_.dim() != 3) { + std::cerr << "GridTriIntersection3D currently only developed for 3D " + "triangular meshes\n"; + std::terminate(); + } + } + /// Two-pass functor. On the first pass we set the number of grid/triangle + /// intersections. On the second pass we fill the CSR array with the indexes + /// to the triangles intersecting with the current grid cell (row) + KOKKOS_INLINE_FUNCTION + LO operator()(LO row, LO* fill) const + { + const auto grid_cell_bbox = grid_(0).GetCellBBOX(row); + LO num_intersections = 0; + // hierarchical parallel may make be very beneficial here... + for (LO elem_idx = 0; elem_idx < nelems_; ++elem_idx) { + const auto elem_tet2verts = Omega_h::gather_verts<4>(tets2verts_, elem_idx); + // 2d mesh with 2d coords, but 3 triangles + const auto vertex_coords = Omega_h::gather_vectors<4, 3>(coords_, elem_tet2verts); + if (simplex_intersects_bbox<3>(vertex_coords, grid_cell_bbox)) { + if (fill) { + fill[num_intersections] = elem_idx; + } + ++num_intersections; + } + } + return num_intersections; + } + +private: + Omega_h::Mesh& mesh_; + Omega_h::LOs tets2verts_; + Omega_h::Reals coords_; + Kokkos::View grid_; +public: + LO nelems_; +}; + // num_grid_cells should be result of grid.GetNumCells(), take as argument to avoid extra copy // of grid from gpu to cpu Kokkos::Crs @@ -238,7 +320,7 @@ construct_intersection_map(Omega_h::Mesh& mesh, int num_grid_cells, Real fuzz) { Kokkos::Crs intersection_map{}; - auto f = detail::GridTriIntersectionFunctor{mesh, grid, fuzz}; + auto f = detail::GridTriIntersectionFunctor2D{mesh, grid, fuzz}; Kokkos::count_and_fill_crs(intersection_map, num_grid_cells, f); return intersection_map; } @@ -265,7 +347,7 @@ OMEGA_H_INLINE double myreduce(const Omega_h::Vector & x, Op op) OMEGA_H_NOEX Kokkos::View GridPointSearch2D::operator()(Kokkos::View points) const { - Kokkos::View results("point search result", points.extent(0)); + Kokkos::View results("point search result", points.extent(0)); auto num_rows = candidate_map_.numRows(); // needed so that we don't capture this ptr which will be memory error on cuda auto grid = grid_; @@ -287,7 +369,7 @@ Kokkos::View GridPointSearch2D::operator()(Kokkos::V bool found = false; auto nearest_triangle = candidates_begin; - auto dimensionality = GridPointSearch::Result::Dimensionality::EDGE; + auto dimensionality = GridPointSearch2D::Result::Dimensionality::EDGE; Omega_h::Real distance_to_nearest { INFINITY }; Omega_h::Vector<3> parametric_coords_to_nearest; // create array that's size of number of candidates x num coords to store @@ -300,7 +382,7 @@ Kokkos::View GridPointSearch2D::operator()(Kokkos::V auto parametric_coords = barycentric_from_global(point, vertex_coords); if (Omega_h::is_barycentric_inside(parametric_coords, fuzz)) { - results(p) = GridPointSearch::Result{GridPointSearch::Result::Dimensionality::FACE, triangleID, parametric_coords}; + results(p) = GridPointSearch2D::Result{GridPointSearch2D::Result::Dimensionality::FACE, triangleID, parametric_coords}; found = true; break; } @@ -329,7 +411,7 @@ Kokkos::View GridPointSearch2D::operator()(Kokkos::V if (distance_to_ab >= distance_to_nearest) { continue; } - dimensionality = GridPointSearch::Result::Dimensionality::EDGE; + dimensionality = GridPointSearch2D::Result::Dimensionality::EDGE; nearest_triangle = i; distance_to_nearest = distance_to_ab; parametric_coords_to_nearest = parametric_coords; @@ -344,7 +426,7 @@ Kokkos::View GridPointSearch2D::operator()(Kokkos::V Omega_h::get_vector<2>(coords, vertexID); if (const auto distance = Omega_h::norm(point - vertex);distance < distance_to_nearest) { - dimensionality = GridPointSearch::Result::Dimensionality::VERTEX; + dimensionality = GridPointSearch2D::Result::Dimensionality::VERTEX; nearest_triangle = i; distance_to_nearest = distance; parametric_coords_to_nearest = parametric_coords; @@ -353,14 +435,14 @@ Kokkos::View GridPointSearch2D::operator()(Kokkos::V } if(!found) { - results(p) = GridPointSearch::Result{dimensionality, -1 * candidate_map.entries(nearest_triangle), parametric_coords_to_nearest}; + results(p) = GridPointSearch2D::Result{dimensionality, -1 * candidate_map.entries(nearest_triangle), parametric_coords_to_nearest}; } }); return results; } -GridPointSearch<2>::GridPointSearch(Omega_h::Mesh& mesh, LO Nx, LO Ny, Real fuzz) +GridPointSearch2D::GridPointSearch2D(Omega_h::Mesh& mesh, LO Nx, LO Ny, Real fuzz) : fuzz_(fuzz) { auto mesh_bbox = Omega_h::get_bounding_box<2>(&mesh); diff --git a/src/pcms/point_search.h b/src/pcms/point_search.h index 11fad310..5759b9a0 100644 --- a/src/pcms/point_search.h +++ b/src/pcms/point_search.h @@ -46,28 +46,21 @@ class PointLocalizationSearch Omega_h::Vector parametric_coords; }; - static constexpr auto DIM = 2; + static constexpr auto DIM = dim; virtual Kokkos::View operator()(Kokkos::View point) const = 0; }; using PointLocalizationSearch2D = PointLocalizationSearch<2>; -template -class GridPointSearch : public PointLocalizationSearch -{ - static_assert(false, "Not implemented"); -}; - -template <> -class GridPointSearch<2> : public PointLocalizationSearch2D +class GridPointSearch2D : public PointLocalizationSearch2D { using CandidateMapT = Kokkos::Crs; public: using Result = PointLocalizationSearch2D::Result; - GridPointSearch(Omega_h::Mesh& mesh, LO Nx, LO Ny, Real fuzz = 1E-12); + GridPointSearch2D(Omega_h::Mesh& mesh, LO Nx, LO Ny, Real fuzz = 1E-12); /** * given a point in global coordinates give the id of the triangle that the * point lies within and the parametric coordinate of the point within the @@ -89,7 +82,5 @@ class GridPointSearch<2> : public PointLocalizationSearch2D Omega_h::Reals coords_; }; -using GridPointSearch2D = GridPointSearch<2>; - } // namespace detail #endif // PCMS_COUPLING_POINT_SEARCH_H diff --git a/src/pcms/uniform_grid.h b/src/pcms/uniform_grid.h index 95d7c66f..8a89049b 100644 --- a/src/pcms/uniform_grid.h +++ b/src/pcms/uniform_grid.h @@ -8,7 +8,7 @@ namespace pcms { -template +template struct UniformGrid { // Make private? @@ -134,7 +134,8 @@ struct UniformGrid } }; -using Uniform2DGrid = UniformGrid<>; +using Uniform2DGrid = UniformGrid<2>; +using Uniform3DGrid = UniformGrid<3>; /** * \brief Create a uniform grid layout from an Omega_h mesh as a bounding box From 29c2b0583c26b06b037975d28bd3c82a0f384384 Mon Sep 17 00:00:00 2001 From: Matthew McCall Date: Thu, 10 Apr 2025 22:26:19 -0400 Subject: [PATCH 04/27] Replace custom barycentric_from_global with Omega_h implementation Replaced the local implementation of barycentric_from_global with Omega_h's standard implementation. Removed the custom function from both source and header files, and updated all calls to use Omega_h::barycentric_from_global<2, 2>. This change enhances code maintainability and aligns functionality with the Omega_h library. --- src/pcms/point_search.cpp | 22 +++++----------------- src/pcms/point_search.h | 2 -- test/test_point_search.cpp | 5 ++--- 3 files changed, 7 insertions(+), 22 deletions(-) diff --git a/src/pcms/point_search.cpp b/src/pcms/point_search.cpp index 5077d22f..5b0737c7 100644 --- a/src/pcms/point_search.cpp +++ b/src/pcms/point_search.cpp @@ -151,13 +151,13 @@ bool within_bbox(const Omega_h::Vector coord, const AABBox & bbox) noe auto right = bbox.center[0] + bbox.half_width[0]; auto bot = bbox.center[1] - bbox.half_width[1]; auto top = bbox.center[1] + bbox.half_width[1]; - auto xi = barycentric_from_global({left,bot}, coords); + auto xi = Omega_h::barycentric_from_global<2, 2>({left,bot}, coords); if(Omega_h::is_barycentric_inside(xi, fuzz)){ return true;} - xi = barycentric_from_global({left,top}, coords); + xi = Omega_h::barycentric_from_global<2, 2>({left,top}, coords); if(Omega_h::is_barycentric_inside(xi, fuzz)){ return true;} - xi = barycentric_from_global({right,top}, coords); + xi = Omega_h::barycentric_from_global<2, 2>({right,top}, coords); if(Omega_h::is_barycentric_inside(xi, fuzz)){ return true;} - xi = barycentric_from_global({right,bot}, coords); + xi = Omega_h::barycentric_from_global<2, 2>({right,bot}, coords); if(Omega_h::is_barycentric_inside(xi, fuzz)){ return true;} return false; } @@ -326,18 +326,6 @@ construct_intersection_map(Omega_h::Mesh& mesh, } } // namespace detail -KOKKOS_FUNCTION -Omega_h::Vector<3> barycentric_from_global( - const Omega_h::Vector<2>& point, const Omega_h::Matrix<2, 3>& vertex_coords) -{ - const auto inverse_basis = - Omega_h::pseudo_invert(Omega_h::simplex_basis<2, 2>(vertex_coords)); - auto xi = inverse_basis * (point - vertex_coords[0]); - // note omega_h form_barycentric is currently broken. - // see https://github.com/sandialabs/omega_h/issues/389 - return {1 - xi[0] - xi[1], xi[0], xi[1]}; -} - template OMEGA_H_INLINE double myreduce(const Omega_h::Vector & x, Op op) OMEGA_H_NOEXCEPT { auto out = x[0]; @@ -379,7 +367,7 @@ Kokkos::View GridPointSearch2D::operator()(Kokkos::V const auto elem_tri2verts = Omega_h::gather_verts<3>(tris2verts, triangleID); // 2d mesh with 2d coords, but 3 triangles auto vertex_coords = Omega_h::gather_vectors<3, 2>(coords, elem_tri2verts); - auto parametric_coords = barycentric_from_global(point, vertex_coords); + auto parametric_coords = Omega_h::barycentric_from_global<2, 2>(point, vertex_coords); if (Omega_h::is_barycentric_inside(parametric_coords, fuzz)) { results(p) = GridPointSearch2D::Result{GridPointSearch2D::Result::Dimensionality::FACE, triangleID, parametric_coords}; diff --git a/src/pcms/point_search.h b/src/pcms/point_search.h index 5759b9a0..086067c7 100644 --- a/src/pcms/point_search.h +++ b/src/pcms/point_search.h @@ -22,8 +22,6 @@ construct_intersection_map(Omega_h::Mesh& mesh, int num_grid_cells, Real fuzz = 1E-12); } KOKKOS_FUNCTION -Omega_h::Vector<3> barycentric_from_global( - const Omega_h::Vector<2>& point, const Omega_h::Matrix<2, 3>& vertex_coords); [[nodiscard]] KOKKOS_FUNCTION bool triangle_intersects_bbox( const Omega_h::Matrix<2, 3>& coords, const AABBox<2>& bbox, diff --git a/test/test_point_search.cpp b/test/test_point_search.cpp index 7590efe3..6652e71a 100644 --- a/test/test_point_search.cpp +++ b/test/test_point_search.cpp @@ -5,7 +5,6 @@ #include using pcms::AABBox; -using pcms::barycentric_from_global; using pcms::Uniform2DGrid; TEST_CASE("global to local") @@ -14,7 +13,7 @@ TEST_CASE("global to local") SECTION("check verts") { for (int i = 0; i < coords.size(); ++i) { - auto xi = barycentric_from_global(coords[i], coords); + auto xi = Omega_h::barycentric_from_global<2, 2>(coords[i], coords); Omega_h::Vector<3> hand_xi{0, 0, 0}; hand_xi[i] = 1; printf("[%f,%f,%f] == [%f,%f,%f]\n", xi[0], xi[1], xi[2], hand_xi[0], @@ -25,7 +24,7 @@ TEST_CASE("global to local") SECTION("check point") { Omega_h::Vector<2> point{0.5, 0.5}; - auto xi = barycentric_from_global(point, coords); + auto xi = Omega_h::barycentric_from_global<2, 2>(point, coords); Omega_h::Vector<3> hand_xi{0.25, 0.25, 0.5}; printf("[%f,%f,%f] == [%f,%f,%f]\n", xi[0], xi[1], xi[2], hand_xi[0], hand_xi[1], hand_xi[2]); From c27347f422c268f9e3881d932961f22f8a1e42ba Mon Sep 17 00:00:00 2001 From: Matthew McCall Date: Fri, 11 Apr 2025 12:01:38 -0400 Subject: [PATCH 05/27] Add bbox_verts_within_simplex function and virtual destructor Introduce bbox_verts_within_simplex to check if an AABBox's vertices are within a simplex using barycentric coordinates. Also, add a virtual destructor to the PointLocalizationSearch class for proper cleanup in derived classes. --- src/pcms/point_search.cpp | 32 ++++++++++++++++++++++++++++++++ src/pcms/point_search.h | 1 + 2 files changed, 33 insertions(+) diff --git a/src/pcms/point_search.cpp b/src/pcms/point_search.cpp index 5b0737c7..bf3056b7 100644 --- a/src/pcms/point_search.cpp +++ b/src/pcms/point_search.cpp @@ -162,6 +162,38 @@ bool within_bbox(const Omega_h::Vector coord, const AABBox & bbox) noe return false; } +template +[[nodiscard]] KOKKOS_INLINE_FUNCTION +bool bbox_verts_within_simplex(const AABBox& bbox, const Omega_h::Matrix& coords) +{ + // each dimension has a pair of opposing "walls" + // 2D: { [left, right], [top, bottom] } -> { left, right, top, bottom } + // 3D: { [left, right], [top, bottom], [front, back] } -> { left, ..., back } + std::array bbox_walls {}; + for (int i = 0; i < dim; i++) { + bbox_walls[i * 2] = bbox.center[i] - bbox.half_width[i]; + bbox_walls[i * 2 + 1] = bbox.center[i] + bbox.half_width[i]; + } + + // 1 << dim == 2 ** dim == num vertices in ndim bounding box / hypercube + // Each vertex is a just a unique combination of walls + // eg [left, bottom] (2D) or [right, top, front] (3) + for (unsigned i = 0; i < 1 << dim; ++i) { + // conveniently, i acts a bit field representing the current combination + Omega_h::Vector vert; + for (unsigned j = 0; j < dim; ++j) { + // eg 110 = 6 = [left, top, back] + // eg 01 = 1 = [right, top] + vert[j] = (i >> j) & 1 ? bbox_walls[j * 2] : bbox_walls[j * 2 + 1]; + } + auto xi = Omega_h::barycentric_from_global(vert, coords); + if (Omega_h::is_barycentric_inside(xi, fuzz)) { + return true; + } + } + return false; +} + /** * Check if a triangle element represented by 3 coordinates in two dimensions * intersects with a bounding box diff --git a/src/pcms/point_search.h b/src/pcms/point_search.h index 086067c7..20f2aaaf 100644 --- a/src/pcms/point_search.h +++ b/src/pcms/point_search.h @@ -47,6 +47,7 @@ class PointLocalizationSearch static constexpr auto DIM = dim; virtual Kokkos::View operator()(Kokkos::View point) const = 0; + virtual ~PointLocalizationSearch() = default; }; using PointLocalizationSearch2D = PointLocalizationSearch<2>; From 379f87c3f59d9923b837f18fbef425f8b087f505 Mon Sep 17 00:00:00 2001 From: Matthew McCall Date: Fri, 11 Apr 2025 13:05:09 -0400 Subject: [PATCH 06/27] Refactor point search to support 3D grids. Renamed `construct_intersection_map` to `construct_intersection_map_2d` and added `construct_intersection_map_3d` for 3D support. Introduced `GridPointSearch3D` class, extending point localization for 3D grids with associated functionality and data structures. Updated tests to reflect these changes. --- src/pcms/point_search.cpp | 97 ++++++++++++++++++++++++++++++++++++-- src/pcms/point_search.h | 31 +++++++++++- test/test_point_search.cpp | 4 +- 3 files changed, 125 insertions(+), 7 deletions(-) diff --git a/src/pcms/point_search.cpp b/src/pcms/point_search.cpp index bf3056b7..1119e6c2 100644 --- a/src/pcms/point_search.cpp +++ b/src/pcms/point_search.cpp @@ -323,7 +323,6 @@ struct GridTriIntersectionFunctor3D // hierarchical parallel may make be very beneficial here... for (LO elem_idx = 0; elem_idx < nelems_; ++elem_idx) { const auto elem_tet2verts = Omega_h::gather_verts<4>(tets2verts_, elem_idx); - // 2d mesh with 2d coords, but 3 triangles const auto vertex_coords = Omega_h::gather_vectors<4, 3>(coords_, elem_tet2verts); if (simplex_intersects_bbox<3>(vertex_coords, grid_cell_bbox)) { if (fill) { @@ -347,7 +346,7 @@ struct GridTriIntersectionFunctor3D // num_grid_cells should be result of grid.GetNumCells(), take as argument to avoid extra copy // of grid from gpu to cpu Kokkos::Crs -construct_intersection_map(Omega_h::Mesh& mesh, +construct_intersection_map_2d(Omega_h::Mesh& mesh, Kokkos::View grid, int num_grid_cells, Real fuzz) { @@ -356,6 +355,15 @@ construct_intersection_map(Omega_h::Mesh& mesh, Kokkos::count_and_fill_crs(intersection_map, num_grid_cells, f); return intersection_map; } + +Kokkos::Crs +construct_intersection_map_3d(Omega_h::Mesh& mesh, Kokkos::View grid, int num_grid_cells) +{ + Kokkos::Crs intersection_map{}; + auto f = detail::GridTriIntersectionFunctor3D{mesh, grid}; + Kokkos::count_and_fill_crs(intersection_map, num_grid_cells, f); + return intersection_map; +} } // namespace detail template @@ -472,8 +480,89 @@ GridPointSearch2D::GridPointSearch2D(Omega_h::Mesh& mesh, LO Nx, LO Ny, Real fuz .bot_left = {mesh_bbox.min[0], mesh_bbox.min[1]}, .divisions = {Nx, Ny}}; Kokkos::deep_copy(grid_, grid_h); - candidate_map_ = detail::construct_intersection_map( - mesh, grid_, grid_h(0).GetNumCells(), fuzz_); + candidate_map_ = detail::construct_intersection_map_2d(mesh, grid_, grid_h(0).GetNumCells(), fuzz_); + coords_ = mesh.coords(); + tris2verts_ = mesh.ask_elem_verts(); + tris2edges_adj_ = mesh.ask_down(Omega_h::FACE, Omega_h::EDGE); + tris2verts_adj_ = mesh.ask_down(Omega_h::FACE, Omega_h::VERT); + edges2verts_adj_ = mesh.ask_down(Omega_h::EDGE, Omega_h::VERT); +} + +Kokkos::View GridPointSearch3D::operator()(Kokkos::View points) const +{ + Kokkos::View results("point search result", points.extent(0)); + auto num_rows = candidate_map_.numRows(); + // needed so that we don't capture this ptr which will be memory error on cuda + auto grid = grid_; + auto candidate_map = candidate_map_; + auto tris2verts = tris2verts_; + auto tris2verts_adj = tris2verts_adj_; + auto tris2edges_adj = tris2edges_adj_; + auto edges2verts_adj = edges2verts_adj_; + auto coords = coords_; + Kokkos::parallel_for(points.extent(0), KOKKOS_LAMBDA(int p) { + + Omega_h::Vector point; + for (int i = 0; i < DIM; ++i) { + point[i] = points(p, i); + } + + auto cell_id = grid(0).ClosestCellID(point); + assert(cell_id < num_rows && cell_id >= 0); + auto candidates_begin = candidate_map.row_map(cell_id); + auto candidates_end = candidate_map.row_map(cell_id + 1); + bool found = false; + + auto nearest_triangle = candidates_begin; + auto dimensionality = GridPointSearch3D::Result::Dimensionality::EDGE; + Omega_h::Real distance_to_nearest { INFINITY }; + Omega_h::Vector parametric_coords_to_nearest; + // create array that's size of number of candidates x num coords to store + // parametric inversion + for (auto i = candidates_begin; i < candidates_end; ++i) { + const int triangleID = candidate_map.entries(i); + const auto elem_tri2verts = Omega_h::gather_verts(tris2verts, triangleID); + auto vertex_coords = Omega_h::gather_vectors(coords, elem_tri2verts); + auto parametric_coords = Omega_h::barycentric_from_global(point, vertex_coords); + + if (Omega_h::is_barycentric_inside(parametric_coords, fuzz)) { + results(p) = GridPointSearch3D::Result{GridPointSearch3D::Result::Dimensionality::FACE, triangleID, parametric_coords}; + found = true; + break; + } + + // TODO: Get nearest element if no triangle found + } + if(!found) + { + results(p) = GridPointSearch3D::Result{dimensionality, -1 * candidate_map.entries(nearest_triangle), parametric_coords_to_nearest}; + } + }); + + return results; +} + +GridPointSearch3D::GridPointSearch3D(Omega_h::Mesh& mesh, LO Nx, LO Ny, LO Nz) +{ + auto mesh_bbox = Omega_h::get_bounding_box<3>(&mesh); + auto grid_h = Kokkos::create_mirror_view(grid_); + + std::array edge_lengths {}; + std::array bot_left {}; + + for (int i = 0; i < DIM; ++i) { + edge_lengths[i] = mesh_bbox.max[i] - mesh_bbox.min[i]; + bot_left[i] = mesh_bbox.min[i]; + } + + grid_h(0) = Uniform3DGrid { + .edge_length = edge_lengths, + .bot_left = bot_left, + .divisions = {Nx, Ny, Nz} + }; + + Kokkos::deep_copy(grid_, grid_h); + candidate_map_ = detail::construct_intersection_map_3d(mesh, grid_, grid_h(0).GetNumCells()); coords_ = mesh.coords(); tris2verts_ = mesh.ask_elem_verts(); tris2edges_adj_ = mesh.ask_down(Omega_h::FACE, Omega_h::EDGE); diff --git a/src/pcms/point_search.h b/src/pcms/point_search.h index 20f2aaaf..e1494b76 100644 --- a/src/pcms/point_search.h +++ b/src/pcms/point_search.h @@ -17,7 +17,7 @@ namespace pcms // used namespace detail { Kokkos::Crs -construct_intersection_map(Omega_h::Mesh& mesh, +construct_intersection_map_2d(Omega_h::Mesh& mesh, Kokkos::View grid, int num_grid_cells, Real fuzz = 1E-12); } @@ -51,6 +51,7 @@ class PointLocalizationSearch }; using PointLocalizationSearch2D = PointLocalizationSearch<2>; +using PointLocalizationSearch3D = PointLocalizationSearch<3>; class GridPointSearch2D : public PointLocalizationSearch2D { @@ -81,5 +82,33 @@ class GridPointSearch2D : public PointLocalizationSearch2D Omega_h::Reals coords_; }; +class GridPointSearch3D : public PointLocalizationSearch3D +{ + using CandidateMapT = Kokkos::Crs; + +public: + using Result = PointLocalizationSearch3D::Result; + + GridPointSearch3D(Omega_h::Mesh& mesh, LO Nx, LO Ny, LO Nz); + /** + * given a point in global coordinates give the id of the triangle that the + * point lies within and the parametric coordinate of the point within the + * triangle. If the point does not lie within any triangle element. Then the + * id will be a negative number and (TODO) will return a negative id of the + * closest element + */ + Kokkos::View operator()(Kokkos::View point) const override; + +private: + Omega_h::Mesh mesh_; + Omega_h::Adj tris2edges_adj_; + Omega_h::Adj tris2verts_adj_; + Omega_h::Adj edges2verts_adj_; + Kokkos::View[1]> grid_{"uniform grid"}; + CandidateMapT candidate_map_; + Omega_h::LOs tris2verts_; + Omega_h::Reals coords_; +}; + } // namespace detail #endif // PCMS_COUPLING_POINT_SEARCH_H diff --git a/test/test_point_search.cpp b/test/test_point_search.cpp index 6652e71a..20b16eb1 100644 --- a/test/test_point_search.cpp +++ b/test/test_point_search.cpp @@ -139,7 +139,7 @@ TEST_CASE("construct intersection map") auto grid_h = Kokkos::create_mirror_view(grid_d); grid_h(0) = Uniform2DGrid{.edge_length{1, 1}, .bot_left = {0, 0}, .divisions = {10, 10}}; Kokkos::deep_copy(grid_d, grid_h); - auto intersection_map = pcms::detail::construct_intersection_map(mesh, grid_d, grid_h(0).GetNumCells()); + auto intersection_map = pcms::detail::construct_intersection_map_2d(mesh, grid_d, grid_h(0).GetNumCells()); // assert(cudaSuccess == cudaDeviceSynchronize()); REQUIRE(intersection_map.numRows() == 100); REQUIRE(num_candidates_within_range(intersection_map, 2, 16)); @@ -151,7 +151,7 @@ TEST_CASE("construct intersection map") grid_h(0) = Uniform2DGrid{.edge_length{1, 1}, .bot_left = {0, 0}, .divisions = {60, 60}}; Kokkos::deep_copy(grid_d, grid_h); // require number of candidates is >=1 and <=6 - auto intersection_map = pcms::detail::construct_intersection_map(mesh, grid_d, grid_h(0).GetNumCells()); + auto intersection_map = pcms::detail::construct_intersection_map_2d(mesh, grid_d, grid_h(0).GetNumCells()); REQUIRE(intersection_map.numRows() == 3600); REQUIRE(num_candidates_within_range(intersection_map, 1, 6)); From 66e3a1488320f32fac0f26032d536e7b632dfb8f Mon Sep 17 00:00:00 2001 From: Matthew McCall Date: Fri, 18 Apr 2025 12:23:54 -0400 Subject: [PATCH 07/27] Add REGION dimensionality to point search Introduce a new REGION dimensionality to represent 3D regions in point search operations. Adjust loops and result assignment logic to incorporate this new dimension for improved handling of 3D cases. --- src/pcms/point_search.cpp | 6 ++++-- src/pcms/point_search.h | 3 ++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/pcms/point_search.cpp b/src/pcms/point_search.cpp index 1119e6c2..301b6749 100644 --- a/src/pcms/point_search.cpp +++ b/src/pcms/point_search.cpp @@ -176,9 +176,11 @@ bool bbox_verts_within_simplex(const AABBox& bbox, const Omega_h::Matrix vert; for (unsigned j = 0; j < dim; ++j) { @@ -526,7 +528,7 @@ Kokkos::View GridPointSearch3D::operator()(Kokkos::V auto parametric_coords = Omega_h::barycentric_from_global(point, vertex_coords); if (Omega_h::is_barycentric_inside(parametric_coords, fuzz)) { - results(p) = GridPointSearch3D::Result{GridPointSearch3D::Result::Dimensionality::FACE, triangleID, parametric_coords}; + results(p) = GridPointSearch3D::Result{GridPointSearch3D::Result::Dimensionality::REGION, triangleID, parametric_coords}; found = true; break; } diff --git a/src/pcms/point_search.h b/src/pcms/point_search.h index e1494b76..84d64a47 100644 --- a/src/pcms/point_search.h +++ b/src/pcms/point_search.h @@ -36,7 +36,8 @@ class PointLocalizationSearch { VERTEX = 0, EDGE = 1, - FACE = 2 + FACE = 2, + REGION = 3 }; Dimensionality dimensionality; From e0d2c53ffcfbe38330d12307b6d07ff91d1ad7da Mon Sep 17 00:00:00 2001 From: Matthew McCall Date: Wed, 8 Oct 2025 12:17:33 -0400 Subject: [PATCH 08/27] Refactor edge and vertex search loops in 2D grid point localization Simplified indentation and adjusted line breaks in edge and vertex search loops for improved readability. Added constructor overloads to `GridPointSearch2D` for handling tolerances via a new `PointSearchTolerances` abstraction. Updated `PointLocalizationSearch` class with support for tolerances. --- src/pcms/point_search.cpp | 62 ++++++++++++++++++++++----------------- src/pcms/point_search.h | 9 ++++++ 2 files changed, 44 insertions(+), 27 deletions(-) diff --git a/src/pcms/point_search.cpp b/src/pcms/point_search.cpp index 301b6749..074dfb76 100644 --- a/src/pcms/point_search.cpp +++ b/src/pcms/point_search.cpp @@ -417,34 +417,35 @@ Kokkos::View GridPointSearch2D::operator()(Kokkos::V break; } - for (int j = 0; j < 3; ++j) { - // Every triangle (face) is connected to 3 edges - const int edgeID = tris2edges_adj.ab2b[triangleID * 3 + j]; + for (int j = 0; j < 3; ++j) { + // Every triangle (face) is connected to 3 edges + const int edgeID = tris2edges_adj.ab2b[triangleID * 3 + j]; - auto vertex_a_id = edges2verts_adj.ab2b[edgeID * 2]; - auto vertex_b_id = edges2verts_adj.ab2b[edgeID * 2 + 1]; + auto vertex_a_id = edges2verts_adj.ab2b[edgeID * 2]; + auto vertex_b_id = edges2verts_adj.ab2b[edgeID * 2 + 1]; - auto vertex_a = Omega_h::get_vector<2>(coords, vertex_a_id); - auto vertex_b = Omega_h::get_vector<2>(coords, vertex_b_id); + auto vertex_a = Omega_h::get_vector<2>(coords, vertex_a_id); + auto vertex_b = Omega_h::get_vector<2>(coords, vertex_b_id); - if (!normal_intersects_segment(point, vertex_a, vertex_b)) continue; + if (!normal_intersects_segment(point, vertex_a, vertex_b)) + continue; - const auto xa = vertex_a[0]; - const auto ya = vertex_a[1]; - const auto xb = vertex_b[0]; - const auto yb = vertex_b[1]; + const auto xa = vertex_a[0]; + const auto ya = vertex_a[1]; + const auto xb = vertex_b[0]; + const auto yb = vertex_b[1]; - const auto xp = point[0]; - const auto yp = point[1]; + const auto xp = point[0]; + const auto yp = point[1]; - const auto distance_to_ab = distance_from_line(xp, yp, xa, ya, xb, yb); + const auto distance_to_ab = distance_from_line(xp, yp, xa, ya, xb, yb); - if (distance_to_ab >= distance_to_nearest) { continue; } + if (distance_to_ab >= distance_to_nearest) continue; - dimensionality = GridPointSearch2D::Result::Dimensionality::EDGE; - nearest_triangle = i; - distance_to_nearest = distance_to_ab; - parametric_coords_to_nearest = parametric_coords; + dimensionality = GridPointSearch2D::Result::Dimensionality::EDGE; + nearest_triangle = i; + distance_to_nearest = distance_to_ab; + parametric_coords_to_nearest = parametric_coords; } // Every triangle (face) is connected to 3 vertices @@ -455,12 +456,13 @@ Kokkos::View GridPointSearch2D::operator()(Kokkos::V const Omega_h::Few vertex = Omega_h::get_vector<2>(coords, vertexID); - if (const auto distance = Omega_h::norm(point - vertex);distance < distance_to_nearest) { - dimensionality = GridPointSearch2D::Result::Dimensionality::VERTEX; - nearest_triangle = i; - distance_to_nearest = distance; - parametric_coords_to_nearest = parametric_coords; - } + if (const auto distance = Omega_h::norm(point - vertex); + distance < distance_to_nearest) { + dimensionality = GridPointSearch2D::Result::Dimensionality::VERTEX; + nearest_triangle = i; + distance_to_nearest = distance; + parametric_coords_to_nearest = parametric_coords; + } } } if(!found) @@ -473,7 +475,13 @@ Kokkos::View GridPointSearch2D::operator()(Kokkos::V } GridPointSearch2D::GridPointSearch2D(Omega_h::Mesh& mesh, LO Nx, LO Ny, Real fuzz) - : fuzz_(fuzz) + : GridPointSearch2D(mesh, Nx, Ny, fuzz) +{ + Kokkos::deep_copy(tolerances_, 0); +} + +GridPointSearch2D::GridPointSearch2D(Omega_h::Mesh& mesh, LO Nx, LO Ny, PointSearchTolerances tolerances, Real fuzz) + : PointLocalizationSearch(tolerances) { auto mesh_bbox = Omega_h::get_bounding_box<2>(&mesh); auto grid_h = Kokkos::create_mirror_view(grid_); diff --git a/src/pcms/point_search.h b/src/pcms/point_search.h index 84d64a47..7beb02f3 100644 --- a/src/pcms/point_search.h +++ b/src/pcms/point_search.h @@ -47,8 +47,15 @@ class PointLocalizationSearch static constexpr auto DIM = dim; + using PointSearchTolerances = Kokkos::View; + + explicit PointLocalizationSearch(PointSearchTolerances tolerances) : tolerances_(tolerances){ } + virtual Kokkos::View operator()(Kokkos::View point) const = 0; virtual ~PointLocalizationSearch() = default; + +protected: + PointSearchTolerances tolerances_; }; using PointLocalizationSearch2D = PointLocalizationSearch<2>; @@ -62,6 +69,8 @@ class GridPointSearch2D : public PointLocalizationSearch2D using Result = PointLocalizationSearch2D::Result; GridPointSearch2D(Omega_h::Mesh& mesh, LO Nx, LO Ny, Real fuzz = 1E-12); + GridPointSearch2D(Omega_h::Mesh& mesh, LO Nx, LO Ny, PointSearchTolerances tolerances, Real fuzz = 1E-12); + /** * given a point in global coordinates give the id of the triangle that the * point lies within and the parametric coordinate of the point within the From 1beba367fa7ad10092d5f7fa90513a2a92691848 Mon Sep 17 00:00:00 2001 From: Matthew McCall Date: Wed, 8 Oct 2025 12:42:15 -0400 Subject: [PATCH 09/27] Reversed algorithm logic and implemented tolerances Previous algorithm checked edges then vertices. Now we check vertices, then edges. --- src/pcms/point_search.cpp | 55 +++++++++++++++++++++++---------------- 1 file changed, 32 insertions(+), 23 deletions(-) diff --git a/src/pcms/point_search.cpp b/src/pcms/point_search.cpp index 074dfb76..c9c47f78 100644 --- a/src/pcms/point_search.cpp +++ b/src/pcms/point_search.cpp @@ -417,6 +417,32 @@ Kokkos::View GridPointSearch2D::operator()(Kokkos::V break; } + // Every triangle (face) is connected to 3 vertices + for (int j = 0; j < 3; ++j) { + // Get the vertex ID from the connectivity array + const int vertexID = tris2verts_adj.ab2b[triangleID * 3 + j]; + // Get the vertex coordinates from the mesh using vertexID + const Omega_h::Few vertex = + Omega_h::get_vector<2>(coords, vertexID); + + const auto distance = Omega_h::norm(point - vertex); + if (distance > tolerances_[0]) continue; + + if (distance < distance_to_nearest) { + dimensionality = GridPointSearch2D::Result::Dimensionality::VERTEX; + nearest_triangle = i; + distance_to_nearest = distance; + parametric_coords_to_nearest = parametric_coords; + } + } + + if (dimensionality == Result::Dimensionality::VERTEX) { + results(p) = GridPointSearch2D::Result{dimensionality, -1 * candidate_map.entries(nearest_triangle), parametric_coords_to_nearest}; + continue; + } + + dimensionality = Result::Dimensionality::FACE; + for (int j = 0; j < 3; ++j) { // Every triangle (face) is connected to 3 edges const int edgeID = tris2edges_adj.ab2b[triangleID * 3 + j]; @@ -440,35 +466,18 @@ Kokkos::View GridPointSearch2D::operator()(Kokkos::V const auto distance_to_ab = distance_from_line(xp, yp, xa, ya, xb, yb); - if (distance_to_ab >= distance_to_nearest) continue; - - dimensionality = GridPointSearch2D::Result::Dimensionality::EDGE; - nearest_triangle = i; - distance_to_nearest = distance_to_ab; - parametric_coords_to_nearest = parametric_coords; - } + if (distance_to_ab > tolerances_[1]) continue; - // Every triangle (face) is connected to 3 vertices - for (int j = 0; j < 3; ++j) { - // Get the vertex ID from the connectivity array - const int vertexID = tris2verts_adj.ab2b[triangleID * 3 + j]; - // Get the vertex coordinates from the mesh using vertexID - const Omega_h::Few vertex = - Omega_h::get_vector<2>(coords, vertexID); - - if (const auto distance = Omega_h::norm(point - vertex); - distance < distance_to_nearest) { - dimensionality = GridPointSearch2D::Result::Dimensionality::VERTEX; + if (distance_to_ab < distance_to_nearest) { + dimensionality = GridPointSearch2D::Result::Dimensionality::EDGE; nearest_triangle = i; - distance_to_nearest = distance; + distance_to_nearest = distance_to_ab; parametric_coords_to_nearest = parametric_coords; } } } - if(!found) - { - results(p) = GridPointSearch2D::Result{dimensionality, -1 * candidate_map.entries(nearest_triangle), parametric_coords_to_nearest}; - } + + results(p) = GridPointSearch2D::Result{dimensionality, -1 * candidate_map.entries(nearest_triangle), parametric_coords_to_nearest}; }); return results; From fdcb8fcb571b368fe79d03725d630f6b339eec8c Mon Sep 17 00:00:00 2001 From: Matthew McCall Date: Thu, 30 Oct 2025 15:38:29 -0400 Subject: [PATCH 10/27] Refactor point search tolerances handling and rename Result fields Converted `PointSearchTolerances` to use `const` references for safer and more efficient handling. Added default tolerances initialization in 2D and 3D grid search constructors. Renamed `tri_id` to `element_id` in `Result` for improved clarity and consistency across dimensionalities. --- src/pcms/interpolator/adj_search.hpp | 6 +++--- src/pcms/point_search.cpp | 12 ++++++++++-- src/pcms/point_search.h | 17 ++++++++++++----- test/test_point_search.cpp | 8 ++++---- 4 files changed, 29 insertions(+), 14 deletions(-) diff --git a/src/pcms/interpolator/adj_search.hpp b/src/pcms/interpolator/adj_search.hpp index 25047b8f..85baa236 100644 --- a/src/pcms/interpolator/adj_search.hpp +++ b/src/pcms/interpolator/adj_search.hpp @@ -32,8 +32,8 @@ inline void checkTargetPoints( pcms::printInfo("INFO: Checking target points...\n"); auto check_target_points = OMEGA_H_LAMBDA(Omega_h::LO i) { - if (results(i).tri_id < 0) { - OMEGA_H_CHECK_PRINTF(results(i).tri_id >= 0, + if (results(i).element_id < 0) { + OMEGA_H_CHECK_PRINTF(results(i).element_id >= 0, "ERROR: Source cell id not found for target %d\n", i); printf("%d, ", i); @@ -130,7 +130,7 @@ inline void FindSupports::adjBasedSearch( Track visited; Omega_h::Real cutoffDistance = radii2[id]; - Omega_h::LO source_cell_id = results(id).tri_id; + Omega_h::LO source_cell_id = results(id).element_id; OMEGA_H_CHECK_PRINTF( source_cell_id >= 0, "ERROR: Source cell id not found for target %d (%f,%f)\n", id, diff --git a/src/pcms/point_search.cpp b/src/pcms/point_search.cpp index c9c47f78..aad4edef 100644 --- a/src/pcms/point_search.cpp +++ b/src/pcms/point_search.cpp @@ -484,12 +484,12 @@ Kokkos::View GridPointSearch2D::operator()(Kokkos::V } GridPointSearch2D::GridPointSearch2D(Omega_h::Mesh& mesh, LO Nx, LO Ny, Real fuzz) - : GridPointSearch2D(mesh, Nx, Ny, fuzz) + : GridPointSearch2D(mesh, Nx, Ny, PointSearchTolerances { "point search 2d tolerances" }, fuzz) { Kokkos::deep_copy(tolerances_, 0); } -GridPointSearch2D::GridPointSearch2D(Omega_h::Mesh& mesh, LO Nx, LO Ny, PointSearchTolerances tolerances, Real fuzz) +GridPointSearch2D::GridPointSearch2D(Omega_h::Mesh& mesh, LO Nx, LO Ny, const PointSearchTolerances& tolerances, Real fuzz) : PointLocalizationSearch(tolerances) { auto mesh_bbox = Omega_h::get_bounding_box<2>(&mesh); @@ -562,6 +562,14 @@ Kokkos::View GridPointSearch3D::operator()(Kokkos::V } GridPointSearch3D::GridPointSearch3D(Omega_h::Mesh& mesh, LO Nx, LO Ny, LO Nz) + : GridPointSearch3D(mesh, Nx, Ny, Nz, PointSearchTolerances { "point search 3d tolerances" }) +{ + Kokkos::deep_copy(tolerances_, 0); +} + +GridPointSearch3D::GridPointSearch3D(Omega_h::Mesh& mesh, LO Nx, LO Ny, LO Nz, + const PointSearchTolerances& tolerances) + : PointLocalizationSearch(tolerances) { auto mesh_bbox = Omega_h::get_bounding_box<3>(&mesh); auto grid_h = Kokkos::create_mirror_view(grid_); diff --git a/src/pcms/point_search.h b/src/pcms/point_search.h index 7beb02f3..83c45d24 100644 --- a/src/pcms/point_search.h +++ b/src/pcms/point_search.h @@ -1,11 +1,13 @@ #ifndef PCMS_COUPLING_POINT_SEARCH_H #define PCMS_COUPLING_POINT_SEARCH_H -#include +#include + #include #include -#include "pcms/utility/types.h" #include #include + +#include "pcms/utility/types.h" #include "pcms/uniform_grid.h" #include "pcms/bounding_box.h" @@ -41,7 +43,7 @@ class PointLocalizationSearch }; Dimensionality dimensionality; - LO tri_id; + LO element_id; Omega_h::Vector parametric_coords; }; @@ -49,7 +51,10 @@ class PointLocalizationSearch using PointSearchTolerances = Kokkos::View; - explicit PointLocalizationSearch(PointSearchTolerances tolerances) : tolerances_(tolerances){ } + explicit PointLocalizationSearch(const PointSearchTolerances& tolerances) : tolerances_(tolerances) + { + assert(tolerances_.is_allocated()); + } virtual Kokkos::View operator()(Kokkos::View point) const = 0; virtual ~PointLocalizationSearch() = default; @@ -69,7 +74,7 @@ class GridPointSearch2D : public PointLocalizationSearch2D using Result = PointLocalizationSearch2D::Result; GridPointSearch2D(Omega_h::Mesh& mesh, LO Nx, LO Ny, Real fuzz = 1E-12); - GridPointSearch2D(Omega_h::Mesh& mesh, LO Nx, LO Ny, PointSearchTolerances tolerances, Real fuzz = 1E-12); + GridPointSearch2D(Omega_h::Mesh& mesh, LO Nx, LO Ny, const PointSearchTolerances& tolerances, Real fuzz = 1E-12); /** * given a point in global coordinates give the id of the triangle that the @@ -100,6 +105,8 @@ class GridPointSearch3D : public PointLocalizationSearch3D using Result = PointLocalizationSearch3D::Result; GridPointSearch3D(Omega_h::Mesh& mesh, LO Nx, LO Ny, LO Nz); + GridPointSearch3D(Omega_h::Mesh& mesh, LO Nx, LO Ny, LO Nz, const PointSearchTolerances& tolerances); + /** * given a point in global coordinates give the id of the triangle that the * point lies within and the parametric coordinate of the point within the diff --git a/test/test_point_search.cpp b/test/test_point_search.cpp index 20b16eb1..54d1bb45 100644 --- a/test/test_point_search.cpp +++ b/test/test_point_search.cpp @@ -209,21 +209,21 @@ TEST_CASE("uniform grid search") { auto out_of_bounds = results_h(2); auto top_right = results_h(3); REQUIRE(out_of_bounds.dimensionality == GridPointSearch2D::Result::Dimensionality::VERTEX); - REQUIRE(-1*out_of_bounds.tri_id == top_right.tri_id); + REQUIRE(-1*out_of_bounds.element_id == top_right.element_id); out_of_bounds = results_h(4); auto bot_left = results_h(0); REQUIRE(out_of_bounds.dimensionality == GridPointSearch2D::Result::Dimensionality::VERTEX); - REQUIRE(-1*out_of_bounds.tri_id == bot_left.tri_id); + REQUIRE(-1*out_of_bounds.element_id == bot_left.element_id); out_of_bounds = results_h(5); REQUIRE(out_of_bounds.dimensionality == GridPointSearch2D::Result::Dimensionality::EDGE); - REQUIRE(-1 * out_of_bounds.tri_id == top_right.tri_id); + REQUIRE(-1 * out_of_bounds.element_id == top_right.element_id); out_of_bounds = results_h(6); REQUIRE(out_of_bounds.dimensionality == GridPointSearch2D::Result::Dimensionality::EDGE); - REQUIRE(-1 * out_of_bounds.tri_id == bot_left.tri_id); + REQUIRE(-1 * out_of_bounds.element_id == bot_left.element_id); } } From a5a8285bb41d60f672c1b2da27ebeeec7f2ee541 Mon Sep 17 00:00:00 2001 From: Matthew McCall Date: Thu, 30 Oct 2025 15:39:21 -0400 Subject: [PATCH 11/27] Refactor GridPointSearch2D result assignment logic and update tolerances handling Revised result assignment logic to incorporate detailed checks for vertex, edge, and face proximity, using new tolerances-based conditions. Updated tests to reflect changes in dimensionality and result handling for improved accuracy verification. --- src/pcms/point_search.cpp | 70 ++++++++++++++++++++++---------------- test/test_point_search.cpp | 14 ++++++-- 2 files changed, 52 insertions(+), 32 deletions(-) diff --git a/src/pcms/point_search.cpp b/src/pcms/point_search.cpp index aad4edef..37026e3e 100644 --- a/src/pcms/point_search.cpp +++ b/src/pcms/point_search.cpp @@ -387,19 +387,21 @@ Kokkos::View GridPointSearch2D::operator()(Kokkos::V auto tris2edges_adj = tris2edges_adj_; auto edges2verts_adj = edges2verts_adj_; auto coords = coords_; + auto tolerances = tolerances_; auto fuzz = fuzz_; - Kokkos::parallel_for( - points.extent(0), KOKKOS_LAMBDA(int p) { - Omega_h::Vector<2> point( - std::initializer_list{points(p, 0), points(p, 1)}); - auto cell_id = grid(0).ClosestCellID(point); - assert(cell_id < num_rows && cell_id >= 0); - auto candidates_begin = candidate_map.row_map(cell_id); - auto candidates_end = candidate_map.row_map(cell_id + 1); - bool found = false; + Kokkos::parallel_for(points.extent(0), KOKKOS_LAMBDA(int p) { + Omega_h::Vector<2> point(std::initializer_list{points(p,0), points(p,1)}); + auto cell_id = grid(0).ClosestCellID(point); + assert(cell_id < num_rows && cell_id >= 0); + auto candidates_begin = candidate_map.row_map(cell_id); + auto candidates_end = candidate_map.row_map(cell_id + 1); - auto nearest_triangle = candidates_begin; - auto dimensionality = GridPointSearch2D::Result::Dimensionality::EDGE; + bool vertex_found = false; + bool edge_found = false; + bool inside_cell = false; + + auto nearest_element_id = candidates_begin; + auto dimensionality = GridPointSearch2D::Result::Dimensionality::REGION; Omega_h::Real distance_to_nearest { INFINITY }; Omega_h::Vector<3> parametric_coords_to_nearest; // create array that's size of number of candidates x num coords to store @@ -411,12 +413,6 @@ Kokkos::View GridPointSearch2D::operator()(Kokkos::V auto vertex_coords = Omega_h::gather_vectors<3, 2>(coords, elem_tri2verts); auto parametric_coords = Omega_h::barycentric_from_global<2, 2>(point, vertex_coords); - if (Omega_h::is_barycentric_inside(parametric_coords, fuzz)) { - results(p) = GridPointSearch2D::Result{GridPointSearch2D::Result::Dimensionality::FACE, triangleID, parametric_coords}; - found = true; - break; - } - // Every triangle (face) is connected to 3 vertices for (int j = 0; j < 3; ++j) { // Get the vertex ID from the connectivity array @@ -426,22 +422,21 @@ Kokkos::View GridPointSearch2D::operator()(Kokkos::V Omega_h::get_vector<2>(coords, vertexID); const auto distance = Omega_h::norm(point - vertex); - if (distance > tolerances_[0]) continue; if (distance < distance_to_nearest) { dimensionality = GridPointSearch2D::Result::Dimensionality::VERTEX; - nearest_triangle = i; + nearest_element_id = vertexID; distance_to_nearest = distance; parametric_coords_to_nearest = parametric_coords; - } - } - if (dimensionality == Result::Dimensionality::VERTEX) { - results(p) = GridPointSearch2D::Result{dimensionality, -1 * candidate_map.entries(nearest_triangle), parametric_coords_to_nearest}; - continue; + if (distance < tolerances(0)) { + vertex_found = true; + }; + } } - dimensionality = Result::Dimensionality::FACE; + if (vertex_found) + break; for (int j = 0; j < 3; ++j) { // Every triangle (face) is connected to 3 edges @@ -466,18 +461,35 @@ Kokkos::View GridPointSearch2D::operator()(Kokkos::V const auto distance_to_ab = distance_from_line(xp, yp, xa, ya, xb, yb); - if (distance_to_ab > tolerances_[1]) continue; - if (distance_to_ab < distance_to_nearest) { + edge_found = true; dimensionality = GridPointSearch2D::Result::Dimensionality::EDGE; - nearest_triangle = i; + nearest_element_id = edgeID; distance_to_nearest = distance_to_ab; parametric_coords_to_nearest = parametric_coords; + + if (distance_to_ab < tolerances(1)) { + edge_found = true; + }; } } + + if (edge_found) + break; + + if (Omega_h::is_barycentric_inside(parametric_coords, fuzz)) { + dimensionality = GridPointSearch2D::Result::Dimensionality::FACE; + nearest_element_id = triangleID; + parametric_coords_to_nearest = parametric_coords; + inside_cell = true; + + // results(p) = GridPointSearch2D::Result{GridPointSearch2D::Result::Dimensionality::FACE, triangleID, parametric_coords}; + // return; + } } - results(p) = GridPointSearch2D::Result{dimensionality, -1 * candidate_map.entries(nearest_triangle), parametric_coords_to_nearest}; + const int inside_mesh = vertex_found || edge_found || inside_cell ? 1 : -1; + results(p) = GridPointSearch2D::Result{dimensionality, inside_mesh * nearest_element_id, parametric_coords_to_nearest}; }); return results; diff --git a/test/test_point_search.cpp b/test/test_point_search.cpp index 54d1bb45..80814ca4 100644 --- a/test/test_point_search.cpp +++ b/test/test_point_search.cpp @@ -163,7 +163,12 @@ TEST_CASE("uniform grid search") { auto world = lib.world(); auto mesh = Omega_h::build_box(world, OMEGA_H_SIMPLEX, 1, 1, 1, 10, 10, 0, false); - GridPointSearch2D search{mesh,10,10}; + auto tolerances = GridPointSearch2D::PointSearchTolerances { "point search 2d tolerances" }; + tolerances(0) = 0.01; + tolerances(1) = 0.01; + + GridPointSearch2D search{mesh,10,10, tolerances}; + Kokkos::View points("test_points", 7); //Kokkos::View points("test_points", 1); auto points_h = Kokkos::create_mirror_view(points); @@ -189,7 +194,10 @@ TEST_CASE("uniform grid search") { { { auto [dim, idx,coords] = results_h(0); - REQUIRE(dim == GridPointSearch2D::Result::Dimensionality::FACE); + + CAPTURE(idx); + + REQUIRE(dim == GridPointSearch2D::Result::Dimensionality::VERTEX); REQUIRE(idx == 0); REQUIRE(coords[0] == Catch::Approx(1)); REQUIRE(coords[1] == Catch::Approx(0)); @@ -197,7 +205,7 @@ TEST_CASE("uniform grid search") { } { auto [dim, idx,coords] = results_h(1); - REQUIRE(dim == GridPointSearch2D::Result::Dimensionality::FACE); + REQUIRE(dim == GridPointSearch2D::Result::Dimensionality::EDGE); REQUIRE(idx == 91); REQUIRE(coords[0] == Catch::Approx(0.5)); REQUIRE(coords[1] == Catch::Approx(0.1)); From 429e71f0421cd67436c2b3bf564e8f525b6aa104 Mon Sep 17 00:00:00 2001 From: Matthew McCall Date: Wed, 5 Nov 2025 14:54:50 -0500 Subject: [PATCH 12/27] Refactor edge handling in GridPointSearch2D and extend test cases Removed unused `edge_found` variable for cleaner logic in result assignment. Updated test cases to add new points and verify dimensionality, element IDs, and coordinates for various scenarios, enhancing accuracy and coverage. --- src/pcms/point_search.cpp | 1 - test/test_point_search.cpp | 13 ++++++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/pcms/point_search.cpp b/src/pcms/point_search.cpp index 37026e3e..42ea5ee8 100644 --- a/src/pcms/point_search.cpp +++ b/src/pcms/point_search.cpp @@ -462,7 +462,6 @@ Kokkos::View GridPointSearch2D::operator()(Kokkos::V const auto distance_to_ab = distance_from_line(xp, yp, xa, ya, xb, yb); if (distance_to_ab < distance_to_nearest) { - edge_found = true; dimensionality = GridPointSearch2D::Result::Dimensionality::EDGE; nearest_element_id = edgeID; distance_to_nearest = distance_to_ab; diff --git a/test/test_point_search.cpp b/test/test_point_search.cpp index 80814ca4..22f4fcff 100644 --- a/test/test_point_search.cpp +++ b/test/test_point_search.cpp @@ -169,7 +169,7 @@ TEST_CASE("uniform grid search") { GridPointSearch2D search{mesh,10,10, tolerances}; - Kokkos::View points("test_points", 7); + Kokkos::View points("test_points", 8); //Kokkos::View points("test_points", 1); auto points_h = Kokkos::create_mirror_view(points); points_h(0,0) = 0; @@ -186,6 +186,8 @@ TEST_CASE("uniform grid search") { points_h(5, 1) = 0.95; points_h(6, 0) = 0.05; points_h(6, 1) = -0.01; + points_h(7, 0) = 0.05; + points_h(7, 1) = 0.02; Kokkos::deep_copy(points, points_h); auto results = search(points); auto results_h = Kokkos::create_mirror_view(results); @@ -206,11 +208,16 @@ TEST_CASE("uniform grid search") { { auto [dim, idx,coords] = results_h(1); REQUIRE(dim == GridPointSearch2D::Result::Dimensionality::EDGE); - REQUIRE(idx == 91); + REQUIRE(idx == 156); REQUIRE(coords[0] == Catch::Approx(0.5)); REQUIRE(coords[1] == Catch::Approx(0.1)); REQUIRE(coords[2] == Catch::Approx(0.4)); } + { + auto [dim, idx,coords] = results_h(7); + REQUIRE(dim == GridPointSearch2D::Result::Dimensionality::FACE); + REQUIRE(idx == 0); + } } // feature needs to be added SECTION("Global coordinate outside mesh", "[!mayfail]") { @@ -227,7 +234,7 @@ TEST_CASE("uniform grid search") { out_of_bounds = results_h(5); REQUIRE(out_of_bounds.dimensionality == GridPointSearch2D::Result::Dimensionality::EDGE); - REQUIRE(-1 * out_of_bounds.element_id == top_right.element_id); + REQUIRE(out_of_bounds.element_id == 219); out_of_bounds = results_h(6); REQUIRE(out_of_bounds.dimensionality == From e2e1ca13157985d75d842ca70efa6498423e13a3 Mon Sep 17 00:00:00 2001 From: Matthew McCall Date: Wed, 5 Nov 2025 15:44:04 -0500 Subject: [PATCH 13/27] Ran clang format --- src/pcms/point_search.cpp | 381 +++++++++++++++++++++---------------- src/pcms/point_search.h | 29 ++- test/test_point_search.cpp | 70 ++++--- 3 files changed, 272 insertions(+), 208 deletions(-) diff --git a/src/pcms/point_search.cpp b/src/pcms/point_search.cpp index 42ea5ee8..e5056bab 100644 --- a/src/pcms/point_search.cpp +++ b/src/pcms/point_search.cpp @@ -2,29 +2,35 @@ #include #include -// From https://en.wikipedia.org/wiki/Distance_from_a_point_to_a_line#Line_defined_by_two_points +// From +// https://en.wikipedia.org/wiki/Distance_from_a_point_to_a_line#Line_defined_by_two_points KOKKOS_INLINE_FUNCTION -double distance_from_line(const double x0, const double y0, const double x1, const double y1, const double x2, const double y2) +double distance_from_line(const double x0, const double y0, const double x1, + const double y1, const double x2, const double y2) { - const Omega_h::Vector<2> p1 = { x1, y1 }; - const Omega_h::Vector<2> p2 = { x2, y2 }; + const Omega_h::Vector<2> p1 = {x1, y1}; + const Omega_h::Vector<2> p2 = {x2, y2}; auto disp = p2 - p1; - return std::abs(disp[1]*x0 - disp[0]*y0 + x2*y1 - y2*x1) / Omega_h::norm(disp); + return std::abs(disp[1] * x0 - disp[0] * y0 + x2 * y1 - y2 * x1) / + Omega_h::norm(disp); } -// Law of Cosines, where a, b, c and gamma are defined here: https://en.wikipedia.org/wiki/Law_of_cosines#Use_in_solving_triangles +// Law of Cosines, where a, b, c and gamma are defined here: +// https://en.wikipedia.org/wiki/Law_of_cosines#Use_in_solving_triangles KOKKOS_INLINE_FUNCTION double angle_from_side_lengths(const double a, const double b, const double c) { - return std::acos((a*a + b*b - c*c) / 2*a*b); + return std::acos((a * a + b * b - c * c) / 2 * a * b); } KOKKOS_INLINE_FUNCTION -bool normal_intersects_segment(const Omega_h::Few a, const Omega_h::Few b, const Omega_h::Few c) +bool normal_intersects_segment(const Omega_h::Few a, + const Omega_h::Few b, + const Omega_h::Few c) { const auto ab_len = Omega_h::norm(a - b); - const auto bc_len = Omega_h::norm(b -c); + const auto bc_len = Omega_h::norm(b - c); const auto ac_len = Omega_h::norm(a - c); const double angle1 = angle_from_side_lengths(bc_len, ac_len, ab_len); @@ -135,11 +141,14 @@ bool line_intersects_bbox(const Omega_h::Vector<2>& p0, } template -[[nodiscard]] KOKKOS_INLINE_FUNCTION -bool within_bbox(const Omega_h::Vector coord, const AABBox & bbox) noexcept { +[[nodiscard]] KOKKOS_INLINE_FUNCTION bool within_bbox( + const Omega_h::Vector coord, const AABBox& bbox) noexcept +{ for (int i = 0; i < dim; ++i) { - if (coord[i] < bbox.center[i] - bbox.half_width[i]) return false; - if (coord[i] > bbox.center[i] + bbox.half_width[i]) return false; + if (coord[i] < bbox.center[i] - bbox.half_width[i]) + return false; + if (coord[i] > bbox.center[i] + bbox.half_width[i]) + return false; } return true; } @@ -151,25 +160,33 @@ bool within_bbox(const Omega_h::Vector coord, const AABBox & bbox) noe auto right = bbox.center[0] + bbox.half_width[0]; auto bot = bbox.center[1] - bbox.half_width[1]; auto top = bbox.center[1] + bbox.half_width[1]; - auto xi = Omega_h::barycentric_from_global<2, 2>({left,bot}, coords); - if(Omega_h::is_barycentric_inside(xi, fuzz)){ return true;} - xi = Omega_h::barycentric_from_global<2, 2>({left,top}, coords); - if(Omega_h::is_barycentric_inside(xi, fuzz)){ return true;} - xi = Omega_h::barycentric_from_global<2, 2>({right,top}, coords); - if(Omega_h::is_barycentric_inside(xi, fuzz)){ return true;} - xi = Omega_h::barycentric_from_global<2, 2>({right,bot}, coords); - if(Omega_h::is_barycentric_inside(xi, fuzz)){ return true;} + auto xi = Omega_h::barycentric_from_global<2, 2>({left, bot}, coords); + if (Omega_h::is_barycentric_inside(xi, fuzz)) { + return true; + } + xi = Omega_h::barycentric_from_global<2, 2>({left, top}, coords); + if (Omega_h::is_barycentric_inside(xi, fuzz)) { + return true; + } + xi = Omega_h::barycentric_from_global<2, 2>({right, top}, coords); + if (Omega_h::is_barycentric_inside(xi, fuzz)) { + return true; + } + xi = Omega_h::barycentric_from_global<2, 2>({right, bot}, coords); + if (Omega_h::is_barycentric_inside(xi, fuzz)) { + return true; + } return false; } template -[[nodiscard]] KOKKOS_INLINE_FUNCTION -bool bbox_verts_within_simplex(const AABBox& bbox, const Omega_h::Matrix& coords) +[[nodiscard]] KOKKOS_INLINE_FUNCTION bool bbox_verts_within_simplex( + const AABBox& bbox, const Omega_h::Matrix& coords) { // each dimension has a pair of opposing "walls" // 2D: { [left, right], [top, bottom] } -> { left, right, top, bottom } // 3D: { [left, right], [top, bottom], [front, back] } -> { left, ..., back } - std::array bbox_walls {}; + std::array bbox_walls{}; for (int i = 0; i < dim; i++) { bbox_walls[i * 2] = bbox.center[i] - bbox.half_width[i]; bbox_walls[i * 2 + 1] = bbox.center[i] + bbox.half_width[i]; @@ -207,8 +224,7 @@ KOKKOS_FUNCTION bool triangle_intersects_bbox( // triangle and grid cell bounding box intersect if (intersects(triangle_bbox(coords), bbox)) { // if any of the triangle verts inside of bbox - if (within_bbox<2>(coords[0], bbox) || - within_bbox<2>(coords[1], bbox) || + if (within_bbox<2>(coords[0], bbox) || within_bbox<2>(coords[1], bbox) || within_bbox<2>(coords[2], bbox)) { return true; } @@ -228,8 +244,8 @@ KOKKOS_FUNCTION bool triangle_intersects_bbox( template [[nodiscard]] -KOKKOS_FUNCTION -bool simplex_intersects_bbox(const Omega_h::Matrix& coords, const AABBox& bbox) +KOKKOS_FUNCTION bool simplex_intersects_bbox( + const Omega_h::Matrix& coords, const AABBox& bbox) { return intersects(simplex_bbox(coords), bbox); // TODO: Add refined cases from triangle_intersects_bbox @@ -274,7 +290,8 @@ struct GridTriIntersectionFunctor2D LO num_intersections = 0; // hierarchical parallel may make be very beneficial here... for (LO elem_idx = 0; elem_idx < nelems_; ++elem_idx) { - const auto elem_tri2verts = Omega_h::gather_verts<3>(tris2verts_, elem_idx); + const auto elem_tri2verts = + Omega_h::gather_verts<3>(tris2verts_, elem_idx); // 2d mesh with 2d coords, but 3 triangles const auto vertex_coords = Omega_h::gather_vectors<3, 2>(coords_, elem_tri2verts); @@ -301,7 +318,8 @@ struct GridTriIntersectionFunctor2D struct GridTriIntersectionFunctor3D { - GridTriIntersectionFunctor3D(Omega_h::Mesh& mesh, Kokkos::View grid) + GridTriIntersectionFunctor3D(Omega_h::Mesh& mesh, + Kokkos::View grid) : mesh_(mesh), tets2verts_(mesh_.ask_elem_verts()), coords_(mesh_.coords()), @@ -324,8 +342,10 @@ struct GridTriIntersectionFunctor3D LO num_intersections = 0; // hierarchical parallel may make be very beneficial here... for (LO elem_idx = 0; elem_idx < nelems_; ++elem_idx) { - const auto elem_tet2verts = Omega_h::gather_verts<4>(tets2verts_, elem_idx); - const auto vertex_coords = Omega_h::gather_vectors<4, 3>(coords_, elem_tet2verts); + const auto elem_tet2verts = + Omega_h::gather_verts<4>(tets2verts_, elem_idx); + const auto vertex_coords = + Omega_h::gather_vectors<4, 3>(coords_, elem_tet2verts); if (simplex_intersects_bbox<3>(vertex_coords, grid_cell_bbox)) { if (fill) { fill[num_intersections] = elem_idx; @@ -341,12 +361,13 @@ struct GridTriIntersectionFunctor3D Omega_h::LOs tets2verts_; Omega_h::Reals coords_; Kokkos::View grid_; + public: LO nelems_; }; -// num_grid_cells should be result of grid.GetNumCells(), take as argument to avoid extra copy -// of grid from gpu to cpu +// num_grid_cells should be result of grid.GetNumCells(), take as argument to +// avoid extra copy of grid from gpu to cpu Kokkos::Crs construct_intersection_map_2d(Omega_h::Mesh& mesh, Kokkos::View grid, @@ -359,7 +380,9 @@ construct_intersection_map_2d(Omega_h::Mesh& mesh, } Kokkos::Crs -construct_intersection_map_3d(Omega_h::Mesh& mesh, Kokkos::View grid, int num_grid_cells) +construct_intersection_map_3d(Omega_h::Mesh& mesh, + Kokkos::View grid, + int num_grid_cells) { Kokkos::Crs intersection_map{}; auto f = detail::GridTriIntersectionFunctor3D{mesh, grid}; @@ -368,16 +391,21 @@ construct_intersection_map_3d(Omega_h::Mesh& mesh, Kokkos::View -OMEGA_H_INLINE double myreduce(const Omega_h::Vector & x, Op op) OMEGA_H_NOEXCEPT { +template +OMEGA_H_INLINE double myreduce(const Omega_h::Vector& x, + Op op) OMEGA_H_NOEXCEPT +{ auto out = x[0]; - for (int i = 1; i < n; ++i) out = op(out, x[i]); + for (int i = 1; i < n; ++i) + out = op(out, x[i]); return out; } -Kokkos::View GridPointSearch2D::operator()(Kokkos::View points) const +Kokkos::View GridPointSearch2D::operator()( + Kokkos::View points) const { - Kokkos::View results("point search result", points.extent(0)); + Kokkos::View results("point search result", + points.extent(0)); auto num_rows = candidate_map_.numRows(); // needed so that we don't capture this ptr which will be memory error on cuda auto grid = grid_; @@ -396,100 +424,108 @@ Kokkos::View GridPointSearch2D::operator()(Kokkos::V auto candidates_begin = candidate_map.row_map(cell_id); auto candidates_end = candidate_map.row_map(cell_id + 1); - bool vertex_found = false; - bool edge_found = false; - bool inside_cell = false; - - auto nearest_element_id = candidates_begin; - auto dimensionality = GridPointSearch2D::Result::Dimensionality::REGION; - Omega_h::Real distance_to_nearest { INFINITY }; - Omega_h::Vector<3> parametric_coords_to_nearest; - // create array that's size of number of candidates x num coords to store - // parametric inversion - for (auto i = candidates_begin; i < candidates_end; ++i) { - const int triangleID = candidate_map.entries(i); - const auto elem_tri2verts = Omega_h::gather_verts<3>(tris2verts, triangleID); - // 2d mesh with 2d coords, but 3 triangles - auto vertex_coords = Omega_h::gather_vectors<3, 2>(coords, elem_tri2verts); - auto parametric_coords = Omega_h::barycentric_from_global<2, 2>(point, vertex_coords); - - // Every triangle (face) is connected to 3 vertices - for (int j = 0; j < 3; ++j) { - // Get the vertex ID from the connectivity array - const int vertexID = tris2verts_adj.ab2b[triangleID * 3 + j]; - // Get the vertex coordinates from the mesh using vertexID - const Omega_h::Few vertex = - Omega_h::get_vector<2>(coords, vertexID); - - const auto distance = Omega_h::norm(point - vertex); - - if (distance < distance_to_nearest) { - dimensionality = GridPointSearch2D::Result::Dimensionality::VERTEX; - nearest_element_id = vertexID; - distance_to_nearest = distance; - parametric_coords_to_nearest = parametric_coords; - - if (distance < tolerances(0)) { - vertex_found = true; - }; + bool vertex_found = false; + bool edge_found = false; + bool inside_cell = false; + + auto nearest_element_id = candidates_begin; + auto dimensionality = GridPointSearch2D::Result::Dimensionality::REGION; + Omega_h::Real distance_to_nearest{INFINITY}; + Omega_h::Vector<3> parametric_coords_to_nearest; + // create array that's size of number of candidates x num coords to store + // parametric inversion + for (auto i = candidates_begin; i < candidates_end; ++i) { + const int triangleID = candidate_map.entries(i); + const auto elem_tri2verts = + Omega_h::gather_verts<3>(tris2verts, triangleID); + // 2d mesh with 2d coords, but 3 triangles + auto vertex_coords = + Omega_h::gather_vectors<3, 2>(coords, elem_tri2verts); + auto parametric_coords = + Omega_h::barycentric_from_global<2, 2>(point, vertex_coords); + + // Every triangle (face) is connected to 3 vertices + for (int j = 0; j < 3; ++j) { + // Get the vertex ID from the connectivity array + const int vertexID = tris2verts_adj.ab2b[triangleID * 3 + j]; + // Get the vertex coordinates from the mesh using vertexID + const Omega_h::Few vertex = + Omega_h::get_vector<2>(coords, vertexID); + + const auto distance = Omega_h::norm(point - vertex); + + if (distance < distance_to_nearest) { + dimensionality = GridPointSearch2D::Result::Dimensionality::VERTEX; + nearest_element_id = vertexID; + distance_to_nearest = distance; + parametric_coords_to_nearest = parametric_coords; + + if (distance < tolerances(0)) { + vertex_found = true; + }; + } } - } - if (vertex_found) - break; + if (vertex_found) + break; - for (int j = 0; j < 3; ++j) { - // Every triangle (face) is connected to 3 edges - const int edgeID = tris2edges_adj.ab2b[triangleID * 3 + j]; + for (int j = 0; j < 3; ++j) { + // Every triangle (face) is connected to 3 edges + const int edgeID = tris2edges_adj.ab2b[triangleID * 3 + j]; - auto vertex_a_id = edges2verts_adj.ab2b[edgeID * 2]; - auto vertex_b_id = edges2verts_adj.ab2b[edgeID * 2 + 1]; + auto vertex_a_id = edges2verts_adj.ab2b[edgeID * 2]; + auto vertex_b_id = edges2verts_adj.ab2b[edgeID * 2 + 1]; - auto vertex_a = Omega_h::get_vector<2>(coords, vertex_a_id); - auto vertex_b = Omega_h::get_vector<2>(coords, vertex_b_id); + auto vertex_a = Omega_h::get_vector<2>(coords, vertex_a_id); + auto vertex_b = Omega_h::get_vector<2>(coords, vertex_b_id); - if (!normal_intersects_segment(point, vertex_a, vertex_b)) - continue; + if (!normal_intersects_segment(point, vertex_a, vertex_b)) + continue; - const auto xa = vertex_a[0]; - const auto ya = vertex_a[1]; - const auto xb = vertex_b[0]; - const auto yb = vertex_b[1]; + const auto xa = vertex_a[0]; + const auto ya = vertex_a[1]; + const auto xb = vertex_b[0]; + const auto yb = vertex_b[1]; - const auto xp = point[0]; - const auto yp = point[1]; + const auto xp = point[0]; + const auto yp = point[1]; - const auto distance_to_ab = distance_from_line(xp, yp, xa, ya, xb, yb); + const auto distance_to_ab = + distance_from_line(xp, yp, xa, ya, xb, yb); - if (distance_to_ab < distance_to_nearest) { - dimensionality = GridPointSearch2D::Result::Dimensionality::EDGE; - nearest_element_id = edgeID; - distance_to_nearest = distance_to_ab; - parametric_coords_to_nearest = parametric_coords; + if (distance_to_ab < distance_to_nearest) { + dimensionality = GridPointSearch2D::Result::Dimensionality::EDGE; + nearest_element_id = edgeID; + distance_to_nearest = distance_to_ab; + parametric_coords_to_nearest = parametric_coords; - if (distance_to_ab < tolerances(1)) { - edge_found = true; - }; + if (distance_to_ab < tolerances(1)) { + edge_found = true; + }; + } } - } - if (edge_found) - break; + if (edge_found) + break; - if (Omega_h::is_barycentric_inside(parametric_coords, fuzz)) { - dimensionality = GridPointSearch2D::Result::Dimensionality::FACE; - nearest_element_id = triangleID; - parametric_coords_to_nearest = parametric_coords; - inside_cell = true; + if (Omega_h::is_barycentric_inside(parametric_coords, fuzz)) { + dimensionality = GridPointSearch2D::Result::Dimensionality::FACE; + nearest_element_id = triangleID; + parametric_coords_to_nearest = parametric_coords; + inside_cell = true; - // results(p) = GridPointSearch2D::Result{GridPointSearch2D::Result::Dimensionality::FACE, triangleID, parametric_coords}; - // return; + // results(p) = + // GridPointSearch2D::Result{GridPointSearch2D::Result::Dimensionality::FACE, + // triangleID, parametric_coords}; return; + } } - } - const int inside_mesh = vertex_found || edge_found || inside_cell ? 1 : -1; - results(p) = GridPointSearch2D::Result{dimensionality, inside_mesh * nearest_element_id, parametric_coords_to_nearest}; - }); + const int inside_mesh = + vertex_found || edge_found || inside_cell ? 1 : -1; + results(p) = GridPointSearch2D::Result{dimensionality, + inside_mesh * nearest_element_id, + parametric_coords_to_nearest}; + }); return results; } @@ -505,10 +541,11 @@ GridPointSearch2D::GridPointSearch2D(Omega_h::Mesh& mesh, LO Nx, LO Ny, const Po { auto mesh_bbox = Omega_h::get_bounding_box<2>(&mesh); auto grid_h = Kokkos::create_mirror_view(grid_); - grid_h(0) = Uniform2DGrid{.edge_length = {mesh_bbox.max[0] - mesh_bbox.min[0], - mesh_bbox.max[1] - mesh_bbox.min[1]}, - .bot_left = {mesh_bbox.min[0], mesh_bbox.min[1]}, - .divisions = {Nx, Ny}}; + grid_h(0) = + Uniform2DGrid{.edge_length = {mesh_bbox.max[0] - mesh_bbox.min[0], + mesh_bbox.max[1] - mesh_bbox.min[1]}, + .bot_left = {mesh_bbox.min[0], mesh_bbox.min[1]}, + .divisions = {Nx, Ny}}; Kokkos::deep_copy(grid_, grid_h); candidate_map_ = detail::construct_intersection_map_2d(mesh, grid_, grid_h(0).GetNumCells(), fuzz_); coords_ = mesh.coords(); @@ -518,9 +555,11 @@ GridPointSearch2D::GridPointSearch2D(Omega_h::Mesh& mesh, LO Nx, LO Ny, const Po edges2verts_adj_ = mesh.ask_down(Omega_h::EDGE, Omega_h::VERT); } -Kokkos::View GridPointSearch3D::operator()(Kokkos::View points) const +Kokkos::View GridPointSearch3D::operator()( + Kokkos::View points) const { - Kokkos::View results("point search result", points.extent(0)); + Kokkos::View results("point search result", + points.extent(0)); auto num_rows = candidate_map_.numRows(); // needed so that we don't capture this ptr which will be memory error on cuda auto grid = grid_; @@ -530,77 +569,83 @@ Kokkos::View GridPointSearch3D::operator()(Kokkos::V auto tris2edges_adj = tris2edges_adj_; auto edges2verts_adj = edges2verts_adj_; auto coords = coords_; - Kokkos::parallel_for(points.extent(0), KOKKOS_LAMBDA(int p) { + Kokkos::parallel_for( + points.extent(0), KOKKOS_LAMBDA(int p) { + Omega_h::Vector point; + for (int i = 0; i < DIM; ++i) { + point[i] = points(p, i); + } - Omega_h::Vector point; - for (int i = 0; i < DIM; ++i) { - point[i] = points(p, i); - } + auto cell_id = grid(0).ClosestCellID(point); + assert(cell_id < num_rows && cell_id >= 0); + auto candidates_begin = candidate_map.row_map(cell_id); + auto candidates_end = candidate_map.row_map(cell_id + 1); + bool found = false; + + auto nearest_triangle = candidates_begin; + auto dimensionality = GridPointSearch3D::Result::Dimensionality::EDGE; + Omega_h::Real distance_to_nearest{INFINITY}; + Omega_h::Vector parametric_coords_to_nearest; + // create array that's size of number of candidates x num coords to store + // parametric inversion + for (auto i = candidates_begin; i < candidates_end; ++i) { + const int triangleID = candidate_map.entries(i); + const auto elem_tri2verts = + Omega_h::gather_verts(tris2verts, triangleID); + auto vertex_coords = + Omega_h::gather_vectors(coords, elem_tri2verts); + auto parametric_coords = + Omega_h::barycentric_from_global(point, vertex_coords); + + if (Omega_h::is_barycentric_inside(parametric_coords, fuzz)) { + results(p) = GridPointSearch3D::Result{ + GridPointSearch3D::Result::Dimensionality::REGION, triangleID, + parametric_coords}; + found = true; + break; + } - auto cell_id = grid(0).ClosestCellID(point); - assert(cell_id < num_rows && cell_id >= 0); - auto candidates_begin = candidate_map.row_map(cell_id); - auto candidates_end = candidate_map.row_map(cell_id + 1); - bool found = false; - - auto nearest_triangle = candidates_begin; - auto dimensionality = GridPointSearch3D::Result::Dimensionality::EDGE; - Omega_h::Real distance_to_nearest { INFINITY }; - Omega_h::Vector parametric_coords_to_nearest; - // create array that's size of number of candidates x num coords to store - // parametric inversion - for (auto i = candidates_begin; i < candidates_end; ++i) { - const int triangleID = candidate_map.entries(i); - const auto elem_tri2verts = Omega_h::gather_verts(tris2verts, triangleID); - auto vertex_coords = Omega_h::gather_vectors(coords, elem_tri2verts); - auto parametric_coords = Omega_h::barycentric_from_global(point, vertex_coords); - - if (Omega_h::is_barycentric_inside(parametric_coords, fuzz)) { - results(p) = GridPointSearch3D::Result{GridPointSearch3D::Result::Dimensionality::REGION, triangleID, parametric_coords}; - found = true; - break; + // TODO: Get nearest element if no triangle found } - - // TODO: Get nearest element if no triangle found - } - if(!found) - { - results(p) = GridPointSearch3D::Result{dimensionality, -1 * candidate_map.entries(nearest_triangle), parametric_coords_to_nearest}; - } - }); + if (!found) { + results(p) = GridPointSearch3D::Result{ + dimensionality, -1 * candidate_map.entries(nearest_triangle), + parametric_coords_to_nearest}; + } + }); return results; } GridPointSearch3D::GridPointSearch3D(Omega_h::Mesh& mesh, LO Nx, LO Ny, LO Nz) - : GridPointSearch3D(mesh, Nx, Ny, Nz, PointSearchTolerances { "point search 3d tolerances" }) + : GridPointSearch3D(mesh, Nx, Ny, Nz, + PointSearchTolerances{"point search 3d tolerances"}) { Kokkos::deep_copy(tolerances_, 0); } GridPointSearch3D::GridPointSearch3D(Omega_h::Mesh& mesh, LO Nx, LO Ny, LO Nz, const PointSearchTolerances& tolerances) - : PointLocalizationSearch(tolerances) + : PointLocalizationSearch(tolerances) { auto mesh_bbox = Omega_h::get_bounding_box<3>(&mesh); auto grid_h = Kokkos::create_mirror_view(grid_); - std::array edge_lengths {}; - std::array bot_left {}; + std::array edge_lengths{}; + std::array bot_left{}; for (int i = 0; i < DIM; ++i) { edge_lengths[i] = mesh_bbox.max[i] - mesh_bbox.min[i]; bot_left[i] = mesh_bbox.min[i]; } - grid_h(0) = Uniform3DGrid { - .edge_length = edge_lengths, - .bot_left = bot_left, - .divisions = {Nx, Ny, Nz} - }; + grid_h(0) = Uniform3DGrid{.edge_length = edge_lengths, + .bot_left = bot_left, + .divisions = {Nx, Ny, Nz}}; Kokkos::deep_copy(grid_, grid_h); - candidate_map_ = detail::construct_intersection_map_3d(mesh, grid_, grid_h(0).GetNumCells()); + candidate_map_ = + detail::construct_intersection_map_3d(mesh, grid_, grid_h(0).GetNumCells()); coords_ = mesh.coords(); tris2verts_ = mesh.ask_elem_verts(); tris2edges_adj_ = mesh.ask_down(Omega_h::FACE, Omega_h::EDGE); diff --git a/src/pcms/point_search.h b/src/pcms/point_search.h index 83c45d24..365668da 100644 --- a/src/pcms/point_search.h +++ b/src/pcms/point_search.h @@ -17,7 +17,8 @@ namespace pcms // TODO take a bounding box as we may want a bbox that's bigger than the mesh! // this function is in the public header for testing, but should not be directly // used -namespace detail { +namespace detail +{ Kokkos::Crs construct_intersection_map_2d(Omega_h::Mesh& mesh, Kokkos::View grid, @@ -33,7 +34,8 @@ template class PointLocalizationSearch { public: - struct Result { + struct Result + { enum class Dimensionality { VERTEX = 0, @@ -51,12 +53,14 @@ class PointLocalizationSearch using PointSearchTolerances = Kokkos::View; - explicit PointLocalizationSearch(const PointSearchTolerances& tolerances) : tolerances_(tolerances) + explicit PointLocalizationSearch(const PointSearchTolerances& tolerances) + : tolerances_(tolerances) { assert(tolerances_.is_allocated()); } - virtual Kokkos::View operator()(Kokkos::View point) const = 0; + virtual Kokkos::View operator()( + Kokkos::View point) const = 0; virtual ~PointLocalizationSearch() = default; protected: @@ -68,7 +72,8 @@ using PointLocalizationSearch3D = PointLocalizationSearch<3>; class GridPointSearch2D : public PointLocalizationSearch2D { - using CandidateMapT = Kokkos::Crs; + using CandidateMapT = + Kokkos::Crs; public: using Result = PointLocalizationSearch2D::Result; @@ -83,7 +88,8 @@ class GridPointSearch2D : public PointLocalizationSearch2D * id will be a negative number and (TODO) will return a negative id of the * closest element */ - Kokkos::View operator()(Kokkos::View point) const override; + Kokkos::View operator()( + Kokkos::View point) const override; private: Omega_h::Mesh mesh_; @@ -99,13 +105,15 @@ class GridPointSearch2D : public PointLocalizationSearch2D class GridPointSearch3D : public PointLocalizationSearch3D { - using CandidateMapT = Kokkos::Crs; + using CandidateMapT = + Kokkos::Crs; public: using Result = PointLocalizationSearch3D::Result; GridPointSearch3D(Omega_h::Mesh& mesh, LO Nx, LO Ny, LO Nz); - GridPointSearch3D(Omega_h::Mesh& mesh, LO Nx, LO Ny, LO Nz, const PointSearchTolerances& tolerances); + GridPointSearch3D(Omega_h::Mesh& mesh, LO Nx, LO Ny, LO Nz, + const PointSearchTolerances& tolerances); /** * given a point in global coordinates give the id of the triangle that the @@ -114,7 +122,8 @@ class GridPointSearch3D : public PointLocalizationSearch3D * id will be a negative number and (TODO) will return a negative id of the * closest element */ - Kokkos::View operator()(Kokkos::View point) const override; + Kokkos::View operator()( + Kokkos::View point) const override; private: Omega_h::Mesh mesh_; @@ -127,5 +136,5 @@ class GridPointSearch3D : public PointLocalizationSearch3D Omega_h::Reals coords_; }; -} // namespace detail +} // namespace pcms #endif // PCMS_COUPLING_POINT_SEARCH_H diff --git a/test/test_point_search.cpp b/test/test_point_search.cpp index 22f4fcff..01093144 100644 --- a/test/test_point_search.cpp +++ b/test/test_point_search.cpp @@ -111,7 +111,8 @@ bool num_candidates_within_range(const T& intersection_map, pcms::LO min, Kokkos::parallel_reduce( intersection_map.numRows(), KOKKOS_LAMBDA(const int i, result_type& update) { - auto num_candidates = intersection_map.row_map(i + 1) - intersection_map.row_map(i); + auto num_candidates = + intersection_map.row_map(i + 1) - intersection_map.row_map(i); if (num_candidates > update.max_val) update.max_val = num_candidates; if (num_candidates < update.min_val) @@ -123,11 +124,11 @@ bool num_candidates_within_range(const T& intersection_map, pcms::LO min, std::cerr << result.min_val << ' ' << result.max_val << '\n'; return within_range; } -//extern Omega_h::Library omega_h_library; +// extern Omega_h::Library omega_h_library; TEST_CASE("construct intersection map") { - //auto world = omega_h_library.world(); + // auto world = omega_h_library.world(); auto lib = Omega_h::Library{}; auto world = lib.world(); auto mesh = @@ -137,9 +138,11 @@ TEST_CASE("construct intersection map") { Kokkos::View grid_d("uniform grid"); auto grid_h = Kokkos::create_mirror_view(grid_d); - grid_h(0) = Uniform2DGrid{.edge_length{1, 1}, .bot_left = {0, 0}, .divisions = {10, 10}}; + grid_h(0) = Uniform2DGrid{ + .edge_length{1, 1}, .bot_left = {0, 0}, .divisions = {10, 10}}; Kokkos::deep_copy(grid_d, grid_h); - auto intersection_map = pcms::detail::construct_intersection_map_2d(mesh, grid_d, grid_h(0).GetNumCells()); + auto intersection_map = pcms::detail::construct_intersection_map_2d( + mesh, grid_d, grid_h(0).GetNumCells()); // assert(cudaSuccess == cudaDeviceSynchronize()); REQUIRE(intersection_map.numRows() == 100); REQUIRE(num_candidates_within_range(intersection_map, 2, 16)); @@ -148,40 +151,44 @@ TEST_CASE("construct intersection map") { Kokkos::View grid_d("uniform grid"); auto grid_h = Kokkos::create_mirror_view(grid_d); - grid_h(0) = Uniform2DGrid{.edge_length{1, 1}, .bot_left = {0, 0}, .divisions = {60, 60}}; + grid_h(0) = Uniform2DGrid{ + .edge_length{1, 1}, .bot_left = {0, 0}, .divisions = {60, 60}}; Kokkos::deep_copy(grid_d, grid_h); // require number of candidates is >=1 and <=6 - auto intersection_map = pcms::detail::construct_intersection_map_2d(mesh, grid_d, grid_h(0).GetNumCells()); + auto intersection_map = pcms::detail::construct_intersection_map_2d( + mesh, grid_d, grid_h(0).GetNumCells()); REQUIRE(intersection_map.numRows() == 3600); REQUIRE(num_candidates_within_range(intersection_map, 1, 6)); } } -TEST_CASE("uniform grid search") { +TEST_CASE("uniform grid search") +{ using pcms::GridPointSearch2D; auto lib = Omega_h::Library{}; auto world = lib.world(); auto mesh = Omega_h::build_box(world, OMEGA_H_SIMPLEX, 1, 1, 1, 10, 10, 0, false); - auto tolerances = GridPointSearch2D::PointSearchTolerances { "point search 2d tolerances" }; + auto tolerances = + GridPointSearch2D::PointSearchTolerances{"point search 2d tolerances"}; tolerances(0) = 0.01; tolerances(1) = 0.01; - GridPointSearch2D search{mesh,10,10, tolerances}; + GridPointSearch2D search{mesh, 10, 10, tolerances}; - Kokkos::View points("test_points", 8); - //Kokkos::View points("test_points", 1); + Kokkos::View points("test_points", 8); + // Kokkos::View points("test_points", 1); auto points_h = Kokkos::create_mirror_view(points); - points_h(0,0) = 0; - points_h(0,1) = 0; - points_h(1,0) = 0.55; - points_h(1,1) = 0.54; - points_h(2,0) = 100; - points_h(2,1) = 100; - points_h(3,0) = 1; - points_h(3,1) = 1; - points_h(4,0) = -1; - points_h(4,1) = -1; + points_h(0, 0) = 0; + points_h(0, 1) = 0; + points_h(1, 0) = 0.55; + points_h(1, 1) = 0.54; + points_h(2, 0) = 100; + points_h(2, 1) = 100; + points_h(3, 0) = 1; + points_h(3, 1) = 1; + points_h(4, 0) = -1; + points_h(4, 1) = -1; points_h(5, 0) = 1.01; points_h(5, 1) = 0.95; points_h(6, 0) = 0.05; @@ -195,7 +202,7 @@ TEST_CASE("uniform grid search") { SECTION("global coordinate within mesh") { { - auto [dim, idx,coords] = results_h(0); + auto [dim, idx, coords] = results_h(0); CAPTURE(idx); @@ -206,7 +213,7 @@ TEST_CASE("uniform grid search") { REQUIRE(coords[2] == Catch::Approx(0)); } { - auto [dim, idx,coords] = results_h(1); + auto [dim, idx, coords] = results_h(1); REQUIRE(dim == GridPointSearch2D::Result::Dimensionality::EDGE); REQUIRE(idx == 156); REQUIRE(coords[0] == Catch::Approx(0.5)); @@ -214,22 +221,25 @@ TEST_CASE("uniform grid search") { REQUIRE(coords[2] == Catch::Approx(0.4)); } { - auto [dim, idx,coords] = results_h(7); + auto [dim, idx, coords] = results_h(7); REQUIRE(dim == GridPointSearch2D::Result::Dimensionality::FACE); REQUIRE(idx == 0); } } // feature needs to be added - SECTION("Global coordinate outside mesh", "[!mayfail]") { + SECTION("Global coordinate outside mesh", "[!mayfail]") + { auto out_of_bounds = results_h(2); auto top_right = results_h(3); - REQUIRE(out_of_bounds.dimensionality == GridPointSearch2D::Result::Dimensionality::VERTEX); - REQUIRE(-1*out_of_bounds.element_id == top_right.element_id); + REQUIRE(out_of_bounds.dimensionality == + GridPointSearch2D::Result::Dimensionality::VERTEX); + REQUIRE(-1 * out_of_bounds.element_id == top_right.element_id); out_of_bounds = results_h(4); auto bot_left = results_h(0); - REQUIRE(out_of_bounds.dimensionality == GridPointSearch2D::Result::Dimensionality::VERTEX); - REQUIRE(-1*out_of_bounds.element_id == bot_left.element_id); + REQUIRE(out_of_bounds.dimensionality == + GridPointSearch2D::Result::Dimensionality::VERTEX); + REQUIRE(-1 * out_of_bounds.element_id == bot_left.element_id); out_of_bounds = results_h(5); REQUIRE(out_of_bounds.dimensionality == From 8a33f77550dcc589209d6836e0dd985e0c2a4078 Mon Sep 17 00:00:00 2001 From: Matthew McCall Date: Wed, 5 Nov 2025 15:49:16 -0500 Subject: [PATCH 14/27] fix narrowing conversion --- src/pcms/point_search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pcms/point_search.cpp b/src/pcms/point_search.cpp index e5056bab..bf70fac9 100644 --- a/src/pcms/point_search.cpp +++ b/src/pcms/point_search.cpp @@ -186,7 +186,7 @@ template // each dimension has a pair of opposing "walls" // 2D: { [left, right], [top, bottom] } -> { left, right, top, bottom } // 3D: { [left, right], [top, bottom], [front, back] } -> { left, ..., back } - std::array bbox_walls{}; + std::array bbox_walls{}; for (int i = 0; i < dim; i++) { bbox_walls[i * 2] = bbox.center[i] - bbox.half_width[i]; bbox_walls[i * 2 + 1] = bbox.center[i] + bbox.half_width[i]; From 4a725bb4bd23ba4a3ae9f57c38ab4280d577ccb1 Mon Sep 17 00:00:00 2001 From: Matthew McCall Date: Mon, 10 Nov 2025 00:16:48 -0500 Subject: [PATCH 15/27] Correctly initialize initial min and max for n dimensions Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/pcms/point_search.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/pcms/point_search.cpp b/src/pcms/point_search.cpp index bf70fac9..bdf7dd4e 100644 --- a/src/pcms/point_search.cpp +++ b/src/pcms/point_search.cpp @@ -59,9 +59,12 @@ AABBox<2> triangle_bbox(const Omega_h::Matrix<2, 3>& coords) template AABBox simplex_bbox(const Omega_h::Matrix& coords) { - std::array max{coords(0, 0), coords(1, 0)}; - std::array min{coords(0, 0), coords(1, 0)}; - + std::array max; + std::array min; + for (int j = 0; j < dim; ++j) { + max[j] = coords(j, 0); + min[j] = coords(j, 0); + } for (int i = 1; i < dim + 1; ++i) { for (int j = 0; j < dim; ++j) { max[j] = std::fmax(max[j], coords(j, i)); From 12a4dc80b7430beb2832c9b319a17755bf9275d5 Mon Sep 17 00:00:00 2001 From: Matthew McCall Date: Mon, 10 Nov 2025 00:17:34 -0500 Subject: [PATCH 16/27] correct wording for 3D meshes Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> correct wording for 3D meshes Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/pcms/point_search.cpp | 2 +- src/pcms/point_search.h | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/pcms/point_search.cpp b/src/pcms/point_search.cpp index bdf7dd4e..cb9092d2 100644 --- a/src/pcms/point_search.cpp +++ b/src/pcms/point_search.cpp @@ -331,7 +331,7 @@ struct GridTriIntersectionFunctor3D { if (mesh_.dim() != 3) { std::cerr << "GridTriIntersection3D currently only developed for 3D " - "triangular meshes\n"; + "tetrahedral meshes\n"; std::terminate(); } } diff --git a/src/pcms/point_search.h b/src/pcms/point_search.h index 365668da..7b65d325 100644 --- a/src/pcms/point_search.h +++ b/src/pcms/point_search.h @@ -116,11 +116,11 @@ class GridPointSearch3D : public PointLocalizationSearch3D const PointSearchTolerances& tolerances); /** - * given a point in global coordinates give the id of the triangle that the - * point lies within and the parametric coordinate of the point within the - * triangle. If the point does not lie within any triangle element. Then the + * Given a point in global coordinates, returns the id of the tetrahedron (3D element) + * that the point lies within and the parametric coordinate of the point within the + * tetrahedron. If the point does not lie within any tetrahedron element, then the * id will be a negative number and (TODO) will return a negative id of the - * closest element + * closest element. */ Kokkos::View operator()( Kokkos::View point) const override; From e57b3923a0328141d9cd38cf260b8a675a01a076 Mon Sep 17 00:00:00 2001 From: Matthew McCall Date: Mon, 10 Nov 2025 00:24:38 -0500 Subject: [PATCH 17/27] update TODO comment in omega_h_field.h --- src/pcms/adapter/omega_h/omega_h_field.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pcms/adapter/omega_h/omega_h_field.h b/src/pcms/adapter/omega_h/omega_h_field.h index 14d2945a..865220d1 100644 --- a/src/pcms/adapter/omega_h/omega_h_field.h +++ b/src/pcms/adapter/omega_h/omega_h_field.h @@ -219,7 +219,7 @@ class OmegaHField private: std::string name_; Omega_h::Mesh& mesh_; - // TODO make this a pointer and introduce base class to Search for alternative search methods + // TODO: introduce base class to Search for alternative search methods std::unique_ptr search_; // bitmask array that specifies a filter on the field Omega_h::Read mask_; From f21e70e1581b23adcffc905f26ead74498eaf752 Mon Sep 17 00:00:00 2001 From: Matthew McCall Date: Wed, 12 Nov 2025 12:12:15 -0500 Subject: [PATCH 18/27] Update OmegaHField2 to use GridPointSearch2D Replaced `GridPointSearch` with `GridPointSearch2D` in `omega_h_field2` for improved dimensionality handling. --- src/pcms/adapter/omega_h/omega_h_field2.cpp | 2 +- src/pcms/adapter/omega_h/omega_h_field2.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pcms/adapter/omega_h/omega_h_field2.cpp b/src/pcms/adapter/omega_h/omega_h_field2.cpp index aab42910..af231a5e 100644 --- a/src/pcms/adapter/omega_h/omega_h_field2.cpp +++ b/src/pcms/adapter/omega_h/omega_h_field2.cpp @@ -161,7 +161,7 @@ struct OmegaHField2LocalizationHint { OmegaHField2LocalizationHint( Omega_h::Mesh& mesh, - Kokkos::View search_results, + Kokkos::View search_results, OutOfBoundsMode mode) : mode_(mode), num_valid_(0), num_missing_(0) { diff --git a/src/pcms/adapter/omega_h/omega_h_field2.h b/src/pcms/adapter/omega_h/omega_h_field2.h index fa6330eb..1f126e52 100644 --- a/src/pcms/adapter/omega_h/omega_h_field2.h +++ b/src/pcms/adapter/omega_h/omega_h_field2.h @@ -61,7 +61,7 @@ class OmegaHField2 : public FieldT const OmegaHFieldLayout& layout_; Omega_h::Mesh& mesh_; std::unique_ptr mesh_field_; - GridPointSearch search_; + GridPointSearch2D search_; Kokkos::View dof_holder_data_; }; From df4442ef195a1cb98db39311b7a1c1ec550c84cf Mon Sep 17 00:00:00 2001 From: Matthew McCall Date: Wed, 19 Nov 2025 14:14:05 -0500 Subject: [PATCH 19/27] Apply minor formatting adjustments and line splits in point_search and omega_h files --- src/pcms/adapter/omega_h/omega_h_field.h | 3 ++- src/pcms/point_search.h | 10 +++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/pcms/adapter/omega_h/omega_h_field.h b/src/pcms/adapter/omega_h/omega_h_field.h index 865220d1..53af1256 100644 --- a/src/pcms/adapter/omega_h/omega_h_field.h +++ b/src/pcms/adapter/omega_h/omega_h_field.h @@ -160,7 +160,8 @@ class OmegaHField [[nodiscard]] auto Search(Kokkos::View points) const { PCMS_FUNCTION_TIMER; - PCMS_ALWAYS_ASSERT(search_ != nullptr && "search data structure must be constructed before use"); + PCMS_ALWAYS_ASSERT(search_ != nullptr && + "search data structure must be constructed before use"); return (*search_)(points); } diff --git a/src/pcms/point_search.h b/src/pcms/point_search.h index 7b65d325..ca37c808 100644 --- a/src/pcms/point_search.h +++ b/src/pcms/point_search.h @@ -116,11 +116,11 @@ class GridPointSearch3D : public PointLocalizationSearch3D const PointSearchTolerances& tolerances); /** - * Given a point in global coordinates, returns the id of the tetrahedron (3D element) - * that the point lies within and the parametric coordinate of the point within the - * tetrahedron. If the point does not lie within any tetrahedron element, then the - * id will be a negative number and (TODO) will return a negative id of the - * closest element. + * Given a point in global coordinates, returns the id of the tetrahedron (3D + * element) that the point lies within and the parametric coordinate of the + * point within the tetrahedron. If the point does not lie within any + * tetrahedron element, then the id will be a negative number and (TODO) will + * return a negative id of the closest element. */ Kokkos::View operator()( Kokkos::View point) const override; From da4ca6182b95d4893797f28b9406f3d01e440c0e Mon Sep 17 00:00:00 2001 From: Matthew McCall Date: Wed, 19 Nov 2025 14:36:15 -0500 Subject: [PATCH 20/27] Remove unused `KOKKOS_FUNCTION` annotation and correct TODO comment in point_search files --- src/pcms/point_search.cpp | 2 +- src/pcms/point_search.h | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/pcms/point_search.cpp b/src/pcms/point_search.cpp index cb9092d2..144120e6 100644 --- a/src/pcms/point_search.cpp +++ b/src/pcms/point_search.cpp @@ -608,7 +608,7 @@ Kokkos::View GridPointSearch3D::operator()( break; } - // TODO: Get nearest element if no triangle found + // TODO: Get nearest element if no tetrahedron found } if (!found) { results(p) = GridPointSearch3D::Result{ diff --git a/src/pcms/point_search.h b/src/pcms/point_search.h index ca37c808..693aebfa 100644 --- a/src/pcms/point_search.h +++ b/src/pcms/point_search.h @@ -24,7 +24,6 @@ construct_intersection_map_2d(Omega_h::Mesh& mesh, Kokkos::View grid, int num_grid_cells, Real fuzz = 1E-12); } -KOKKOS_FUNCTION [[nodiscard]] KOKKOS_FUNCTION bool triangle_intersects_bbox( const Omega_h::Matrix<2, 3>& coords, const AABBox<2>& bbox, From 33625cf98aa78bf1d8204885fb64bddae17f5683 Mon Sep 17 00:00:00 2001 From: Matthew McCall Date: Wed, 10 Dec 2025 11:15:24 -0500 Subject: [PATCH 21/27] Replace `GridPointSearch` with `GridPointSearch2D` in `omega_h_field2` for 2D-specific compatibility improvements and clarity --- src/pcms/adapter/omega_h/omega_h_field2.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/pcms/adapter/omega_h/omega_h_field2.cpp b/src/pcms/adapter/omega_h/omega_h_field2.cpp index af231a5e..36db8729 100644 --- a/src/pcms/adapter/omega_h/omega_h_field2.cpp +++ b/src/pcms/adapter/omega_h/omega_h_field2.cpp @@ -97,11 +97,11 @@ struct ComputeOffsetsFunctor struct CountPointsPerElementFunctor { Kokkos::View elem_counts_; - Kokkos::View search_results_; + Kokkos::View search_results_; CountPointsPerElementFunctor( Kokkos::View elem_counts, - Kokkos::View search_results) + Kokkos::View search_results) : elem_counts_(elem_counts), search_results_(search_results) { } @@ -121,14 +121,14 @@ struct FillCoordinatesAndIndicesFunctor Kokkos::View offsets_; Kokkos::View coordinates_; Kokkos::View indices_; - Kokkos::View search_results_; + Kokkos::View search_results_; Omega_h::Int dim_; FillCoordinatesAndIndicesFunctor( Omega_h::Mesh& mesh, Kokkos::View elem_counts, Kokkos::View offsets, Kokkos::View coordinates, Kokkos::View indices, - Kokkos::View search_results) + Kokkos::View search_results) : mesh_(mesh), elem_counts_(elem_counts), offsets_(offsets), @@ -355,7 +355,7 @@ LocalizationHint OmegaHField2::GetLocalizationHint( coordinates.data_handle(), coordinates.extent(0), coordinates.extent(1)); deep_copy_mismatch_layouts(coords, coordinates_host); auto results = search_(coords); - Kokkos::View results_h( + Kokkos::View results_h( "results_h", results.size()); Kokkos::deep_copy(results_h, results); auto hint = std::make_shared( From 0c7fbcd796f54a996dbe8732685041a6e64a0a46 Mon Sep 17 00:00:00 2001 From: Matthew McCall Date: Wed, 10 Dec 2025 12:49:32 -0500 Subject: [PATCH 22/27] Refactor `distance_from_line` to use vector-based formulation and simplify call sites in `point_search.cpp`. Fix test expectation in `test_point_search.cpp`. --- src/pcms/point_search.cpp | 25 +++++++------------------ test/test_point_search.cpp | 2 +- 2 files changed, 8 insertions(+), 19 deletions(-) diff --git a/src/pcms/point_search.cpp b/src/pcms/point_search.cpp index 144120e6..1af7841f 100644 --- a/src/pcms/point_search.cpp +++ b/src/pcms/point_search.cpp @@ -3,17 +3,14 @@ #include // From -// https://en.wikipedia.org/wiki/Distance_from_a_point_to_a_line#Line_defined_by_two_points +// https://en.wikipedia.org/wiki/Distance_from_a_point_to_a_line#Vector_formulation +template KOKKOS_INLINE_FUNCTION -double distance_from_line(const double x0, const double y0, const double x1, - const double y1, const double x2, const double y2) +Omega_h::Real distance_from_line(const Omega_h::Vector& a, const Omega_h::Vector& b, const Omega_h::Vector& p) { - const Omega_h::Vector<2> p1 = {x1, y1}; - const Omega_h::Vector<2> p2 = {x2, y2}; - auto disp = p2 - p1; - - return std::abs(disp[1] * x0 - disp[0] * y0 + x2 * y1 - y2 * x1) / - Omega_h::norm(disp); + Omega_h::Vector n = Omega_h::normalize(b - a); + Omega_h::Vector ap = a - p; + return Omega_h::norm(ap - (ap*n)*n); } // Law of Cosines, where a, b, c and gamma are defined here: @@ -485,16 +482,8 @@ Kokkos::View GridPointSearch2D::operator()( if (!normal_intersects_segment(point, vertex_a, vertex_b)) continue; - const auto xa = vertex_a[0]; - const auto ya = vertex_a[1]; - const auto xb = vertex_b[0]; - const auto yb = vertex_b[1]; - - const auto xp = point[0]; - const auto yp = point[1]; - const auto distance_to_ab = - distance_from_line(xp, yp, xa, ya, xb, yb); + distance_from_line(vertex_a, vertex_b, point); if (distance_to_ab < distance_to_nearest) { dimensionality = GridPointSearch2D::Result::Dimensionality::EDGE; diff --git a/test/test_point_search.cpp b/test/test_point_search.cpp index 01093144..002f6d3a 100644 --- a/test/test_point_search.cpp +++ b/test/test_point_search.cpp @@ -244,7 +244,7 @@ TEST_CASE("uniform grid search") out_of_bounds = results_h(5); REQUIRE(out_of_bounds.dimensionality == GridPointSearch2D::Result::Dimensionality::EDGE); - REQUIRE(out_of_bounds.element_id == 219); + REQUIRE(out_of_bounds.element_id == -219); out_of_bounds = results_h(6); REQUIRE(out_of_bounds.dimensionality == From 63b9cd94ca8ef419aaee0489637d91d3712ada43 Mon Sep 17 00:00:00 2001 From: Matthew McCall Date: Tue, 10 Feb 2026 14:34:13 -0500 Subject: [PATCH 23/27] Refactor `normal_intersects_segment` to use vector-based approach, simplify logic, and update call sites in `point_search.cpp`. --- src/pcms/point_search.cpp | 30 ++++++++++-------------------- 1 file changed, 10 insertions(+), 20 deletions(-) diff --git a/src/pcms/point_search.cpp b/src/pcms/point_search.cpp index 1af7841f..b32fbe22 100644 --- a/src/pcms/point_search.cpp +++ b/src/pcms/point_search.cpp @@ -13,27 +13,17 @@ Omega_h::Real distance_from_line(const Omega_h::Vector& a, const Omega_h::V return Omega_h::norm(ap - (ap*n)*n); } -// Law of Cosines, where a, b, c and gamma are defined here: -// https://en.wikipedia.org/wiki/Law_of_cosines#Use_in_solving_triangles -KOKKOS_INLINE_FUNCTION -double angle_from_side_lengths(const double a, const double b, const double c) -{ - return std::acos((a * a + b * b - c * c) / 2 * a * b); -} - +template KOKKOS_INLINE_FUNCTION -bool normal_intersects_segment(const Omega_h::Few a, - const Omega_h::Few b, - const Omega_h::Few c) +bool normal_intersects_segment(const Omega_h::Vector a, + const Omega_h::Vector b, + const Omega_h::Vector p) { - const auto ab_len = Omega_h::norm(a - b); - const auto bc_len = Omega_h::norm(b - c); - const auto ac_len = Omega_h::norm(a - c); - - const double angle1 = angle_from_side_lengths(bc_len, ac_len, ab_len); - const double angle2 = angle_from_side_lengths(bc_len, ab_len, ac_len); - - return angle1 <= (Omega_h::PI / 2) && angle2 <= (Omega_h::PI / 2); + auto ab = b - a; + auto ba = a - b; + auto ap = p - a; + auto bp = p - b; + return (ap * ab) * (bp * ba) >= 0; } namespace pcms @@ -479,7 +469,7 @@ Kokkos::View GridPointSearch2D::operator()( auto vertex_a = Omega_h::get_vector<2>(coords, vertex_a_id); auto vertex_b = Omega_h::get_vector<2>(coords, vertex_b_id); - if (!normal_intersects_segment(point, vertex_a, vertex_b)) + if (!normal_intersects_segment(vertex_a, vertex_b, point)) continue; const auto distance_to_ab = From 39c1fed9559c4709a0dc4bc307669b8d76a1b152 Mon Sep 17 00:00:00 2001 From: Matthew McCall Date: Thu, 19 Feb 2026 11:59:38 -0500 Subject: [PATCH 24/27] Fix `PointSearchTolerances` initialization in `test_point_search.cpp` to use `Kokkos::deep_copy` for proper device-host synchronization. --- test/test_point_search.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/test_point_search.cpp b/test/test_point_search.cpp index 002f6d3a..58d5100e 100644 --- a/test/test_point_search.cpp +++ b/test/test_point_search.cpp @@ -171,8 +171,10 @@ TEST_CASE("uniform grid search") Omega_h::build_box(world, OMEGA_H_SIMPLEX, 1, 1, 1, 10, 10, 0, false); auto tolerances = GridPointSearch2D::PointSearchTolerances{"point search 2d tolerances"}; - tolerances(0) = 0.01; - tolerances(1) = 0.01; + auto tolerances_h = Kokkos::create_mirror_view(tolerances); + tolerances_h(0) = 0.01; + tolerances_h(1) = 0.01; + Kokkos::deep_copy(tolerances, tolerances_h); GridPointSearch2D search{mesh, 10, 10, tolerances}; From f26275ca713b6097d43e534a3d52a3936a23d977 Mon Sep 17 00:00:00 2001 From: Matthew McCall Date: Thu, 26 Feb 2026 16:27:48 -0500 Subject: [PATCH 25/27] Apply minor formatting fixes and update variable types in `distance_from_line` and `normal_intersects_segment` implementations in `point_search.cpp`. --- src/pcms/point_search.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/pcms/point_search.cpp b/src/pcms/point_search.cpp index b32fbe22..e86a5c00 100644 --- a/src/pcms/point_search.cpp +++ b/src/pcms/point_search.cpp @@ -5,25 +5,25 @@ // From // https://en.wikipedia.org/wiki/Distance_from_a_point_to_a_line#Vector_formulation template -KOKKOS_INLINE_FUNCTION -Omega_h::Real distance_from_line(const Omega_h::Vector& a, const Omega_h::Vector& b, const Omega_h::Vector& p) +KOKKOS_INLINE_FUNCTION Omega_h::Real distance_from_line( + const Omega_h::Vector& a, const Omega_h::Vector& b, + const Omega_h::Vector& p) { - Omega_h::Vector n = Omega_h::normalize(b - a); - Omega_h::Vector ap = a - p; - return Omega_h::norm(ap - (ap*n)*n); + Omega_h::Vector n = Omega_h::normalize(b - a); + Omega_h::Vector ap = a - p; + return Omega_h::norm(ap - (ap * n) * n); } template -KOKKOS_INLINE_FUNCTION -bool normal_intersects_segment(const Omega_h::Vector a, - const Omega_h::Vector b, - const Omega_h::Vector p) +KOKKOS_INLINE_FUNCTION bool normal_intersects_segment( + const Omega_h::Vector a, const Omega_h::Vector b, + const Omega_h::Vector p) { auto ab = b - a; auto ba = a - b; auto ap = p - a; auto bp = p - b; - return (ap * ab) * (bp * ba) >= 0; + return (ap * ab) * (bp * ba) >= 0; } namespace pcms From d0f9e2e4c551b74e8208d083078e5321ac67d840 Mon Sep 17 00:00:00 2001 From: Matthew McCall Date: Wed, 4 Mar 2026 16:16:57 -0500 Subject: [PATCH 26/27] Add `fuzz` parameter to `GridPointSearch3D` for adjustable tolerance and update call sites. Replace `tri_id` with `element_id` in `results_h` check for clarity. --- src/pcms/create_field.cpp | 4 ++-- src/pcms/point_search.cpp | 11 +++++++---- src/pcms/point_search.h | 5 +++-- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/pcms/create_field.cpp b/src/pcms/create_field.cpp index 6aa224b2..84204b78 100644 --- a/src/pcms/create_field.cpp +++ b/src/pcms/create_field.cpp @@ -41,7 +41,7 @@ CreateUniformGridBinaryFieldFromGrid<2>(Omega_h::Mesh& mesh, const LO num_vertices = (grid.divisions[0] + 1) * (grid.divisions[1] + 1); // Create GridPointSearch for point-in-mesh queries - GridPointSearch point_search(mesh, grid.divisions[0], grid.divisions[1]); + GridPointSearch2D point_search(mesh, grid.divisions[0], grid.divisions[1]); // Create array of grid vertex points Kokkos::View vertices("vertices", num_vertices); @@ -80,7 +80,7 @@ CreateUniformGridBinaryFieldFromGrid<2>(Omega_h::Mesh& mesh, // Create binary data as Real values (0.0 or 1.0) Kokkos::View binary_data("binary_data", num_vertices); for (LO i = 0; i < num_vertices; ++i) { - binary_data(i) = (results_h(i).tri_id >= 0) ? 1.0 : 0.0; + binary_data(i) = (results_h(i).element_id >= 0) ? 1.0 : 0.0; } fprintf(stderr, "Generated binary inside/outside data for grid vertices.\n"); diff --git a/src/pcms/point_search.cpp b/src/pcms/point_search.cpp index e86a5c00..ed52e52e 100644 --- a/src/pcms/point_search.cpp +++ b/src/pcms/point_search.cpp @@ -171,7 +171,7 @@ template template [[nodiscard]] KOKKOS_INLINE_FUNCTION bool bbox_verts_within_simplex( - const AABBox& bbox, const Omega_h::Matrix& coords) + const AABBox& bbox, const Omega_h::Matrix& coords, Real fuzz) { // each dimension has a pair of opposing "walls" // 2D: { [left, right], [top, bottom] } -> { left, right, top, bottom } @@ -535,6 +535,7 @@ GridPointSearch2D::GridPointSearch2D(Omega_h::Mesh& mesh, LO Nx, LO Ny, const Po tris2edges_adj_ = mesh.ask_down(Omega_h::FACE, Omega_h::EDGE); tris2verts_adj_ = mesh.ask_down(Omega_h::FACE, Omega_h::VERT); edges2verts_adj_ = mesh.ask_down(Omega_h::EDGE, Omega_h::VERT); + fuzz_ = fuzz; } Kokkos::View GridPointSearch3D::operator()( @@ -551,6 +552,7 @@ Kokkos::View GridPointSearch3D::operator()( auto tris2edges_adj = tris2edges_adj_; auto edges2verts_adj = edges2verts_adj_; auto coords = coords_; + auto fuzz = fuzz_; Kokkos::parallel_for( points.extent(0), KOKKOS_LAMBDA(int p) { Omega_h::Vector point; @@ -599,15 +601,15 @@ Kokkos::View GridPointSearch3D::operator()( return results; } -GridPointSearch3D::GridPointSearch3D(Omega_h::Mesh& mesh, LO Nx, LO Ny, LO Nz) +GridPointSearch3D::GridPointSearch3D(Omega_h::Mesh& mesh, LO Nx, LO Ny, LO Nz, Real fuzz) : GridPointSearch3D(mesh, Nx, Ny, Nz, - PointSearchTolerances{"point search 3d tolerances"}) + PointSearchTolerances{"point search 3d tolerances"}, fuzz) { Kokkos::deep_copy(tolerances_, 0); } GridPointSearch3D::GridPointSearch3D(Omega_h::Mesh& mesh, LO Nx, LO Ny, LO Nz, - const PointSearchTolerances& tolerances) + const PointSearchTolerances& tolerances, Real fuzz) : PointLocalizationSearch(tolerances) { auto mesh_bbox = Omega_h::get_bounding_box<3>(&mesh); @@ -633,5 +635,6 @@ GridPointSearch3D::GridPointSearch3D(Omega_h::Mesh& mesh, LO Nx, LO Ny, LO Nz, tris2edges_adj_ = mesh.ask_down(Omega_h::FACE, Omega_h::EDGE); tris2verts_adj_ = mesh.ask_down(Omega_h::FACE, Omega_h::VERT); edges2verts_adj_ = mesh.ask_down(Omega_h::EDGE, Omega_h::VERT); + fuzz_ = fuzz; } } // namespace pcms diff --git a/src/pcms/point_search.h b/src/pcms/point_search.h index 693aebfa..7cd65a38 100644 --- a/src/pcms/point_search.h +++ b/src/pcms/point_search.h @@ -110,9 +110,9 @@ class GridPointSearch3D : public PointLocalizationSearch3D public: using Result = PointLocalizationSearch3D::Result; - GridPointSearch3D(Omega_h::Mesh& mesh, LO Nx, LO Ny, LO Nz); + GridPointSearch3D(Omega_h::Mesh& mesh, LO Nx, LO Ny, LO Nz, Real fuzz = 1E-12); GridPointSearch3D(Omega_h::Mesh& mesh, LO Nx, LO Ny, LO Nz, - const PointSearchTolerances& tolerances); + const PointSearchTolerances& tolerances, Real fuzz = 1E-12); /** * Given a point in global coordinates, returns the id of the tetrahedron (3D @@ -133,6 +133,7 @@ class GridPointSearch3D : public PointLocalizationSearch3D CandidateMapT candidate_map_; Omega_h::LOs tris2verts_; Omega_h::Reals coords_; + Real fuzz_; }; } // namespace pcms From 0ee12fc615ab4de90d2aece76005b88aa16293a5 Mon Sep 17 00:00:00 2001 From: Matthew McCall Date: Wed, 4 Mar 2026 16:18:12 -0500 Subject: [PATCH 27/27] Apply minor formatting adjustments and line splits in `point_search.cpp` and `point_search.h` for improved readability. --- src/pcms/point_search.cpp | 46 ++++++++++++++++++++++----------------- src/pcms/point_search.h | 10 +++++---- 2 files changed, 32 insertions(+), 24 deletions(-) diff --git a/src/pcms/point_search.cpp b/src/pcms/point_search.cpp index ed52e52e..eb135916 100644 --- a/src/pcms/point_search.cpp +++ b/src/pcms/point_search.cpp @@ -171,7 +171,8 @@ template template [[nodiscard]] KOKKOS_INLINE_FUNCTION bool bbox_verts_within_simplex( - const AABBox& bbox, const Omega_h::Matrix& coords, Real fuzz) + const AABBox& bbox, const Omega_h::Matrix& coords, + Real fuzz) { // each dimension has a pair of opposing "walls" // 2D: { [left, right], [top, bottom] } -> { left, right, top, bottom } @@ -256,7 +257,7 @@ namespace detail struct GridTriIntersectionFunctor2D { GridTriIntersectionFunctor2D(Omega_h::Mesh& mesh, - Kokkos::View grid, Real fuzz) + Kokkos::View grid, Real fuzz) : mesh_(mesh), tris2verts_(mesh_.ask_elem_verts()), coords_(mesh_.coords()), @@ -360,8 +361,8 @@ struct GridTriIntersectionFunctor3D // avoid extra copy of grid from gpu to cpu Kokkos::Crs construct_intersection_map_2d(Omega_h::Mesh& mesh, - Kokkos::View grid, - int num_grid_cells, Real fuzz) + Kokkos::View grid, + int num_grid_cells, Real fuzz) { Kokkos::Crs intersection_map{}; auto f = detail::GridTriIntersectionFunctor2D{mesh, grid, fuzz}; @@ -407,12 +408,14 @@ Kokkos::View GridPointSearch2D::operator()( auto coords = coords_; auto tolerances = tolerances_; auto fuzz = fuzz_; - Kokkos::parallel_for(points.extent(0), KOKKOS_LAMBDA(int p) { - Omega_h::Vector<2> point(std::initializer_list{points(p,0), points(p,1)}); - auto cell_id = grid(0).ClosestCellID(point); - assert(cell_id < num_rows && cell_id >= 0); - auto candidates_begin = candidate_map.row_map(cell_id); - auto candidates_end = candidate_map.row_map(cell_id + 1); + Kokkos::parallel_for( + points.extent(0), KOKKOS_LAMBDA(int p) { + Omega_h::Vector<2> point( + std::initializer_list{points(p, 0), points(p, 1)}); + auto cell_id = grid(0).ClosestCellID(point); + assert(cell_id < num_rows && cell_id >= 0); + auto candidates_begin = candidate_map.row_map(cell_id); + auto candidates_end = candidate_map.row_map(cell_id + 1); bool vertex_found = false; bool edge_found = false; @@ -495,10 +498,6 @@ Kokkos::View GridPointSearch2D::operator()( nearest_element_id = triangleID; parametric_coords_to_nearest = parametric_coords; inside_cell = true; - - // results(p) = - // GridPointSearch2D::Result{GridPointSearch2D::Result::Dimensionality::FACE, - // triangleID, parametric_coords}; return; } } @@ -512,13 +511,17 @@ Kokkos::View GridPointSearch2D::operator()( return results; } -GridPointSearch2D::GridPointSearch2D(Omega_h::Mesh& mesh, LO Nx, LO Ny, Real fuzz) - : GridPointSearch2D(mesh, Nx, Ny, PointSearchTolerances { "point search 2d tolerances" }, fuzz) +GridPointSearch2D::GridPointSearch2D(Omega_h::Mesh& mesh, LO Nx, LO Ny, + Real fuzz) + : GridPointSearch2D(mesh, Nx, Ny, + PointSearchTolerances{"point search 2d tolerances"}, fuzz) { Kokkos::deep_copy(tolerances_, 0); } -GridPointSearch2D::GridPointSearch2D(Omega_h::Mesh& mesh, LO Nx, LO Ny, const PointSearchTolerances& tolerances, Real fuzz) +GridPointSearch2D::GridPointSearch2D(Omega_h::Mesh& mesh, LO Nx, LO Ny, + const PointSearchTolerances& tolerances, + Real fuzz) : PointLocalizationSearch(tolerances) { auto mesh_bbox = Omega_h::get_bounding_box<2>(&mesh); @@ -529,7 +532,8 @@ GridPointSearch2D::GridPointSearch2D(Omega_h::Mesh& mesh, LO Nx, LO Ny, const Po .bot_left = {mesh_bbox.min[0], mesh_bbox.min[1]}, .divisions = {Nx, Ny}}; Kokkos::deep_copy(grid_, grid_h); - candidate_map_ = detail::construct_intersection_map_2d(mesh, grid_, grid_h(0).GetNumCells(), fuzz_); + candidate_map_ = detail::construct_intersection_map_2d( + mesh, grid_, grid_h(0).GetNumCells(), fuzz_); coords_ = mesh.coords(); tris2verts_ = mesh.ask_elem_verts(); tris2edges_adj_ = mesh.ask_down(Omega_h::FACE, Omega_h::EDGE); @@ -601,7 +605,8 @@ Kokkos::View GridPointSearch3D::operator()( return results; } -GridPointSearch3D::GridPointSearch3D(Omega_h::Mesh& mesh, LO Nx, LO Ny, LO Nz, Real fuzz) +GridPointSearch3D::GridPointSearch3D(Omega_h::Mesh& mesh, LO Nx, LO Ny, LO Nz, + Real fuzz) : GridPointSearch3D(mesh, Nx, Ny, Nz, PointSearchTolerances{"point search 3d tolerances"}, fuzz) { @@ -609,7 +614,8 @@ GridPointSearch3D::GridPointSearch3D(Omega_h::Mesh& mesh, LO Nx, LO Ny, LO Nz, R } GridPointSearch3D::GridPointSearch3D(Omega_h::Mesh& mesh, LO Nx, LO Ny, LO Nz, - const PointSearchTolerances& tolerances, Real fuzz) + const PointSearchTolerances& tolerances, + Real fuzz) : PointLocalizationSearch(tolerances) { auto mesh_bbox = Omega_h::get_bounding_box<3>(&mesh); diff --git a/src/pcms/point_search.h b/src/pcms/point_search.h index 7cd65a38..d68f4432 100644 --- a/src/pcms/point_search.h +++ b/src/pcms/point_search.h @@ -21,8 +21,8 @@ namespace detail { Kokkos::Crs construct_intersection_map_2d(Omega_h::Mesh& mesh, - Kokkos::View grid, - int num_grid_cells, Real fuzz = 1E-12); + Kokkos::View grid, + int num_grid_cells, Real fuzz = 1E-12); } [[nodiscard]] KOKKOS_FUNCTION bool triangle_intersects_bbox( @@ -78,7 +78,8 @@ class GridPointSearch2D : public PointLocalizationSearch2D using Result = PointLocalizationSearch2D::Result; GridPointSearch2D(Omega_h::Mesh& mesh, LO Nx, LO Ny, Real fuzz = 1E-12); - GridPointSearch2D(Omega_h::Mesh& mesh, LO Nx, LO Ny, const PointSearchTolerances& tolerances, Real fuzz = 1E-12); + GridPointSearch2D(Omega_h::Mesh& mesh, LO Nx, LO Ny, + const PointSearchTolerances& tolerances, Real fuzz = 1E-12); /** * given a point in global coordinates give the id of the triangle that the @@ -110,7 +111,8 @@ class GridPointSearch3D : public PointLocalizationSearch3D public: using Result = PointLocalizationSearch3D::Result; - GridPointSearch3D(Omega_h::Mesh& mesh, LO Nx, LO Ny, LO Nz, Real fuzz = 1E-12); + GridPointSearch3D(Omega_h::Mesh& mesh, LO Nx, LO Ny, LO Nz, + Real fuzz = 1E-12); GridPointSearch3D(Omega_h::Mesh& mesh, LO Nx, LO Ny, LO Nz, const PointSearchTolerances& tolerances, Real fuzz = 1E-12);