LCOV - code coverage report
Current view: top level - include/utils - FormattedTable.h (source / functions) Hit Total Coverage
Test: idaholab/moose framework: #33194 (2f6aab) with base 583127 Lines: 64 68 94.1 %
Date: 2026-07-01 13:50:39 Functions: 52 113 46.0 %
Legend: Lines: hit not hit

          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);

Generated by: LCOV version 1.14