-
-
Notifications
You must be signed in to change notification settings - Fork 243
Add maximum marking #4156
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Add maximum marking #4156
Changes from 15 commits
0cc1f68
60086aa
bfedde2
be03e81
fef53a4
3510412
0424889
31f0494
f7f5c34
d39a7f6
0973ff4
93a0211
5354ad9
cde14c7
cf7afab
a77ae09
188e189
10700b1
8065683
1a9bfa9
602f768
358dc52
506589a
3a94aa1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,61 @@ | ||
| // Copyright (C) 2026 Paul T. Kühner | ||
| // | ||
| // This file is part of DOLFINX (https://www.fenicsproject.org) | ||
| // | ||
| // SPDX-License-Identifier: LGPL-3.0-or-later | ||
|
|
||
| #pragma once | ||
|
|
||
| #include <algorithm> | ||
| #include <cassert> | ||
| #include <concepts> | ||
| #include <cstdint> | ||
| #include <iterator> | ||
| #include <limits> | ||
| #include <mpi.h> | ||
| #include <spdlog/spdlog.h> | ||
| #include <vector> | ||
|
|
||
| #include "dolfinx/common/MPI.h" | ||
|
|
||
| namespace dolfinx::refinement | ||
| { | ||
|
|
||
| /// @brief Maximum marking of a marker. | ||
| /// | ||
| /// @param[in] marker Input marker (local) - usually an error indicator per | ||
| /// entity | ||
| /// @param[in] theta Cut off parameter, 0 ≤ θ ≤ 1 | ||
| /// @param[in] comm Communicator over which the maximum is computed. | ||
| /// @return Indices (local) of marker elements, which satisfy: marker_i ≥ θ | ||
| /// max(marker). | ||
| template <std::floating_point T> | ||
| std::vector<std::int32_t> mark_maximum(std::span<const T> marker, T theta, | ||
| MPI_Comm comm) | ||
| { | ||
| if ((theta < 0) || (theta > 1)) | ||
| throw std::invalid_argument("Theta needs to fullfill 0 ≤ θ ≤ 1."); | ||
|
|
||
| T max = marker.empty() ? std::numeric_limits<T>::lowest() | ||
| : std::ranges::max(marker); | ||
| MPI_Allreduce(MPI_IN_PLACE, &max, 1, dolfinx::MPI::mpi_t<T>, MPI_MAX, comm); | ||
|
|
||
| auto mark = [=](T e) | ||
| { return e + std::numeric_limits<T>::epsilon() * 1e2 > theta * max; }; | ||
|
|
||
| std::vector<std::int32_t> indices; | ||
| indices.reserve(std::ranges::count_if(marker, mark)); | ||
|
|
||
| for (std::int32_t i = 0; i < static_cast<std::int32_t>(marker.size()); ++i) | ||
|
schnellerhase marked this conversation as resolved.
|
||
| { | ||
| if (mark(marker[i])) | ||
| indices.push_back(i); | ||
| } | ||
|
|
||
| spdlog::info("Marking (max) {} / {} (local) entities.", indices.size(), | ||
| marker.size()); | ||
|
|
||
| return indices; | ||
| } | ||
|
|
||
| } // namespace dolfinx::refinement | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,61 @@ | ||
| // Copyright (C) 2026 Paul T. Kühner | ||
| // | ||
| // This file is part of DOLFINX (https://www.fenicsproject.org) | ||
| // | ||
| // SPDX-License-Identifier: LGPL-3.0-or-later | ||
|
|
||
| #include <algorithm> | ||
| #include <catch2/catch_template_test_macros.hpp> | ||
| #include <dolfinx/common/MPI.h> | ||
| #include <dolfinx/refinement/mark.h> | ||
| #include <mpi.h> | ||
| #include <vector> | ||
|
|
||
| using namespace dolfinx; | ||
| using namespace dolfinx::refinement; | ||
|
|
||
| TEMPLATE_TEST_CASE("Mark maximum empty", "[refinement][mark][maximum]", double, | ||
| float) | ||
| { | ||
| std::vector<TestType> marker; | ||
| auto indices = mark_maximum<TestType>(marker, .5, MPI_COMM_WORLD); | ||
| CHECK(indices.size() == 0); | ||
| } | ||
|
|
||
| TEMPLATE_TEST_CASE("Mark maximum ones", "[refinement][mark][maximum]", double, | ||
| float) | ||
| { | ||
| std::vector<TestType> marker(10, 1.0); | ||
| auto indices = mark_maximum<TestType>(marker, 1.0, MPI_COMM_WORLD); | ||
| CHECK(indices.size() == 10); | ||
| } | ||
|
|
||
| TEMPLATE_TEST_CASE("Mark maximum", "[refinement][mark][maximum]", double, float) | ||
| { | ||
| MPI_Comm comm = MPI_COMM_WORLD; | ||
|
|
||
| std::vector<TestType> marker; | ||
| marker.reserve(10); | ||
| for (std::size_t i = 0; i < 10; i++) | ||
| marker.push_back(10 * dolfinx::MPI::rank(comm) + i); | ||
|
|
||
| TestType theta = 0.5; | ||
| auto indices = mark_maximum<TestType>(marker, theta, comm); | ||
|
|
||
| CHECK(std::ranges::all_of( | ||
| indices, [&](auto e) | ||
| { return (0 <= e) && (e <= static_cast<std::int32_t>(marker.size())); })); | ||
|
|
||
| TestType max = dolfinx::MPI::size(comm) * 10 - 1; | ||
| auto mark = [=](auto e) { return e >= theta * max; }; | ||
|
|
||
| CHECK(std::ranges::count_if(marker, mark) | ||
| == static_cast<std::int32_t>(indices.size())); | ||
|
|
||
| for (std::int32_t i = 0; i < static_cast<std::int32_t>(marker.size()); ++i) | ||
| { | ||
| bool expect_marked = mark(marker[i]); | ||
| bool marked = std::ranges::find(indices, i) != indices.end(); | ||
| CHECK(expect_marked == marked); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| # Copyright (C) 2026 Paul T. Kühner | ||
| # | ||
| # This file is part of DOLFINx (https://www.fenicsproject.org) | ||
| # | ||
| # SPDX-License-Identifier: LGPL-3.0-or-later | ||
|
|
||
| from mpi4py import MPI | ||
|
|
||
| import numpy as np | ||
| import pytest | ||
|
|
||
| from dolfinx import mesh | ||
|
|
||
|
|
||
| @pytest.mark.parametrize("theta", np.linspace(0, 1, num=5, endpoint=True)) | ||
| @pytest.mark.parametrize("dtype", [np.float32, np.float64]) | ||
| def test_mark_maximum(theta: float, dtype: np.dtype) -> None: | ||
| msh = mesh.create_unit_square(comm := MPI.COMM_WORLD, n := 10, n, dtype=dtype) | ||
|
Check warning on line 18 in python/test/unit/refinement/test_mark.py
|
||
|
|
||
| tdim = msh.topology.dim | ||
| cell_count = (cell_im := msh.topology.index_map(tdim)).size_local + cell_im.num_ghosts | ||
| marker = np.random.default_rng(0).random(cell_count) | ||
|
|
||
| marked_cells = mesh.mark_maximum(marker, theta, comm) | ||
|
|
||
| assert np.allclose( | ||
| marked_cells, | ||
| np.argwhere(marker >= theta * comm.allreduce(np.max(marker), MPI.MAX)).flatten(), | ||
|
schnellerhase marked this conversation as resolved.
Outdated
|
||
| ) | ||
|
|
||
| msh.topology.create_entities(1) | ||
| marked_edges = mesh.compute_incident_entities(msh.topology, marked_cells, tdim, 1) | ||
| mesh.refine(msh, marked_edges) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should there be something more at the end of this test ?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I just wanted to check that the input works as expected with the refine calls - so the test being "refine works". Not sure what to exactly test for on the refined mesh, up to it being a mesh. |
||
Uh oh!
There was an error while loading. Please reload this page.