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