Line data Source code
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 : #pragma once
11 :
12 : // MOOSE includes
13 : #include "Moose.h"
14 : #include "MooseEnum.h"
15 : #include "DataIO.h"
16 : #include "MooseUtils.h"
17 :
18 : // C++ includes
19 : #include <fstream>
20 :
21 : // Forward declarations
22 : class FormattedTable;
23 : class TableValueBase;
24 : namespace libMesh
25 : {
26 : class ExodusII_IO;
27 : }
28 :
29 : template <>
30 : void dataStore(std::ostream & stream, FormattedTable & table, void * context);
31 : template <>
32 : void dataLoad(std::istream & stream, FormattedTable & v, void * context);
33 : template <>
34 : void dataStore(std::ostream & stream, TableValueBase *& value, void * context);
35 : template <>
36 : void dataLoad(std::istream & stream, TableValueBase *& value, void * context);
37 :
38 : class TableValueBase
39 : {
40 : public:
41 2139246 : virtual ~TableValueBase() = default;
42 :
43 : template <typename T>
44 2141328 : static constexpr bool isSupportedType()
45 : {
46 2141328 : return std::is_fundamental<T>::value || std::is_same<T, std::string>::value;
47 : }
48 :
49 : virtual void print(std::ostream & os) const = 0;
50 :
51 : virtual void store(std::ostream & stream, void * context) = 0;
52 : };
53 :
54 : std::ostream & operator<<(std::ostream & os, const TableValueBase & value);
55 :
56 : template <typename T>
57 : class TableValue : public TableValueBase
58 : {
59 : public:
60 2141328 : TableValue(const T & value) : _value(value)
61 : {
62 2141328 : if (!this->isSupportedType<T>())
63 0 : mooseError("Unsupported type ", MooseUtils::prettyCppType<T>(), " for FormattedTable.");
64 2141328 : }
65 :
66 : const T & get() const { return _value; }
67 352 : T & set() { return _value; }
68 :
69 8833943 : virtual void print(std::ostream & os) const override { os << this->_value; };
70 :
71 : virtual void store(std::ostream & stream, void * context) override;
72 : static void
73 : load(std::istream & stream, std::shared_ptr<TableValueBase> & value_base, void * context);
74 :
75 : private:
76 : T _value;
77 : };
78 :
79 : template <>
80 : inline void
81 142 : TableValue<bool>::print(std::ostream & os) const
82 : {
83 142 : os << (this->_value ? "True" : "False");
84 142 : }
85 :
86 : template <typename T>
87 : void
88 3229196 : TableValue<T>::store(std::ostream & stream, void * context)
89 : {
90 1614598 : std::string type = typeid(T).name();
91 1614598 : ::dataStore(stream, type, context);
92 1614598 : ::dataStore(stream, _value, context);
93 1614598 : }
94 :
95 : template <typename T>
96 : void
97 64852 : TableValue<T>::load(std::istream & stream,
98 : std::shared_ptr<TableValueBase> & value_base,
99 : void * context)
100 : {
101 222 : T value;
102 64852 : ::dataLoad(stream, value, context);
103 64852 : value_base = std::dynamic_pointer_cast<TableValueBase>(std::make_shared<TableValue<T>>(value));
104 64852 : }
105 :
106 : /**
107 : * This class is used for building, formatting, and outputting tables of numbers.
108 : */
109 : class FormattedTable
110 : {
111 : public:
112 : /**
113 : * Default constructor - The default constructor takes an optional parameter to turn off
114 : * stateful printing. This means that each time you ask the FormattedTable to print to a file,
115 : * it'll, print the entire table. The default is to only print the part of the table that hasn't
116 : * already been printed.
117 : */
118 : FormattedTable();
119 :
120 : /**
121 : * Copy constructor - The copy constructor will duplicate the data structures but is not
122 : * designed to work with FormattedTables with open streams (e.g. CSV Output mode).
123 : */
124 : FormattedTable(const FormattedTable & o);
125 :
126 : /**
127 : * The destructor is used to close the file handle
128 : */
129 : ~FormattedTable();
130 :
131 : /**
132 : * Returns a boolean value based on whether the FormattedTable contains data or not
133 : */
134 : bool empty() const;
135 :
136 : /**
137 : * Sets append mode which means an existing file is not truncated on opening. This mode
138 : * is typically used for recovery.
139 : */
140 : void append(bool append_existing_file);
141 :
142 : /**
143 : * Force a new row in the table with the passed in time.
144 : */
145 : void addRow(Real time);
146 :
147 : /**
148 : * Method for adding data to the output table. Data is added to the last row. Method will
149 : * error if called on an empty table.
150 : */
151 : template <typename T = Real>
152 : void addData(const std::string & name, const T & value);
153 :
154 : /**
155 : * Method for adding data to the output table. The dependent variable is named "time"
156 : */
157 : template <typename T = Real>
158 : void addData(const std::string & name, const T & value, Real time);
159 :
160 : /**
161 : * Method for adding an entire vector to a table at a time. Checks are made to ensure that
162 : * the dependent variable index lines up with the vector indices.
163 : */
164 : template <typename T = Real>
165 : void addData(const std::string & name, const std::vector<T> & vector);
166 :
167 : /**
168 : * Retrieve the last time (or independent variable) value.
169 : */
170 : Real getLastTime() const;
171 :
172 : /**
173 : * Retrieve Data for last value of given name
174 : */
175 : template <typename T = Real>
176 : T & getLastData(const std::string & name) const;
177 :
178 : void clear();
179 :
180 : /**
181 : * Set whether or not to output time column.
182 : */
183 272920 : void outputTimeColumn(bool output_time) { _output_time = output_time; }
184 :
185 : // const std::map<Real, std::map<std::string, Real>> & getData() const { return _data; }
186 :
187 : /**
188 : * Methods for dumping the table to the stream - either by filename or by stream handle. If
189 : * a filename is supplied opening and closing of the file is properly handled. In the
190 : * screen version of the method, an optional parameters can be passed to print only the last
191 : * "n" entries. A value of zero means don't skip any rows
192 : *
193 : * Note: Only call these from processor 0!
194 : */
195 : void printTable(std::ostream & out, unsigned int last_n_entries = 0);
196 : void printTable(std::ostream & out,
197 : unsigned int last_n_entries,
198 : const MooseEnum & suggested_term_width);
199 : void printTable(const std::string & file_name);
200 :
201 : /**
202 : * Method for dumping the table to a csv file - opening and closing the file handle is handled
203 : *
204 : * Note: Only call this on processor 0!
205 : */
206 : void printCSV(const std::string & file_name, int interval = 1, bool align = false);
207 :
208 : void printEnsight(const std::string & file_name);
209 : void writeExodus(libMesh::ExodusII_IO * ex_out, Real time);
210 : void makeGnuplot(const std::string & base_file, const std::string & format);
211 :
212 : static MooseEnum getWidthModes();
213 :
214 : /**
215 : * By default printCSV places "," between each entry, this allows this to be changed
216 : */
217 25070 : void setDelimiter(std::string delimiter) { _csv_delimiter = delimiter; }
218 :
219 : /**
220 : * By default printCSV prints output to a precision of 14, this allows this to be changed
221 : */
222 25070 : void setPrecision(unsigned int precision) { _csv_precision = precision; }
223 :
224 : /**
225 : * Set whether printCSV uses scientific notation for floating point values.
226 : */
227 25158 : void setUseScientificNotation(bool use_scientific_notation)
228 : {
229 25158 : _csv_use_scientific_notation = use_scientific_notation;
230 25158 : }
231 :
232 : /**
233 : * Sorts columns alphabetically.
234 : */
235 : void sortColumns();
236 :
237 : protected:
238 : void printTablePiece(std::ostream & out,
239 : unsigned int last_n_entries,
240 : std::map<std::string, unsigned short> & col_widths,
241 : std::vector<std::string>::iterator & col_begin,
242 : std::vector<std::string>::iterator & col_end);
243 :
244 : void printOmittedRow(std::ostream & out,
245 : std::map<std::string, unsigned short> & col_widths,
246 : std::vector<std::string>::iterator & col_begin,
247 : std::vector<std::string>::iterator & col_end) const;
248 : void printRowDivider(std::ostream & out,
249 : std::map<std::string, unsigned short> & col_widths,
250 : std::vector<std::string>::iterator & col_begin,
251 : std::vector<std::string>::iterator & col_end) const;
252 :
253 : void printNoDataRow(char intersect_char,
254 : char fill_char,
255 : std::ostream & out,
256 : std::map<std::string, unsigned short> & col_widths,
257 : std::vector<std::string>::iterator & col_begin,
258 : std::vector<std::string>::iterator & col_end) const;
259 :
260 : /**
261 : * Data structure for the console table:
262 : * The first part of the pair tracks the independent variable (normally time) and is associated
263 : * with the second part of the table which is the map of dependent variables and their associated
264 : * values.
265 : */
266 : std::vector<std::pair<Real, std::map<std::string, std::shared_ptr<TableValueBase>>>> _data;
267 :
268 : /// Alignment widths (only used if asked to print aligned to CSV output)
269 : std::map<std::string, unsigned int> _align_widths;
270 :
271 : /// The set of column names updated when data is inserted through the setter methods
272 : std::vector<std::string> _column_names;
273 :
274 : /// The single cell width used for all columns in the table
275 : static const unsigned short _column_width;
276 :
277 : /// The absolute minimum PPS table width
278 : static const unsigned short _min_pps_width;
279 :
280 : private:
281 : /// Close the underlying output file stream if any. This is idempotent.
282 : void close();
283 :
284 : /// Open or switch the underlying file stream to point to file_name. This is idempotent.
285 : void open(const std::string & file_name);
286 :
287 : void printRow(std::pair<Real, std::map<std::string, std::shared_ptr<TableValueBase>>> & row_data,
288 : bool align);
289 :
290 : /**
291 : * Fill any values that are not defined (usually when there are mismatched column lengths)
292 : *
293 : * If \p last_n_entries is non-zero, only values in that many final
294 : * rows will be examined and filled.
295 : */
296 : void fillEmptyValues(unsigned int last_n_entries = 0);
297 :
298 : /// The optional output file stream
299 : std::string _output_file_name;
300 :
301 : /// The stream handle (corresponds to _output_file_name)
302 : std::ofstream _output_file;
303 :
304 : /**
305 : * Keeps track of the index indicating which vector elements have been output. All items
306 : * with an index less than this index have been output. Higher values have not.
307 : */
308 : std::size_t _output_row_index;
309 :
310 : /**
311 : * Keeps track of whether the header has been output. This is separate from _output_row_index
312 : * because it's possible to output the header with zero rows. We don't consider this a bug,
313 : * it helps users understand that they have declared vectors properly but maybe haven't populated
314 : * them correctly.
315 : */
316 : bool _headers_output;
317 :
318 : /// Keeps track of whether we want to open an existing file for appending or overwriting.
319 : bool _append;
320 :
321 : /// Whether or not to output the Time column
322 : bool _output_time;
323 :
324 : /// *.csv file delimiter, defaults to ","
325 : std::string _csv_delimiter;
326 :
327 : /// *.csv file precision, defaults to 14
328 : unsigned int _csv_precision;
329 :
330 : /// Whether to print floating point CSV values in scientific notation
331 : bool _csv_use_scientific_notation;
332 :
333 : /// Flag indicating that sorting is necessary (used by sortColumns method).
334 : bool _column_names_unsorted = true;
335 :
336 : friend void
337 : dataStore<FormattedTable>(std::ostream & stream, FormattedTable & table, void * context);
338 : friend void dataLoad<FormattedTable>(std::istream & stream, FormattedTable & v, void * context);
339 : };
340 :
341 : template <typename T>
342 : void
343 745321 : FormattedTable::addData(const std::string & name, const T & value)
344 : {
345 745321 : if (empty())
346 0 : mooseError("No Data stored in the the FormattedTable");
347 :
348 745321 : auto back_it = _data.rbegin();
349 745321 : back_it->second[name] =
350 : std::dynamic_pointer_cast<TableValueBase>(std::make_shared<TableValue<T>>(value));
351 :
352 745321 : if (std::find(_column_names.begin(), _column_names.end(), name) == _column_names.end())
353 : {
354 159231 : _column_names.push_back(name);
355 159231 : _column_names_unsorted = true;
356 : }
357 745321 : }
358 :
359 : template <typename T>
360 : void
361 71070 : FormattedTable::addData(const std::string & name, const T & value, Real time)
362 : {
363 71070 : auto back_it = _data.rbegin();
364 :
365 : mooseAssert(back_it == _data.rend() || !MooseUtils::absoluteFuzzyLessThan(time, back_it->first),
366 : "Attempting to add data to FormattedTable with the dependent variable in a "
367 : "non-increasing order.\nDid you mean to use addData(std::string &, const "
368 : "std::vector<Real> &)?");
369 :
370 : // See if the current "row" is already in the table
371 71070 : if (back_it == _data.rend() || !MooseUtils::absoluteFuzzyEqual(time, back_it->first))
372 : {
373 28762 : _data.emplace_back(time, std::map<std::string, std::shared_ptr<TableValueBase>>());
374 28762 : back_it = _data.rbegin();
375 : }
376 : // Insert or update value
377 71070 : back_it->second[name] =
378 : std::dynamic_pointer_cast<TableValueBase>(std::make_shared<TableValue<T>>(value));
379 :
380 71070 : if (std::find(_column_names.begin(), _column_names.end(), name) == _column_names.end())
381 : {
382 9823 : _column_names.push_back(name);
383 9823 : _column_names_unsorted = true;
384 : }
385 71070 : }
386 :
387 : template <typename T>
388 : void
389 43611 : FormattedTable::addData(const std::string & name, const std::vector<T> & vector)
390 : {
391 1302789 : for (MooseIndex(vector) i = 0; i < vector.size(); ++i)
392 : {
393 1259178 : if (i == _data.size())
394 189926 : _data.emplace_back(i, std::map<std::string, std::shared_ptr<TableValueBase>>());
395 :
396 : mooseAssert(MooseUtils::absoluteFuzzyEqual(_data[i].first, i),
397 : "Inconsistent indexing in VPP vector");
398 :
399 1259178 : auto & curr_entry = _data[i];
400 1259178 : curr_entry.second[name] =
401 1259186 : std::dynamic_pointer_cast<TableValueBase>(std::make_shared<TableValue<T>>(vector[i]));
402 : }
403 :
404 43611 : if (std::find(_column_names.begin(), _column_names.end(), name) == _column_names.end())
405 : {
406 15464 : _column_names.push_back(name);
407 15464 : _column_names_unsorted = true;
408 : }
409 43611 : }
410 :
411 : template <typename T>
412 : T &
413 352 : FormattedTable::getLastData(const std::string & name) const
414 : {
415 : mooseAssert(!empty(), "No Data stored in the FormattedTable");
416 :
417 352 : auto & last_data_map = _data.rbegin()->second;
418 352 : auto it = last_data_map.find(name);
419 352 : if (it == last_data_map.end())
420 0 : mooseError("No Data found for name: " + name);
421 :
422 352 : auto value = std::dynamic_pointer_cast<TableValue<T>>(it->second);
423 352 : if (!value)
424 0 : mooseError("Data for ", name, " is not of the requested type.");
425 704 : return value->set();
426 352 : }
427 :
428 : template <>
429 : void dataStore(std::ostream & stream, FormattedTable & table, void * context);
430 : template <>
431 : void dataLoad(std::istream & stream, FormattedTable & v, void * context);
432 : template <>
433 : void dataStore(std::ostream & stream, TableValueBase *& value, void * context);
434 : template <>
435 : void dataLoad(std::istream & stream, TableValueBase *& value, void * context);
|