OGS
MeshLayerMapper.cpp
Go to the documentation of this file.
1
15#include "MeshLayerMapper.h"
16
17#include <algorithm>
18
19#include "BaseLib/Logging.h"
20#include "GeoLib/Raster.h"
21#include "MathLib/MathTools.h"
26#include "MeshLib/Properties.h"
28
29namespace MeshToolsLib
30{
32 MeshLib::Mesh const& mesh, std::vector<float> const& layer_thickness_vector,
33 std::string const& mesh_name)
34{
35 std::vector<float> thickness;
36 for (std::size_t i = 0; i < layer_thickness_vector.size(); ++i)
37 {
38 if (layer_thickness_vector[i] > std::numeric_limits<float>::epsilon())
39 {
40 thickness.push_back(layer_thickness_vector[i]);
41 }
42 else
43 {
44 WARN("Ignoring layer {:d} with thickness {:f}.", i,
45 layer_thickness_vector[i]);
46 }
47 }
48
49 const std::size_t nLayers(thickness.size());
50 if (nLayers < 1 || mesh.getDimension() != 2)
51 {
52 ERR("MeshLayerMapper::createStaticLayers(): A 2D mesh with nLayers > 0 "
53 "is required as input.");
54 return nullptr;
55 }
56
57 const std::size_t nNodes = mesh.getNumberOfNodes();
58 // count number of 2d elements in the original mesh
59 const std::size_t nElems(
60 std::count_if(mesh.getElements().begin(), mesh.getElements().end(),
61 [](MeshLib::Element const* elem)
62 { return (elem->getDimension() == 2); }));
63
64 const std::size_t nOrgElems(mesh.getNumberOfElements());
65 const std::vector<MeshLib::Node*>& nodes = mesh.getNodes();
66 const std::vector<MeshLib::Element*>& elems = mesh.getElements();
67 std::vector<MeshLib::Node*> new_nodes(nNodes + (nLayers * nNodes));
68 std::vector<MeshLib::Element*> new_elems;
69 new_elems.reserve(nElems * nLayers);
70 MeshLib::Properties properties;
71 auto* const materials = properties.createNewPropertyVector<int>(
72 "MaterialIDs", MeshLib::MeshItemType::Cell);
73 if (!materials)
74 {
75 ERR("Could not create PropertyVector object 'MaterialIDs'.");
76 return nullptr;
77 }
78
79 materials->reserve(nElems * nLayers);
80 double z_offset(0.0);
81
82 for (unsigned layer_id = 0; layer_id <= nLayers; ++layer_id)
83 {
84 // add nodes for new layer
85 unsigned node_offset(nNodes * layer_id);
86 if (layer_id > 0)
87 {
88 z_offset += thickness[layer_id - 1];
89 }
90
91 std::transform(nodes.cbegin(), nodes.cend(),
92 new_nodes.begin() + node_offset,
93 [&z_offset](MeshLib::Node* node) {
94 return new MeshLib::Node((*node)[0], (*node)[1],
95 (*node)[2] - z_offset);
96 });
97
98 // starting with 2nd layer create prism or hex elements connecting the
99 // last layer with the current one
100 if (layer_id == 0)
101 {
102 continue;
103 }
104
105 node_offset -= nNodes;
106 const unsigned mat_id(nLayers - layer_id);
107
108 for (unsigned i = 0; i < nOrgElems; ++i)
109 {
110 const MeshLib::Element* sfc_elem(elems[i]);
111 if (sfc_elem->getDimension() < 2)
112 { // ignore line-elements
113 continue;
114 }
115
116 const unsigned nElemNodes(sfc_elem->getNumberOfBaseNodes());
117 auto** e_nodes = new MeshLib::Node*[2 * nElemNodes];
118
119 for (unsigned j = 0; j < nElemNodes; ++j)
120 {
121 const unsigned node_id =
122 sfc_elem->getNode(j)->getID() + node_offset;
123 e_nodes[j] = new_nodes[node_id + nNodes];
124 e_nodes[j + nElemNodes] = new_nodes[node_id];
125 }
127 {
128 // extrude triangles to prism
129 new_elems.push_back(new MeshLib::Prism(e_nodes));
130 }
131 else if (sfc_elem->getGeomType() == MeshLib::MeshElemType::QUAD)
132 {
133 // extrude quads to hexes
134 new_elems.push_back(new MeshLib::Hex(e_nodes));
135 }
136 else
137 {
138 OGS_FATAL("MeshLayerMapper: Unknown element type to extrude.");
139 }
140 materials->push_back(mat_id);
141 }
142 }
143 return new MeshLib::Mesh(mesh_name, new_nodes, new_elems,
144 true /* compute_element_neighbors */, properties);
145}
146
148 MeshLib::Mesh const& mesh,
149 std::vector<GeoLib::Raster const*> const& rasters,
150 double minimum_thickness,
151 double noDataReplacementValue)
152{
153 const std::size_t nLayers(rasters.size());
154 if (nLayers < 2 || mesh.getDimension() != 2)
155 {
156 ERR("MeshLayerMapper::createRasterLayers(): A 2D mesh and at least two "
157 "rasters required as input.");
158 return false;
159 }
160
161 auto top = std::make_unique<MeshLib::Mesh>(mesh);
162 if (!layerMapping(*top, *rasters.back(), noDataReplacementValue))
163 {
164 return false;
165 }
166
167 auto bottom = std::make_unique<MeshLib::Mesh>(mesh);
168 if (!layerMapping(*bottom, *rasters[0], 0))
169 {
170 return false;
171 }
172
173 this->_minimum_thickness = minimum_thickness;
174 std::size_t const nNodes = mesh.getNumberOfNodes();
175 _nodes.reserve(nLayers * nNodes);
176
177 // number of triangles in the original mesh
178 std::size_t const nElems(std::count_if(
179 mesh.getElements().begin(), mesh.getElements().end(),
180 [](MeshLib::Element const* elem)
181 { return (elem->getGeomType() == MeshLib::MeshElemType::TRIANGLE); }));
182 _elements.reserve(nElems * (nLayers - 1));
183 _materials.reserve(nElems * (nLayers - 1));
184
185 // add bottom layer
186 std::vector<MeshLib::Node*> const& nodes = bottom->getNodes();
187 std::transform(nodes.begin(), nodes.end(), std::back_inserter(_nodes),
188 [](auto const* node) { return new MeshLib::Node(*node); });
189
190 // add the other layers
191 for (std::size_t i = 0; i < nLayers - 1; ++i)
192 {
193 addLayerToMesh(*top, i, *rasters[i + 1]);
194 }
195
196 return true;
197}
198
200 unsigned layer_id,
201 GeoLib::Raster const& raster)
202{
203 const unsigned pyramid_base[3][4] = {
204 {1, 3, 4, 2}, // Point 4 missing
205 {2, 4, 3, 0}, // Point 5 missing
206 {0, 3, 4, 1}, // Point 6 missing
207 };
208
209 std::size_t const nNodes = dem_mesh.getNumberOfNodes();
210 std::vector<MeshLib::Node*> const& top_nodes = dem_mesh.getNodes();
211 int const last_layer_node_offset = layer_id * nNodes;
212
213 // add nodes for new layer
214 for (std::size_t i = 0; i < nNodes; ++i)
215 {
216 _nodes.push_back(getNewLayerNode(*top_nodes[i],
217 *_nodes[last_layer_node_offset + i],
218 raster, _nodes.size()));
219 }
220
221 std::vector<MeshLib::Element*> const& elems = dem_mesh.getElements();
222 std::size_t const nElems(dem_mesh.getNumberOfElements());
223
224 for (std::size_t i = 0; i < nElems; ++i)
225 {
226 MeshLib::Element* elem(elems[i]);
228 {
229 continue;
230 }
231 unsigned node_counter(3);
232 unsigned missing_idx(0);
233 std::array<MeshLib::Node*, 6> new_elem_nodes{};
234 for (unsigned j = 0; j < 3; ++j)
235 {
236 new_elem_nodes[j] =
237 _nodes[_nodes[last_layer_node_offset + getNodeIndex(*elem, j)]
238 ->getID()];
239 new_elem_nodes[node_counter] =
240 (_nodes[last_layer_node_offset + getNodeIndex(*elem, j) +
241 nNodes]);
242 if (new_elem_nodes[j]->getID() !=
243 new_elem_nodes[node_counter]->getID())
244 {
245 node_counter++;
246 }
247 else
248 {
249 missing_idx = j;
250 }
251 }
252
253 switch (node_counter)
254 {
255 case 6:
256 _elements.push_back(new MeshLib::Prism(new_elem_nodes));
257 _materials.push_back(layer_id);
258 break;
259 case 5:
260 {
261 std::array<MeshLib::Node*, 5> pyramid_nodes{};
262 pyramid_nodes[0] = new_elem_nodes[pyramid_base[missing_idx][0]];
263 pyramid_nodes[1] = new_elem_nodes[pyramid_base[missing_idx][1]];
264 pyramid_nodes[2] = new_elem_nodes[pyramid_base[missing_idx][2]];
265 pyramid_nodes[3] = new_elem_nodes[pyramid_base[missing_idx][3]];
266 pyramid_nodes[4] = new_elem_nodes[missing_idx];
267 _elements.push_back(new MeshLib::Pyramid(pyramid_nodes));
268 _materials.push_back(layer_id);
269 break;
270 }
271 case 4:
272 {
273 std::array<MeshLib::Node*, 4> tet_nodes{};
274 std::copy(new_elem_nodes.begin(),
275 new_elem_nodes.begin() + node_counter,
276 tet_nodes.begin());
277 _elements.push_back(new MeshLib::Tet(tet_nodes));
278 _materials.push_back(layer_id);
279 break;
280 }
281 default:
282 continue;
283 }
284 }
285}
286
288 GeoLib::Raster const& raster,
289 double const nodata_replacement,
290 bool const ignore_nodata)
291{
292 if (mesh.getDimension() != 2)
293 {
294 ERR("MshLayerMapper::layerMapping() - requires 2D mesh");
295 return false;
296 }
297
298 GeoLib::RasterHeader const& header(raster.getHeader());
299 const double x0(header.origin[0]);
300 const double y0(header.origin[1]);
301 const double delta(header.cell_size);
302
303 const std::pair<double, double> xDim(
304 x0, x0 + header.n_cols * delta); // extension in x-dimension
305 const std::pair<double, double> yDim(
306 y0, y0 + header.n_rows * delta); // extension in y-dimension
307
308 const std::size_t nNodes(mesh.getNumberOfNodes());
309 const std::vector<MeshLib::Node*>& nodes = mesh.getNodes();
310 for (unsigned i = 0; i < nNodes; ++i)
311 {
312 if (!ignore_nodata && !raster.isPntOnRaster(*nodes[i]))
313 {
314 // use either default value or elevation from layer above
315 (*nodes[i])[2] = nodata_replacement;
316 continue;
317 }
318
319 double elevation(raster.getValueAtPoint(*nodes[i]));
320 constexpr double eps = std::numeric_limits<double>::epsilon();
321 if (std::abs(elevation - header.no_data) < eps)
322 {
323 if (ignore_nodata)
324 {
325 continue;
326 }
327 elevation = nodata_replacement;
328 }
329 else
330 {
331 elevation = raster.interpolateValueAtPoint(*nodes[i]);
332 }
333 (*nodes[i])[2] = elevation;
334 }
335
336 return true;
337}
338
340 double const value)
341{
342 if (mesh.getDimension() != 2)
343 {
344 ERR("MshLayerMapper::mapToStaticValue() - requires 2D mesh");
345 return false;
346 }
347
348 std::vector<MeshLib::Node*> const& nodes(mesh.getNodes());
349 for (MeshLib::Node* node : nodes)
350 {
351 (*node)[2] = value;
352 }
353 return true;
354}
355
356} // namespace MeshToolsLib
#define OGS_FATAL(...)
Definition Error.h:26
Definition of the Hex class.
void ERR(fmt::format_string< Args... > fmt, Args &&... args)
Definition Logging.h:45
void WARN(fmt::format_string< Args... > fmt, Args &&... args)
Definition Logging.h:40
Definition of the MeshLayerMapper class.
Definition of the class Properties that implements a container of properties.
Definition of the MeshSurfaceExtraction class.
Definition of the Prism class.
Definition of the Pyramid class.
Definition of the GeoLib::Raster class.
Definition of the Tet class.
Class Raster is used for managing raster data.
Definition Raster.h:49
double getValueAtPoint(const MathLib::Point3d &pnt) const
Definition Raster.cpp:56
bool isPntOnRaster(MathLib::Point3d const &pnt) const
Definition Raster.cpp:151
double interpolateValueAtPoint(const MathLib::Point3d &pnt) const
Definition Raster.cpp:85
RasterHeader const & getHeader() const
Returns the complete header information.
Definition Raster.h:85
std::vector< int > _materials
MeshLib::Node * getNewLayerNode(MeshLib::Node const &dem_node, MeshLib::Node const &last_layer_node, GeoLib::Raster const &raster, std::size_t new_node_id) const
std::vector< MeshLib::Node * > _nodes
std::vector< MeshLib::Element * > _elements
std::size_t getID() const
virtual MeshElemType getGeomType() const =0
virtual unsigned getNumberOfBaseNodes() const =0
virtual const Node * getNode(unsigned idx) const =0
virtual constexpr unsigned getDimension() const =0
Get dimension of the mesh element.
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
unsigned getDimension() const
Returns the dimension of the mesh (determined by the maximum dimension over all elements).
Definition Mesh.h:88
std::size_t getNumberOfNodes() const
Get the number of nodes.
Definition Mesh.h:100
std::size_t getNumberOfElements() const
Get the number of elements.
Definition Mesh.h:97
Property manager on mesh items. Class Properties manages scalar, vector or matrix properties....
Definition Properties.h:36
PropertyVector< T > * createNewPropertyVector(std::string_view name, MeshItemType mesh_item_type, std::size_t n_components=1)
bool createRasterLayers(MeshLib::Mesh const &mesh, std::vector< GeoLib::Raster const * > const &rasters, double minimum_thickness, double noDataReplacementValue=0.0) override
static MeshLib::Mesh * createStaticLayers(MeshLib::Mesh const &mesh, std::vector< float > const &layer_thickness_vector, std::string const &mesh_name="SubsurfaceMesh")
void addLayerToMesh(const MeshLib::Mesh &dem_mesh, unsigned layer_id, GeoLib::Raster const &raster) override
Adds another layer to a subsurface mesh.
static bool layerMapping(MeshLib::Mesh const &mesh, const GeoLib::Raster &raster, double nodata_replacement=0.0, bool const ignore_nodata=false)
static bool mapToStaticValue(MeshLib::Mesh const &mesh, double value)
Maps the elevation of all mesh nodes to the specified static value.
Contains the relevant information when storing a geoscientific raster data.
Definition Raster.h:28
MathLib::Point3d origin
Definition Raster.h:32
std::size_t n_cols
Definition Raster.h:29
std::size_t n_rows
Definition Raster.h:30