OGS
MeshValidation.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 "MeshValidation.h"
5
6#include <algorithm>
7#include <numeric>
8#include <stack>
9
10#include "BaseLib/Logging.h"
12#include "MeshLib/Mesh.h"
14#include "MeshLib/Node.h"
17
18namespace MeshToolsLib
19{
29static void trackSurface(MeshLib::Element const* const element,
30 std::vector<unsigned>& sfc_idx,
31 unsigned const current_index)
32{
33 std::stack<MeshLib::Element const*> elem_stack;
34 elem_stack.push(element);
35 while (!elem_stack.empty())
36 {
37 MeshLib::Element const* const elem = elem_stack.top();
38 elem_stack.pop();
39 sfc_idx[elem->getID()] = current_index;
40 std::size_t const n_neighbors(elem->getNumberOfNeighbors());
41 for (std::size_t i = 0; i < n_neighbors; ++i)
42 {
43 MeshLib::Element const* neighbor(elem->getNeighbor(i));
44 if (neighbor != nullptr && sfc_idx[neighbor->getID()] ==
45 std::numeric_limits<unsigned>::max())
46 {
47 elem_stack.push(neighbor);
48 }
49 }
50 }
51}
52
54{
55 INFO("Looking for unused nodes...");
56 MeshLib::NodeSearch ns(mesh);
57 ns.searchUnused();
58 if (!ns.getSearchedNodeIDs().empty())
59 {
60 INFO("{:d} unused mesh nodes found.", ns.getSearchedNodeIDs().size());
61 return false;
62 }
63 return true;
64}
65
67{
68 MeshRevision const rev(mesh);
69 INFO("Found {:d} potentially collapsible nodes.",
71 return (rev.getNumberOfCollapsibleNodes() > 0);
72}
73
75{
76 const std::vector<ElementErrorCode> codes(
78 std::array<std::string,
79 static_cast<std::size_t>(ElementErrorFlag::MaxValue)>
81 for (auto& i : output_str)
82 {
83 INFO("{:s}", i);
84 }
85}
86
87std::vector<ElementErrorCode> MeshValidation::testElementGeometry(
88 const MeshLib::Mesh& mesh, double min_volume)
89{
90 INFO("Testing mesh element geometry:");
91 const auto nErrorCodes(
92 static_cast<std::size_t>(ElementErrorFlag::MaxValue));
93 unsigned error_count[nErrorCodes];
94 std::fill_n(error_count, 4, 0);
95 const std::size_t nElements(mesh.getNumberOfElements());
96 const std::vector<MeshLib::Element*>& elements(mesh.getElements());
97 std::vector<ElementErrorCode> error_code_vector;
98 error_code_vector.reserve(nElements);
99
100 for (std::size_t i = 0; i < nElements; ++i)
101 {
102 const ElementErrorCode e = elements[i]->validate();
103 error_code_vector.push_back(e);
104 if (e.none())
105 {
106 continue;
107 }
108
109 // increment error statistics
110 const std::bitset<static_cast<std::size_t>(ElementErrorFlag::MaxValue)>
111 flags(static_cast<std::bitset<static_cast<std::size_t>(
113 for (unsigned j = 0; j < nErrorCodes; ++j)
114 {
115 error_count[j] += flags[j];
116 }
117 }
118
119 // if a larger volume threshold is given, evaluate elements again to add
120 // them even if they are formally okay
121 if (min_volume > std::numeric_limits<double>::epsilon())
122 {
123 for (std::size_t i = 0; i < nElements; ++i)
124 {
125 if (elements[i]->getContent() < min_volume)
126 {
127 error_code_vector[i].set(ElementErrorFlag::ZeroVolume);
128 }
129 }
130 }
131
132 // output
133 const auto error_sum(static_cast<unsigned>(
134 std::accumulate(error_count, error_count + nErrorCodes, 0.0)));
135 if (error_sum != 0)
136 {
137 ElementErrorFlag const flags[nErrorCodes] = {
140 for (std::size_t i = 0; i < nErrorCodes; ++i)
141 {
142 if (error_count[i])
143 {
144 INFO("{:d} elements found with {:s}.",
145 error_count[i],
147 }
148 }
149 }
150 else
151 {
152 INFO("No errors found.");
153 }
154 return error_code_vector;
155}
156
157std::array<std::string, static_cast<std::size_t>(ElementErrorFlag::MaxValue)>
159 const std::vector<ElementErrorCode>& error_codes)
160{
161 const auto nErrorFlags(
162 static_cast<std::size_t>(ElementErrorFlag::MaxValue));
163 const ElementErrorFlag flags[nErrorFlags] = {
166 const std::size_t nElements(error_codes.size());
167 std::array<std::string,
168 static_cast<std::size_t>(ElementErrorFlag::MaxValue)>
169 output;
170
171 for (std::size_t i = 0; i < nErrorFlags; ++i)
172 {
173 unsigned count(0);
174 std::string elementIdStr;
175
176 for (std::size_t j = 0; j < nElements; ++j)
177 {
178 if (error_codes[j][flags[i]])
179 {
180 elementIdStr += (std::to_string(j) + ", ");
181 count++;
182 }
183 }
184 const std::string nErrorsStr = (count) ? std::to_string(count) : "No";
185 output[i] = (nErrorsStr + " elements found with " +
186 ElementErrorCode::toString(flags[i]) + ".\n");
187
188 if (count)
189 {
190 output[i] += ("ElementIDs: " + elementIdStr + "\n");
191 }
192 }
193 return output;
194}
195
197{
198 if (mesh.getDimension() == 1)
199 {
200 return 0;
201 }
202
203 auto boundary_mesh =
205 mesh,
209 std::vector<MeshLib::Element*> const& elements(
210 boundary_mesh->getElements());
211
212 std::vector<unsigned> sfc_idx(elements.size(),
213 std::numeric_limits<unsigned>::max());
214 unsigned current_surface_id(0);
215 auto it = sfc_idx.cbegin();
216
217 while (it != sfc_idx.cend())
218 {
219 std::size_t const idx =
220 static_cast<std::size_t>(std::distance(sfc_idx.cbegin(), it));
221 trackSurface(elements[idx], sfc_idx, current_surface_id++);
222 it = std::find(sfc_idx.cbegin(),
223 sfc_idx.cend(),
224 std::numeric_limits<unsigned>::max());
225 }
226
227 // Subtract "1" from the number of surfaces found to get the number of
228 // holes.
229 return (--current_surface_id);
230}
231} // namespace MeshToolsLib
ElementErrorFlag
Possible error flags for mesh elements.
void INFO(fmt::format_string< Args... > fmt, Args &&... args)
Definition Logging.h:28
Collects error flags for mesh elements.
static std::string toString(const ElementErrorFlag e)
Returns a string output for a specific error flag.
virtual unsigned getNumberOfNeighbors() const =0
Get the number of neighbors for this element.
std::size_t getID() const
Returns the ID of the element.
Definition Element.h:80
virtual const Element * getNeighbor(unsigned i) const =0
Get the specified neighbor.
std::vector< Element * > const & getElements() const
Get the element-vector for the mesh.
Definition Mesh.h:100
unsigned getDimension() const
Returns the dimension of the mesh (determined by the maximum dimension over all elements).
Definition Mesh.h:79
std::size_t getNumberOfElements() const
Get the number of elements.
Definition Mesh.h:88
Node search class.
Definition NodeSearch.h:18
const std::vector< std::size_t > & getSearchedNodeIDs() const
return marked node IDs
Definition NodeSearch.h:23
std::size_t searchUnused()
Marks all unused nodes.
unsigned getNumberOfCollapsibleNodes(double eps=std::numeric_limits< double >::epsilon()) const
Returns the number of potentially collapsible nodes.
constexpr std::string_view getBulkIDString(MeshItemType mesh_item_type)
std::unique_ptr< MeshLib::Mesh > getBoundaryElementsAsMesh(MeshLib::Mesh const &bulk_mesh, std::string_view subsfc_node_id_prop_name, std::string_view subsfc_element_id_prop_name, std::string_view face_id_prop_name)
static void trackSurface(MeshLib::Element const *const element, std::vector< unsigned > &sfc_idx, unsigned const current_index)
static bool existCollapsibleNodes(MeshLib::Mesh &mesh)
static unsigned detectHoles(MeshLib::Mesh const &mesh)
static std::array< std::string, static_cast< std::size_t >(ElementErrorFlag::MaxValue)> ElementErrorCodeOutput(const std::vector< ElementErrorCode > &error_codes)
static bool allNodesUsed(MeshLib::Mesh const &mesh)
static std::vector< ElementErrorCode > testElementGeometry(const MeshLib::Mesh &mesh, double min_volume=std::numeric_limits< double >::epsilon())
static void evaluateElementGeometry(MeshLib::Mesh const &mesh)