OGS
TecPlotTools.cpp
Go to the documentation of this file.
1
10#include <tclap/CmdLine.h>
11
12#include <iostream>
13#include <memory>
14#include <string>
15#include <string_view>
16#include <vector>
17
18#include "BaseLib/MPI.h"
19#include "BaseLib/StringTools.h"
20#include "GeoLib/Point.h"
21#include "InfoLib/GitInfo.h"
23#include "MeshLib/Mesh.h"
25
28std::string getValue(std::string const& line,
29 std::string const& val_name,
30 bool is_string)
31{
32 std::string value;
33 std::size_t start(line.find(val_name));
34 std::size_t end(std::string::npos);
35 if (start == end)
36 {
37 ERR("Value not found.");
38 return "";
39 }
40 value = line.substr(start + 1, std::string::npos);
41 if (is_string)
42 {
43 start = value.find("\"");
44 value = value.substr(start + 1, std::string::npos);
45 end = value.find("\"");
46 }
47 else
48 {
49 start = value.find_first_not_of(" ");
50 value = value.substr(start + 1, std::string::npos);
51 BaseLib::trim(value);
52 end = value.find_first_of(" ");
53 }
54 value = value.substr(0, end);
55 BaseLib::trim(value);
56 return value;
57}
58
60std::string getName(std::string const& line)
61{
62 return getValue(line, "T=", true);
63}
64
65std::string_view getZonetype(std::string const& line)
66{
67 auto start = line.find("ZONETYPE=");
68 if (start == std::string::npos)
69 {
71 "A required 'ZONETYPE=' substring is not available in the ZONE "
72 "description: '{:s}'.",
73 line);
74 }
75 start += std::size("ZONETYPE=") - 1;
76
77 auto end = line.find(',', start);
78 if (end == std::string::npos)
79 {
81 "Expected the 'ZONETYPE=type' to be followed by a comma in the "
82 "ZONE description '{:s}'. The zone type starts at position {:d}.",
83 line, start);
84 }
85 if (start == end)
86 {
87 ERR("ZONETYPE string is empty in ZONE description '{:s}'.", line);
88 }
89 return std::string_view(&line[start], end - start);
90}
91
93std::pair<std::size_t, std::size_t> getDimensions(std::string const& line)
94{
95 std::pair<std::size_t, std::size_t> dims;
96 std::stringstream start(getValue(line, "I=", false));
97 start >> dims.first;
98 std::stringstream end(getValue(line, "J=", false));
99 end >> dims.second;
100 return dims;
101}
102
104std::string trimVariable(std::string& var)
105{
106 std::size_t const start = var.find_first_not_of("\"");
107 var = var.substr(start, std::string::npos);
108 std::size_t const end = var.find_first_of("\"");
109 return var.substr(0, end);
110}
111
113std::vector<std::string> getVariables(std::string const& line)
114{
115 std::string const var_str("VARIABLES");
116 std::size_t start(line.find(var_str));
117 std::string all_vars =
118 line.substr(start + var_str.length(), std::string::npos);
119 start = all_vars.find("=");
120 all_vars = all_vars.substr(start + 1, std::string::npos);
121 BaseLib::trim(all_vars);
122
123 std::vector<std::string> variables;
124 std::size_t end = 0;
125 while (end != std::string::npos)
126 {
127 end = all_vars.find_first_of(" ");
128 std::string var = all_vars.substr(0, end);
129 variables.push_back(trimVariable(var));
130 all_vars = all_vars.substr(end + 1, std::string::npos);
131 BaseLib::trim(all_vars);
132 }
133
134 return variables;
135}
136
138bool dataCountError(std::string const& name,
139 std::size_t const& current,
140 std::size_t const& total)
141{
142 if (current != total)
143 {
144 ERR("Data rows found do not fit specified dimensions for section "
145 "'{:s}'.",
146 name);
147 return true;
148 }
149 return false;
150}
151
154bool dataCountError(std::ofstream& out,
155 std::string const& name,
156 std::size_t const& current,
157 std::size_t const& total)
158{
159 if (dataCountError(name, current, total))
160 {
161 out.close();
162 return true;
163 }
164 return false;
165}
166
168void resetDataStructures(std::size_t const& n_scalars,
169 std::vector<std::vector<double>>& scalars,
170 std::size_t& val_count)
171{
172 scalars.clear();
173 scalars.reserve(n_scalars);
174 for (std::size_t i = 0; i < n_scalars; ++i)
175 {
176 scalars.emplace_back(0);
177 }
178 val_count = 0;
179}
180
182void writeTecPlotSection(std::ofstream& out,
183 std::string const& file_name,
184 std::size_t& write_count,
185 std::size_t& val_count,
186 std::size_t& val_total)
187{
188 if (write_count == 0 || val_total != 0)
189 {
190 std::size_t const delim_pos(file_name.find_last_of("."));
191 std::string const base_name(file_name.substr(0, delim_pos + 1));
192 std::string const extension(
193 file_name.substr(delim_pos, std::string::npos));
194
195 val_count = 0;
196 val_total = 0;
197 INFO("Writing section #{}", write_count);
198 out.close();
199 out.open(base_name + std::to_string(write_count++) + extension);
200 }
201}
202
204int writeDataToMesh(std::string const& file_name,
205 std::size_t& write_count,
206 std::vector<std::string> const& vec_names,
207 std::vector<std::vector<double>> const& scalars,
208 std::pair<std::size_t, std::size_t> const& dims)
209{
210 double cellsize = 0;
211 for (std::size_t i = 0; i < vec_names.size(); ++i)
212 {
213 if (vec_names[i] == "x" || vec_names[i] == "X")
214 {
215 cellsize = scalars[i][1] - scalars[i][0];
216 break;
217 }
218 }
219
220 if (cellsize == 0)
221 {
222 ERR("Cell size not found. Aborting...");
223 return -4;
224 }
225
226 GeoLib::Point origin(0, 0, 0);
227 GeoLib::RasterHeader header{dims.first, dims.second, 1,
228 origin, cellsize, -9999};
229
230 std::unique_ptr<MeshLib::Mesh> mesh(
231 MeshToolsLib::RasterToMesh::convert(scalars[0].data(),
232 header,
235 vec_names[0]));
236 MeshLib::Properties& properties = mesh->getProperties();
237 for (std::size_t i = 1; i < vec_names.size(); ++i)
238 {
239 auto* const prop = properties.createNewPropertyVector<double>(
240 vec_names[i], MeshLib::MeshItemType::Cell, 1);
241 if (!prop)
242 {
243 ERR("Error creating array '{:s}'.", vec_names[i]);
244 return -5;
245 }
246 prop->assign(scalars[i].begin(), scalars[i].end());
247 }
248
249 std::size_t const delim_pos(file_name.find_last_of("."));
250 std::string const base_name(file_name.substr(0, delim_pos + 1));
251 std::string const extension(file_name.substr(delim_pos, std::string::npos));
252
253 INFO("Writing section #{}", write_count);
254 MeshLib::IO::VtuInterface vtu(mesh.get());
255 vtu.writeToFile(base_name + std::to_string(write_count++) + extension);
256 return 0;
257}
258
260void skipGeometrySection(std::ifstream& in, std::string& line)
261{
262 while (std::getline(in, line))
263 {
264 if ((line.find("TITLE") != std::string::npos) ||
265 (line.find("VARIABLES") != std::string::npos) ||
266 (line.find("ZONE") != std::string::npos))
267 {
268 return;
269 }
270 }
271}
272
274int splitFile(std::ifstream& in, std::string const& file_name)
275{
276 std::ofstream out;
277 std::string line;
278 std::string name;
279 std::size_t val_count(0);
280 std::size_t val_total(0);
281 std::size_t write_count(0);
282 while (std::getline(in, line))
283 {
284 if (line.find("TITLE") != std::string::npos)
285 {
286 if (dataCountError(out, name, val_count, val_total))
287 {
288 return -3;
289 }
290 writeTecPlotSection(out, file_name, write_count, val_count,
291 val_total);
292 out << line << "\n";
293 continue;
294 }
295 if (line.find("VARIABLES") != std::string::npos)
296 {
297 if (dataCountError(out, name, val_count, val_total))
298 {
299 return -3;
300 }
301 writeTecPlotSection(out, file_name, write_count, val_count,
302 val_total);
303 out << line << "\n";
304 continue;
305 }
306 if (line.find("ZONE") != std::string::npos)
307 {
308 if (dataCountError(out, name, val_count, val_total))
309 {
310 return -3;
311 }
312 writeTecPlotSection(out, file_name, write_count, val_count,
313 val_total);
314 out << line << "\n";
315 name = getName(line);
316 std::pair<std::size_t, std::size_t> dims = getDimensions(line);
317 val_total = dims.first * dims.second;
318 val_count = 0;
319 continue;
320 }
321
322 out << line << "\n";
323 val_count++;
324 }
325 if (dataCountError(out, name, val_count, val_total))
326 {
327 return -3;
328 }
329 INFO("Writing time step #{}", write_count);
330 out.close();
331 INFO("Finished split.");
332 return 0;
333}
334
337int convertFile(std::ifstream& in, std::string const& file_name)
338{
339 std::string line;
340 std::string name;
341 std::pair<std::size_t, std::size_t> dims(0, 0);
342 std::vector<std::string> var_names;
343 std::vector<std::vector<double>> scalars;
344 std::size_t val_count(0);
345 std::size_t val_total(0);
346 std::size_t write_count(0);
347 while (std::getline(in, line))
348 {
349 if (line.find("GEOMETRY") != std::string::npos)
350 {
351 skipGeometrySection(in, line);
352 }
353
354 if (line.empty())
355 {
356 continue;
357 }
358 if (line.find("TITLE") != std::string::npos)
359 {
360 if (dataCountError(name, val_count, val_total))
361 {
362 return -3;
363 }
364 if (val_count != 0)
365 {
366 writeDataToMesh(file_name, write_count, var_names, scalars,
367 dims);
368 resetDataStructures(var_names.size(), scalars, val_count);
369 }
370 continue;
371 }
372 if (line.find("VARIABLES") != std::string::npos)
373 {
374 if (val_count != 0)
375 {
376 if (dataCountError(name, val_count, val_total))
377 {
378 return -3;
379 }
380 writeDataToMesh(file_name, write_count, var_names, scalars,
381 dims);
382 }
383 var_names.clear();
384 var_names = getVariables(line);
385 resetDataStructures(var_names.size(), scalars, val_count);
386 continue;
387 }
388 if (line.find("ZONE") != std::string::npos)
389 {
390 if (val_count != 0)
391 {
392 if (dataCountError(name, val_count, val_total))
393 {
394 return -3;
395 }
396 writeDataToMesh(file_name, write_count, var_names, scalars,
397 dims);
398 resetDataStructures(var_names.size(), scalars, val_count);
399 }
400 name = getName(line);
401 if (auto const zonetype = getZonetype(line); zonetype != "ORDERED")
402 {
403 ERR("Given zonetype '{:s}' is not supported. Only 'ORDERED' "
404 "zonetype data can be converted.",
405 zonetype);
406 return -4;
407 }
408 dims = getDimensions(line);
409 val_total = dims.first * dims.second;
410 val_count = 0;
411 continue;
412 }
413
414 double x;
415 std::stringstream iss(line);
416 std::size_t i(0);
417 std::size_t const n_scalars(scalars.size());
418 while (iss >> x)
419 {
420 if (i > n_scalars - 1)
421 {
422 ERR("Too much data for existing scalar arrays");
423 return -3;
424 }
425 scalars[i++].push_back(x);
426 }
427 if (i < n_scalars)
428 {
429 ERR("Not enough data for existing scalar arrays");
430 return -3;
431 }
432 val_count++;
433 }
434 if (dataCountError(name, val_count, val_total))
435 {
436 return -3;
437 }
438 writeDataToMesh(file_name, write_count, var_names, scalars, dims);
439 INFO("Finished conversion.");
440 return 0;
441}
442
454int main(int argc, char* argv[])
455{
456 TCLAP::CmdLine cmd(
457 "TecPlot Parser\n\n"
458 "OpenGeoSys-6 software, version " +
460 ".\n"
461 "Copyright (c) 2012-2025, OpenGeoSys Community "
462 "(http://www.opengeosys.org)",
464 TCLAP::SwitchArg split_arg("s", "split",
465 "split time steps into separate files");
466 cmd.add(split_arg);
467 TCLAP::SwitchArg convert_arg("c", "convert",
468 "convert TecPlot data into OGS meshes");
469 cmd.add(convert_arg);
470 TCLAP::ValueArg<std::string> output_arg(
471 "o", "output-file", "output mesh file", false, "", "string");
472 cmd.add(output_arg);
473 TCLAP::ValueArg<std::string> input_arg(
474 "i", "input-file", "TecPlot input file", true, "", "string");
475 cmd.add(input_arg);
476 cmd.parse(argc, argv);
477
478 BaseLib::MPI::Setup mpi_setup(argc, argv);
479
480 if (!input_arg.isSet())
481 {
482 ERR("No input file given. Please specify TecPlot (*.plt) file");
483 return -1;
484 }
485
486 if (convert_arg.getValue() && !output_arg.isSet())
487 {
488 ERR("No output file given. Please specify OGS mesh (*.vtu) file");
489 return -1;
490 }
491
492 std::ifstream in(input_arg.getValue().c_str());
493 if (!in.is_open())
494 {
495 ERR("Could not open file {:s}.", input_arg.getValue());
496 return -2;
497 }
498
499 if (!convert_arg.isSet() && !split_arg.isSet())
500 {
501 INFO("Nothing to do. Use -s to split or -c to convert.");
502 return 0;
503 }
504
505 std::string const filename =
506 (output_arg.isSet()) ? output_arg.getValue() : input_arg.getValue();
507 int return_val(0);
508 if (split_arg.getValue())
509 {
510 return_val = splitFile(in, filename);
511 }
512 else if (convert_arg.getValue())
513 {
514 return_val = convertFile(in, filename);
515 }
516
517 in.close();
518 return return_val;
519}
#define OGS_FATAL(...)
Definition Error.h:26
Definition of the Point class.
Git information.
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
Definition of the Mesh class.
Definition of string helper functions.
int convertFile(std::ifstream &in, std::string const &file_name)
std::string getName(std::string const &line)
Returns the name/title from the "Zone"-description.
int main(int argc, char *argv[])
std::string_view getZonetype(std::string const &line)
int splitFile(std::ifstream &in, std::string const &file_name)
Splits a TecPlot file containing multiple sections/zones into separate files.
bool dataCountError(std::string const &name, std::size_t const &current, std::size_t const &total)
Tests if the number of read values equals the number of expected values.
std::string getValue(std::string const &line, std::string const &val_name, bool is_string)
std::string trimVariable(std::string &var)
Trims a substring containing a variable-name.
void skipGeometrySection(std::ifstream &in, std::string &line)
If a geometry-section is encountered, it is currently ignored.
void writeTecPlotSection(std::ofstream &out, std::string const &file_name, std::size_t &write_count, std::size_t &val_count, std::size_t &val_total)
Writes one section/zone into a separate TecPlot file.
void resetDataStructures(std::size_t const &n_scalars, std::vector< std::vector< double > > &scalars, std::size_t &val_count)
Resets all data structures after a new "Variables"-description is found.
std::pair< std::size_t, std::size_t > getDimensions(std::string const &line)
Returns raster dimensions from the "Zone"-description.
int writeDataToMesh(std::string const &file_name, std::size_t &write_count, std::vector< std::string > const &vec_names, std::vector< std::vector< double > > const &scalars, std::pair< std::size_t, std::size_t > const &dims)
Writes one section/zone into a separate OGS-mesh.
std::vector< std::string > getVariables(std::string const &line)
Returns an vector of variable names from the "Variables"-description.
Implementation of the VtuInterface class.
Reads and writes VtkXMLUnstructuredGrid-files (vtu) to and from OGS data structures....
bool writeToFile(std::filesystem::path const &file_path)
Property manager on mesh items. Class Properties manages scalar, vector or matrix properties....
Definition Properties.h:33
PropertyVector< T > * createNewPropertyVector(std::string_view name, MeshItemType mesh_item_type, std::size_t n_components=1)
Definition Properties.h:17
static std::unique_ptr< MeshLib::Mesh > convert(GeoLib::Raster const &raster, MeshLib::MeshElemType elem_type, MeshLib::UseIntensityAs intensity_type, std::string const &array_name="Colour")
void trim(std::string &str, char ch)
GITINFOLIB_EXPORT const std::string ogs_version
Contains the relevant information when storing a geoscientific raster data.
Definition Raster.h:28