OGS
Layers2Grid.cpp
Go to the documentation of this file.
1 
10 #include <algorithm>
11 #include <memory>
12 #include <string>
13 #include <vector>
14 
15 // ThirdParty
16 #include <tclap/CmdLine.h>
17 
19 #include "GeoLib/AABB.h"
20 #include "InfoLib/GitInfo.h"
21 #include "MathLib/Point3d.h"
25 #include "MeshLib/Mesh.h"
30 #include "MeshLib/Node.h"
31 
32 static std::string mat_name = "MaterialIDs";
33 
34 // returns the AABB of all mesh nodes of layers read so far
35 void adjustExtent(std::pair<MathLib::Point3d, MathLib::Point3d>& extent,
36  MeshLib::Mesh const& mesh)
37 {
38  auto const& nodes = mesh.getNodes();
39  GeoLib::AABB aabb(nodes.cbegin(), nodes.cend());
40  for (std::size_t i = 0; i < 3; ++i)
41  {
42  extent.first[i] = std::min(extent.first[i], aabb.getMinPoint()[i]);
43  extent.second[i] = std::max(extent.second[i], aabb.getMaxPoint()[i]);
44  }
45 }
46 
47 // creates a voxel grid of the AABB of all layers
48 std::unique_ptr<MeshLib::Mesh> generateInitialMesh(
49  std::pair<MathLib::Point3d, MathLib::Point3d>& extent,
50  std::array<double, 3> const& res)
51 {
52  INFO("Creating initial mesh...");
53  std::array<double, 3> mesh_range{{extent.second[0] - extent.first[0],
54  extent.second[1] - extent.first[1],
55  extent.second[2] - extent.first[2]}};
56  std::array<std::size_t, 3> const n_cells{
57  {static_cast<std::size_t>(std::ceil(mesh_range[0] / res[0])),
58  static_cast<std::size_t>(std::ceil(mesh_range[1] / res[1])),
59  static_cast<std::size_t>(std::ceil(mesh_range[2] / res[2]))}};
60  for (std::size_t i = 0; i < 3; ++i)
61  {
62  double const ext_range = n_cells[i] * res[i];
63  double const offset = (ext_range - mesh_range[i]) / 2.0;
64  mesh_range[i] = ext_range;
65  extent.first[i] -= offset;
66  extent.second[i] += offset;
67  }
68  std::unique_ptr<MeshLib::Mesh> mesh(
70  mesh_range[0], mesh_range[1], mesh_range[2], n_cells[0], n_cells[1],
71  n_cells[2], extent.first));
72  auto mat_id = mesh->getProperties().createNewPropertyVector<int>(
74  if (!mat_id)
75  {
76  return nullptr;
77  }
78  mat_id->insert(mat_id->end(), mesh->getNumberOfElements(), -1);
79  return mesh;
80 }
81 
82 // returns the element the given node is projected on (or nullptr otherwise)
84  MeshLib::MeshElementGrid const& grid,
85  MeshLib::Node const& node,
86  double const max_edge)
87 {
88  constexpr double max_val = std::numeric_limits<double>::max();
89  MathLib::Point3d const min_vol{
90  {node[0] - max_edge, node[1] - max_edge, -max_val}};
91  MathLib::Point3d const max_vol{
92  {node[0] + max_edge, node[1] + max_edge, max_val}};
93  auto const& intersection_candidates =
94  grid.getElementsInVolume(min_vol, max_vol);
96  intersection_candidates, node);
97 }
98 
99 // casts vote if the given nodes belongs to lower layer, upper layer or no layer
100 // at all
101 void voteMatId(MeshLib::Node const& node, MeshLib::MeshElementGrid const& grid,
102  double const max_edge, std::size_t& nullptr_cnt,
103  std::size_t& upper_layer_cnt, std::size_t& lower_layer_cnt)
104 {
105  auto const& proj_elem = getProjectedElement(grid, node, max_edge);
106  if (proj_elem == nullptr)
107  {
108  nullptr_cnt++;
109  return;
110  }
111  if (node[2] > MeshLib::ProjectPointOnMesh::getElevation(*proj_elem, node))
112  {
113  upper_layer_cnt++;
114  return;
115  }
116  lower_layer_cnt++;
117 }
118 
119 // sets material IDs for all elements depending on the layers they are located
120 // between
122  std::vector<std::unique_ptr<MeshLib::Mesh>> const& layers,
123  bool const dilate)
124 {
125  INFO("Setting material properties...");
126  std::size_t const n_layers = layers.size();
127  auto const& elems = mesh.getElements();
128  std::size_t const n_elems = mesh.getNumberOfElements();
129  auto mat_ids = mesh.getProperties().getPropertyVector<int>(mat_name);
130  std::vector<bool> is_set(n_elems, false);
131  for (int i = n_layers - 1; i >= 0; --i)
132  {
133  INFO("-> Layer {:d}", n_layers - i - 1);
134  MeshLib::MeshElementGrid const grid(*layers[i]);
135  double const max_edge(layers[i]->getMaxEdgeLength());
136  for (std::size_t j = 0; j < n_elems; ++j)
137  {
138  if (is_set[j])
139  {
140  continue;
141  }
142 
143  std::size_t nullptr_cnt(0);
144  std::size_t upper_layer_cnt(0);
145  std::size_t lower_layer_cnt(0);
146 
147  MeshLib::Node const node = MeshLib::getCenterOfGravity(*elems[j]);
148  voteMatId(node, grid, max_edge, nullptr_cnt, upper_layer_cnt,
149  lower_layer_cnt);
150  if (nullptr_cnt)
151  {
152  // if no element was found at centre point, vote via corners
153  for (std::size_t k = 0; k < 8; ++k)
154  {
155  MeshLib::Node const& n = *elems[j]->getNode(k);
156  voteMatId(n, grid, max_edge, nullptr_cnt, upper_layer_cnt,
157  lower_layer_cnt);
158  }
159 
160  // If the "dilate"-param is set, a mat ID will be assigned if at
161  // least one node was voting for a specific layer. Without the
162  // "dilate"-param, an absolute majority is needed. In case of a
163  // tie, the lower layer will be favoured.
164  if ((upper_layer_cnt == 0 && lower_layer_cnt == 0) ||
165  (!dilate && nullptr_cnt >= upper_layer_cnt &&
166  nullptr_cnt >= lower_layer_cnt))
167  {
168  continue;
169  }
170  if (upper_layer_cnt > lower_layer_cnt)
171  {
172  (*mat_ids)[j] = n_layers - i - 1;
173  }
174  else
175  {
176  is_set[j] = true;
177  }
178  continue;
179  }
180  if (upper_layer_cnt)
181  {
182  (*mat_ids)[j] = n_layers - i - 1;
183  }
184  else
185  {
186  is_set[j] = true;
187  }
188  }
189  }
190  // set all elements above uppermost layer back to -1 so they are
191  // subsequently cut
192  std::replace(mat_ids->begin(), mat_ids->end(),
193  static_cast<int>(n_layers - 1), -1);
194 }
195 
196 // Removes all elements from mesh that have not been marked as being located
197 // between two layers. If all elements remain unmarked, a nullptr is returned.
199 {
200  std::vector<std::size_t> marked_elems;
201  auto const mat_ids = *MeshLib::materialIDs(mesh);
202  std::size_t const n_elems = mat_ids.size();
203  for (std::size_t i = 0; i < n_elems; ++i)
204  {
205  if (mat_ids[i] == -1)
206  {
207  marked_elems.push_back(i);
208  }
209  }
210 
211  if (marked_elems.size() == mesh.getNumberOfElements())
212  {
213  return nullptr;
214  }
215  return MeshLib::removeElements(mesh, marked_elems, "mesh");
216 }
217 
218 int main(int argc, char* argv[])
219 {
220  TCLAP::CmdLine cmd(
221  "Reads a list of 2D unstructured mesh layers and samples them onto a "
222  "structured grid of the same extent. Note, that a large cube size may "
223  "result in an undersampling of the original structure.\nCube sizes are "
224  "defines by x/y/z-parameters. For equilateral cubes, only the "
225  "x-parameter needs to be set.\n\n"
226  "OpenGeoSys-6 software, version " +
228  ".\n"
229  "Copyright (c) 2012-2021, OpenGeoSys Community "
230  "(http://www.opengeosys.org)",
232  TCLAP::SwitchArg dilate_arg(
233  "d", "dilate",
234  "assign mat IDs based on single nodes instead of a majority of nodes, "
235  "which can result in a slightly increased voxel grid extent",
236  false);
237  cmd.add(dilate_arg);
238 
239  TCLAP::ValueArg<double> z_arg("z", "cellsize-z",
240  "edge length of cubes in z-direction (depth)",
241  false, 1000, "floating point number");
242  cmd.add(z_arg);
243 
244  TCLAP::ValueArg<double> y_arg(
245  "y", "cellsize-y", "edge length of cubes in y-direction (latitude)",
246  false, 1000, "floating point number");
247  cmd.add(y_arg);
248 
249  TCLAP::ValueArg<double> x_arg(
250  "x", "cellsize-x",
251  "edge length of cubes in x-direction (longitude) or all directions, if "
252  "y and z are not set",
253  true, 1000, "floating point number");
254  cmd.add(x_arg);
255 
256  TCLAP::ValueArg<std::string> output_arg(
257  "o", "output", "name of output mesh (*.vtu)", true, "", "string");
258  cmd.add(output_arg);
259 
260  TCLAP::ValueArg<std::string> input_arg(
261  "i", "input",
262  "name of the input file list containing the paths the all input layers "
263  "in correct order from top to bottom",
264  true, "", "string");
265  cmd.add(input_arg);
266  cmd.parse(argc, argv);
267 
268  if ((y_arg.isSet() && !z_arg.isSet()) ||
269  ((!y_arg.isSet() && z_arg.isSet())))
270  {
271  ERR("For equilateral cubes, only x needs to be set. For unequal "
272  "cuboids, all three edge lengths (x/y/z) need to be specified.");
273  return EXIT_FAILURE;
274  }
275 
276  double const x_size = x_arg.getValue();
277  double const y_size = (y_arg.isSet()) ? y_arg.getValue() : x_arg.getValue();
278  double const z_size = (z_arg.isSet()) ? z_arg.getValue() : x_arg.getValue();
279  std::array<double, 3> const cellsize = {x_size, y_size, z_size};
280 
281  std::string const input_name = input_arg.getValue();
282  std::string const output_name = output_arg.getValue();
283  auto const layer_names = BaseLib::IO::readStringListFromFile(input_name);
284  if (layer_names.size() < 2)
285  {
286  ERR("At least two layers are required to create a 3D Mesh");
287  return EXIT_FAILURE;
288  }
289 
290  std::vector<std::unique_ptr<MeshLib::Mesh>> layers;
291  layers.reserve(layer_names.size());
292  constexpr double minval = std::numeric_limits<double>::max();
293  constexpr double maxval = std::numeric_limits<double>::lowest();
294  std::pair<MathLib::Point3d, MathLib::Point3d> extent(
295  MathLib::Point3d{{minval, minval, minval}},
296  MathLib::Point3d{{maxval, maxval, maxval}});
297  for (auto const& layer : layer_names)
298  {
299  std::unique_ptr<MeshLib::Mesh> mesh(
301  if (mesh == nullptr)
302  {
303  ERR("Input layer '{:s}' not found. Aborting...", layer);
304  return EXIT_FAILURE;
305  }
306  adjustExtent(extent, *mesh);
307  layers.emplace_back(std::move(mesh));
308  }
309 
310  std::unique_ptr<MeshLib::Mesh> mesh(generateInitialMesh(extent, cellsize));
311  if (mesh == nullptr)
312  {
313  ERR("Error creating mesh...");
314  return EXIT_FAILURE;
315  }
316  setMaterialIDs(*mesh, layers, dilate_arg.getValue());
317 
318  std::unique_ptr<MeshLib::Mesh> new_mesh(removeUnusedElements(*mesh));
319  if (new_mesh == nullptr)
320  {
321  ERR("Error generating mesh...");
322  return EXIT_FAILURE;
323  }
324 
325  MeshLib::IO::VtuInterface vtu(new_mesh.get());
326  vtu.writeToFile(output_name);
327  return EXIT_SUCCESS;
328 }
Definition of the AABB class.
Definition of the Element class.
Git information.
int main(int argc, char *argv[])
static std::string mat_name
Definition: Layers2Grid.cpp:32
MeshLib::Element const * getProjectedElement(MeshLib::MeshElementGrid const &grid, MeshLib::Node const &node, double const max_edge)
Definition: Layers2Grid.cpp:83
void voteMatId(MeshLib::Node const &node, MeshLib::MeshElementGrid const &grid, double const max_edge, std::size_t &nullptr_cnt, std::size_t &upper_layer_cnt, std::size_t &lower_layer_cnt)
void setMaterialIDs(MeshLib::Mesh &mesh, std::vector< std::unique_ptr< MeshLib::Mesh >> const &layers, bool const dilate)
std::unique_ptr< MeshLib::Mesh > generateInitialMesh(std::pair< MathLib::Point3d, MathLib::Point3d > &extent, std::array< double, 3 > const &res)
Definition: Layers2Grid.cpp:48
MeshLib::Mesh * removeUnusedElements(MeshLib::Mesh const &mesh)
void adjustExtent(std::pair< MathLib::Point3d, MathLib::Point3d > &extent, MeshLib::Mesh const &mesh)
Definition: Layers2Grid.cpp:35
void INFO(char const *fmt, Args const &... args)
Definition: Logging.h:32
void ERR(char const *fmt, Args const &... args)
Definition: Logging.h:42
Definition of the Mesh class.
Definition of the Node class.
Definition of the Point3d class.
Implementation of the VtuInterface class.
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
Reads and writes VtkXMLUnstructuredGrid-files (vtu) to and from OGS data structures....
Definition: VtuInterface.h:38
bool writeToFile(std::filesystem::path const &file_path)
std::vector< MeshLib::Element const * > getElementsInVolume(POINT const &min, POINT const &max) const
std::vector< Node * > const & getNodes() const
Get the nodes-vector for the mesh.
Definition: Mesh.h:95
std::vector< Element * > const & getElements() const
Get the element-vector for the mesh.
Definition: Mesh.h:98
Properties & getProperties()
Definition: Mesh.h:123
std::size_t getNumberOfElements() const
Get the number of elements.
Definition: Mesh.h:86
PropertyVector< T > const * getPropertyVector(std::string const &name) 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.
GITINFOLIB_EXPORT const std::string ogs_version
MeshLib::Mesh * readMeshFromFile(const std::string &file_name)
Mesh * generateRegularHexMesh(const BaseLib::ISubdivision &div_x, const BaseLib::ISubdivision &div_y, const BaseLib::ISubdivision &div_z, MathLib::Point3d const &origin=MathLib::ORIGIN, std::string const &mesh_name="mesh")
double getElevation(Element const &element, Node const &node)
Element const * getProjectedElement(std::vector< const Element * > const &elements, Node const &node)
MeshLib::Mesh * removeElements(const MeshLib::Mesh &mesh, const std::vector< std::size_t > &removed_element_ids, const std::string &new_mesh_name)
PropertyVector< int > const * materialIDs(Mesh const &mesh)
Definition: Mesh.cpp:258
MeshLib::Node getCenterOfGravity(Element const &element)
Calculates the center of gravity for the mesh element.
Definition: Element.cpp:126
Definition of readMeshFromFile function.