OGS
MeshToolsLib::MeshLayerMapper Class Referencefinal

Detailed Description

Manipulating and adding prism element layers to an existing 2D mesh.

Definition at line 14 of file MeshLayerMapper.h.

#include <MeshLayerMapper.h>

Inheritance diagram for MeshToolsLib::MeshLayerMapper:
[legend]
Collaboration diagram for MeshToolsLib::MeshLayerMapper:
[legend]

Public Member Functions

 ~MeshLayerMapper () override=default
bool createRasterLayers (MeshLib::Mesh const &mesh, std::vector< GeoLib::Raster const * > const &rasters, double minimum_thickness, double noDataReplacementValue=0.0) override
Public Member Functions inherited from LayeredMeshGenerator
virtual bool createLayers (MeshLib::Mesh const &mesh, std::vector< GeoLib::Raster const * > const &rasters, double minimum_thickness, double noDataReplacementValue=0.0) final
std::unique_ptr< MeshLib::MeshgetMesh (std::string const &mesh_name) const
 Returns a mesh of the subsurface representation.

Static Public Member Functions

static MeshLib::MeshcreateStaticLayers (MeshLib::Mesh const &mesh, std::vector< float > const &layer_thickness_vector, std::string const &mesh_name="SubsurfaceMesh")
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.

Private Member Functions

void addLayerToMesh (const MeshLib::Mesh &dem_mesh, unsigned layer_id, GeoLib::Raster const &raster) override
 Adds another layer to a subsurface mesh.

Additional Inherited Members

Protected Member Functions inherited from LayeredMeshGenerator
 LayeredMeshGenerator ()=default
virtual ~LayeredMeshGenerator ()=default
MeshLib::NodegetNewLayerNode (MeshLib::Node const &dem_node, MeshLib::Node const &last_layer_node, GeoLib::Raster const &raster, std::size_t new_node_id) const
Static Protected Member Functions inherited from LayeredMeshGenerator
static double calcEpsilon (GeoLib::Raster const &low, GeoLib::Raster const &high)
 Calculates a data-dependent epsilon value.
Protected Attributes inherited from LayeredMeshGenerator
double _elevation_epsilon {0.0001}
double _minimum_thickness {std::numeric_limits<float>::epsilon()}
std::vector< int > _materials
std::vector< MeshLib::Node * > _nodes
std::vector< MeshLib::Element * > _elements

Constructor & Destructor Documentation

◆ ~MeshLayerMapper()

MeshToolsLib::MeshLayerMapper::~MeshLayerMapper ( )
overridedefault

Member Function Documentation

◆ addLayerToMesh()

void MeshToolsLib::MeshLayerMapper::addLayerToMesh ( const MeshLib::Mesh & dem_mesh,
unsigned layer_id,
GeoLib::Raster const & raster )
overrideprivatevirtual

Adds another layer to a subsurface mesh.

Implements LayeredMeshGenerator.

Definition at line 195 of file MeshLayerMapper.cpp.

198{
199 const unsigned pyramid_base[3][4] = {
200 {1, 3, 4, 2}, // Point 4 missing
201 {2, 4, 3, 0}, // Point 5 missing
202 {0, 3, 4, 1}, // Point 6 missing
203 };
204
205 std::size_t const nNodes = dem_mesh.getNumberOfNodes();
206 std::vector<MeshLib::Node*> const& top_nodes = dem_mesh.getNodes();
207 int const last_layer_node_offset = layer_id * nNodes;
208
209 // add nodes for new layer
210 for (std::size_t i = 0; i < nNodes; ++i)
211 {
212 _nodes.push_back(getNewLayerNode(*top_nodes[i],
213 *_nodes[last_layer_node_offset + i],
214 raster, _nodes.size()));
215 }
216
217 std::vector<MeshLib::Element*> const& elems = dem_mesh.getElements();
218 std::size_t const nElems(dem_mesh.getNumberOfElements());
219
220 for (std::size_t i = 0; i < nElems; ++i)
221 {
222 MeshLib::Element* elem(elems[i]);
223 if (elem->getGeomType() != MeshLib::MeshElemType::TRIANGLE)
224 {
225 continue;
226 }
227 unsigned node_counter(3);
228 unsigned missing_idx(0);
229 std::array<MeshLib::Node*, 6> new_elem_nodes{};
230 for (unsigned j = 0; j < 3; ++j)
231 {
232 new_elem_nodes[j] =
233 _nodes[_nodes[last_layer_node_offset + getNodeIndex(*elem, j)]
234 ->getID()];
235 new_elem_nodes[node_counter] =
236 (_nodes[last_layer_node_offset + getNodeIndex(*elem, j) +
237 nNodes]);
238 if (new_elem_nodes[j]->getID() !=
239 new_elem_nodes[node_counter]->getID())
240 {
241 node_counter++;
242 }
243 else
244 {
245 missing_idx = j;
246 }
247 }
248
249 switch (node_counter)
250 {
251 case 6:
252 _elements.push_back(new MeshLib::Prism(new_elem_nodes));
253 _materials.push_back(layer_id);
254 break;
255 case 5:
256 {
257 std::array<MeshLib::Node*, 5> pyramid_nodes{};
258 pyramid_nodes[0] = new_elem_nodes[pyramid_base[missing_idx][0]];
259 pyramid_nodes[1] = new_elem_nodes[pyramid_base[missing_idx][1]];
260 pyramid_nodes[2] = new_elem_nodes[pyramid_base[missing_idx][2]];
261 pyramid_nodes[3] = new_elem_nodes[pyramid_base[missing_idx][3]];
262 pyramid_nodes[4] = new_elem_nodes[missing_idx];
263 _elements.push_back(new MeshLib::Pyramid(pyramid_nodes));
264 _materials.push_back(layer_id);
265 break;
266 }
267 case 4:
268 {
269 std::array<MeshLib::Node*, 4> tet_nodes{};
270 std::copy(new_elem_nodes.begin(),
271 new_elem_nodes.begin() + node_counter,
272 tet_nodes.begin());
273 _elements.push_back(new MeshLib::Tet(tet_nodes));
274 _materials.push_back(layer_id);
275 break;
276 }
277 default:
278 continue;
279 }
280 }
281}
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::vector< Node * > const & getNodes() const
Get the nodes-vector for the mesh.
Definition Mesh.h:97
std::vector< Element * > const & getElements() const
Get the element-vector for the mesh.
Definition Mesh.h:100
std::size_t getNumberOfNodes() const
Get the number of nodes.
Definition Mesh.h:91
std::size_t getNumberOfElements() const
Get the number of elements.
Definition Mesh.h:88
TemplateElement< MeshLib::TetRule4 > Tet
Definition Tet.h:14
std::size_t getNodeIndex(Element const &element, unsigned const idx)
Definition Element.cpp:226
TemplateElement< MeshLib::PyramidRule5 > Pyramid
Definition Pyramid.h:14
TemplateElement< MeshLib::PrismRule6 > Prism
Definition Prism.h:14

References LayeredMeshGenerator::_elements, LayeredMeshGenerator::_materials, LayeredMeshGenerator::_nodes, MeshLib::Mesh::getElements(), MeshLib::Element::getGeomType(), LayeredMeshGenerator::getNewLayerNode(), MeshLib::Mesh::getNodes(), MeshLib::Mesh::getNumberOfElements(), MeshLib::Mesh::getNumberOfNodes(), and MeshLib::TRIANGLE.

Referenced by createRasterLayers().

◆ createRasterLayers()

bool MeshToolsLib::MeshLayerMapper::createRasterLayers ( MeshLib::Mesh const & mesh,
std::vector< GeoLib::Raster const * > const & rasters,
double minimum_thickness,
double noDataReplacementValue = 0.0 )
overridevirtual

Based on a 2D triangle mesh this method creates a 3D mesh with a given number of prism-layers. Note: While this method would technically also work with quad meshes, this is discouraged as quad elements will most likely not be coplanar after the mapping process which result in invalid mesh elements.

Parameters
meshThe 2D triangle mesh that is the basis for the new 3D prism mesh
rastersContaining all the raster-data for the subsurface layers from bottom to top (starting with the bottom of the oldest layer and ending with the DEM)
minimum_thicknessMinimum thickness of each of the newly created layers (i.e. nodes with a vertical distance smaller than this will be collapsed)
noDataReplacementValueDefault z-coordinate if there are mesh nodes not located on the DEM raster (i.e. raster_paths[0])
Returns
A mesh with the requested number of layers of prism elements (also including Tet- & Pyramid-elements in case of degenerated prisms)

Implements LayeredMeshGenerator.

Definition at line 143 of file MeshLayerMapper.cpp.

148{
149 const std::size_t nLayers(rasters.size());
150 if (nLayers < 2 || mesh.getDimension() != 2)
151 {
152 ERR("MeshLayerMapper::createRasterLayers(): A 2D mesh and at least two "
153 "rasters required as input.");
154 return false;
155 }
156
157 auto top = std::make_unique<MeshLib::Mesh>(mesh);
158 if (!layerMapping(*top, *rasters.back(), noDataReplacementValue))
159 {
160 return false;
161 }
162
163 auto bottom = std::make_unique<MeshLib::Mesh>(mesh);
164 if (!layerMapping(*bottom, *rasters[0], 0))
165 {
166 return false;
167 }
168
169 this->_minimum_thickness = minimum_thickness;
170 std::size_t const nNodes = mesh.getNumberOfNodes();
171 _nodes.reserve(nLayers * nNodes);
172
173 // number of triangles in the original mesh
174 std::size_t const nElems(std::count_if(
175 mesh.getElements().begin(), mesh.getElements().end(),
176 [](MeshLib::Element const* elem)
177 { return (elem->getGeomType() == MeshLib::MeshElemType::TRIANGLE); }));
178 _elements.reserve(nElems * (nLayers - 1));
179 _materials.reserve(nElems * (nLayers - 1));
180
181 // add bottom layer
182 std::vector<MeshLib::Node*> const& nodes = bottom->getNodes();
183 std::transform(nodes.begin(), nodes.end(), std::back_inserter(_nodes),
184 [](auto const* node) { return new MeshLib::Node(*node); });
185
186 // add the other layers
187 for (std::size_t i = 0; i < nLayers - 1; ++i)
188 {
189 addLayerToMesh(*top, i, *rasters[i + 1]);
190 }
191
192 return true;
193}
void ERR(fmt::format_string< Args... > fmt, Args &&... args)
Definition Logging.h:40
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)

References LayeredMeshGenerator::_elements, LayeredMeshGenerator::_materials, LayeredMeshGenerator::_minimum_thickness, LayeredMeshGenerator::_nodes, addLayerToMesh(), ERR(), MeshLib::Mesh::getDimension(), MeshLib::Mesh::getElements(), MeshLib::Mesh::getNumberOfNodes(), and layerMapping().

◆ createStaticLayers()

MeshLib::Mesh * MeshToolsLib::MeshLayerMapper::createStaticLayers ( MeshLib::Mesh const & mesh,
std::vector< float > const & layer_thickness_vector,
std::string const & mesh_name = "SubsurfaceMesh" )
static

Based on a 2D triangle-or quad mesh this method creates a 3D mesh with a given number of prism- or hex-layers

Parameters
meshThe triangle/quad mesh that is the basis for the new prism/hex mesh
layer_thickness_vectorThe size of the vector equals the number of layers of prism/hex elements that will be extruded from the triangle/quad elements of the original mesh. The thickness of the \(i\)-th layer corresponds to the \(i\) entry of the vector.
mesh_nameThe name of the newly created mesh
Returns
A mesh with the requested number of layers of prism/hex elements

Definition at line 24 of file MeshLayerMapper.cpp.

27{
28 std::vector<float> thickness;
29 for (std::size_t i = 0; i < layer_thickness_vector.size(); ++i)
30 {
31 if (layer_thickness_vector[i] > std::numeric_limits<float>::epsilon())
32 {
33 thickness.push_back(layer_thickness_vector[i]);
34 }
35 else
36 {
37 WARN("Ignoring layer {:d} with thickness {:f}.", i,
38 layer_thickness_vector[i]);
39 }
40 }
41
42 const std::size_t nLayers(thickness.size());
43 if (nLayers < 1 || mesh.getDimension() != 2)
44 {
45 ERR("MeshLayerMapper::createStaticLayers(): A 2D mesh with nLayers > 0 "
46 "is required as input.");
47 return nullptr;
48 }
49
50 const std::size_t nNodes = mesh.getNumberOfNodes();
51 // count number of 2d elements in the original mesh
52 const std::size_t nElems(
53 std::count_if(mesh.getElements().begin(), mesh.getElements().end(),
54 [](MeshLib::Element const* elem)
55 { return (elem->getDimension() == 2); }));
56
57 const std::size_t nOrgElems(mesh.getNumberOfElements());
58 const std::vector<MeshLib::Node*>& nodes = mesh.getNodes();
59 const std::vector<MeshLib::Element*>& elems = mesh.getElements();
60 std::vector<MeshLib::Node*> new_nodes(nNodes + (nLayers * nNodes));
61 std::vector<MeshLib::Element*> new_elems;
62 new_elems.reserve(nElems * nLayers);
63 MeshLib::Properties properties;
64 auto* const materials = properties.createNewPropertyVector<int>(
65 "MaterialIDs", MeshLib::MeshItemType::Cell);
66 if (!materials)
67 {
68 ERR("Could not create PropertyVector object 'MaterialIDs'.");
69 return nullptr;
70 }
71
72 auto layer_to_material_id = [&](unsigned const layer_id)
73 { return ranges::views::repeat_n(nLayers - layer_id, nOrgElems); };
74 materials->assign(ranges::views::iota(0u, nLayers + 1) |
75 ranges::views::transform(layer_to_material_id) |
76 ranges::views::join);
77
78 double z_offset(0.0);
79
80 for (unsigned layer_id = 0; layer_id <= nLayers; ++layer_id)
81 {
82 // add nodes for new layer
83 unsigned node_offset(nNodes * layer_id);
84 if (layer_id > 0)
85 {
86 z_offset += thickness[layer_id - 1];
87 }
88
89 std::transform(nodes.cbegin(), nodes.cend(),
90 new_nodes.begin() + node_offset,
91 [&z_offset](MeshLib::Node* node) {
92 return new MeshLib::Node((*node)[0], (*node)[1],
93 (*node)[2] - z_offset);
94 });
95
96 // starting with 2nd layer create prism or hex elements connecting the
97 // last layer with the current one
98 if (layer_id == 0)
99 {
100 continue;
101 }
102
103 node_offset -= nNodes;
104
105 for (unsigned i = 0; i < nOrgElems; ++i)
106 {
107 const MeshLib::Element* sfc_elem(elems[i]);
108 if (sfc_elem->getDimension() < 2)
109 { // ignore line-elements
110 continue;
111 }
112
113 const unsigned nElemNodes(sfc_elem->getNumberOfBaseNodes());
114 auto** e_nodes = new MeshLib::Node*[2 * nElemNodes];
115
116 for (unsigned j = 0; j < nElemNodes; ++j)
117 {
118 const unsigned node_id =
119 sfc_elem->getNode(j)->getID() + node_offset;
120 e_nodes[j] = new_nodes[node_id + nNodes];
121 e_nodes[j + nElemNodes] = new_nodes[node_id];
122 }
123 if (sfc_elem->getGeomType() == MeshLib::MeshElemType::TRIANGLE)
124 {
125 // extrude triangles to prism
126 new_elems.push_back(new MeshLib::Prism(e_nodes));
127 }
128 else if (sfc_elem->getGeomType() == MeshLib::MeshElemType::QUAD)
129 {
130 // extrude quads to hexes
131 new_elems.push_back(new MeshLib::Hex(e_nodes));
132 }
133 else
134 {
135 OGS_FATAL("MeshLayerMapper: Unknown element type to extrude.");
136 }
137 }
138 }
139 return new MeshLib::Mesh(mesh_name, new_nodes, new_elems,
140 true /* compute_element_neighbors */, properties);
141}
#define OGS_FATAL(...)
Definition Error.h:19
void WARN(fmt::format_string< Args... > fmt, Args &&... args)
Definition Logging.h:34
PropertyVector< T > * createNewPropertyVector(std::string_view name, MeshItemType mesh_item_type, std::size_t n_components=1)
constexpr void assign(R &&r)
TemplateElement< MeshLib::HexRule8 > Hex
Definition Hex.h:14

References MeshLib::PropertyVector< PROP_VAL_TYPE >::assign(), MeshLib::Cell, MeshLib::Properties::createNewPropertyVector(), ERR(), MeshLib::Element::getDimension(), MeshLib::Mesh::getDimension(), MeshLib::Mesh::getElements(), MeshLib::Element::getGeomType(), MathLib::Point3dWithID::getID(), MeshLib::Element::getNode(), MeshLib::Mesh::getNodes(), MeshLib::Element::getNumberOfBaseNodes(), MeshLib::Mesh::getNumberOfElements(), MeshLib::Mesh::getNumberOfNodes(), OGS_FATAL, MeshLib::QUAD, MeshLib::TRIANGLE, and WARN().

Referenced by MeshLayerEditDialog::createPrismMesh(), and MeshLayerEditDialog::createTetMesh().

◆ layerMapping()

bool MeshToolsLib::MeshLayerMapper::layerMapping ( MeshLib::Mesh const & mesh,
const GeoLib::Raster & raster,
double nodata_replacement = 0.0,
bool const ignore_nodata = false )
static

Maps the elevation of nodes of a given 2D mesh according to the raster. At locations where no information is given, node elevation is set to noDataReplacementValue.

Definition at line 283 of file MeshLayerMapper.cpp.

287{
288 if (mesh.getDimension() != 2)
289 {
290 ERR("MeshLayerMapper::layerMapping() - requires 2D mesh");
291 return false;
292 }
293
294 GeoLib::RasterHeader const& header(raster.getHeader());
295 const double x0(header.origin[0]);
296 const double y0(header.origin[1]);
297 const double delta(header.cell_size);
298
299 const std::pair<double, double> xDim(
300 x0, x0 + header.n_cols * delta); // extension in x-dimension
301 const std::pair<double, double> yDim(
302 y0, y0 + header.n_rows * delta); // extension in y-dimension
303
304 const std::size_t nNodes(mesh.getNumberOfNodes());
305 const std::vector<MeshLib::Node*>& nodes = mesh.getNodes();
306 for (unsigned i = 0; i < nNodes; ++i)
307 {
308 if (!ignore_nodata && !raster.isPntOnRaster(*nodes[i]))
309 {
310 // use either default value or elevation from layer above
311 (*nodes[i])[2] = nodata_replacement;
312 continue;
313 }
314
315 double elevation(raster.getValueAtPoint(*nodes[i]));
316 constexpr double eps = std::numeric_limits<double>::epsilon();
317 if (std::abs(elevation - header.no_data) < eps)
318 {
319 if (ignore_nodata)
320 {
321 continue;
322 }
323 elevation = nodata_replacement;
324 }
325 else
326 {
327 elevation = raster.interpolateValueAtPoint(*nodes[i]);
328 }
329 (*nodes[i])[2] = elevation;
330 }
331
332 return true;
333}
double getValueAtPoint(const MathLib::Point3d &pnt) const
Definition Raster.cpp:46
bool isPntOnRaster(MathLib::Point3d const &pnt) const
Definition Raster.cpp:141
double interpolateValueAtPoint(const MathLib::Point3d &pnt) const
Definition Raster.cpp:75
RasterHeader const & getHeader() const
Returns the complete header information.
Definition Raster.h:75

References GeoLib::RasterHeader::cell_size, ERR(), MeshLib::Mesh::getDimension(), GeoLib::Raster::getHeader(), MeshLib::Mesh::getNodes(), MeshLib::Mesh::getNumberOfNodes(), GeoLib::Raster::getValueAtPoint(), GeoLib::Raster::interpolateValueAtPoint(), GeoLib::Raster::isPntOnRaster(), GeoLib::RasterHeader::n_cols, GeoLib::RasterHeader::n_rows, GeoLib::RasterHeader::no_data, and GeoLib::RasterHeader::origin.

Referenced by LayeredVolume::createRasterLayers(), createRasterLayers(), main(), and MeshView::openMap2dMeshDialog().

◆ mapToStaticValue()

bool MeshToolsLib::MeshLayerMapper::mapToStaticValue ( MeshLib::Mesh const & mesh,
double value )
static

Maps the elevation of all mesh nodes to the specified static value.

Definition at line 335 of file MeshLayerMapper.cpp.

337{
338 if (mesh.getDimension() != 2)
339 {
340 ERR("MshLayerMapper::mapToStaticValue() - requires 2D mesh");
341 return false;
342 }
343
344 std::vector<MeshLib::Node*> const& nodes(mesh.getNodes());
345 for (MeshLib::Node* node : nodes)
346 {
347 (*node)[2] = value;
348 }
349 return true;
350}

References ERR(), MeshLib::Mesh::getDimension(), and MeshLib::Mesh::getNodes().

Referenced by main(), and MeshView::openMap2dMeshDialog().


The documentation for this class was generated from the following files: