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