OGS
VerticalSliceFromLayers.cpp
Go to the documentation of this file.
1
10#include <algorithm>
11#include <cmath>
12#include <memory>
13#include <string>
14#include <vector>
15
16// ThirdParty
17#include <tclap/CmdLine.h>
18
19#ifdef USE_PETSC
20#include <mpi.h>
21#endif
22
23#include <QCoreApplication>
24
27#include "BaseLib/FileTools.h"
29#include "GeoLib/AABB.h"
31#include "GeoLib/GEOObjects.h"
33#include "GeoLib/Point.h"
34#include "GeoLib/Polygon.h"
35#include "GeoLib/Polyline.h"
36#include "InfoLib/GitInfo.h"
37#include "MathLib/MathTools.h"
38#include "MathLib/Point3d.h"
43#include "MeshLib/Mesh.h"
45#include "MeshLib/Node.h"
49
51std::vector<GeoLib::Point*> createPoints(MathLib::Point3d const& start,
52 MathLib::Point3d const& end,
53 std::size_t const n_intervals)
54{
55 std::vector<GeoLib::Point*> points;
56 points.push_back(new GeoLib::Point(start, 0));
57 double const length = std::sqrt(MathLib::sqrDist(start, end));
58 double const interval = length / n_intervals;
59
60 GeoLib::Point const vec((end[0] - start[0]), (end[1] - start[1]),
61 (end[2] - start[2]));
62 for (std::size_t i = 1; i < n_intervals; ++i)
63 {
64 points.push_back(new GeoLib::Point(
65 start[0] + ((i * interval) / length * vec[0]),
66 start[1] + ((i * interval) / length * vec[1]), 0, i));
67 }
68 points.push_back(new GeoLib::Point(end, n_intervals));
69 return points;
70}
71
73GeoLib::Polyline* createPolyline(std::vector<GeoLib::Point*> const& points)
74{
75 GeoLib::Polyline* line = new GeoLib::Polyline(points);
76 std::size_t const length = points.size();
77 for (std::size_t i = 0; i < length; ++i)
78 {
79 line->addPoint(i);
80 }
81 return line;
82}
83
85std::vector<std::string> createGeometries(
87 std::vector<std::string> const& layer_names,
88 MathLib::Point3d const& pnt_start,
89 MathLib::Point3d const& pnt_end,
90 double const resolution)
91{
92 std::vector<std::string> geo_name_list;
93 std::size_t const n_layers = layer_names.size();
94 for (std::size_t i = 0; i < n_layers; ++i)
95 {
96 std::unique_ptr<MeshLib::Mesh> const layer(
97 MeshLib::IO::readMeshFromFile(layer_names[i]));
98 if (layer == nullptr)
99 {
100 ERR("Could not read file {:s}. Skipping layer...", layer_names[i]);
101 continue;
102 }
103 if (layer->getDimension() != 2)
104 {
105 ERR("Layer {:d} is not a 2D mesh. Skipping layer...", i);
106 continue;
107 }
108
109 std::string geo_name(std::to_string(i));
110 auto points(createPoints(pnt_start, pnt_end, resolution));
111 geo.addPointVec(std::move(points), geo_name,
113
114 std::vector<GeoLib::Polyline*> lines;
115 lines.push_back(createPolyline(*geo.getPointVec(geo_name)));
116 geo.addPolylineVec(std::move(lines), geo_name,
118
119 MeshGeoToolsLib::GeoMapper mapper(geo, geo_name);
120 mapper.mapOnMesh(layer.get());
121 geo_name_list.push_back(geo_name);
122 }
123 return geo_name_list;
124}
125
127{
128 return ((*a)[2] < (*b)[2]) ? a : b;
129}
130
135 std::vector<std::string> const& geo_names,
136 std::string& merged_geo_name)
137{
138 std::vector<GeoLib::Point*> points;
139 std::vector<GeoLib::Polyline*> lines;
140
141 auto DEM_pnts = *geo.getPointVec(geo_names[0]);
142 std::size_t const pnts_per_line = DEM_pnts.size();
143 std::size_t const n_layers = geo_names.size();
144 std::vector<std::size_t> last_line_idx(pnts_per_line, 0);
145
146 auto layer_pnts = *geo.getPointVec(geo_names.back());
147 for (std::size_t i = 0; i < pnts_per_line; ++i)
148 {
149 points.push_back(new GeoLib::Point(
150 *getMinElevationPoint(layer_pnts[i], DEM_pnts[i]), i));
151 last_line_idx[i] = i;
152 }
153 for (int j = n_layers - 2; j >= 0; --j)
154 {
155 GeoLib::Polyline* line = new GeoLib::Polyline(points);
156 for (std::size_t i = 0; i < pnts_per_line; ++i)
157 {
158 line->addPoint(last_line_idx[pnts_per_line - i - 1]);
159 }
160 layer_pnts = *geo.getPointVec(geo_names[j]);
161 for (std::size_t i = 0; i < pnts_per_line; ++i)
162 {
163 // check if for current point the upper layer boundary is actually
164 // located above the upper boundary
165 std::size_t idx = last_line_idx[i];
166 if ((*points[idx])[2] < (*layer_pnts[i])[2])
167 {
168 idx = points.size();
169 // check current point against DEM
170 points.push_back(new GeoLib::Point(
171 *getMinElevationPoint(layer_pnts[i], DEM_pnts[i]), idx));
172 last_line_idx[i] = idx;
173 }
174 line->addPoint(idx);
175 }
176 // close polygon
177 line->addPoint(line->getPointID(0));
178 lines.push_back(line);
179 }
180
181 geo.addPointVec(std::move(points), merged_geo_name,
183 geo.addPolylineVec(std::move(lines), merged_geo_name,
185}
186
188std::pair<Eigen::Matrix3d, double> rotateGeometryToXY(
189 std::vector<GeoLib::Point*>& points)
190{
191 // compute the plane normal
192 auto const [plane_normal, d] =
193 GeoLib::getNewellPlane(points.begin(), points.end());
194 // rotate points into x-y-plane
195 Eigen::Matrix3d const rotation_matrix =
197 GeoLib::rotatePoints(rotation_matrix, points.begin(), points.end());
198
199 GeoLib::AABB aabb(points.begin(), points.end());
200 double const z_shift =
201 (aabb.getMinPoint()[2] + aabb.getMaxPoint()[2]) / 2.0;
202 std::for_each(points.begin(), points.end(),
203 [z_shift](GeoLib::Point* p) { (*p)[2] -= z_shift; });
204 return {rotation_matrix, z_shift};
205}
206
212 std::string const& output_name,
213 std::string& merged_geo_name,
214 bool const keep_gml_file)
215{
216 std::string const filename(output_name + ".gml");
218 xml.export_name = merged_geo_name;
220
221 geo.removePolylineVec(merged_geo_name);
222 geo.removePointVec(merged_geo_name);
223
224 xml.readFile(filename);
225
226 if (!keep_gml_file)
227 {
228 BaseLib::removeFile(filename);
229 BaseLib::removeFile(filename + ".md5");
230 }
231}
232
235 std::string const& geo_name,
236 std::string const& output_name, double res)
237{
238 std::string const gmsh_geo_name(output_name + ".geo");
239 std::vector<std::string> gmsh_geo;
240 gmsh_geo.push_back(geo_name);
243 0, gmsh_geo, false, false);
244 gmsh_io.writePhysicalGroups(true);
245 if (!BaseLib::IO::writeStringToFile(gmsh_io.writeToString(), gmsh_geo_name))
246 {
247 ERR("Writing gmsh geo file '{:s}' failed.", gmsh_geo_name);
248 }
249
250 std::string const gmsh_mesh_name = output_name + ".msh";
251 std::string gmsh_command = "gmsh -2 -algo meshadapt " + gmsh_geo_name;
252 gmsh_command += " -o " + gmsh_mesh_name + " -format msh22";
253 int const return_value = std::system(gmsh_command.c_str());
254 if (return_value != 0)
255 {
256 ERR("Execution of gmsh command returned non-zero status, %d",
257 return_value);
258 }
259 return FileIO::GMSH::readGMSHMesh(gmsh_mesh_name,
260 false /*is_created_with_gmsh2*/);
261}
262
264void rotateMesh(MeshLib::Mesh& mesh, Eigen::Matrix3d const& rot_mat,
265 double const z_shift)
266{
267 std::vector<MeshLib::Node*> const& nodes = mesh.getNodes();
268 std::for_each(nodes.begin(), nodes.end(),
269 [z_shift](MeshLib::Node* n) { (*n)[2] += z_shift; });
270 GeoLib::rotatePoints(rot_mat.transpose(), nodes.begin(), nodes.end());
271}
272
275{
276 std::vector<std::size_t> line_idx;
277 std::vector<MeshLib::Element*> const& elems = mesh.getElements();
278 std::for_each(elems.begin(), elems.end(),
279 [&](auto e)
280 {
281 if (e->getGeomType() == MeshLib::MeshElemType::LINE)
282 {
283 line_idx.push_back(e->getID());
284 }
285 });
286 if (line_idx.size() == mesh.getNumberOfElements())
287 {
288 return nullptr;
289 }
290 return MeshToolsLib::removeElements(mesh, line_idx, "mesh");
291}
292
294 std::vector<std::size_t> const& idx_array,
295 std::string const& file_name)
296{
297 std::unique_ptr<MeshLib::Mesh> const boundary(
298 MeshToolsLib::removeElements(mesh, idx_array, "mesh"));
299 if (boundary == nullptr)
300 {
301 ERR("Error extracting boundary '{:s}'", file_name);
302 return;
303 }
304 MeshLib::IO::VtuInterface vtu(boundary.get());
305 vtu.writeToFile(file_name + ".vtu");
306}
307
309 std::string const& output_name,
310 MathLib::Point3d const& pnt_start)
311{
312 auto const edge_length = minMaxEdgeLength(mesh.getElements());
313 double const eps = edge_length.first / 100.0;
314 std::unique_ptr<MeshLib::Mesh> boundary_mesh(
316 mesh,
320
321 auto const& elems = boundary_mesh->getElements();
322 std::vector<std::size_t> left_bound_idx, right_bound_idx, top_bound_idx,
323 bottom_bound_idx;
324 Eigen::Vector2d const anchor(pnt_start[0], pnt_start[1]);
325 for (auto e : elems)
326 {
327 Eigen::Vector2d const n1((*e->getNode(0))[0], (*e->getNode(0))[1]);
328 Eigen::Vector2d const n2((*e->getNode(1))[0], (*e->getNode(1))[1]);
329 Eigen::Vector2d const dist1(n1 - anchor);
330 Eigen::Vector2d const dist2(n2 - anchor);
331 std::size_t const id = e->getID();
332 // elements located at left or right side
333 if ((dist1 - dist2).squaredNorm() < eps)
334 {
335 top_bound_idx.push_back(id);
336 bottom_bound_idx.push_back(id);
337
338 if (dist1.squaredNorm() < eps)
339 {
340 right_bound_idx.push_back(id);
341 }
342 else
343 {
344 left_bound_idx.push_back(id);
345 }
346 continue;
347 }
348 // elements located at top or bottom
349 if (dist2.squaredNorm() < dist1.squaredNorm())
350 {
351 top_bound_idx.push_back(id);
352 left_bound_idx.push_back(id);
353 right_bound_idx.push_back(id);
354 }
355 else
356 {
357 bottom_bound_idx.push_back(id);
358 left_bound_idx.push_back(id);
359 right_bound_idx.push_back(id);
360 }
361 }
362
363 writeBoundary(*boundary_mesh, left_bound_idx, output_name + "_left");
364 writeBoundary(*boundary_mesh, right_bound_idx, output_name + "_right");
365 writeBoundary(*boundary_mesh, top_bound_idx, output_name + "_top");
366 writeBoundary(*boundary_mesh, bottom_bound_idx, output_name + "_bottom");
367}
368
369int main(int argc, char* argv[])
370{
371 QCoreApplication a(argc, argv);
372 TCLAP::CmdLine cmd(
373 "Creates a triangle-mesh of a vertical slice out of a list of input "
374 "layer meshes. The slice is defined by a start- and end-point. In "
375 "addition, the resolution for meshing the extracted slice (i.e. the "
376 "maximum edge length of the domain discretisation) needs to be "
377 "specified. The utility requires access to the meshing utility GMSH to "
378 "work correctly.\n\n"
379 "OpenGeoSys-6 software, version " +
381 ".\n"
382 "Copyright (c) 2012-2024, OpenGeoSys Community "
383 "(http://www.opengeosys.org)",
385 TCLAP::SwitchArg test_arg("t", "testdata", "keep test data", false);
386 cmd.add(test_arg);
387 TCLAP::SwitchArg bound_arg("b", "bounds", "save mesh boundaries", false);
388 cmd.add(bound_arg);
389 TCLAP::ValueArg<double> res_arg(
390 "r", "resolution",
391 "desired edge length of triangles in the resulting slice.", true, 0,
392 "floating point number");
393 cmd.add(res_arg);
394 TCLAP::ValueArg<double> end_y_arg(
395 "", "end-y", "y-coordinates of the end point defining the slice", true,
396 0, "floating point number");
397 cmd.add(end_y_arg);
398 TCLAP::ValueArg<double> end_x_arg(
399 "", "end-x", "x-coordinates of the end point defining the slice", true,
400 0, "floating point number");
401 cmd.add(end_x_arg);
402 TCLAP::ValueArg<double> start_y_arg(
403 "", "start-y", "y-coordinates of the start point defining the slice",
404 true, 0, "floating point number");
405 cmd.add(start_y_arg);
406 TCLAP::ValueArg<double> start_x_arg(
407 "", "start-x", "x-coordinates of the start point defining the slice",
408 true, 0, "floating point number");
409 cmd.add(start_x_arg);
410 TCLAP::ValueArg<std::string> output_arg(
411 "o", "output", "name of output mesh (*.vtu)", true, "", "string");
412 cmd.add(output_arg);
413 TCLAP::ValueArg<std::string> input_arg(
414 "i", "input",
415 "name of the input file list containing the paths the all input layers "
416 "in correct order from top to bottom",
417 true, "", "string");
418 cmd.add(input_arg);
419 cmd.parse(argc, argv);
420
421#ifdef USE_PETSC
422 MPI_Init(&argc, &argv);
423#endif
424
425 std::string const input_name = input_arg.getValue();
426 std::string const output_name = output_arg.getValue();
427
428 MathLib::Point3d const pnt_start{
429 {start_x_arg.getValue(), start_y_arg.getValue(), 0.0}};
430 MathLib::Point3d const pnt_end{
431 {end_x_arg.getValue(), end_y_arg.getValue(), 0.0}};
432 double const length = std::sqrt(MathLib::sqrDist(pnt_start, pnt_end));
433
434 std::size_t const res = std::ceil(length / res_arg.getValue());
435 double const interval_length = length / res;
436
437 std::vector<std::string> const layer_names =
439 if (layer_names.size() < 2)
440 {
441 ERR("At least two layers are required to extract a slice.");
442#ifdef USE_PETSC
443 MPI_Finalize();
444#endif
445 return EXIT_FAILURE;
446 }
447
449 std::vector<std::string> const geo_name_list =
450 createGeometries(geo, layer_names, pnt_start, pnt_end, res);
451
452 if (geo_name_list.size() < 2)
453 {
454 ERR("Less than two geometries could be created from layers. Aborting "
455 "extraction...");
456#ifdef USE_PETSC
457 MPI_Finalize();
458#endif
459 return EXIT_FAILURE;
460 }
461
462 std::string merged_geo_name = "merged_geometries";
463 mergeGeometries(geo, geo_name_list, merged_geo_name);
464 std::vector<GeoLib::Point*> points = *geo.getPointVec(merged_geo_name);
465 auto const [rot_mat, z_shift] = rotateGeometryToXY(points);
466 consolidateGeometry(geo, output_name, merged_geo_name, test_arg.getValue());
467
468 std::unique_ptr<MeshLib::Mesh> mesh(
469 generateMesh(geo, merged_geo_name, output_name, interval_length));
470 if (!test_arg.getValue())
471 {
472 BaseLib::removeFile(output_name + ".geo");
473 BaseLib::removeFile(output_name + ".msh");
474 }
475 if (mesh == nullptr)
476 {
477 ERR("Error generating mesh... (GMSH was unable to output mesh)");
478#ifdef USE_PETSC
479 MPI_Finalize();
480#endif
481 return EXIT_FAILURE;
482 }
483 rotateMesh(*mesh, rot_mat, z_shift);
484 std::unique_ptr<MeshLib::Mesh> new_mesh(removeLineElements(*mesh));
485 if (new_mesh == nullptr)
486 {
487 ERR("Error generating mesh... (GMSH created line mesh)");
488#ifdef USE_PETSC
489 MPI_Finalize();
490#endif
491 return EXIT_FAILURE;
492 }
493
494 // collapse all nodes that might have been created due to gmsh physically
495 // separating layers
496 MeshToolsLib::MeshRevision rev(*new_mesh);
497 auto const edge_length = minMaxEdgeLength(new_mesh->getElements());
498 double const minimum_node_distance = edge_length.first / 100.0;
499
500 std::unique_ptr<MeshLib::Mesh> revised_mesh(
501 rev.simplifyMesh("RevisedMesh", minimum_node_distance));
502
503 if (bound_arg.getValue())
504 {
505 extractBoundaries(*revised_mesh, output_name, pnt_start);
506 }
507 MeshLib::IO::VtuInterface vtu(revised_mesh.get());
508 vtu.writeToFile(output_name + ".vtu");
509#ifdef USE_PETSC
510 MPI_Finalize();
511#endif
512 return EXIT_SUCCESS;
513}
Definition of the AABB class.
Definition of analytical geometry functions.
Definition of the Element class.
Filename manipulation routines.
Definition of the GEOObjects class.
Definition of the Point class.
Definition of the GeoMapper class.
Git information.
void ERR(fmt::format_string< Args... > fmt, Args &&... args)
Definition Logging.h:45
Definition of the MeshRevision class.
Definition of the MeshSurfaceExtraction class.
Definition of the Mesh class.
Definition of the Node class.
Definition of the Point3d class.
Definition of the Polygon class.
Definition of the PolyLine class.
int main(int argc, char *argv[])
void rotateMesh(MeshLib::Mesh &mesh, Eigen::Matrix3d const &rot_mat, double const z_shift)
inverse rotation of the mesh, back into original position
void extractBoundaries(MeshLib::Mesh const &mesh, std::string const &output_name, MathLib::Point3d const &pnt_start)
std::vector< std::string > createGeometries(GeoLib::GEOObjects &geo, std::vector< std::string > const &layer_names, MathLib::Point3d const &pnt_start, MathLib::Point3d const &pnt_end, double const resolution)
creates a mapped line for each of the mesh layers
void writeBoundary(MeshLib::Mesh const &mesh, std::vector< std::size_t > const &idx_array, std::string const &file_name)
void mergeGeometries(GeoLib::GEOObjects &geo, std::vector< std::string > const &geo_names, std::string &merged_geo_name)
MeshLib::Mesh * removeLineElements(MeshLib::Mesh const &mesh)
removes line elements from mesh such that only triangles remain
GeoLib::Polyline * createPolyline(std::vector< GeoLib::Point * > const &points)
creates a polyline to be mapped on a mesh layer
std::pair< Eigen::Matrix3d, double > rotateGeometryToXY(std::vector< GeoLib::Point * > &points)
rotates the merged geometry into the XY-plane
std::vector< GeoLib::Point * > createPoints(MathLib::Point3d const &start, MathLib::Point3d const &end, std::size_t const n_intervals)
creates a vector of sampling points based on the specified resolution
static GeoLib::Point * getMinElevationPoint(GeoLib::Point *a, GeoLib::Point *b)
void consolidateGeometry(GeoLib::GEOObjects &geo, std::string const &output_name, std::string &merged_geo_name, bool const keep_gml_file)
MeshLib::Mesh * generateMesh(GeoLib::GEOObjects &geo, std::string const &geo_name, std::string const &output_name, double res)
converts geometry into GMSH format and creates mesh
Implementation of the VtuInterface class.
Definition of the XmlGmlInterface class.
std::string writeToString()
Writes the object to a string.
Definition Writer.cpp:31
Reads and writes GMSH-files to and from OGS data structures.
void writePhysicalGroups(bool flag)
Class AABB is an axis aligned bounding box around a given set of geometric points of (template) type ...
Definition AABB.h:56
Eigen::Vector3d const & getMaxPoint() const
Definition AABB.h:187
Eigen::Vector3d const & getMinPoint() const
Definition AABB.h:180
Container class for geometric objects.
Definition GEOObjects.h:57
void addPolylineVec(std::vector< Polyline * > &&lines, std::string const &name, PolylineVec::NameIdMap &&ply_names)
const std::vector< Point * > * getPointVec(const std::string &name) const
void addPointVec(std::vector< Point * > &&points, std::string &name, PointVec::NameIdMap &&pnt_id_name_map, double const eps=std::sqrt(std::numeric_limits< double >::epsilon()))
bool removePointVec(const std::string &name)
bool removePolylineVec(const std::string &name)
Reads and writes GeoObjects to and from XML files.
int readFile(const QString &fileName) override
Reads an xml-file containing geometric object definitions into the GEOObjects used in the constructor...
Class Polyline consists mainly of a reference to a point vector and a vector that stores the indices ...
Definition Polyline.h:40
std::size_t getPointID(std::size_t const i) const
Definition Polyline.cpp:160
virtual bool addPoint(std::size_t pnt_id)
Definition Polyline.cpp:35
std::map< std::string, std::size_t > NameIdMap
Definition TemplateVec.h:41
A set of tools for mapping the elevation of geometric objects.
Definition GeoMapper.h:40
void mapOnMesh(MeshLib::Mesh const *const mesh)
Definition GeoMapper.cpp:73
Reads and writes VtkXMLUnstructuredGrid-files (vtu) to and from OGS data structures....
bool writeToFile(std::filesystem::path const &file_path)
std::vector< Node * > const & getNodes() const
Get the nodes-vector for the mesh.
Definition Mesh.h:106
std::vector< Element * > const & getElements() const
Get the element-vector for the mesh.
Definition Mesh.h:109
MeshLib::Mesh * simplifyMesh(const std::string &new_mesh_name, double eps, unsigned min_elem_dim=1) const
std::vector< std::string > readStringListFromFile(std::string const &filename)
Reads non-empty lines from a list of strings from a file into a vector.
int writeStringToFile(std::string_view content, std::filesystem::path const &file_path)
Definition Writer.cpp:45
void removeFile(std::string const &filename)
@ FixedMeshDensity
set the parameter with a fixed value
MeshLib::Mesh * readGMSHMesh(std::string const &fname, bool const is_created_with_gmsh2)
void rotatePoints(Eigen::Matrix3d const &rot_mat, InputIterator pnts_begin, InputIterator pnts_end)
Eigen::Matrix3d computeRotationMatrixToXY(Eigen::Vector3d const &n)
std::pair< Eigen::Vector3d, double > getNewellPlane(InputIterator pnts_begin, InputIterator pnts_end)
GITINFOLIB_EXPORT const std::string ogs_version
double sqrDist(MathLib::Point3d const &p0, MathLib::Point3d const &p1)
Definition Point3d.cpp:26
MeshLib::Mesh * readMeshFromFile(const std::string &file_name, bool const compute_element_neighbors)
constexpr std::string_view getBulkIDString(MeshItemType mesh_item_type)
Definition Properties.h:188
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)
MeshLib::Mesh * removeElements(const MeshLib::Mesh &mesh, const std::vector< std::size_t > &removed_element_ids, const std::string &new_mesh_name)
Definition of readMeshFromFile function.