OGS
FEFLOWGeoInterface.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
5
6#include <libxml/parser.h>
7#include <libxml/tree.h>
8
9#include <boost/algorithm/string/trim.hpp>
10#include <cctype>
11#include <fstream>
12#include <memory>
13#include <sstream>
14
15#include "BaseLib/FileTools.h"
16#include "BaseLib/Logging.h"
17#include "BaseLib/StringTools.h"
18#include "GeoLib/GEOObjects.h"
19#include "GeoLib/Point.h"
20#include "GeoLib/Polygon.h"
21
22namespace
23{
24
25inline std::string xmlCharsToString(xmlChar* v)
26{
27 if (!v)
28 {
29 return {};
30 }
31 std::string s(reinterpret_cast<const char*>(v));
33 xmlFree(v);
34 return s;
35}
36
37inline xmlNodePtr firstChild(xmlNodePtr parent, const xmlChar* name)
38{
39 if (!parent)
40 {
41 return nullptr;
42 }
43 for (xmlNodePtr n = parent->children; n != nullptr; n = n->next)
44 {
45 if (n->type == XML_ELEMENT_NODE && xmlStrcmp(n->name, name) == 0)
46 {
47 return n;
48 }
49 }
50 return nullptr;
51}
52} // namespace
53
54namespace FileIO
55{
56void FEFLOWGeoInterface::readFEFLOWFile(const std::string& filename,
57 GeoLib::GEOObjects& geo_objects)
58{
59 std::ifstream in(filename.c_str());
60 if (!in)
61 {
62 ERR("FEFLOWGeoInterface::readFEFLOWFile(): Could not open file {:s}.",
63 filename);
64 return;
65 }
66
67 unsigned dimension = 0;
68 std::vector<GeoLib::Point*> points;
69 std::vector<GeoLib::Polyline*> lines;
70
71 bool isXZplane = false;
72
73 std::string line_string;
74 std::stringstream line_stream;
75 while (!in.eof())
76 {
77 std::getline(in, line_string);
78 if (line_string.find("CLASS") != std::string::npos)
79 {
80 std::getline(in, line_string);
81 line_stream.str(line_string);
82 unsigned dummy = 0;
83 for (int i = 0; i < 3; i++)
84 {
85 line_stream >> dummy;
86 }
87 line_stream >> dimension;
88 line_stream.clear();
89 }
90 else if (line_string == "GRAVITY")
91 {
92 std::getline(in, line_string);
93 line_stream.str(line_string);
94 double vec[3] = {};
95 line_stream >> vec[0] >> vec[1] >> vec[2];
96 if (vec[0] == 0.0 && vec[1] == -1.0 && vec[2] == 0.0)
97 {
98 isXZplane = true;
99 }
100 line_stream.clear();
101 }
102 else if (line_string == "SUPERMESH")
103 {
104 readSuperMesh(in, dimension, points, lines);
105 }
106 }
107 in.close();
108
109 if (isXZplane && !points.empty())
110 {
111 for (auto* pt : points)
112 {
113 (*pt)[2] = (*pt)[1];
114 (*pt)[1] = .0;
115 }
116 }
117 std::string project_name(
119 if (!points.empty())
120 {
121 geo_objects.addPointVec(std::move(points), project_name,
123 }
124 if (!lines.empty())
125 {
126 geo_objects.addPolylineVec(std::move(lines), project_name,
128 }
129}
130
131void FEFLOWGeoInterface::readPoints(xmlNodePtr nodesEle,
132 const xmlChar* tag,
133 int dim,
134 std::vector<GeoLib::Point*>& points)
135{
136 xmlNodePtr xmlEle = firstChild(nodesEle, tag);
137 if (xmlEle == nullptr)
138 {
139 return;
140 }
141
142 std::istringstream ss(xmlCharsToString(xmlNodeGetContent(xmlEle)));
143 std::string line_str;
144 while (std::getline(ss, line_str))
145 {
146 boost::algorithm::trim_right(line_str);
147 if (line_str.empty())
148 {
149 continue;
150 }
151 std::istringstream line_ss(line_str);
152 std::size_t pt_id = 0;
153 std::array<double, 3> pt_xyz{};
154 line_ss >> pt_id;
155 for (int i = 0; i < dim; ++i)
156 {
157 line_ss >> pt_xyz[i];
158 }
159 points[pt_id - 1] = new GeoLib::Point(pt_xyz, pt_id);
160 }
161}
162
164 unsigned dimension,
165 std::vector<GeoLib::Point*>& points,
166 std::vector<GeoLib::Polyline*>& lines)
167{
168 // Read XML block
169 std::ostringstream oss;
170 std::string line_string;
171 while (true)
172 {
173 std::getline(in, line_string);
174 BaseLib::trim(line_string);
175 oss << line_string << "\n";
176 if (line_string.find("</supermesh>") != std::string::npos)
177 {
178 break;
179 }
180 }
181 const std::string xml_str = oss.str();
182
183 xmlInitParser();
184 xmlDocPtr doc =
185 xmlReadMemory(xml_str.c_str(), static_cast<int>(xml_str.size()),
186 "supermesh.xml", nullptr, XML_PARSE_NOBLANKS);
187 if (doc == nullptr)
188 {
189 ERR("FEFLOWGeoInterface::readSuperMesh(): Illegal XML format error");
190 xmlCleanupParser();
191 return;
192 }
193
194 xmlNodePtr docElem = xmlDocGetRootElement(doc);
195 if (docElem == nullptr)
196 {
197 ERR("FEFLOWGeoInterface::readSuperMesh(): Error in getting XML root "
198 "element");
199 xmlFreeDoc(doc);
200 xmlCleanupParser();
201 return;
202 }
203
204 static constexpr xmlChar nodes_name[] = "nodes";
205 xmlNodePtr nodesEle = firstChild(docElem, nodes_name);
206 if (nodesEle == nullptr)
207 {
208 ERR("FEFLOWGeoInterface::readSuperMesh(): Error in getting 'nodes' "
209 "element");
210 xmlFreeDoc(doc);
211 xmlCleanupParser();
212 return;
213 }
214
215 static constexpr xmlChar count_name[] = "count";
216 const std::string cnt = xmlCharsToString(xmlGetProp(nodesEle, count_name));
217 const std::size_t n_points = cnt.empty() ? 0u : std::stoul(cnt);
218
219 // Convert to raw pointer vector for readPoints compatibility
220 std::vector<GeoLib::Point*> raw_points(n_points, nullptr);
221
222 static constexpr xmlChar fixed_name[] = "fixed";
223 readPoints(nodesEle, fixed_name, static_cast<int>(dimension), raw_points);
224 static constexpr xmlChar linear_name[] = "linear";
225 readPoints(nodesEle, linear_name, static_cast<int>(dimension), raw_points);
226 static constexpr xmlChar parabolic_name[] = "parabolic";
227 readPoints(nodesEle, parabolic_name, static_cast<int>(dimension),
228 raw_points);
229
230 // Transfer ownership to unique_ptrs
231 std::vector<std::unique_ptr<GeoLib::Point>> temp_points(n_points);
232 for (std::size_t i = 0; i < raw_points.size(); ++i)
233 {
234 temp_points[i].reset(raw_points[i]);
235 }
236
237 static constexpr xmlChar polygons_name[] = "polygons";
238 xmlNodePtr polygonsEle = firstChild(docElem, polygons_name);
239 if (polygonsEle == nullptr)
240 {
241 // unique_ptrs will automatically clean up
242 xmlFreeDoc(doc);
243 xmlCleanupParser();
244 return;
245 }
246
247 for (xmlNodePtr child = polygonsEle->children; child; child = child->next)
248 {
249 static constexpr xmlChar polygon_name[] = "polygon";
250 if (child->type != XML_ELEMENT_NODE ||
251 xmlStrcmp(child->name, polygon_name) != 0)
252 {
253 continue;
254 }
255
256 xmlNodePtr nodes = firstChild(child, nodes_name);
257 if (nodes == nullptr)
258 {
259 continue;
260 }
261
262 const std::string cnt_nodes =
263 xmlCharsToString(xmlGetProp(nodes, count_name));
264 const std::size_t n_pts =
265 cnt_nodes.empty() ? 0u
266 : static_cast<std::size_t>(std::stoul(cnt_nodes));
267 std::istringstream ss(xmlCharsToString(xmlNodeGetContent(nodes)));
268
269 auto line = std::make_unique<GeoLib::Polyline>(raw_points);
270
271 for (std::size_t i = 0; i < n_pts; ++i)
272 {
273 int pt_id = 0;
274 ss >> pt_id;
275 line->addPoint(pt_id - 1);
276 }
277 // close the polygon
278 line->addPoint(line->getPointID(0));
279
280 lines.push_back(line.release());
281 }
282
283 xmlFreeDoc(doc);
284 xmlCleanupParser();
285
286 // Only transfer ownership to output vectors if everything succeeded
287 for (auto& pt : temp_points)
288 {
289 points.push_back(pt.release());
290 }
291}
292
293} // namespace FileIO
void ERR(fmt::format_string< Args... > fmt, Args &&... args)
Definition Logging.h:40
static void readFEFLOWFile(const std::string &filename, GeoLib::GEOObjects &geo_objects)
static void readPoints(xmlNodePtr nodesEle, const xmlChar *tag, int dim, std::vector< GeoLib::Point * > &points)
static void readSuperMesh(std::ifstream &in, unsigned dimension, std::vector< GeoLib::Point * > &points, std::vector< GeoLib::Polyline * > &lines)
Container class for geometric objects.
Definition GEOObjects.h:46
void addPolylineVec(std::vector< Polyline * > &&lines, std::string const &name, PolylineVec::NameIdMap &&ply_names)
void addPointVec(std::vector< Point * > &&points, std::string &name, PointVec::NameIdMap &&pnt_id_name_map, double const eps=std::sqrt(std::numeric_limits< double >::epsilon()))
std::map< std::string, std::size_t > NameIdMap
Definition TemplateVec.h:30
void trim(std::string &str, char ch)
std::string extractBaseNameWithoutExtension(std::string const &pathname)
xmlNodePtr firstChild(xmlNodePtr parent, const xmlChar *name)