OGS
IdentifySubdomainMesh.cpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: Copyright (c) OpenGeoSys Community (opengeosys.org)
2// SPDX-License-Identifier: BSD-3-Clause
3
4#include <algorithm>
5#include <functional>
6#include <range/v3/algorithm/equal.hpp>
7#include <range/v3/range/conversion.hpp>
8#include <unordered_map>
9#include <vector>
10
11#include "BaseLib/RunTime.h"
13#include "MeshLib/Mesh.h"
14#include "MeshLib/Node.h"
16#include "MeshNodeSearcher.h"
17
18namespace
19{
22std::vector<std::size_t> identifySubdomainMeshNodes(
23 MeshLib::Mesh const& subdomain_mesh,
24 MeshGeoToolsLib::MeshNodeSearcher const& mesh_node_searcher)
25{
26 // Convert nodes pointers needed for the mesh_node_searcher algorithm.
27 auto const& nodes = subdomain_mesh.getNodes();
28 std::vector<MathLib::Point3dWithID*> subdomain_points{begin(nodes),
29 end(nodes)};
30
31 auto const& bulk_node_ids =
32 mesh_node_searcher.getMeshNodeIDs(subdomain_points);
33
34 if (bulk_node_ids.size() != subdomain_mesh.getNumberOfNodes())
35 {
37 "Expected to find exactly one node in the bulk mesh for each node "
38 "of the subdomain; Found {:d} nodes in the bulk mesh out of {:d} "
39 "nodes in the subdomain.",
40 bulk_node_ids.size(), subdomain_mesh.getNumberOfNodes());
41 }
42
43 return bulk_node_ids;
44}
45
49std::vector<std::size_t> findElementsInMesh(
50 std::vector<std::size_t> const& node_ids,
51 std::vector<std::vector<std::size_t>> const& connected_element_ids_per_node)
52{
53 if (node_ids.empty())
54 {
55 return {};
56 }
57
58 // An element belongs to the subdomain element when it is connected to all
59 // of the given nodes. Use the node with the fewest connected elements as
60 // the candidate set and keep only those candidates that are connected to
61 // every other node. This avoids any allocation that scales with the bulk
62 // mesh size, so the work per call stays cheap inside the parallel loop
63 // over subdomain elements. Only local state is used, hence thread-safe.
64 auto const& smallest_elements =
65 connected_element_ids_per_node[*std::min_element(
66 begin(node_ids), end(node_ids),
67 [&connected_element_ids_per_node](std::size_t const a,
68 std::size_t const b)
69 {
70 return connected_element_ids_per_node[a].size() <
71 connected_element_ids_per_node[b].size();
72 })];
73
74 std::vector<std::size_t> element_ids;
75 for (auto const element_id : smallest_elements)
76 {
77 bool const shared_by_all = std::all_of(
78 begin(node_ids), end(node_ids),
79 [&connected_element_ids_per_node,
80 element_id](std::size_t const node_id)
81 {
82 auto const& elements = connected_element_ids_per_node[node_id];
83 return std::find(begin(elements), end(elements), element_id) !=
84 end(elements);
85 });
86 if (shared_by_all)
87 {
88 element_ids.push_back(element_id);
89 }
90 }
91
92 // Sort descending so the result is deterministic across compilers and
93 // parallel execution ordering.
94 std::sort(element_ids.begin(), element_ids.end(),
95 std::greater<std::size_t>());
96
97 return element_ids;
98}
99
103std::vector<std::vector<std::size_t>> identifySubdomainMeshElements(
104 MeshLib::Mesh const& subdomain_mesh, MeshLib::Mesh const& bulk_mesh)
105{
106 auto const& bulk_node_ids = *MeshLib::bulkNodeIDs(subdomain_mesh);
107
108 // Allocate space for all elements for random insertion.
109 std::vector<std::vector<std::size_t>> bulk_element_ids_map(
110 subdomain_mesh.getNumberOfElements());
111
112 // For each node a vector of connected element ids of that node.
113 std::vector<std::vector<std::size_t>> connected_element_ids_per_node(
114 bulk_mesh.getNumberOfNodes());
115 for (auto const node_id : bulk_mesh.getNodes() | MeshLib::views::ids)
116 {
117 connected_element_ids_per_node[node_id] =
118 bulk_mesh.getElementsConnectedToNode(node_id) |
119 MeshLib::views::ids | ranges::to<std::vector>;
120 }
121
122 auto const& elements = subdomain_mesh.getElements();
123#pragma omp parallel for
124 for (std::ptrdiff_t j = 0; j < std::ssize(elements); ++j)
125 {
126 auto* const e = elements[j];
127 std::vector<std::size_t> element_node_ids(e->getNumberOfBaseNodes());
128 for (unsigned n = 0; n < e->getNumberOfBaseNodes(); ++n)
129 {
130 element_node_ids[n] = MeshLib::getNodeIndex(*e, n);
131 }
132 std::vector<std::size_t> element_node_ids_bulk(
133 e->getNumberOfBaseNodes());
134 std::transform(begin(element_node_ids), end(element_node_ids),
135 begin(element_node_ids_bulk),
136 [&bulk_node_ids](std::size_t const id)
137 { return bulk_node_ids[id]; });
138
139 std::vector<std::size_t> bulk_element_ids = findElementsInMesh(
140 element_node_ids_bulk, connected_element_ids_per_node);
141
142 if (bulk_element_ids.empty())
143 {
144 ERR("No element could be found for the subdomain element {:d}. "
145 "Corresponding bulk mesh node ids are:",
146 e->getID());
147 for (auto const i : element_node_ids_bulk)
148 {
149 ERR("\t{:d}", i);
150 }
151 OGS_FATAL(
152 "Expect at least one element to be found in the bulk mesh.");
153 }
154
155 bulk_element_ids_map[e->getID()] = std::move(bulk_element_ids);
156 }
157
158 return bulk_element_ids_map;
159}
160
163 MeshLib::Mesh& mesh, std::string_view property_name,
164 std::vector<std::size_t> const& values,
165 MeshLib::MeshItemType const mesh_item_type, bool const force_overwrite)
166{
167 auto& properties = mesh.getProperties();
168 const bool property_name_exists =
169 properties.hasPropertyVector(property_name);
170 if (!properties.existsPropertyVector<std::size_t>(property_name,
171 mesh_item_type, 1))
172 {
173 if (property_name_exists)
174 {
175 if (!force_overwrite)
176 {
177 OGS_FATAL(
178 "A property named '{:s}' already exists on mesh '{:s}', "
179 "but it has a different mesh item type or number of "
180 "components. Use force overwrite to replace it.",
181 property_name, mesh.getName());
182 }
183
184 WARN(
185 "A property named '{:s}' exists on mesh '{:s}' with a "
186 "different mesh item type or number of components. "
187 "Overwriting it.",
188 property_name, mesh.getName());
189 properties.removePropertyVector(property_name);
190 }
191
192 addPropertyToMesh<std::size_t>(mesh, property_name, mesh_item_type, 1,
193 {values});
194 return;
195 }
196
197 //
198 // Check the existing property against new values.
199 //
200 auto& original_property =
201 *properties.getPropertyVector<std::size_t>(property_name);
202 if (ranges::equal(original_property, values))
203 {
204 INFO(
205 "There is already a '{:s}' property present in the subdomain mesh "
206 "'{:s}' and it is equal to the newly computed values.",
207 property_name, mesh.getName());
208 return;
209 }
210
211 //
212 // Property differs. Notify and update if forced.
213 //
214 WARN(
215 "There is already a '{:s}' property present in the subdomain mesh "
216 "'{:s}' and it is not equal to the newly computed values.",
217 property_name,
218 mesh.getName());
219
220 if (!force_overwrite)
221 {
222 OGS_FATAL("The force overwrite flag was not specified, exiting.");
223 }
224
225 INFO("Overwriting '{:s}' property.", property_name);
226 original_property.assign(values);
227}
228} // namespace
229
230namespace MeshGeoToolsLib
231{
233 MeshLib::Mesh const& bulk_mesh,
234 MeshNodeSearcher const& mesh_node_searcher,
235 bool const force_overwrite = false)
236{
237 BaseLib::RunTime time;
238 time.start();
239 auto const& bulk_node_ids =
240 identifySubdomainMeshNodes(subdomain_mesh, mesh_node_searcher);
241 INFO("identifySubdomainMesh(): identifySubdomainMeshNodes took {:g} s",
242 time.elapsed());
243
244 updateOrCheckExistingSubdomainProperty(
246 bulk_node_ids, MeshLib::MeshItemType::Node, force_overwrite);
247
248 time.start();
249 auto const& bulk_element_ids =
250 identifySubdomainMeshElements(subdomain_mesh, bulk_mesh);
251 INFO("identifySubdomainMesh(): identifySubdomainMeshElements took {:g} s",
252 time.elapsed());
253
254 // The bulk_element_ids could be of two types: one element per entry---this
255 // is the expected case for the boundary meshes; multiple elements per
256 // entry---this happens if the subdomain mesh lies inside the bulk mesh and
257 // has lower dimension.
258 // First find out the type, then add/check the CellData or FieldData.
259 bool const all_single_elements =
260 all_of(begin(bulk_element_ids), end(bulk_element_ids),
261 [](std::vector<std::size_t> const& v) { return v.size() == 1; });
262 if (all_single_elements)
263 {
264 // All vectors are of size 1, so the data can be flattened and
265 // stored in CellData or compared to existing CellData.
266 std::vector<std::size_t> unique_bulk_element_ids;
267 unique_bulk_element_ids.reserve(bulk_element_ids.size());
268 transform(begin(bulk_element_ids), end(bulk_element_ids),
269 back_inserter(unique_bulk_element_ids),
270 [](std::vector<std::size_t> const& v) { return v[0]; });
271
272 updateOrCheckExistingSubdomainProperty(
273 subdomain_mesh,
275 unique_bulk_element_ids, MeshLib::MeshItemType::Cell,
276 force_overwrite);
277 }
278 else
279 {
280 // Some of the boundary elements are connected to multiple bulk
281 // elements; Store the array in FieldData with additional CellData array
282 // for the number of elements, which also provides the offsets.
283 std::vector<std::size_t> flat_bulk_element_ids;
284 flat_bulk_element_ids.reserve(2 * bulk_element_ids.size()); // Guess.
285 std::vector<std::size_t> number_of_bulk_element_ids;
286 number_of_bulk_element_ids.reserve(bulk_element_ids.size());
287
288 for (auto const& v : bulk_element_ids)
289 {
290 // The element ids are already sorted descending by
291 // findElementsInMesh(), making the ordering deterministic across
292 // compilers and parallel execution.
293 // TODO: sort the bulk element ids by the positions of the
294 // corresponding subdomain element to the subdomain element, e.g.
295 // left-to-right for 1D subdomains in 2D bulk meshes or 2D
296 // subdomains in 3D bulk meshes.
297 number_of_bulk_element_ids.push_back(v.size());
298 flat_bulk_element_ids.insert(end(flat_bulk_element_ids), begin(v),
299 end(v));
300 }
301
302 updateOrCheckExistingSubdomainProperty(
303 subdomain_mesh, "number_bulk_elements", number_of_bulk_element_ids,
304 MeshLib::MeshItemType::Cell, force_overwrite);
305 updateOrCheckExistingSubdomainProperty(
306 subdomain_mesh,
308 flat_bulk_element_ids, MeshLib::MeshItemType::IntegrationPoint,
309 force_overwrite);
310 }
311}
312} // namespace MeshGeoToolsLib
#define OGS_FATAL(...)
Definition Error.h:10
void INFO(fmt::format_string< Args... > fmt, Args &&... args)
Definition Logging.h:28
void ERR(fmt::format_string< Args... > fmt, Args &&... args)
Definition Logging.h:40
void WARN(fmt::format_string< Args... > fmt, Args &&... args)
Definition Logging.h:34
Count the running time.
Definition RunTime.h:18
double elapsed() const
Get the elapsed time in seconds.
Definition RunTime.h:31
void start()
Start the timer.
Definition RunTime.h:21
std::vector< std::size_t > getMeshNodeIDs(GeoLib::GeoObject const &geoObj) const
std::vector< Node * > const & getNodes() const
Get the nodes-vector for the mesh.
Definition Mesh.h:98
std::vector< Element * > const & getElements() const
Get the element-vector for the mesh.
Definition Mesh.h:101
Properties & getProperties()
Definition Mesh.h:127
const std::string getName() const
Get name of the mesh.
Definition Mesh.h:95
std::size_t getNumberOfNodes() const
Get the number of nodes.
Definition Mesh.h:92
std::vector< Element const * > const & getElementsConnectedToNode(std::size_t node_id) const
Definition Mesh.cpp:248
std::size_t getNumberOfElements() const
Get the number of elements.
Definition Mesh.h:89
bool hasPropertyVector(std::string_view name) const
void identifySubdomainMesh(MeshLib::Mesh &subdomain_mesh, MeshLib::Mesh const &bulk_mesh, MeshNodeSearcher const &mesh_node_searcher, bool const force_overwrite=false)
constexpr ranges::views::view_closure ids
For an element of a range view return its id.
Definition Mesh.h:223
constexpr std::string_view getBulkIDString(MeshItemType mesh_item_type)
std::size_t getNodeIndex(Element const &element, unsigned const idx)
Definition Element.cpp:226
PropertyVector< std::size_t > const * bulkNodeIDs(Mesh const &mesh)
Definition Mesh.cpp:284
std::vector< std::size_t > identifySubdomainMeshNodes(MeshLib::Mesh const &subdomain_mesh, MeshGeoToolsLib::MeshNodeSearcher const &mesh_node_searcher)
void updateOrCheckExistingSubdomainProperty(MeshLib::Mesh &mesh, std::string_view property_name, std::vector< std::size_t > const &values, MeshLib::MeshItemType const mesh_item_type, bool const force_overwrite)
Updates or checks the existing mesh's property with the given values.
std::vector< std::vector< std::size_t > > identifySubdomainMeshElements(MeshLib::Mesh const &subdomain_mesh, MeshLib::Mesh const &bulk_mesh)
std::vector< std::size_t > findElementsInMesh(std::vector< std::size_t > const &node_ids, std::vector< std::vector< std::size_t > > const &connected_element_ids_per_node)