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#include <QCoreApplication>
20
23#include "BaseLib/FileTools.h"
25#include "BaseLib/MPI.h"
26#include "GeoLib/AABB.h"
28#include "GeoLib/GEOObjects.h"
30#include "GeoLib/Point.h"
31#include "GeoLib/Polygon.h"
32#include "GeoLib/Polyline.h"
33#include "InfoLib/GitInfo.h"
34#include "MathLib/MathTools.h"
35#include "MathLib/Point3d.h"
40#include "MeshLib/Mesh.h"
42#include "MeshLib/Node.h"
46
48std::vector<GeoLib::Point*> createPoints(MathLib::Point3d const& start,
49 MathLib::Point3d const& end,
50 std::size_t const n_intervals)
51{
52 std::vector<GeoLib::Point*> points;
53 points.push_back(new GeoLib::Point(start, 0));
54 double const length = std::sqrt(MathLib::sqrDist(start, end));
55 double const interval = length / n_intervals;
56
57 GeoLib::Point const vec((end[0] - start[0]), (end[1] - start[1]),
58 (end[2] - start[2]));
59 for (std::size_t i = 1; i < n_intervals; ++i)
60 {
61 points.push_back(new GeoLib::Point(
62 start[0] + ((i * interval) / length * vec[0]),
63 start[1] + ((i * interval) / length * vec[1]), 0, i));
64 }
65 points.push_back(new GeoLib::Point(end, n_intervals));
66 return points;
67}
68
70GeoLib::Polyline* createPolyline(std::vector<GeoLib::Point*> const& points)
71{
72 GeoLib::Polyline* line = new GeoLib::Polyline(points);
73 std::size_t const length = points.size();
74 for (std::size_t i = 0; i < length; ++i)
75 {
76 line->addPoint(i);
77 }
78 return line;
79}
80
82std::vector<std::string> createGeometries(
84 std::vector<std::string> const& layer_names,
85 MathLib::Point3d const& pnt_start,
86 MathLib::Point3d const& pnt_end,
87 double const resolution)
88{
89 std::vector<std::string> geo_name_list;
90 std::size_t const n_layers = layer_names.size();
91 for (std::size_t i = 0; i < n_layers; ++i)
92 {
93 std::unique_ptr<MeshLib::Mesh> const layer(
94 MeshLib::IO::readMeshFromFile(layer_names[i]));
95 if (layer == nullptr)
96 {
97 ERR("Could not read file {:s}. Skipping layer...", layer_names[i]);
98 continue;
99 }
100 if (layer->getDimension() != 2)
101 {
102 ERR("Layer {:d} is not a 2D mesh. Skipping layer...", i);
103 continue;
104 }
105
106 std::string geo_name(std::to_string(i));
107 auto points(createPoints(pnt_start, pnt_end, resolution));
108 geo.addPointVec(std::move(points), geo_name,
110
111 std::vector<GeoLib::Polyline*> lines;
112 lines.push_back(createPolyline(*geo.getPointVec(geo_name)));
113 geo.addPolylineVec(std::move(lines), geo_name,
115
116 MeshGeoToolsLib::GeoMapper mapper(geo, geo_name);
117 mapper.mapOnMesh(layer.get());
118 geo_name_list.push_back(geo_name);
119 }
120 return geo_name_list;
121}
122
124{
125 return ((*a)[2] < (*b)[2]) ? a : b;
126}
127
132 std::vector<std::string> const& geo_names,
133 std::string& merged_geo_name)
134{
135 std::vector<GeoLib::Point*> points;
136 std::vector<GeoLib::Polyline*> lines;
137
138 auto DEM_pnts = *geo.getPointVec(geo_names[0]);
139 std::size_t const pnts_per_line = DEM_pnts.size();
140 std::size_t const n_layers = geo_names.size();
141 std::vector<std::size_t> last_line_idx(pnts_per_line, 0);
142
143 auto layer_pnts = *geo.getPointVec(geo_names.back());
144 for (std::size_t i = 0; i < pnts_per_line; ++i)
145 {
146 points.push_back(new GeoLib::Point(
147 *getMinElevationPoint(layer_pnts[i], DEM_pnts[i]), i));
148 last_line_idx[i] = i;
149 }
150 for (int j = n_layers - 2; j >= 0; --j)
151 {
152 GeoLib::Polyline* line = new GeoLib::Polyline(points);
153 for (std::size_t i = 0; i < pnts_per_line; ++i)
154 {
155 line->addPoint(last_line_idx[pnts_per_line - i - 1]);
156 }
157 layer_pnts = *geo.getPointVec(geo_names[j]);
158 for (std::size_t i = 0; i < pnts_per_line; ++i)
159 {
160 // check if for current point the upper layer boundary is actually
161 // located above the upper boundary
162 std::size_t idx = last_line_idx[i];
163 if ((*points[idx])[2] < (*layer_pnts[i])[2])
164 {
165 idx = points.size();
166 // check current point against DEM
167 points.push_back(new GeoLib::Point(
168 *getMinElevationPoint(layer_pnts[i], DEM_pnts[i]), idx));
169 last_line_idx[i] = idx;
170 }
171 line->addPoint(idx);
172 }
173 // close polygon
174 line->addPoint(line->getPointID(0));
175 lines.push_back(line);
176 }
177
178 geo.addPointVec(std::move(points), merged_geo_name,
180 geo.addPolylineVec(std::move(lines), merged_geo_name,
182}
183
185std::pair<Eigen::Matrix3d, double> rotateGeometryToXY(
186 std::vector<GeoLib::Point*>& points)
187{
188 // compute the plane normal
189 auto const [plane_normal, d] =
190 GeoLib::getNewellPlane(points.begin(), points.end());
191 // rotate points into x-y-plane
192 Eigen::Matrix3d const rotation_matrix =
194 GeoLib::rotatePoints(rotation_matrix, points.begin(), points.end());
195
196 GeoLib::AABB aabb(points.begin(), points.end());
197 double const z_shift =
198 (aabb.getMinPoint()[2] + aabb.getMaxPoint()[2]) / 2.0;
199 std::for_each(points.begin(), points.end(),
200 [z_shift](GeoLib::Point* p) { (*p)[2] -= z_shift; });
201 return {rotation_matrix, z_shift};
202}
203
209 std::string const& output_name,
210 std::string& merged_geo_name,
211 bool const keep_gml_file)
212{
213 std::string const filename(output_name + ".gml");
215 xml.export_name = merged_geo_name;
217
218 geo.removePolylineVec(merged_geo_name);
219 geo.removePointVec(merged_geo_name);
220
221 xml.readFile(filename);
222
223 if (!keep_gml_file)
224 {
225 BaseLib::removeFile(filename);
226 BaseLib::removeFile(filename + ".md5");
227 }
228}
229
232 std::string const& geo_name,
233 std::string const& output_name, double res)
234{
235 std::string const gmsh_geo_name(output_name + ".geo");
236 std::vector<std::string> gmsh_geo;
237 gmsh_geo.push_back(geo_name);
240 0, gmsh_geo, false, false);
241 gmsh_io.writePhysicalGroups(true);
242 if (!BaseLib::IO::writeStringToFile(gmsh_io.writeToString(), gmsh_geo_name))
243 {
244 ERR("Writing gmsh geo file '{:s}' failed.", gmsh_geo_name);
245 }
246
247 std::string const gmsh_mesh_name = output_name + ".msh";
248 std::string gmsh_command = "gmsh -2 -algo meshadapt " + gmsh_geo_name;
249 gmsh_command += " -o " + gmsh_mesh_name + " -format msh22";
250 int const return_value = std::system(gmsh_command.c_str());
251 if (return_value != 0)
252 {
253 ERR("Execution of gmsh command returned non-zero status, %d",
254 return_value);
255 }
256 return FileIO::GMSH::readGMSHMesh(gmsh_mesh_name,
257 false /*is_created_with_gmsh2*/);
258}
259
261void rotateMesh(MeshLib::Mesh& mesh, Eigen::Matrix3d const& rot_mat,
262 double const z_shift)
263{
264 std::vector<MeshLib::Node*> const& nodes = mesh.getNodes();
265 std::for_each(nodes.begin(), nodes.end(),
266 [z_shift](MeshLib::Node* n) { (*n)[2] += z_shift; });
267 GeoLib::rotatePoints(rot_mat.transpose(), nodes.begin(), nodes.end());
268}
269
272{
273 std::vector<std::size_t> line_idx;
274 std::vector<MeshLib::Element*> const& elems = mesh.getElements();
275 std::for_each(elems.begin(), elems.end(),
276 [&](auto e)
277 {
278 if (e->getGeomType() == MeshLib::MeshElemType::LINE)
279 {
280 line_idx.push_back(e->getID());
281 }
282 });
283 if (line_idx.size() == mesh.getNumberOfElements())
284 {
285 return nullptr;
286 }
287 return MeshToolsLib::removeElements(mesh, line_idx, "mesh");
288}
289
291 std::vector<std::size_t> const& idx_array,
292 std::string const& file_name)
293{
294 std::unique_ptr<MeshLib::Mesh> const boundary(
295 MeshToolsLib::removeElements(mesh, idx_array, "mesh"));
296 if (boundary == nullptr)
297 {
298 ERR("Error extracting boundary '{:s}'", file_name);
299 return;
300 }
301 MeshLib::IO::VtuInterface vtu(boundary.get());
302 vtu.writeToFile(file_name + ".vtu");
303}
304
306 std::string const& output_name,
307 MathLib::Point3d const& pnt_start)
308{
309 auto const edge_length = minMaxEdgeLength(mesh.getElements());
310 double const eps = edge_length.first / 100.0;
311 std::unique_ptr<MeshLib::Mesh> boundary_mesh(
313 mesh,
317
318 auto const& elems = boundary_mesh->getElements();
319 std::vector<std::size_t> left_bound_idx, right_bound_idx, top_bound_idx,
320 bottom_bound_idx;
321 Eigen::Vector2d const anchor(pnt_start[0], pnt_start[1]);
322 for (auto e : elems)
323 {
324 Eigen::Vector2d const n1((*e->getNode(0))[0], (*e->getNode(0))[1]);
325 Eigen::Vector2d const n2((*e->getNode(1))[0], (*e->getNode(1))[1]);
326 Eigen::Vector2d const dist1(n1 - anchor);
327 Eigen::Vector2d const dist2(n2 - anchor);
328 std::size_t const id = e->getID();
329 // elements located at left or right side
330 if ((dist1 - dist2).squaredNorm() < eps)
331 {
332 top_bound_idx.push_back(id);
333 bottom_bound_idx.push_back(id);
334
335 if (dist1.squaredNorm() < eps)
336 {
337 right_bound_idx.push_back(id);
338 }
339 else
340 {
341 left_bound_idx.push_back(id);
342 }
343 continue;
344 }
345 // elements located at top or bottom
346 if (dist2.squaredNorm() < dist1.squaredNorm())
347 {
348 top_bound_idx.push_back(id);
349 left_bound_idx.push_back(id);
350 right_bound_idx.push_back(id);
351 }
352 else
353 {
354 bottom_bound_idx.push_back(id);
355 left_bound_idx.push_back(id);
356 right_bound_idx.push_back(id);
357 }
358 }
359
360 writeBoundary(*boundary_mesh, left_bound_idx, output_name + "_left");
361 writeBoundary(*boundary_mesh, right_bound_idx, output_name + "_right");
362 writeBoundary(*boundary_mesh, top_bound_idx, output_name + "_top");
363 writeBoundary(*boundary_mesh, bottom_bound_idx, output_name + "_bottom");
364}
365
366int main(int argc, char* argv[])
367{
368 QCoreApplication a(argc, argv);
369 TCLAP::CmdLine cmd(
370 "Creates a triangle-mesh of a vertical slice out of a list of input "
371 "layer meshes. The slice is defined by a start- and end-point. In "
372 "addition, the resolution for meshing the extracted slice (i.e. the "
373 "maximum edge length of the domain discretisation) needs to be "
374 "specified. The utility requires access to the meshing utility GMSH to "
375 "work correctly.\n\n"
376 "OpenGeoSys-6 software, version " +
378 ".\n"
379 "Copyright (c) 2012-2024, OpenGeoSys Community "
380 "(http://www.opengeosys.org)",
382 TCLAP::SwitchArg test_arg("t", "testdata", "keep test data", false);
383 cmd.add(test_arg);
384 TCLAP::SwitchArg bound_arg("b", "bounds", "save mesh boundaries", false);
385 cmd.add(bound_arg);
386 TCLAP::ValueArg<double> res_arg(
387 "r", "resolution",
388 "desired edge length of triangles in the resulting slice.", true, 0,
389 "floating point number");
390 cmd.add(res_arg);
391 TCLAP::ValueArg<double> end_y_arg(
392 "", "end-y", "y-coordinates of the end point defining the slice", true,
393 0, "floating point number");
394 cmd.add(end_y_arg);
395 TCLAP::ValueArg<double> end_x_arg(
396 "", "end-x", "x-coordinates of the end point defining the slice", true,
397 0, "floating point number");
398 cmd.add(end_x_arg);
399 TCLAP::ValueArg<double> start_y_arg(
400 "", "start-y", "y-coordinates of the start point defining the slice",
401 true, 0, "floating point number");
402 cmd.add(start_y_arg);
403 TCLAP::ValueArg<double> start_x_arg(
404 "", "start-x", "x-coordinates of the start point defining the slice",
405 true, 0, "floating point number");
406 cmd.add(start_x_arg);
407 TCLAP::ValueArg<std::string> output_arg(
408 "o", "output", "name of output mesh (*.vtu)", true, "", "string");
409 cmd.add(output_arg);
410 TCLAP::ValueArg<std::string> input_arg(
411 "i", "input",
412 "name of the input file list containing the paths the all input layers "
413 "in correct order from top to bottom",
414 true, "", "string");
415 cmd.add(input_arg);
416 cmd.parse(argc, argv);
417
418 BaseLib::MPI::Setup mpi_setup(argc, argv);
419
420 std::string const input_name = input_arg.getValue();
421 std::string const output_name = output_arg.getValue();
422
423 MathLib::Point3d const pnt_start{
424 {start_x_arg.getValue(), start_y_arg.getValue(), 0.0}};
425 MathLib::Point3d const pnt_end{
426 {end_x_arg.getValue(), end_y_arg.getValue(), 0.0}};
427 double const length = std::sqrt(MathLib::sqrDist(pnt_start, pnt_end));
428
429 std::size_t const res = std::ceil(length / res_arg.getValue());
430 double const interval_length = length / res;
431
432 std::vector<std::string> const layer_names =
434 if (layer_names.size() < 2)
435 {
436 ERR("At least two layers are required to extract a slice.");
437 return EXIT_FAILURE;
438 }
439
441 std::vector<std::string> const geo_name_list =
442 createGeometries(geo, layer_names, pnt_start, pnt_end, res);
443
444 if (geo_name_list.size() < 2)
445 {
446 ERR("Less than two geometries could be created from layers. Aborting "
447 "extraction...");
448 return EXIT_FAILURE;
449 }
450
451 std::string merged_geo_name = "merged_geometries";
452 mergeGeometries(geo, geo_name_list, merged_geo_name);
453 std::vector<GeoLib::Point*> points = *geo.getPointVec(merged_geo_name);
454 auto const [rot_mat, z_shift] = rotateGeometryToXY(points);
455 consolidateGeometry(geo, output_name, merged_geo_name, test_arg.getValue());
456
457 std::unique_ptr<MeshLib::Mesh> mesh(
458 generateMesh(geo, merged_geo_name, output_name, interval_length));
459 if (!test_arg.getValue())
460 {
461 BaseLib::removeFile(output_name + ".geo");
462 BaseLib::removeFile(output_name + ".msh");
463 }
464 if (mesh == nullptr)
465 {
466 ERR("Error generating mesh... (GMSH was unable to output mesh)");
467 return EXIT_FAILURE;
468 }
469 rotateMesh(*mesh, rot_mat, z_shift);
470 std::unique_ptr<MeshLib::Mesh> new_mesh(removeLineElements(*mesh));
471 if (new_mesh == nullptr)
472 {
473 ERR("Error generating mesh... (GMSH created line mesh)");
474 return EXIT_FAILURE;
475 }
476
477 // collapse all nodes that might have been created due to gmsh physically
478 // separating layers
479 MeshToolsLib::MeshRevision rev(*new_mesh);
480 auto const edge_length = minMaxEdgeLength(new_mesh->getElements());
481 double const minimum_node_distance = edge_length.first / 100.0;
482
483 std::unique_ptr<MeshLib::Mesh> revised_mesh(
484 rev.simplifyMesh("RevisedMesh", minimum_node_distance));
485
486 if (bound_arg.getValue())
487 {
488 extractBoundaries(*revised_mesh, output_name, pnt_start);
489 }
490 MeshLib::IO::VtuInterface vtu(revised_mesh.get());
491 vtu.writeToFile(output_name + ".vtu");
492 return EXIT_SUCCESS;
493}
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.