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 "GeoLib/AABB.h"
27 #include "GeoLib/GEOObjects.h"
29 #include "GeoLib/Point.h"
30 #include "GeoLib/Polygon.h"
31 #include "GeoLib/Polyline.h"
32 #include "InfoLib/GitInfo.h"
33 #include "MathLib/MathTools.h"
34 #include "MathLib/Point3d.h"
39 #include "MeshLib/Mesh.h"
44 #include "MeshLib/Node.h"
45 
47 std::unique_ptr<std::vector<GeoLib::Point*>> createPoints(
48  MathLib::Point3d const& start,
49  MathLib::Point3d const& end,
50  std::size_t const n_intervals)
51 {
52  auto points = std::make_unique<std::vector<GeoLib::Point*>>();
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 
70 GeoLib::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 
82 std::vector<std::string> createGeometries(
83  GeoLib::GEOObjects& geo,
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  std::unique_ptr<std::vector<GeoLib::Point*>> points(
108  createPoints(pnt_start, pnt_end, resolution));
109  geo.addPointVec(std::move(points), geo_name);
110 
111  auto lines = std::make_unique<std::vector<GeoLib::Polyline*>>();
112  lines->push_back(createPolyline(*geo.getPointVec(geo_name)));
113  geo.addPolylineVec(std::move(lines), geo_name);
114 
115  MeshGeoToolsLib::GeoMapper mapper(geo, geo_name);
116  mapper.mapOnMesh(layer.get());
117  geo_name_list.push_back(geo_name);
118  }
119  return geo_name_list;
120 }
121 
126  std::vector<std::string> const& geo_names,
127  std::string& merged_geo_name)
128 {
129  auto points = std::make_unique<std::vector<GeoLib::Point*>>();
130  auto lines = std::make_unique<std::vector<GeoLib::Polyline*>>();
131 
132  auto layer_pnts = *geo.getPointVec(geo_names[0]);
133  std::size_t const pnts_per_line = layer_pnts.size();
134  std::size_t const n_layers = geo_names.size();
135  std::vector<std::size_t> last_line_idx(pnts_per_line, 0);
136 
137  for (std::size_t i = 0; i < pnts_per_line; ++i)
138  {
139  std::size_t const idx = pnts_per_line - i - 1;
140  points->push_back(new GeoLib::Point(*layer_pnts[i], idx));
141  last_line_idx[i] = idx;
142  }
143  for (std::size_t j = 1; j < n_layers; ++j)
144  {
145  GeoLib::Polyline* line = new GeoLib::Polyline(*points);
146  for (std::size_t i = 0; i < pnts_per_line; ++i)
147  {
148  line->addPoint(last_line_idx[i]);
149  }
150  layer_pnts = *geo.getPointVec(geo_names[j]);
151  for (std::size_t i = 0; i < pnts_per_line; ++i)
152  {
153  // check if for current point the lower layer boundary is actually
154  // located below the upper boundary
155  std::size_t idx = last_line_idx[pnts_per_line - i - 1];
156  if ((*(*points)[idx])[2] > (*layer_pnts[i])[2])
157  {
158  idx = points->size();
159  points->push_back(new GeoLib::Point(*layer_pnts[i], idx));
160  last_line_idx[pnts_per_line - i - 1] = idx;
161  }
162  line->addPoint(idx);
163  }
164  // close polygon
165  line->addPoint(line->getPointID(0));
166  lines->push_back(line);
167  }
168 
169  geo.addPointVec(std::move(points), merged_geo_name);
170  geo.addPolylineVec(std::move(lines), merged_geo_name);
171 }
172 
174 std::pair<Eigen::Matrix3d, double> rotateGeometryToXY(
175  std::vector<GeoLib::Point*>& points)
176 {
177  // compute the plane normal
178  auto const [plane_normal, d] =
179  GeoLib::getNewellPlane(points.begin(), points.end());
180  // rotate points into x-y-plane
181  Eigen::Matrix3d const rotation_matrix =
182  GeoLib::computeRotationMatrixToXY(plane_normal);
183  GeoLib::rotatePoints(rotation_matrix, points.begin(), points.end());
184 
185  GeoLib::AABB aabb(points.begin(), points.end());
186  double const z_shift =
187  (aabb.getMinPoint()[2] + aabb.getMaxPoint()[2]) / 2.0;
188  std::for_each(points.begin(), points.end(),
189  [z_shift](GeoLib::Point* p) { (*p)[2] -= z_shift; });
190  return {rotation_matrix, z_shift};
191 }
192 
198  std::string const& output_name,
199  std::string& merged_geo_name,
200  bool const keep_gml_file)
201 {
202  std::string const filename(output_name + ".gml");
204  xml.export_name = merged_geo_name;
206 
207  geo.removePolylineVec(merged_geo_name);
208  geo.removePointVec(merged_geo_name);
209 
210  xml.readFile(filename);
211 
212  if (!keep_gml_file)
213  {
214  BaseLib::removeFile(filename);
215  BaseLib::removeFile(filename + ".md5");
216  }
217 }
218 
221  std::string const& geo_name,
222  std::string const& output_name, double res)
223 {
224  std::string const gmsh_geo_name(output_name + ".geo");
225  std::vector<std::string> gmsh_geo;
226  gmsh_geo.push_back(geo_name);
229  0, gmsh_geo, false, false);
230  gmsh_io.writePhysicalGroups(true);
231  if (!BaseLib::IO::writeStringToFile(gmsh_io.writeToString(), gmsh_geo_name))
232  {
233  ERR("Writing gmsh geo file '{:s}' failed.", gmsh_geo_name);
234  }
235 
236  std::string const gmsh_mesh_name = output_name + ".msh";
237  std::string gmsh_command = "gmsh -2 -algo meshadapt " + gmsh_geo_name;
238  gmsh_command += " -o " + gmsh_mesh_name + " -format msh22";
239  int const return_value = std::system(gmsh_command.c_str());
240  if (return_value != 0)
241  {
242  ERR("Execution of gmsh command returned non-zero status, %d",
243  return_value);
244  }
245  return FileIO::GMSH::readGMSHMesh(gmsh_mesh_name);
246 }
247 
249 void rotateMesh(MeshLib::Mesh& mesh, Eigen::Matrix3d const& rot_mat,
250  double const z_shift)
251 {
252  std::vector<MeshLib::Node*> const& nodes = mesh.getNodes();
253  std::for_each(nodes.begin(), nodes.end(),
254  [z_shift](MeshLib::Node* n) { (*n)[2] += z_shift; });
255  GeoLib::rotatePoints(rot_mat.transpose(), nodes.begin(), nodes.end());
256 }
257 
260 {
261  std::vector<std::size_t> line_idx;
262  std::vector<MeshLib::Element*> const& elems = mesh.getElements();
263  std::for_each(elems.begin(), elems.end(),
264  [&](auto e)
265  {
266  if (e->getGeomType() == MeshLib::MeshElemType::LINE)
267  {
268  line_idx.push_back(e->getID());
269  }
270  });
271  if (line_idx.size() == mesh.getNumberOfElements())
272  {
273  return nullptr;
274  }
275  return MeshLib::removeElements(mesh, line_idx, "mesh");
276 }
277 
278 void writeBoundary(MeshLib::Mesh const& mesh,
279  std::vector<std::size_t> const& idx_array,
280  std::string const& file_name)
281 {
282  std::unique_ptr<MeshLib::Mesh> const boundary(
283  MeshLib::removeElements(mesh, idx_array, "mesh"));
284  if (boundary == nullptr)
285  {
286  ERR("Error extracting boundary '{:s}'", file_name);
287  return;
288  }
289  MeshLib::IO::VtuInterface vtu(boundary.get());
290  vtu.writeToFile(file_name + ".vtu");
291 }
292 
294  std::string const& output_name,
295  MathLib::Point3d const& pnt_start)
296 {
297  double const eps = mesh.getMinEdgeLength() / 100.0;
298  std::unique_ptr<MeshLib::Mesh> boundary_mesh(
300  mesh, "bulk_node_ids", "bulk_element_ids", "bulk_face_ids"));
301 
302  auto const& elems = boundary_mesh->getElements();
303  std::vector<std::size_t> left_bound_idx, right_bound_idx, top_bound_idx,
304  bottom_bound_idx;
305  Eigen::Vector2d const anchor(pnt_start[0], pnt_start[1]);
306  for (auto e : elems)
307  {
308  Eigen::Vector2d const n1((*e->getNode(0))[0], (*e->getNode(0))[1]);
309  Eigen::Vector2d const n2((*e->getNode(1))[0], (*e->getNode(1))[1]);
310  Eigen::Vector2d const dist1(n1 - anchor);
311  Eigen::Vector2d const dist2(n2 - anchor);
312  std::size_t const id = e->getID();
313  // elements located at left or right side
314  if ((dist1 - dist2).squaredNorm() < eps)
315  {
316  top_bound_idx.push_back(id);
317  bottom_bound_idx.push_back(id);
318 
319  if (dist1.squaredNorm() < eps)
320  {
321  right_bound_idx.push_back(id);
322  }
323  else
324  {
325  left_bound_idx.push_back(id);
326  }
327  continue;
328  }
329  // elements located at top or bottom
330  if (dist2.squaredNorm() < dist1.squaredNorm())
331  {
332  top_bound_idx.push_back(id);
333  left_bound_idx.push_back(id);
334  right_bound_idx.push_back(id);
335  }
336  else
337  {
338  bottom_bound_idx.push_back(id);
339  left_bound_idx.push_back(id);
340  right_bound_idx.push_back(id);
341  }
342  }
343 
344  writeBoundary(*boundary_mesh, left_bound_idx, output_name + "_left");
345  writeBoundary(*boundary_mesh, right_bound_idx, output_name + "_right");
346  writeBoundary(*boundary_mesh, top_bound_idx, output_name + "_top");
347  writeBoundary(*boundary_mesh, bottom_bound_idx, output_name + "_bottom");
348 }
349 
350 int main(int argc, char* argv[])
351 {
352  QCoreApplication a(argc, argv);
353  TCLAP::CmdLine cmd(
354  "Creates a triangle-mesh of a vertical slice out of a list of input "
355  "layer meshes. The slice is defined by a start- and end-point. In "
356  "addition, the resolution for meshing the extracted slice (i.e. the "
357  "maximum edge length of the domain discretisation) needs to be "
358  "specified. The utility requires access to the meshing utility GMSH to "
359  "work correctly.\n\n"
360  "OpenGeoSys-6 software, version " +
362  ".\n"
363  "Copyright (c) 2012-2021, OpenGeoSys Community "
364  "(http://www.opengeosys.org)",
366  TCLAP::SwitchArg test_arg("t", "testdata", "keep test data", false);
367  cmd.add(test_arg);
368  TCLAP::SwitchArg bound_arg("b", "bounds", "save mesh boundaries", false);
369  cmd.add(bound_arg);
370  TCLAP::ValueArg<double> res_arg(
371  "r", "resolution",
372  "desired edge length of triangles in the resulting slice.", true, 0,
373  "floating point number");
374  cmd.add(res_arg);
375  TCLAP::ValueArg<double> end_y_arg(
376  "", "end-y", "y-coordinates of the end point defining the slice", true,
377  0, "floating point number");
378  cmd.add(end_y_arg);
379  TCLAP::ValueArg<double> end_x_arg(
380  "", "end-x", "x-coordinates of the end point defining the slice", true,
381  0, "floating point number");
382  cmd.add(end_x_arg);
383  TCLAP::ValueArg<double> start_y_arg(
384  "", "start-y", "y-coordinates of the start point defining the slice",
385  true, 0, "floating point number");
386  cmd.add(start_y_arg);
387  TCLAP::ValueArg<double> start_x_arg(
388  "", "start-x", "x-coordinates of the start point defining the slice",
389  true, 0, "floating point number");
390  cmd.add(start_x_arg);
391  TCLAP::ValueArg<std::string> output_arg(
392  "o", "output", "name of output mesh (*.vtu)", true, "", "string");
393  cmd.add(output_arg);
394  TCLAP::ValueArg<std::string> input_arg(
395  "i", "input",
396  "name of the input file list containing the paths the all input layers "
397  "in correct order from top to bottom",
398  true, "", "string");
399  cmd.add(input_arg);
400  cmd.parse(argc, argv);
401 
402  std::string const input_name = input_arg.getValue();
403  std::string const output_name = output_arg.getValue();
404 
405  MathLib::Point3d const pnt_start{
406  {start_x_arg.getValue(), start_y_arg.getValue(), 0.0}};
407  MathLib::Point3d const pnt_end{
408  {end_x_arg.getValue(), end_y_arg.getValue(), 0.0}};
409  double const length = std::sqrt(MathLib::sqrDist(pnt_start, pnt_end));
410 
411  std::size_t const res = std::ceil(length / res_arg.getValue());
412  double const interval_length = length / res;
413 
414  std::vector<std::string> const layer_names =
416  if (layer_names.size() < 2)
417  {
418  ERR("At least two layers are required to extract a slice.");
419  return EXIT_FAILURE;
420  }
421 
422  GeoLib::GEOObjects geo;
423  std::vector<std::string> const geo_name_list =
424  createGeometries(geo, layer_names, pnt_start, pnt_end, res);
425 
426  if (geo_name_list.size() < 2)
427  {
428  ERR("Less than two geometries could be created from layers. Aborting "
429  "extraction...");
430  return EXIT_FAILURE;
431  }
432 
433  std::string merged_geo_name = "merged_geometries";
434  mergeGeometries(geo, geo_name_list, merged_geo_name);
435  std::vector<GeoLib::Point*> points = *geo.getPointVec(merged_geo_name);
436  auto const [rot_mat, z_shift] = rotateGeometryToXY(points);
437  consolidateGeometry(geo, output_name, merged_geo_name, test_arg.getValue());
438 
439  std::unique_ptr<MeshLib::Mesh> mesh(
440  generateMesh(geo, merged_geo_name, output_name, interval_length));
441  if (!test_arg.getValue())
442  {
443  BaseLib::removeFile(output_name + ".geo");
444  BaseLib::removeFile(output_name + ".msh");
445  }
446  if (mesh == nullptr)
447  {
448  ERR("Error generating mesh... (GMSH was unable to output mesh)");
449  return EXIT_FAILURE;
450  }
451  rotateMesh(*mesh, rot_mat, z_shift);
452  std::unique_ptr<MeshLib::Mesh> new_mesh(removeLineElements(*mesh));
453  if (new_mesh == nullptr)
454  {
455  ERR("Error generating mesh... (GMSH created line mesh)");
456  return EXIT_FAILURE;
457  }
458 
459  // collapse all nodes that might have been created due to gmsh physically
460  // separating layers
461  MeshLib::MeshRevision rev(*new_mesh);
462  std::unique_ptr<MeshLib::Mesh> revised_mesh(
463  rev.simplifyMesh("RevisedMesh", new_mesh->getMinEdgeLength() / 100.0));
464 
465  if (bound_arg.getValue())
466  {
467  extractBoundaries(*revised_mesh, output_name, pnt_start);
468  }
469  MeshLib::IO::VtuInterface vtu(revised_mesh.get());
470  vtu.writeToFile(output_name + ".vtu");
471  return EXIT_SUCCESS;
472 }
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(char const *fmt, Args const &... args)
Definition: Logging.h:42
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.
MeshLib::Mesh * removeLineElements(MeshLib::Mesh const &mesh)
removes line elements from mesh such that only triangles remain
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)
void writeBoundary(MeshLib::Mesh const &mesh, std::vector< std::size_t > const &idx_array, std::string const &file_name)
std::unique_ptr< 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
std::pair< Eigen::Matrix3d, double > rotateGeometryToXY(std::vector< GeoLib::Point * > &points)
rotates the merged geometry into the XY-plane
void mergeGeometries(GeoLib::GEOObjects &geo, std::vector< std::string > const &geo_names, std::string &merged_geo_name)
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
GeoLib::Polyline * createPolyline(std::vector< GeoLib::Point * > const &points)
creates a polyline to be mapped on a mesh layer
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 consolidateGeometry(GeoLib::GEOObjects &geo, std::string const &output_name, std::string &merged_geo_name, bool const keep_gml_file)
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.
Definition: GMSHInterface.h:46
void writePhysicalGroups(bool flag)
Definition: GMSHInterface.h:83
Class AABB is an axis aligned bounding box around a given set of geometric points of (template) type ...
Definition: AABB.h:49
Eigen::Vector3d const & getMinPoint() const
Definition: AABB.h:174
Eigen::Vector3d const & getMaxPoint() const
Definition: AABB.h:181
Container class for geometric objects.
Definition: GEOObjects.h:61
const std::vector< Point * > * getPointVec(const std::string &name) const
Definition: GEOObjects.cpp:71
bool removePointVec(const std::string &name)
Definition: GEOObjects.cpp:97
void addPointVec(std::unique_ptr< std::vector< Point * >> points, std::string &name, std::unique_ptr< std::map< std::string, std::size_t >> pnt_id_name_map=nullptr, double eps=std::sqrt(std::numeric_limits< double >::epsilon()))
Definition: GEOObjects.cpp:51
void addPolylineVec(std::unique_ptr< std::vector< Polyline * >> lines, const std::string &name, std::unique_ptr< std::map< std::string, std::size_t >> ply_names=nullptr)
Definition: GEOObjects.cpp:150
bool removePolylineVec(const std::string &name)
Definition: GEOObjects.cpp:243
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:51
std::size_t getPointID(std::size_t i) const
Definition: Polyline.cpp:150
virtual bool addPoint(std::size_t pnt_id)
Definition: Polyline.cpp:34
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....
Definition: VtuInterface.h:38
bool writeToFile(std::filesystem::path const &file_path)
MeshLib::Mesh * simplifyMesh(const std::string &new_mesh_name, double eps, unsigned min_elem_dim=1) const
std::vector< Node * > const & getNodes() const
Get the nodes-vector for the mesh.
Definition: Mesh.h:95
double getMinEdgeLength() const
Get the minimum edge length over all elements of the mesh.
Definition: Mesh.h:80
std::vector< Element * > const & getElements() const
Get the element-vector for the mesh.
Definition: Mesh.h:98
int writeStringToFile(std::string content, std::filesystem::path const &file_path)
Definition: Writer.cpp:45
std::vector< std::string > readStringListFromFile(std::string const &filename)
Reads non-empty lines from a list of strings from a file into a vector.
void removeFile(std::string const &filename)
Definition: FileTools.cpp:236
@ FixedMeshDensity
set the parameter with a fixed value
MeshLib::Mesh * readGMSHMesh(std::string const &fname)
Definition: GmshReader.cpp:166
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
static const double p
double sqrDist(MathLib::Point3d const &p0, MathLib::Point3d const &p1)
Definition: Point3d.h:48
std::unique_ptr< MeshLib::Mesh > getBoundaryElementsAsMesh(MeshLib::Mesh const &bulk_mesh, std::string const &subsfc_node_id_prop_name, std::string const &subsfc_element_id_prop_name, std::string const &face_id_prop_name)
MeshLib::Mesh * readMeshFromFile(const std::string &file_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.