OGS
NodeReordering.cpp
Go to the documentation of this file.
1
12#include <tclap/CmdLine.h>
13
14#include <Eigen/Dense>
15#include <algorithm>
16#include <array>
17#include <concepts>
18#include <functional>
19#include <vector>
20
21#include "BaseLib/Algorithm.h"
22#include "BaseLib/Logging.h"
23#include "BaseLib/MPI.h"
25#include "InfoLib/GitInfo.h"
30#include "MeshLib/Mesh.h"
31#include "MeshLib/MeshEnums.h"
32#include "MeshLib/Node.h"
50
51template <typename GradShapeFunction, int Dim>
53 int const mesh_space_dimension,
54 std::array<double, Dim> const& xi,
55 bool const check_reordered = false)
56{
57 Eigen::Matrix<double, GradShapeFunction::DIM, GradShapeFunction::NPOINTS,
58 Eigen::RowMajor>
59 dNdxi;
60 Eigen::Map<Eigen::VectorXd> dN_vec(dNdxi.data(), dNdxi.size());
61 GradShapeFunction::computeGradShapeFunction(xi.data(), dN_vec);
63 e, mesh_space_dimension);
64
65 Eigen::MatrixXd J = Eigen::MatrixXd::Zero(Dim, Dim);
66 for (unsigned k = 0; k < GradShapeFunction::NPOINTS; k++)
67 {
68 const MathLib::Point3d& mapped_pt =
69 ele_local_coord.getMappedCoordinates(k);
70 J += dNdxi.col(k) * mapped_pt.asEigenVector3d().head(Dim).transpose();
71 }
72
73 if (!check_reordered)
74 {
75 return !(J.determinant() < 0);
76 }
77
78 if (J.determinant() < 0)
79 {
81 "Element {:d} has negative Jacobian determinant {:g}. "
82 "NodeReordering fails.",
83 e.getID(), J.determinant());
84 }
85
86 return true;
87}
88
89template <typename T>
90concept ShapeFunction = requires(double* xi, Eigen::Map<Eigen::VectorXd> dN) {
91 { T::DIM } -> std::convertible_to<int>;
92 { T::NPOINTS } -> std::convertible_to<int>;
93 T::computeGradShapeFunction(xi, dN);
94};
95
97{
98 std::function<bool(MeshLib::Element&, int, bool)> is_node_ordering_correct;
99 std::function<void(MeshLib::Element&, const std::vector<MeshLib::Node*>&)>
101};
102
103template <ShapeFunction ShapeFunc, int Dim>
105 std::array<double, Dim> xi,
106 std::function<void(MeshLib::Element&, const std::vector<MeshLib::Node*>&)>
107 reorder)
108{
110 [xi](MeshLib::Element& e, int mesh_space_dimension,
111 bool check_reordered)
112 {
114 e, mesh_space_dimension, xi, check_reordered);
115 },
116 reorder};
117}
118
119static const std::array<ElementReorderConfigBase,
120 static_cast<int>(MeshLib::CellType::enum_length)>
122{
123 // Generic node reversal for 1D/2D elements
124 auto reverse_nodes_non_3d =
125 [](MeshLib::Element& element, const std::vector<MeshLib::Node*>& nodes)
126 {
127 const unsigned nElemNodes = nodes.size();
128 for (std::size_t j = 0; j < nElemNodes; ++j)
129 {
130 element.setNode(j, nodes[nElemNodes - j - 1]);
131 }
132 };
133 std::array<ElementReorderConfigBase,
134 static_cast<int>(MeshLib::CellType::enum_length)>
135 arr{};
136
137 arr[static_cast<int>(MeshLib::CellType::LINE2)] =
139 {0.5},
140 [&reverse_nodes_non_3d](MeshLib::Element& element,
141 const std::vector<MeshLib::Node*>& nodes)
142 { reverse_nodes_non_3d(element, nodes); });
143
144 arr[static_cast<int>(MeshLib::CellType::TRI3)] =
146 {1.0 / 3.0, 1.0 / 3.0},
147 [&reverse_nodes_non_3d](MeshLib::Element& element,
148 const std::vector<MeshLib::Node*>& nodes)
149 { reverse_nodes_non_3d(element, nodes); });
150
151 arr[static_cast<int>(MeshLib::CellType::QUAD4)] =
153 {0.0, 0.0},
154 [&reverse_nodes_non_3d](MeshLib::Element& element,
155 const std::vector<MeshLib::Node*>& nodes)
156 { reverse_nodes_non_3d(element, nodes); });
157
158 arr[static_cast<int>(MeshLib::CellType::TET4)] =
160 {0.25, 0.25, 0.25},
161 [](MeshLib::Element& element,
162 const std::vector<MeshLib::Node*>& nodes)
163 {
164 for (std::size_t j = 0; j < 4; ++j)
165 {
166 element.setNode(j, nodes[(j + 1) % 4]);
167 }
168 });
169
170 arr[static_cast<int>(MeshLib::CellType::PRISM6)] =
172 {1.0 / 3.0, 1.0 / 3.0, 0.5},
173 [](MeshLib::Element& element,
174 const std::vector<MeshLib::Node*>& nodes)
175 {
176 for (std::size_t j = 0; j < 3; ++j)
177 {
178 element.setNode(j, nodes[j + 3]);
179 element.setNode(j + 3, nodes[j]);
180 }
181 });
182
183 arr[static_cast<int>(MeshLib::CellType::PYRAMID5)] =
185 {0.25, 0.25, 0.5},
186 [](MeshLib::Element& element,
187 const std::vector<MeshLib::Node*>& nodes)
188 {
189 element.setNode(0, nodes[1]);
190 element.setNode(1, nodes[0]);
191 element.setNode(2, nodes[3]);
192 element.setNode(3, nodes[2]);
193 });
194
195 arr[static_cast<int>(MeshLib::CellType::HEX8)] =
197 {0.5, 0.5, 0.5},
198 [](MeshLib::Element& element,
199 const std::vector<MeshLib::Node*>& nodes)
200 {
201 for (std::size_t j = 0; j < 4; ++j)
202 {
203 element.setNode(j, nodes[j + 4]);
204 element.setNode(j + 4, nodes[j]);
205 }
206 });
207
208 return arr;
209}();
210
220void reverseNodeOrder(std::vector<MeshLib::Element*>& elements,
221 int const mesh_space_dimension, bool const forced)
222{
223 std::size_t n_corrected_elements = 0;
224
225 for (auto* element : elements)
226 {
227 auto const cell_type = element->getCellType();
228
229 if (cell_type == MeshLib::CellType::INVALID)
230 {
231 OGS_FATAL("Element type `{:s}' does not exist.",
232 MeshLib::CellType2String(cell_type));
233 }
234
235 if (cell_type == MeshLib::CellType::POINT1)
236 {
237 continue;
238 }
239
240 const auto& element_config =
241 element_configs_array[static_cast<int>(cell_type)];
242
243 if (element_config.is_node_ordering_correct(
244 *element, mesh_space_dimension, false /*check_reordered*/))
245 {
246 continue;
247 }
248
249 // Save nodes before reordering
250 const unsigned nElemNodes = element->getNumberOfBaseNodes();
251 std::vector<MeshLib::Node*> nodes(element->getNodes(),
252 element->getNodes() + nElemNodes);
253
254 element_config.reorder_element_nodes(*element, nodes);
255
256 // Verify reordering again if forced
257 if (forced)
258 {
259 element_config.is_node_ordering_correct(
260 *element, mesh_space_dimension, true /*check_reordered*/);
261 }
262
263 ++n_corrected_elements;
264 }
265
266 INFO("Corrected {:d} elements.", n_corrected_elements);
267}
268
273void fixVtkInconsistencies(std::vector<MeshLib::Element*>& elements)
274{
275 for (auto* const element : elements)
276 {
277 const unsigned nElemNodes(element->getNumberOfBaseNodes());
278 std::vector<MeshLib::Node*> nodes(element->getNodes(),
279 element->getNodes() + nElemNodes);
280
281 for (std::size_t j = 0; j < nElemNodes; ++j)
282 {
283 if (element->getGeomType() == MeshLib::MeshElemType::PRISM)
284 {
285 for (std::size_t k = 0; k < 3; ++k)
286 {
287 element->setNode(k, nodes[k + 3]);
288 element->setNode(k + 3, nodes[k]);
289 }
290 break;
291 }
292 }
293 }
294}
295
298{
299 std::vector<MeshLib::Node*> base_nodes;
300 std::vector<MeshLib::Node*> nonlinear_nodes;
301 for (MeshLib::Element const* e : mesh.getElements())
302 {
303 for (unsigned i = 0; i < e->getNumberOfBaseNodes(); i++)
304 {
305 base_nodes.push_back(const_cast<MeshLib::Node*>(e->getNode(i)));
306 }
307 for (unsigned i = e->getNumberOfBaseNodes(); i < e->getNumberOfNodes();
308 i++)
309 {
310 nonlinear_nodes.push_back(
311 const_cast<MeshLib::Node*>(e->getNode(i)));
312 }
313 }
314
315 BaseLib::makeVectorUnique(base_nodes,
317 BaseLib::makeVectorUnique(nonlinear_nodes,
319
320 std::vector<MeshLib::Node*>& allnodes =
321 const_cast<std::vector<MeshLib::Node*>&>(mesh.getNodes());
322 allnodes.clear();
323
324 allnodes.insert(allnodes.end(), base_nodes.begin(), base_nodes.end());
325 allnodes.insert(allnodes.end(), nonlinear_nodes.begin(),
326 nonlinear_nodes.end());
327
328 mesh.resetNodeIDs();
329}
330
331int main(int argc, char* argv[])
332{
333 TCLAP::CmdLine cmd(
334 "Reorders mesh nodes in elements to make old or incorrectly ordered "
335 "meshes compatible with OGS6.\n"
336 "Three options are available:\n"
337 "Method 0: Reversing order of nodes and checking again for all "
338 "elements.\n"
339 "Method 1: Reversing order of nodes unless it's perceived correct by "
340 "OGS6 standards. This is the default selection.\n"
341 "Method 2: Fixing node ordering issues between VTK and OGS6 (only "
342 "applies to prism-elements)\n"
343 "Method 3: Re-ordering of mesh node vector such that all base nodes "
344 "are sorted before all nonlinear nodes.\n\n"
345 "OpenGeoSys-6 software, version " +
347 ".\n"
348 "Copyright (c) 2012-2025, OpenGeoSys Community "
349 "(http://www.opengeosys.org)",
351
352 std::vector<int> method_ids{0, 1, 2, 3};
353 TCLAP::ValuesConstraint<int> allowed_values(method_ids);
354 TCLAP::ValueArg<int> method_arg("m", "method",
355 "reordering method selection", false, 1,
356 &allowed_values);
357 cmd.add(method_arg);
358 TCLAP::ValueArg<std::string> output_mesh_arg(
359 "o", "output_mesh", "Output (.vtu). The name of the output mesh file",
360 true, "", "OUTPUT_FILE");
361 cmd.add(output_mesh_arg);
362 TCLAP::ValueArg<std::string> input_mesh_arg(
363 "i", "input_mesh",
364 "Input (.vtu | .vtk | .msh). The name of the input mesh file", true, "",
365 "INPUT_FILE");
366 cmd.add(input_mesh_arg);
367 auto log_level_arg = BaseLib::makeLogLevelArg();
368 cmd.add(log_level_arg);
369 cmd.parse(argc, argv);
370
371 BaseLib::MPI::Setup mpi_setup(argc, argv);
372 BaseLib::initOGSLogger(log_level_arg.getValue());
373
374 std::unique_ptr<MeshLib::Mesh> mesh(
375 MeshLib::IO::readMeshFromFile(input_mesh_arg.getValue()));
376
377 if (!mesh)
378 {
379 return EXIT_FAILURE;
380 }
381
382 INFO("Reordering nodes... ");
383 if (!method_arg.isSet() || method_arg.getValue() < 2)
384 {
385 bool const forced = (method_arg.getValue() == 0);
386
387 if (forced)
388 {
389 INFO("Method 0: Reversing order of nodes will be checked again.");
390 }
391 INFO(
392 "Method: Reversing order of nodes unless it is considered correct "
393 "by the OGS6 standard, i.e. such that det(J) > 0, where J is the "
394 "Jacobian of the global-to-local coordinate transformation.");
395 int const mesh_space_dimension =
396 MeshLib::getSpaceDimension(mesh->getNodes());
398 const_cast<std::vector<MeshLib::Element*>&>(mesh->getElements()),
399 mesh_space_dimension, forced);
400 }
401 else if (method_arg.getValue() == 2)
402 {
404 const_cast<std::vector<MeshLib::Element*>&>(mesh->getElements()));
405 }
406 else if (method_arg.getValue() == 3)
407 {
409 }
410
411 MeshLib::IO::writeMeshToFile(*mesh, output_mesh_arg.getValue());
412
413 INFO("VTU file written.");
414
415 return EXIT_SUCCESS;
416}
Definition of the Element class.
#define OGS_FATAL(...)
Definition Error.h:26
Git information.
void INFO(fmt::format_string< Args... > fmt, Args &&... args)
Definition Logging.h:36
Definition of mesh-related Enumerations.
Definition of the Mesh class.
int main(int argc, char *argv[])
bool checkJacobianDeterminant(MeshLib::Element const &e, int const mesh_space_dimension, std::array< double, Dim > const &xi, bool const check_reordered=false)
static const std::array< ElementReorderConfigBase, static_cast< int >(MeshLib::CellType::enum_length)> element_configs_array
void reverseNodeOrder(std::vector< MeshLib::Element * > &elements, int const mesh_space_dimension, bool const forced)
Reverses order of nodes. In particular, this fixes issues between (Gmsh or OGS5) and OGS6 meshes.
void fixVtkInconsistencies(std::vector< MeshLib::Element * > &elements)
void reorderNonlinearNodes(MeshLib::Mesh &mesh)
Orders the base nodes of each elements before its non-linear nodes.
ElementReorderConfigBase makeElementConfig(std::array< double, Dim > xi, std::function< void(MeshLib::Element &, const std::vector< MeshLib::Node * > &)> reorder)
Definition of the Node class.
Eigen::Vector3d const & asEigenVector3d() const
Definition Point3d.h:64
MathLib::Point3d const & getMappedCoordinates(std::size_t node_id) const
return mapped coordinates of the node
virtual void setNode(unsigned idx, Node *node)=0
std::size_t getID() const
Returns the ID of the element.
Definition Element.h:91
std::vector< Node * > const & getNodes() const
Get the nodes-vector for the mesh.
Definition Mesh.h:108
std::vector< Element * > const & getElements() const
Get the element-vector for the mesh.
Definition Mesh.h:111
void resetNodeIDs()
Resets the IDs of all mesh-nodes to their position in the node vector.
Definition Mesh.cpp:160
TCLAP::ValueArg< std::string > makeLogLevelArg()
void initOGSLogger(std::string const &log_level)
Definition Logging.cpp:64
void makeVectorUnique(std::vector< T > &v)
Definition Algorithm.h:180
GITINFOLIB_EXPORT const std::string ogs_version
MeshLib::Mesh * readMeshFromFile(const std::string &file_name, bool const compute_element_neighbors)
int writeMeshToFile(const MeshLib::Mesh &mesh, std::filesystem::path const &file_path, std::set< std::string > variable_output_names)
bool idsComparator(T const a, T const b)
Definition Mesh.h:208
std::string CellType2String(const CellType t)
Given a MeshElemType this returns the appropriate string.
int getSpaceDimension(std::vector< Node * > const &nodes)
Computes dimension of the embedding space containing the set of given points.
Definition of readMeshFromFile function.
std::function< bool(MeshLib::Element &, int, bool)> is_node_ordering_correct
std::function< void(MeshLib::Element &, const std::vector< MeshLib::Node * > &)> reorder_element_nodes