OGS
GMSInterface.cpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: Copyright (c) OpenGeoSys Community (opengeosys.org)
2// SPDX-License-Identifier: BSD-3-Clause
3
4#include "GMSInterface.h"
5
6#include <fstream>
7#include <list>
8
9#include "BaseLib/FileTools.h"
10#include "BaseLib/Logging.h"
11#include "BaseLib/StringTools.h"
17#include "MeshLib/Mesh.h"
18#include "MeshLib/MeshEnums.h"
19#include "MeshLib/Node.h"
20
21namespace
22{
23template <typename It>
24std::array<double, 3> parsePointCoordinates(It& it)
25{
26 return {std::strtod((++it)->c_str(), nullptr),
27 std::strtod((++it)->c_str(), nullptr),
28 std::strtod((++it)->c_str(), nullptr)};
29}
30} // namespace
31
32namespace FileIO
33{
34int GMSInterface::readBoreholesFromGMS(std::vector<GeoLib::Point*>& boreholes,
35 const std::string& filename)
36{
37 std::ifstream in(filename.c_str());
38 if (!in.is_open())
39 {
40 ERR("GMSInterface::readBoreholeFromGMS(): Could not open file {:s}.",
41 filename);
42 return 0;
43 }
44
45 double depth(-9999.0);
46 std::string line;
47 std::string cName;
48 std::string sName;
49 std::list<std::string>::const_iterator it;
50 GeoLib::StationBorehole* newBorehole = nullptr;
51
52 /* skipping first line because it contains field names */
53 std::getline(in, line);
54
55 /* read all stations */
56 while (std::getline(in, line))
57 {
58 std::list<std::string> fields = BaseLib::splitString(line, '\t');
59
60 if (fields.size() >= 5)
61 {
62 if (*fields.begin() == cName) // add new layer
63 {
64 it = fields.begin();
65 auto const pnt = parsePointCoordinates(it);
66
67 // check if current layer has a thickness of 0.0.
68 // if so skip it since it will mess with the vtk-visualisation
69 // later on!
70 if (pnt[2] != depth)
71 {
72 if (newBorehole == nullptr)
73 OGS_FATAL("Trying to access a nullptr.");
74 newBorehole->addSoilLayer(pnt[0], pnt[1], pnt[2], sName);
75 sName = (*(++it));
76 depth = pnt[2];
77 }
78 else
79 {
80 WARN(
81 "GMSInterface::readBoreholeFromGMS(): Skipped layer "
82 "'{:s}' in borehole '{:s}' because of thickness 0.0.",
83 sName, cName);
84 }
85 }
86 else // add new borehole
87 {
88 if (newBorehole != nullptr)
89 {
90 newBorehole->setDepth((*newBorehole)[2] - depth);
91 boreholes.push_back(newBorehole);
92 }
93 cName = *fields.begin();
94 it = fields.begin();
95 auto const pnt = parsePointCoordinates(it);
96 sName = (*(++it));
98 cName, pnt[0], pnt[1], pnt[2], 0);
99 depth = pnt[2];
100 }
101 }
102 else
103 {
104 ERR("GMSInterface::readBoreholeFromGMS(): Error reading format.");
105 }
106 }
107 // write the last borehole from the file
108 if (newBorehole != nullptr)
109 {
110 newBorehole->setDepth((*newBorehole)[2] - depth);
111 boreholes.push_back(newBorehole);
112 }
113 in.close();
114
115 if (boreholes.empty())
116 {
117 return 0;
118 }
119 return 1;
120}
121
123 const std::vector<GeoLib::Point*>* stations, const std::string& filename)
124{
125 std::ofstream out(filename.c_str(), std::ios::out);
126
127 // write header
128 out << "name"
129 << "\t" << std::fixed << "X"
130 << "\t"
131 << "Y"
132 << "\t"
133 << "Z"
134 << "\t"
135 << "soilID"
136 << "\n";
137
138 for (auto station_as_point : *stations)
139 {
140 auto const* station =
141 static_cast<GeoLib::StationBorehole*>(station_as_point);
142 std::vector<GeoLib::Point*> const& profile = station->getProfile();
143 std::vector<std::string> const& soilNames = station->getSoilNames();
144 std::string current_soil_name;
145
146 std::size_t nLayers = profile.size();
147 for (std::size_t i = 1; i < nLayers; i++)
148 {
149 if ((i > 1) && (soilNames[i] == soilNames[i - 1]))
150 {
151 continue;
152 }
153 current_soil_name = soilNames[i];
154
155 out << station->getName() << "\t" << std::fixed
156 << (*(profile[i - 1]))[0] << "\t" << (*(profile[i - 1]))[1]
157 << "\t" << (*(profile[i - 1]))[2] << "\t"
158 << current_soil_name /*idx*/ << "\n";
159 }
160 out << station->getName() << "\t" << std::fixed
161 << (*(profile[nLayers - 1]))[0] << "\t"
162 << (*(profile[nLayers - 1]))[1] << "\t"
163 << (*(profile[nLayers - 1]))[2] << "\t" << current_soil_name
164 << "\n"; // this line marks the end of the borehole
165 }
166
167 out.close();
168}
169
170MeshLib::Mesh* GMSInterface::readMesh(const std::string& filename)
171{
172 std::string line;
173
174 std::ifstream in(filename.c_str());
175 if (!in.is_open())
176 {
177 ERR("GMSInterface::readMesh(): Could not open file {:s}.", filename);
178 return nullptr;
179 }
180
181 // Read data from file
182 std::getline(in, line); // "MESH3D"
183 if (line != "MESH3D" && line != "MESH2D")
184 {
185 ERR("GMSInterface::readMesh(): Could not read expected file "
186 "header.");
187 return nullptr;
188 }
189 bool const is_3d = (line == "MESH3D");
190
191 std::string mesh_name = BaseLib::extractBaseNameWithoutExtension(filename);
192 INFO("Reading SMS/GMS mesh...");
193 std::vector<MeshLib::Node*> nodes;
194 std::vector<MeshLib::Element*> elements;
195 std::vector<int> mat_ids;
196 std::map<unsigned, unsigned> id_map;
197
198 // elements are listed before nodes in 3dm-format, therefore
199 // traverse file twice and read first nodes and then elements
200 std::string dummy;
201 unsigned id(0);
202 unsigned count(0);
203 double x[3];
204 // read nodes
205 while (std::getline(in, line))
206 {
207 if (line[0] == 'N') // "ND" for Node
208 {
209 std::stringstream str(line);
210 str >> dummy >> id >> x[0] >> x[1] >> x[2];
211 auto* node = new MeshLib::Node(x, id);
212 id_map.insert(std::pair<unsigned, unsigned>(id, count++));
213 nodes.push_back(node);
214 }
215 }
216 in.close();
217
218 // NOTE: Element types E8H (Hex), E4Q (Quad) are not implemented yet
219 // read elements
220 in.open(filename.c_str());
221 std::getline(in, line); // "MESH2D" / "MESH3D"
222 unsigned node_idx[6];
223 int mat_id(0);
224 while (std::getline(in, line))
225 {
226 std::string element_id(line.substr(0, 3));
227 std::stringstream str(line);
228
229 if (element_id == "MES") // "MESHNAME"
230 {
231 str >> dummy >> mesh_name;
232 mesh_name = mesh_name.substr(1, mesh_name.length() - 2);
233 }
234 else if (!is_3d && element_id == "E3T") // Triangle
235 {
236 str >> dummy >> id >> node_idx[0] >> node_idx[1] >> node_idx[2] >>
237 mat_id;
238 std::array<MeshLib::Node*, 3> tri_nodes;
239 for (unsigned k(0); k < 3; k++)
240 {
241 tri_nodes[k] = nodes[id_map.find(node_idx[k])->second];
242 }
243 elements.push_back(new MeshLib::Tri(tri_nodes));
244 mat_ids.push_back(mat_id);
245 }
246 else if (!is_3d && element_id == "E6T") // Triangle
247 {
248 str >> dummy >> id >> node_idx[0] >> node_idx[3] >> node_idx[1] >>
249 node_idx[4] >> node_idx[2] >> node_idx[5] >> mat_id;
250 std::array<MeshLib::Node*, 3> tri_nodes;
251 for (unsigned k(0); k < 3; k++)
252 {
253 tri_nodes[k] = nodes[id_map.find(node_idx[k])->second];
254 }
255 elements.push_back(new MeshLib::Tri(tri_nodes));
256 mat_ids.push_back(mat_id);
257 }
258 else if (is_3d && element_id == "E6W") // Prism
259 {
260 str >> dummy >> id >> node_idx[0] >> node_idx[1] >> node_idx[2] >>
261 node_idx[3] >> node_idx[4] >> node_idx[5] >> mat_id;
262 std::array<MeshLib::Node*, 6> prism_nodes;
263 for (unsigned k(0); k < 6; k++)
264 {
265 prism_nodes[k] = nodes[id_map.find(node_idx[k])->second];
266 }
267 elements.push_back(new MeshLib::Prism(prism_nodes));
268 mat_ids.push_back(mat_id);
269 }
270 else if (is_3d && element_id == "E4T") // Tet
271 {
272 str >> dummy >> id >> node_idx[0] >> node_idx[1] >> node_idx[2] >>
273 node_idx[3] >> mat_id;
274 std::array<MeshLib::Node*, 4> tet_nodes;
275 for (unsigned k(0); k < 4; k++)
276 {
277 tet_nodes[k] = nodes[id_map.find(node_idx[k])->second];
278 }
279 elements.push_back(new MeshLib::Tet(tet_nodes));
280 mat_ids.push_back(mat_id);
281 }
282 // Pyramid (two versions exist for some reason)
283 else if (is_3d && (element_id == "E4P" || element_id == "E5P"))
284 {
285 str >> dummy >> id >> node_idx[0] >> node_idx[1] >> node_idx[2] >>
286 node_idx[3] >> node_idx[4] >> mat_id;
287 std::array<MeshLib::Node*, 5> pyramid_nodes;
288 for (unsigned k(0); k < 5; k++)
289 {
290 pyramid_nodes[k] = nodes[id_map.find(node_idx[k])->second];
291 }
292 elements.push_back(new MeshLib::Pyramid(pyramid_nodes));
293 mat_ids.push_back(mat_id);
294 }
295 else if (element_id == "ND ")
296 { // Node
297 continue; // skip because nodes have already been read
298 }
299 else // default
300 {
301 WARN(
302 "GMSInterface::readMesh() - Element type '{:s}' not "
303 "recognised.",
304 element_id);
305 return nullptr;
306 }
307 }
308
309 in.close();
310 INFO("finished.");
311
312 MeshLib::Properties properties;
313 if (mat_ids.size() == elements.size())
314 {
315 auto* const opt_pv = properties.createNewPropertyVector<int>(
316 "MaterialIDs", MeshLib::MeshItemType::Cell);
317 if (!opt_pv)
318 {
319 ERR("Could not create PropertyVector for material ids.");
320 BaseLib::cleanupVectorElements(nodes, elements);
321 return nullptr;
322 }
323 opt_pv->assign(mat_ids);
324 }
325 else
326 {
327 ERR("Ignoring Material IDs information (does not match number of "
328 "elements).");
329 }
330 return new MeshLib::Mesh(mesh_name, nodes, elements,
331 true /* compute_element_neighbors */, properties);
332}
333
334} // namespace FileIO
#define OGS_FATAL(...)
Definition Error.h:19
void INFO(fmt::format_string< Args... > fmt, Args &&... args)
Definition Logging.h:28
void ERR(fmt::format_string< Args... > fmt, Args &&... args)
Definition Logging.h:40
void WARN(fmt::format_string< Args... > fmt, Args &&... args)
Definition Logging.h:34
static int readBoreholesFromGMS(std::vector< GeoLib::Point * > &boreholes, const std::string &filename)
Imports borehole data from a file in GMS-format.
static MeshLib::Mesh * readMesh(const std::string &filename)
Reads a GMS *.3dm file and converts it to an CFEMesh.
static void writeBoreholesToGMS(const std::vector< GeoLib::Point * > *stations, const std::string &filename)
A borehole as a geometric object.
static StationBorehole * createStation(const std::string &name, double x, double y, double z, double depth, const std::string &date="")
Creates a new borehole object based on the given parameters.
void addSoilLayer(double thickness, const std::string &soil_name)
Add a soil layer to the boreholes stratigraphy.
const std::vector< Point * > & getProfile() const
void setDepth(double depth)
Sets the depth of the borehole.
Property manager on mesh items. Class Properties manages scalar, vector or matrix properties....
PropertyVector< T > * createNewPropertyVector(std::string_view name, MeshItemType mesh_item_type, std::size_t n_components=1)
constexpr void assign(R &&r)
void cleanupVectorElements(std::vector< T * > &items)
Definition Algorithm.h:249
std::string extractBaseNameWithoutExtension(std::string const &pathname)
std::vector< std::string > splitString(std::string const &str)
TemplateElement< MeshLib::TetRule4 > Tet
Definition Tet.h:14
TemplateElement< MeshLib::TriRule3 > Tri
Definition Tri.h:15
TemplateElement< MeshLib::PyramidRule5 > Pyramid
Definition Pyramid.h:14
TemplateElement< MeshLib::PrismRule6 > Prism
Definition Prism.h:14
std::array< double, 3 > parsePointCoordinates(It &it)