Loading [MathJax]/extensions/tex2jax.js
PISM, A Parallel Ice Sheet Model 2.2.1-cd005eec8 committed by Constantine Khrulev on 2025-03-07
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
label_components_impl.hh
Go to the documentation of this file.
1/* Copyright (C) 2023 PISM Authors
2 *
3 * This file is part of PISM.
4 *
5 * PISM is free software; you can redistribute it and/or modify it under the
6 * terms of the GNU General Public License as published by the Free Software
7 * Foundation; either version 3 of the License, or (at your option) any later
8 * version.
9 *
10 * PISM is distributed in the hope that it will be useful, but WITHOUT ANY
11 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
13 * details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with PISM; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20#include "pism/util/array/Scalar.hh"
22#include <map>
23
24namespace pism {
25
26namespace details {
27
28int first_label(const Grid &grid);
29
30//! Assign new labels to elements of `mask`. Does not touch background grid cells.
31void relabel(array::Scalar &mask, const std::map<int, int> &labels);
32
33std::map<int, int> final_labels(array::Scalar1 &input, bool subdomain_is_not_empty,
34 bool mark_isolated_patches);
35
36} // end of namespace details
37
38/*!
39 * Labels connected components in parallel, using a custom definition of "foreground" and
40 * "attached" grid cells.
41 *
42 * Note that `connected_components::details::label()` below is a template function: this
43 * is why we have to include the implementation here.
44 *
45 * The type `T` has to implement `is_foreground(row, col)` and `is_attached(row, col)`.
46 *
47 * Here's the idea:
48 *
49 * 1. Identify connected components in each sub-domain.
50 *
51 * 2. "Update ghosts" of `output`, then iterate over sub-domain edges to identify
52 * connections between patches in sub-domains that make up connected components
53 * spanning multiple sub-domains. This defines a graph: each patch on a sub-domain is a
54 * node, two nodes are connected by an edge if and only if they "touch".
55 *
56 * 3. Gather graph description on all sub-domains that have at least one patch.
57 *
58 * 4. Use breadth-first search to traverse this graph and compute final labels.
59 *
60 * 5. Apply final labels.
61 *
62 * This method communicates ghosts (once), number of graph edges per sub-domain (once) and
63 * then all graph edges (once, only to sub-domains that have at least one patch).
64 *
65 * Graph traversal is done "redundantly", i.e. each participating sub-domain traverses the
66 * whole graph even if it contains one isolated node. This is needed to ensure that
67 * resulting labels use consecutive numbers. (Consecutive labels are useful for indexing
68 * elsewhere in the code).
69 *
70 * We /could/ gather graph information on one MPI processor, traverse the graph to compute
71 * final labels, then scatter final labels. It is not clear if this would be better, though.
72 *
73 * The current implementation uses
74 *
75 * - MPI_Allgather() to gather number of graph edges per subdomain (send 4 bytes, receive
76 4 bytes per subdomain).
77 *
78 * - MPI_Allgatherv() to gather edges to all participating subdomains (send 8 bytes per
79 local edge, receive 8-16 bytes per edge).
80 *
81 * The alternative implementation would use
82 *
83 * - MPI_Gather() to gather number of graph edges per sub-domain to *one* of sub-domains.
84 * (each sub-domain sends 4 bytes, one sub-domain receives 4 bytes per sub-domain).
85 *
86 * - MPI_Gatherv() to gather edges from all participating sub-domains. (All sub-domains send
87 * 8 bytes per local edge, one sub-domain receives 8 bytes per edge in the whole graph.)
88 *
89 * - MPI_Bcast() to scatter the mapping from old labels to new labels (8 bytes per local
90 * sub-domain).
91 *
92 * TO DO: run benchmarks!
93 */
94template <typename T>
95void label_components_impl(const T &input, bool mark_isolated_patches, array::Scalar1 &output) {
96
97 // 1. Label patches owned by individual sub-domains (independently and in parallel):
98 bool non_empty_subdomain = false;
99 {
101
102 using namespace connected_components::details;
103 bool assign_final_labels = false;
104 non_empty_subdomain = label(input, mark_isolated_patches, details::first_label(*output.grid()),
105 assign_final_labels, out);
106 }
107
108 // 2. Resolve labels:
109 auto labels = details::final_labels(output, non_empty_subdomain, mark_isolated_patches);
110
111 // 3. Apply final labels:
112 details::relabel(output, labels);
113}
114
115} // end of namespace pism
std::shared_ptr< const Grid > grid() const
Definition Array.cc:131
int first_label(const Grid &grid)
void relabel(array::Scalar &mask, const std::map< int, int > &labels)
Assign new labels to elements of mask. Does not touch background grid cells.
std::map< int, int > final_labels(array::Scalar1 &input, bool subdomain_is_not_empty, bool mark_isolated_patches)
void label_components_impl(const T &input, bool mark_isolated_patches, array::Scalar1 &output)