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 1974148 : virtual ~TableValueBase() = default;
42 :
43 : template <typename T>
44 1979404 : static constexpr bool isSupportedType()
45 : {
46 1979404 : 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 1979404 : TableValue(const T & value) : _value(value)
61 : {
62 1979404 : if (!this->isSupportedType<T>())
63 0 : mooseError("Unsupported type ", MooseUtils::prettyCppType<T>(), " for FormattedTable.");
64 1979404 : }
65 :
66 : const T & get() const { return _value; }
67 : T & set() { return _value; }
68 :
69 2298140 : 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 152 : TableValue<bool>::print(std::ostream & os) const
82 : {
83 152 : os << (this->_value ? "True" : "False");
84 152 : }
85 :
86 : template <typename T>
87 : void
88 1573015 : TableValue<T>::store(std::ostream & stream, void * context)
89 : {
90 1573015 : std::string type = typeid(T).name();
91 1573015 : ::dataStore(stream, type, context);
92 1573015 : ::dataStore(stream, _value, context);
93 1573015 : }
94 :
95 : template <typename T>
96 : void
97 64470 : TableValue<T>::load(std::istream & stream,
98 : std::shared_ptr<TableValueBase> & value_base,
99 : void * context)
100 : {
101 364 : T value;
102 64470 : ::dataLoad(stream, value, context);
103 64470 : value_base = std::dynamic_pointer_cast<TableValueBase>(std::make_shared<TableValue<T>>(value));
104 64470 : }
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();
171 :
172 : /**
173 : * Retrieve Data for last value of given name
174 : */
175 : template <typename T = Real>
176 : T & getLastData(const std::string & name);
177 :
178 : void clear();
179 :
180 : /**
181 : * Set whether or not to output time column.
182 : */
183 251654 : 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 22241 : 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 22241 : void setPrecision(unsigned int precision) { _csv_precision = precision; }
223 :
224 : /**
225 : * Sorts columns alphabetically.
226 : */
227 : void sortColumns();
228 :
229 : protected:
230 : void printTablePiece(std::ostream & out,
231 : unsigned int last_n_entries,
232 : std::map<std::string, unsigned short> & col_widths,
233 : std::vector<std::string>::iterator & col_begin,
234 : std::vector<std::string>::iterator & col_end);
235 :
236 : void printOmittedRow(std::ostream & out,
237 : std::map<std::string, unsigned short> & col_widths,
238 : std::vector<std::string>::iterator & col_begin,
239 : std::vector<std::string>::iterator & col_end) const;
240 : void printRowDivider(std::ostream & out,
241 : std::map<std::string, unsigned short> & col_widths,
242 : std::vector<std::string>::iterator & col_begin,
243 : std::vector<std::string>::iterator & col_end) const;
244 :
245 : void printNoDataRow(char intersect_char,
246 : char fill_char,
247 : std::ostream & out,
248 : std::map<std::string, unsigned short> & col_widths,
249 : std::vector<std::string>::iterator & col_begin,
250 : std::vector<std::string>::iterator & col_end) const;
251 :
252 : /**
253 : * Data structure for the console table:
254 : * The first part of the pair tracks the independent variable (normally time) and is associated
255 : * with the second part of the table which is the map of dependent variables and their associated
256 : * values.
257 : */
258 : std::vector<std::pair<Real, std::map<std::string, std::shared_ptr<TableValueBase>>>> _data;
259 :
260 : /// Alignment widths (only used if asked to print aligned to CSV output)
261 : std::map<std::string, unsigned int> _align_widths;
262 :
263 : /// The set of column names updated when data is inserted through the setter methods
264 : std::vector<std::string> _column_names;
265 :
266 : /// The single cell width used for all columns in the table
267 : static const unsigned short _column_width;
268 :
269 : /// The absolute minimum PPS table width
270 : static const unsigned short _min_pps_width;
271 :
272 : private:
273 : /// Close the underlying output file stream if any. This is idempotent.
274 : void close();
275 :
276 : /// Open or switch the underlying file stream to point to file_name. This is idempotent.
277 : void open(const std::string & file_name);
278 :
279 : void printRow(std::pair<Real, std::map<std::string, std::shared_ptr<TableValueBase>>> & row_data,
280 : bool align);
281 :
282 : /**
283 : * Fill any values that are not defined (usually when there are mismatched column lengths)
284 : *
285 : * If \p last_n_entries is non-zero, only values in that many final
286 : * rows will be examined and filled.
287 : */
288 : void fillEmptyValues(unsigned int last_n_entries = 0);
289 :
290 : /// The optional output file stream
291 : std::string _output_file_name;
292 :
293 : /// The stream handle (corresponds to _output_file_name)
294 : std::ofstream _output_file;
295 :
296 : /**
297 : * Keeps track of the index indicating which vector elements have been output. All items
298 : * with an index less than this index have been output. Higher values have not.
299 : */
300 : std::size_t _output_row_index;
301 :
302 : /**
303 : * Keeps track of whether the header has been output. This is separate from _output_row_index
304 : * because it's possible to output the header with zero rows. We don't consider this a bug,
305 : * it helps users understand that they have declared vectors properly but maybe haven't populated
306 : * them correctly.
307 : */
308 : bool _headers_output;
309 :
310 : /// Keeps track of whether we want to open an existing file for appending or overwriting.
311 : bool _append;
312 :
313 : /// Whether or not to output the Time column
314 : bool _output_time;
315 :
316 : /// *.csv file delimiter, defaults to ","
317 : std::string _csv_delimiter;
318 :
319 : /// *.csv file precision, defaults to 14
320 : unsigned int _csv_precision;
321 :
322 : /// Flag indicating that sorting is necessary (used by sortColumns method).
323 : bool _column_names_unsorted = true;
324 :
325 : friend void
326 : dataStore<FormattedTable>(std::ostream & stream, FormattedTable & table, void * context);
327 : friend void dataLoad<FormattedTable>(std::istream & stream, FormattedTable & v, void * context);
328 : };
329 :
330 : template <typename T>
331 : void
332 723968 : FormattedTable::addData(const std::string & name, const T & value)
333 : {
334 723968 : if (empty())
335 0 : mooseError("No Data stored in the the FormattedTable");
336 :
337 723968 : auto back_it = _data.rbegin();
338 723968 : back_it->second[name] =
339 : std::dynamic_pointer_cast<TableValueBase>(std::make_shared<TableValue<T>>(value));
340 :
341 723968 : if (std::find(_column_names.begin(), _column_names.end(), name) == _column_names.end())
342 : {
343 156868 : _column_names.push_back(name);
344 156868 : _column_names_unsorted = true;
345 : }
346 723968 : }
347 :
348 : template <typename T>
349 : void
350 84136 : FormattedTable::addData(const std::string & name, const T & value, Real time)
351 : {
352 84136 : auto back_it = _data.rbegin();
353 :
354 : mooseAssert(back_it == _data.rend() || !MooseUtils::absoluteFuzzyLessThan(time, back_it->first),
355 : "Attempting to add data to FormattedTable with the dependent variable in a "
356 : "non-increasing order.\nDid you mean to use addData(std::string &, const "
357 : "std::vector<Real> &)?");
358 :
359 : // See if the current "row" is already in the table
360 84136 : if (back_it == _data.rend() || !MooseUtils::absoluteFuzzyEqual(time, back_it->first))
361 : {
362 33534 : _data.emplace_back(time, std::map<std::string, std::shared_ptr<TableValueBase>>());
363 33534 : back_it = _data.rbegin();
364 : }
365 : // Insert or update value
366 84136 : back_it->second[name] =
367 : std::dynamic_pointer_cast<TableValueBase>(std::make_shared<TableValue<T>>(value));
368 :
369 84136 : if (std::find(_column_names.begin(), _column_names.end(), name) == _column_names.end())
370 : {
371 10285 : _column_names.push_back(name);
372 10285 : _column_names_unsorted = true;
373 : }
374 84136 : }
375 :
376 : template <typename T>
377 : void
378 37875 : FormattedTable::addData(const std::string & name, const std::vector<T> & vector)
379 : {
380 1144646 : for (MooseIndex(vector) i = 0; i < vector.size(); ++i)
381 : {
382 1106771 : if (i == _data.size())
383 158526 : _data.emplace_back(i, std::map<std::string, std::shared_ptr<TableValueBase>>());
384 :
385 : mooseAssert(MooseUtils::absoluteFuzzyEqual(_data[i].first, i),
386 : "Inconsistent indexing in VPP vector");
387 :
388 1106771 : auto & curr_entry = _data[i];
389 1106771 : curr_entry.second[name] =
390 1106775 : std::dynamic_pointer_cast<TableValueBase>(std::make_shared<TableValue<T>>(vector[i]));
391 : }
392 :
393 37875 : if (std::find(_column_names.begin(), _column_names.end(), name) == _column_names.end())
394 : {
395 11879 : _column_names.push_back(name);
396 11879 : _column_names_unsorted = true;
397 : }
398 37875 : }
399 :
400 : template <typename T>
401 : T &
402 : FormattedTable::getLastData(const std::string & name)
403 : {
404 : mooseAssert(!empty(), "No Data stored in the FormattedTable");
405 :
406 : auto & last_data_map = _data.rbegin()->second;
407 : auto it = last_data_map.find(name);
408 : if (it == last_data_map.end())
409 : mooseError("No Data found for name: " + name);
410 :
411 : auto value = std::dynamic_pointer_cast<TableValue<T>>(it->second);
412 : if (!value)
413 : mooseError("Data for ", name, " is not of the requested type.");
414 : return value->set();
415 : }
416 :
417 : template <>
418 : void dataStore(std::ostream & stream, FormattedTable & table, void * context);
419 : template <>
420 : void dataLoad(std::istream & stream, FormattedTable & v, void * context);
421 : template <>
422 : void dataStore(std::ostream & stream, TableValueBase *& value, void * context);
423 : template <>
424 : void dataLoad(std::istream & stream, TableValueBase *& value, void * context);
|