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