https://mooseframework.inl.gov
Loading...
Searching...
No Matches
FormattedTable.C
Go to the documentation of this file.
1//* This file is part of the MOOSE framework
2//* https://mooseframework.inl.gov
3//*
4//* All rights reserved, see COPYRIGHT for full restrictions
5//* https://github.com/idaholab/moose/blob/master/COPYRIGHT
6//*
7//* Licensed under LGPL 2.1, please see LICENSE for details
8//* https://www.gnu.org/licenses/lgpl-2.1.html
9
10#include "FormattedTable.h"
11#include "MooseError.h"
12#include "MooseUtils.h"
13
14#include "libmesh/exodusII_io.h"
15
16#include <iomanip>
17#include <iterator>
18
19const unsigned short FormattedTable::_column_width = 15;
20const unsigned short FormattedTable::_min_pps_width = 40;
21
22const unsigned short DEFAULT_CSV_PRECISION = 14;
23const std::string DEFAULT_CSV_DELIMITER = ",";
24
25template <>
26void
27dataStore(std::ostream & stream, FormattedTable & table, void * context)
28{
29 table.fillEmptyValues();
30 storeHelper(stream, table._data, context);
31 storeHelper(stream, table._align_widths, context);
32 storeHelper(stream, table._column_names, context);
33 storeHelper(stream, table._output_row_index, context);
34 storeHelper(stream, table._headers_output, context);
35}
36
37template <>
38void
39dataLoad(std::istream & stream, FormattedTable & table, void * context)
40{
41 loadHelper(stream, table._data, context);
42 loadHelper(stream, table._align_widths, context);
43 loadHelper(stream, table._column_names, context);
44 loadHelper(stream, table._output_row_index, context);
45 loadHelper(stream, table._headers_output, context);
46}
47
48template <>
49void
50dataStore(std::ostream & stream, std::shared_ptr<TableValueBase> & value_base, void * context)
51{
52 value_base->store(stream, context);
53}
54
55template <>
56void
57dataLoad(std::istream & stream, std::shared_ptr<TableValueBase> & value_base, void * context)
58{
59 std::string type;
60 dataLoad(stream, type, context);
61 if (type == typeid(bool).name())
62 TableValue<bool>::load(stream, value_base, context);
63
64 else if (type == typeid(unsigned short int).name())
65 TableValue<unsigned short int>::load(stream, value_base, context);
66
67 else if (type == typeid(unsigned int).name())
68 TableValue<unsigned int>::load(stream, value_base, context);
69
70 else if (type == typeid(unsigned long int).name())
71 TableValue<unsigned long int>::load(stream, value_base, context);
72
73 else if (type == typeid(unsigned long long int).name())
74 TableValue<unsigned long long int>::load(stream, value_base, context);
75
76 else if (type == typeid(short int).name())
77 TableValue<short int>::load(stream, value_base, context);
78
79 else if (type == typeid(int).name())
80 TableValue<int>::load(stream, value_base, context);
81
82 else if (type == typeid(long int).name())
83 TableValue<long int>::load(stream, value_base, context);
84
85 else if (type == typeid(long long int).name())
86 TableValue<long long int>::load(stream, value_base, context);
87
88 else if (type == typeid(float).name())
89 TableValue<float>::load(stream, value_base, context);
90
91 else if (type == typeid(double).name())
92 TableValue<double>::load(stream, value_base, context);
93
94 else if (type == typeid(long double).name())
95 TableValue<long double>::load(stream, value_base, context);
96
97 else if (type == typeid(char).name())
98 TableValue<char>::load(stream, value_base, context);
99
100 else if (type == typeid(char *).name())
101 TableValue<char *>::load(stream, value_base, context);
102
103 else if (type == typeid(std::string).name())
104 TableValue<std::string>::load(stream, value_base, context);
105
106 else
107 mooseError("Unsupported table value type ", demangle(type.c_str()));
108}
109
110void
112{
113 if (!_output_file.is_open())
114 return;
115 _output_file.flush();
116 _output_file.close();
118}
119
120void
121FormattedTable::open(const std::string & file_name)
122{
123 if (_output_file.is_open() && _output_file_name == file_name)
124 return;
125 close();
126 _output_file_name = file_name;
127
128 std::ios_base::openmode open_flags = std::ios::out;
129 if (_append)
130 open_flags |= std::ios::app;
131 else
132 {
133 open_flags |= std::ios::trunc;
135 _headers_output = false;
136 }
137
138 _output_file.open(file_name.c_str(), open_flags);
139 if (_output_file.fail())
140 mooseError("Unable to open file ", file_name);
141}
142
144 : _output_row_index(0),
145 _headers_output(false),
146 _append(false),
147 _output_time(true),
148 _csv_delimiter(DEFAULT_CSV_DELIMITER),
149 _csv_precision(DEFAULT_CSV_PRECISION),
150 _csv_use_scientific_notation(false)
151{
152}
153
155 : _column_names(o._column_names),
156 _output_file_name(""),
157 _output_row_index(o._output_row_index),
158 _headers_output(o._headers_output),
159 _append(o._append),
160 _output_time(o._output_time),
161 _csv_delimiter(o._csv_delimiter),
162 _csv_precision(o._csv_precision),
163 _csv_use_scientific_notation(o._csv_use_scientific_notation),
164 _column_names_unsorted(o._column_names_unsorted)
165{
166 if (_output_file.is_open())
167 mooseError("Copying a FormattedTable with an open stream is not supported");
168
169 for (const auto & it : o._data)
170 _data.emplace_back(it.first, it.second);
171}
172
174
175bool
177{
178 return _data.empty();
179}
180
181void
182FormattedTable::append(bool append_existing_file)
183{
184 _append = append_existing_file;
185}
186
187void
189{
190 _data.emplace_back(time, std::map<std::string, std::shared_ptr<TableValueBase>>());
191}
192
193Real
195{
196 mooseAssert(!empty(), "No Data stored in the FormattedTable");
197 return _data.rbegin()->first;
198}
199
200void
202 std::map<std::string, unsigned short> & col_widths,
203 std::vector<std::string>::iterator & col_begin,
204 std::vector<std::string>::iterator & col_end) const
205{
206 printNoDataRow(':', ' ', out, col_widths, col_begin, col_end);
207}
208
209void
211 std::map<std::string, unsigned short> & col_widths,
212 std::vector<std::string>::iterator & col_begin,
213 std::vector<std::string>::iterator & col_end) const
214{
215 printNoDataRow('+', '-', out, col_widths, col_begin, col_end);
216}
217
218void
220 char fill_char,
221 std::ostream & out,
222 std::map<std::string, unsigned short> & col_widths,
223 std::vector<std::string>::iterator & col_begin,
224 std::vector<std::string>::iterator & col_end) const
225{
226 out.fill(fill_char);
227 out << std::right << intersect_char;
228 if (_output_time)
229 out << std::setw(_column_width + 2) << intersect_char;
230 for (auto header_it = col_begin; header_it != col_end; ++header_it)
231 out << std::setw(col_widths[*header_it] + 2) << intersect_char;
232 out << "\n";
233
234 // Clear the fill character
235 out.fill(' ');
236}
237
238void
239FormattedTable::printTable(const std::string & file_name)
240{
241 open(file_name);
243}
244
245void
246FormattedTable::printTable(std::ostream & out, unsigned int last_n_entries)
247{
248 printTable(out, last_n_entries, MooseEnum("ENVIRONMENT=-1", "ENVIRONMENT"));
249}
250
251void
252FormattedTable::printTable(std::ostream & out,
253 unsigned int last_n_entries,
254 const MooseEnum & suggested_term_width)
255{
256 unsigned short term_width;
257
258 if (suggested_term_width == "ENVIRONMENT")
259 term_width = MooseUtils::getTermWidth(true);
260 else if (suggested_term_width == "AUTO")
261 term_width = MooseUtils::getTermWidth(false);
262 else
263 term_width = MooseUtils::stringToInteger(suggested_term_width);
264
265 if (term_width < _min_pps_width)
266 term_width = _min_pps_width;
267
268 std::vector<std::string>::iterator col_it = _column_names.begin();
269 std::vector<std::string>::iterator col_end = _column_names.end();
270
271 std::vector<std::string>::iterator curr_begin = col_it;
272 std::vector<std::string>::iterator curr_end;
273 while (col_it != col_end)
274 {
275 std::map<std::string, unsigned short> col_widths;
276 unsigned int curr_width = _column_width + 4;
277 unsigned int cols_in_group = 0;
278 while (curr_width < term_width && col_it != col_end)
279 {
280 curr_end = col_it;
281 col_widths[*col_it] = col_it->length() > _column_width ? col_it->length() + 1 : _column_width;
282
283 curr_width += col_widths[*col_it] + 3;
284 ++col_it;
285 ++cols_in_group;
286 }
287 if (col_it != col_end && cols_in_group >= 2)
288 {
289 // curr_width -= col_widths[*curr_end];
290 col_widths.erase(*curr_end);
291 col_it = curr_end;
292 }
293 else
294 curr_end = col_it;
295
296 printTablePiece(out, last_n_entries, col_widths, curr_begin, curr_end);
297 curr_begin = curr_end;
298 }
299}
300
301void
303 unsigned int last_n_entries,
304 std::map<std::string, unsigned short> & col_widths,
305 std::vector<std::string>::iterator & col_begin,
306 std::vector<std::string>::iterator & col_end)
307{
308 fillEmptyValues(last_n_entries);
312 printRowDivider(out, col_widths, col_begin, col_end);
313 out << "|";
314 if (_output_time)
315 out << std::setw(_column_width) << std::left << " time" << " |";
316 for (auto header_it = col_begin; header_it != col_end; ++header_it)
317 out << " " << std::setw(col_widths[*header_it]) << *header_it << "|";
318 out << "\n";
319 printRowDivider(out, col_widths, col_begin, col_end);
320
321 auto data_it = _data.begin();
322 if (last_n_entries)
323 {
324 if (_data.size() > last_n_entries)
325 {
326 // Print a blank row to indicate that values have been ommited
327 printOmittedRow(out, col_widths, col_begin, col_end);
328
329 // Jump to the right place in the vector
330 data_it += _data.size() - last_n_entries;
331 }
332 }
333 // Now print the remaining data rows
334 for (; data_it != _data.end(); ++data_it)
335 {
336 out << "|";
337 if (_output_time)
338 out << std::right << std::setw(_column_width) << std::scientific << data_it->first << " |";
339 for (auto header_it = col_begin; header_it != col_end; ++header_it)
340 {
341 auto & tmp = data_it->second;
342 out << std::setw(col_widths[*header_it]) << *tmp[*header_it] << " |";
343 }
344 out << "\n";
345 }
346
347 printRowDivider(out, col_widths, col_begin, col_end);
348}
349
350void
351FormattedTable::printCSV(const std::string & file_name, int interval, bool align)
352{
354
355 open(file_name);
356
357 if (_output_row_index == 0)
358 {
365 if (align)
366 {
367 // Set the initial width to the names of the columns
368 _align_widths["time"] = 4;
369
370 for (const auto & col_name : _column_names)
371 _align_widths[col_name] = col_name.size();
372
373 // Loop through the various times
374 for (const auto & it : _data)
375 {
376 // Update the time _align_width
377 {
378 std::ostringstream oss;
380 oss << std::scientific;
381 oss << std::setprecision(_csv_precision) << it.first;
382 unsigned int w = oss.str().size();
383 _align_widths["time"] = std::max(_align_widths["time"], w);
384 }
385
386 // Loop through the data for the current time and update the _align_widths
387 for (const auto & jt : it.second)
388 {
389 std::ostringstream oss;
391 oss << std::scientific;
392 oss << std::setprecision(_csv_precision) << *jt.second;
393 unsigned int w = oss.str().size();
394 _align_widths[jt.first] = std::max(_align_widths[jt.first], w);
395 }
396 }
397 }
398
399 // Output Header
400 if (!_headers_output)
401 {
402 if (_output_time)
403 {
404 if (align)
405 _output_file << std::setw(_align_widths["time"]) << "time";
406 else
407 _output_file << "time";
408 _headers_output = true;
409 }
410
411 for (const auto & col_name : _column_names)
412 {
413 if (_headers_output)
415
416 if (align)
417 _output_file << std::right << std::setw(_align_widths[col_name]) << col_name;
418 else
419 _output_file << col_name;
420 _headers_output = true;
421 }
422 _output_file << "\n";
423 }
424 }
425
426 for (; _output_row_index < _data.size(); ++_output_row_index)
427 {
428 if (_output_row_index % interval == 0)
430 }
431
432 close();
433}
434
435void
437 std::pair<Real, std::map<std::string, std::shared_ptr<TableValueBase>>> & row_data, bool align)
438{
439 bool first = true;
440
442 _output_file << std::scientific;
443 else
444 _output_file << std::defaultfloat;
445
446 if (_output_time)
447 {
448 if (align)
449 _output_file << std::setprecision(_csv_precision) << std::right
450 << std::setw(_align_widths["time"]) << row_data.first;
451 else
452 _output_file << std::setprecision(_csv_precision) << row_data.first;
453 first = false;
454 }
455
456 for (const auto & col_name : _column_names)
457 {
458 std::map<std::string, std::shared_ptr<TableValueBase>> & tmp = row_data.second;
459
460 if (!first)
462 else
463 first = false;
464
465 if (align)
466 _output_file << std::setprecision(_csv_precision) << std::right
467 << std::setw(_align_widths[col_name]) << *tmp[col_name];
468 else
469 _output_file << std::setprecision(_csv_precision) << *tmp[col_name];
470 }
471 _output_file << "\n";
472}
473
474// const strings that the gnuplot generator needs
475namespace gnuplot
476{
477const std::string before_terminal = "set terminal ";
478const std::string before_ext = "\nset output 'all";
479const std::string after_ext =
480 "'\nset title 'All Postprocessors'\nset xlabel 'time'\nset ylabel 'values'\nplot";
481}
482
483void
484FormattedTable::makeGnuplot(const std::string & base_file, const std::string & format)
485{
487
488 // TODO: run this once at end of simulation, right now it runs every iteration
489 // TODO: do I need to be more careful escaping column names?
490 // Note: open and close the files each time, having open files may mess with gnuplot
491
492 // supported filetypes: ps, png
493 std::string extension, terminal;
494 if (format == "png")
495 {
496 extension = ".png";
497 terminal = "png";
498 }
499
500 else if (format == "ps")
501 {
502 extension = ".ps";
503 terminal = "postscript";
504 }
505
506 else if (format == "gif")
507 {
508 extension = ".gif";
509 terminal = "gif";
510 }
511
512 else
513 mooseError("gnuplot format \"" + format + "\" is not supported.");
514
515 // Write the data to disk
516 std::string dat_name = base_file + ".dat";
517 std::ofstream datfile;
518 datfile.open(dat_name.c_str(), std::ios::trunc | std::ios::out);
519 if (datfile.fail())
520 mooseError("Unable to open file ", dat_name);
521
522 datfile << "# time";
523 for (const auto & col_name : _column_names)
524 datfile << '\t' << col_name;
525 datfile << '\n';
526
527 for (auto & data_it : _data)
528 {
529 datfile << data_it.first;
530 for (const auto & col_name : _column_names)
531 {
532 auto & tmp = data_it.second;
533 datfile << '\t' << *tmp[col_name];
534 }
535 datfile << '\n';
536 }
537 datfile.flush();
538 datfile.close();
539
540 // Write the gnuplot script
541 std::string gp_name = base_file + ".gp";
542 std::ofstream gpfile;
543 gpfile.open(gp_name.c_str(), std::ios::trunc | std::ios::out);
544 if (gpfile.fail())
545 mooseError("Unable to open file ", gp_name);
546
547 gpfile << gnuplot::before_terminal << terminal << gnuplot::before_ext << extension
549
550 // plot all postprocessors in one plot
551 int column = 2;
552 for (const auto & col_name : _column_names)
553 {
554 gpfile << " '" << dat_name << "' using 1:" << column << " title '" << col_name
555 << "' with linespoints";
556 column++;
557 if (column - 2 < static_cast<int>(_column_names.size()))
558 gpfile << ", \\\n";
559 }
560 gpfile << "\n\n";
561
562 // plot the postprocessors individually
563 column = 2;
564 for (const auto & col_name : _column_names)
565 {
566 gpfile << "set output '" << col_name << extension << "'\n";
567 gpfile << "set ylabel '" << col_name << "'\n";
568 gpfile << "plot '" << dat_name << "' using 1:" << column << " title '" << col_name
569 << "' with linespoints\n\n";
570 column++;
571 }
572
573 gpfile.flush();
574 gpfile.close();
575}
576
577void
579{
580 _data.clear();
581 _output_file.close();
583}
584
585void
586FormattedTable::fillEmptyValues(unsigned int last_n_entries)
587{
588 auto begin = _data.begin();
589 auto end = _data.end();
590 if (last_n_entries && (last_n_entries < _data.size()))
591 begin = end - last_n_entries;
592
593 for (auto it = begin; it != end; ++it)
594 {
595 auto & datamap = it->second;
596 if (datamap.size() != _column_names.size())
597 {
598 for (const auto & col_name : _column_names)
599 if (!datamap[col_name])
600 datamap[col_name] =
601 std::dynamic_pointer_cast<TableValueBase>(std::make_shared<TableValue<char>>('0'));
602 }
603 else
604 {
605 for (auto & [key, val] : datamap)
606 if (!val)
607 val = std::dynamic_pointer_cast<TableValueBase>(std::make_shared<TableValue<char>>('0'));
608 }
609 }
610}
611
614{
615 return MooseEnum("ENVIRONMENT=-1 AUTO=0 80=80 120=120 160=160", "ENVIRONMENT", true);
616}
617
618void
620{
622 {
623 std::sort(_column_names.begin(), _column_names.end());
625 }
626}
627
628std::ostream &
629operator<<(std::ostream & os, const TableValueBase & value)
630{
631 value.print(os);
632 return os;
633}
void storeHelper(std::ostream &stream, P &data, void *context)
Scalar helper routine.
Definition DataIO.h:989
void loadHelper(std::istream &stream, P &data, void *context)
Scalar helper routine.
Definition DataIO.h:1081
void dataStore(std::ostream &stream, FormattedTable &table, void *context)
const std::string DEFAULT_CSV_DELIMITER
std::ostream & operator<<(std::ostream &os, const TableValueBase &value)
void dataLoad(std::istream &stream, FormattedTable &table, void *context)
const unsigned short DEFAULT_CSV_PRECISION
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application.
Definition MooseError.h:311
This class is used for building, formatting, and outputting tables of numbers.
static const unsigned short _min_pps_width
The absolute minimum PPS table width.
FormattedTable()
Default constructor - The default constructor takes an optional parameter to turn off stateful printi...
bool _headers_output
Keeps track of whether the header has been output.
void printTablePiece(std::ostream &out, unsigned int last_n_entries, std::map< std::string, unsigned short > &col_widths, std::vector< std::string >::iterator &col_begin, std::vector< std::string >::iterator &col_end)
static MooseEnum getWidthModes()
void printRow(std::pair< Real, std::map< std::string, std::shared_ptr< TableValueBase > > > &row_data, bool align)
unsigned int _csv_precision
*.csv file precision, defaults to 14
bool _output_time
Whether or not to output the Time column.
static const unsigned short _column_width
The single cell width used for all columns in the table.
bool empty() const
Returns a boolean value based on whether the FormattedTable contains data or not.
void printTable(std::ostream &out, unsigned int last_n_entries=0)
Methods for dumping the table to the stream - either by filename or by stream handle.
std::ofstream _output_file
The stream handle (corresponds to _output_file_name)
void open(const std::string &file_name)
Open or switch the underlying file stream to point to file_name. This is idempotent.
void printCSV(const std::string &file_name, int interval=1, bool align=false)
Method for dumping the table to a csv file - opening and closing the file handle is handled.
bool _append
Keeps track of whether we want to open an existing file for appending or overwriting.
std::string _csv_delimiter
*.csv file delimiter, defaults to ","
void append(bool append_existing_file)
Sets append mode which means an existing file is not truncated on opening.
bool _column_names_unsorted
Flag indicating that sorting is necessary (used by sortColumns method).
void printRowDivider(std::ostream &out, std::map< std::string, unsigned short > &col_widths, std::vector< std::string >::iterator &col_begin, std::vector< std::string >::iterator &col_end) const
std::size_t _output_row_index
Keeps track of the index indicating which vector elements have been output.
bool _csv_use_scientific_notation
Whether to print floating point CSV values in scientific notation.
~FormattedTable()
The destructor is used to close the file handle.
void printNoDataRow(char intersect_char, char fill_char, std::ostream &out, std::map< std::string, unsigned short > &col_widths, std::vector< std::string >::iterator &col_begin, std::vector< std::string >::iterator &col_end) const
Real getLastTime() const
Retrieve the last time (or independent variable) value.
void close()
Close the underlying output file stream if any. This is idempotent.
std::map< std::string, unsigned int > _align_widths
Alignment widths (only used if asked to print aligned to CSV output)
void addRow(Real time)
Force a new row in the table with the passed in time.
void makeGnuplot(const std::string &base_file, const std::string &format)
std::vector< std::pair< Real, std::map< std::string, std::shared_ptr< TableValueBase > > > > _data
Data structure for the console table: The first part of the pair tracks the independent variable (nor...
std::string _output_file_name
The optional output file stream.
void sortColumns()
Sorts columns alphabetically.
std::vector< std::string > _column_names
The set of column names updated when data is inserted through the setter methods.
void printOmittedRow(std::ostream &out, std::map< std::string, unsigned short > &col_widths, std::vector< std::string >::iterator &col_begin, std::vector< std::string >::iterator &col_end) const
void fillEmptyValues(unsigned int last_n_entries=0)
Fill any values that are not defined (usually when there are mismatched column lengths)
This is a "smart" enum class intended to replace many of the shortcomings in the C++ enum type It sho...
Definition MooseEnum.h:55
static void load(std::istream &stream, std::shared_ptr< TableValueBase > &value_base, void *context)
int stringToInteger(const std::string &input, bool throw_on_failure)
Definition MooseUtils.C:978
unsigned short getTermWidth(bool use_environment)
Definition MooseUtils.C:651
const std::string before_ext
const std::string before_terminal
const std::string after_ext