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 <range/v3/algorithm/equal.hpp>
5#include <range/v3/range/conversion.hpp>
6#include <unordered_map>
7#include <vector>
8
9#include "BaseLib/RunTime.h"
11#include "MeshLib/Mesh.h"
12#include "MeshLib/Node.h"
14#include "MeshNodeSearcher.h"
15
16namespace
17{
20std::vector<std::size_t> identifySubdomainMeshNodes(
21 MeshLib::Mesh const& subdomain_mesh,
22 MeshGeoToolsLib::MeshNodeSearcher const& mesh_node_searcher)
23{
24 // Convert nodes pointers needed for the mesh_node_searcher algorithm.
25 auto const& nodes = subdomain_mesh.getNodes();
26 std::vector<MathLib::Point3dWithID*> subdomain_points{begin(nodes),
27 end(nodes)};
28
29 auto const& bulk_node_ids =
30 mesh_node_searcher.getMeshNodeIDs(subdomain_points);
31
32 if (bulk_node_ids.size() != subdomain_mesh.getNumberOfNodes())
33 {
35 "Expected to find exactly one node in the bulk mesh for each node "
36 "of the subdomain; Found {:d} nodes in the bulk mesh out of {:d} "
37 "nodes in the subdomain.",
38 bulk_node_ids.size(), subdomain_mesh.getNumberOfNodes());
39 }
40
41 return bulk_node_ids;
42}
43
47std::vector<std::size_t> findElementsInMesh(
48 std::vector<std::size_t> const& node_ids,
49 std::vector<std::vector<std::size_t>> const& connected_element_ids_per_node)
50{
51 //
52 // Count how often an element is shared by all nodes.
53 //
54 std::unordered_map<std::size_t, int> element_counts(8);
55 for (auto const node_id : node_ids)
56 {
57 for (auto const element_id : connected_element_ids_per_node[node_id])
58 {
59 element_counts[element_id]++;
60 }
61 }
62
63 //
64 // Elements which are shared by as many nodes as the input nodes are the
65 // desired elements.
66 //
67 auto const nnodes = node_ids.size();
68 std::vector<std::size_t> element_ids;
69 for (auto const& pair : element_counts)
70 {
71 if (pair.second == static_cast<int>(nnodes))
72 {
73 element_ids.push_back(pair.first);
74 }
75 }
76
77 return element_ids;
78}
79
83std::vector<std::vector<std::size_t>> identifySubdomainMeshElements(
84 MeshLib::Mesh const& subdomain_mesh, MeshLib::Mesh const& bulk_mesh)
85{
86 auto const& bulk_node_ids = *MeshLib::bulkNodeIDs(subdomain_mesh);
87
88 // Allocate space for all elements for random insertion.
89 std::vector<std::vector<std::size_t>> bulk_element_ids_map(
90 subdomain_mesh.getNumberOfElements());
91
92 // For each node a vector of connected element ids of that node.
93 std::vector<std::vector<std::size_t>> connected_element_ids_per_node(
94 bulk_mesh.getNumberOfNodes());
95 for (auto const node_id : bulk_mesh.getNodes() | MeshLib::views::ids)
96 {
97 connected_element_ids_per_node[node_id] =
98 bulk_mesh.getElementsConnectedToNode(node_id) |
99 MeshLib::views::ids | ranges::to<std::vector>;
100 }
101
102 auto const& elements = subdomain_mesh.getElements();
103#pragma omp parallel for
104 for (std::ptrdiff_t j = 0; j < std::ssize(elements); ++j)
105 {
106 auto* const e = elements[j];
107 std::vector<std::size_t> element_node_ids(e->getNumberOfBaseNodes());
108 for (unsigned n = 0; n < e->getNumberOfBaseNodes(); ++n)
109 {
110 element_node_ids[n] = MeshLib::getNodeIndex(*e, n);
111 }
112 std::vector<std::size_t> element_node_ids_bulk(
113 e->getNumberOfBaseNodes());
114 std::transform(begin(element_node_ids), end(element_node_ids),
115 begin(element_node_ids_bulk),
116 [&bulk_node_ids](std::size_t const id)
117 { return bulk_node_ids[id]; });
118
119 std::vector<std::size_t> bulk_element_ids = findElementsInMesh(
120 element_node_ids_bulk, connected_element_ids_per_node);
121
122 if (bulk_element_ids.empty())
123 {
124 ERR("No element could be found for the subdomain element {:d}. "
125 "Corresponding bulk mesh node ids are:",
126 e->getID());
127 for (auto const i : element_node_ids_bulk)
128 {
129 ERR("\t{:d}", i);
130 }
131 OGS_FATAL(
132 "Expect at least one element to be found in the bulk mesh.");
133 }
134
135 bulk_element_ids_map[e->getID()] = std::move(bulk_element_ids);
136 }
137
138 return bulk_element_ids_map;
139}
140
143 MeshLib::Mesh& mesh, std::string_view property_name,
144 std::vector<std::size_t> const& values,
145 MeshLib::MeshItemType const mesh_item_type, bool const force_overwrite)
146{
147 auto& properties = mesh.getProperties();
148 if (!properties.existsPropertyVector<std::size_t>(property_name,
149 mesh_item_type, 1))
150 {
151 addPropertyToMesh<std::size_t>(mesh, property_name, mesh_item_type, 1,
152 {values});
153 return;
154 }
155
156 //
157 // Check the existing property against new values.
158 //
159 auto& original_property =
160 *properties.getPropertyVector<std::size_t>(property_name);
161 if (ranges::equal(original_property, values))
162 {
163 INFO(
164 "There is already a '{:s}' property present in the subdomain mesh "
165 "'{:s}' and it is equal to the newly computed values.",
166 property_name, mesh.getName());
167 return;
168 }
169
170 //
171 // Property differs. Notify and update if forced.
172 //
173 WARN(
174 "There is already a '{:s}' property present in the subdomain mesh "
175 "'{:s}' and it is not equal to the newly computed values.",
176 property_name,
177 mesh.getName());
178
179 if (!force_overwrite)
180 {
181 OGS_FATAL("The force overwrite flag was not specified, exiting.");
182 }
183
184 INFO("Overwriting '{:s}' property.", property_name);
185 original_property.assign(values);
186}
187} // namespace
188
189namespace MeshGeoToolsLib
190{
192 MeshLib::Mesh const& bulk_mesh,
193 MeshNodeSearcher const& mesh_node_searcher,
194 bool const force_overwrite = false)
195{
196 BaseLib::RunTime time;
197 time.start();
198 auto const& bulk_node_ids =
199 identifySubdomainMeshNodes(subdomain_mesh, mesh_node_searcher);
200 INFO("identifySubdomainMesh(): identifySubdomainMeshNodes took {:g} s",
201 time.elapsed());
202
203 updateOrCheckExistingSubdomainProperty(
205 bulk_node_ids, MeshLib::MeshItemType::Node, force_overwrite);
206
207 time.start();
208 auto const& bulk_element_ids =
209 identifySubdomainMeshElements(subdomain_mesh, bulk_mesh);
210 INFO("identifySubdomainMesh(): identifySubdomainMeshElements took {:g} s",
211 time.elapsed());
212
213 // The bulk_element_ids could be of two types: one element per entry---this
214 // is the expected case for the boundary meshes; multiple elements per
215 // entry---this happens if the subdomain mesh lies inside the bulk mesh and
216 // has lower dimension.
217 // First find out the type, then add/check the CellData or FieldData.
218 if (all_of(begin(bulk_element_ids), end(bulk_element_ids),
219 [](std::vector<std::size_t> const& v) { return v.size() == 1; }))
220 {
221 // All vectors are of size 1, so the data can be flattened and
222 // stored in CellData or compared to existing CellData.
223 std::vector<std::size_t> unique_bulk_element_ids;
224 unique_bulk_element_ids.reserve(bulk_element_ids.size());
225 transform(begin(bulk_element_ids), end(bulk_element_ids),
226 back_inserter(unique_bulk_element_ids),
227 [](std::vector<std::size_t> const& v) { return v[0]; });
228
229 updateOrCheckExistingSubdomainProperty(
230 subdomain_mesh,
232 unique_bulk_element_ids, MeshLib::MeshItemType::Cell,
233 force_overwrite);
234 }
235 else
236 {
237 // Some of the boundary elements are connected to multiple bulk
238 // elements; Store the array in FieldData with additional CellData array
239 // for the number of elements, which also provides the offsets.
240 std::vector<std::size_t> flat_bulk_element_ids;
241 flat_bulk_element_ids.reserve(2 * bulk_element_ids.size()); // Guess.
242 std::vector<std::size_t> number_of_bulk_element_ids;
243 number_of_bulk_element_ids.reserve(bulk_element_ids.size());
244
245 for (std::vector<std::size_t> const& v : bulk_element_ids)
246 {
247 number_of_bulk_element_ids.push_back(v.size());
248 flat_bulk_element_ids.insert(end(flat_bulk_element_ids), begin(v),
249 end(v));
250 }
251
252 updateOrCheckExistingSubdomainProperty(
253 subdomain_mesh, "number_bulk_elements", number_of_bulk_element_ids,
254 MeshLib::MeshItemType::Cell, force_overwrite);
255 updateOrCheckExistingSubdomainProperty(
256 subdomain_mesh,
258 flat_bulk_element_ids, MeshLib::MeshItemType::IntegrationPoint,
259 force_overwrite);
260 }
261}
262} // namespace MeshGeoToolsLib
#define OGS_FATAL(...)
Definition Error.h:19
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:97
std::vector< Element * > const & getElements() const
Get the element-vector for the mesh.
Definition Mesh.h:100
Properties & getProperties()
Definition Mesh.h:125
const std::string getName() const
Get name of the mesh.
Definition Mesh.h:94
std::size_t getNumberOfNodes() const
Get the number of nodes.
Definition Mesh.h:91
std::vector< Element const * > const & getElementsConnectedToNode(std::size_t node_id) const
Definition Mesh.cpp:246
std::size_t getNumberOfElements() const
Get the number of elements.
Definition Mesh.h:88
PropertyVector< T > const * getPropertyVector(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:216
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:282
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)