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