LCOV - code coverage report
Current view: top level - include/utils - FormattedTable.h (source / functions) Hit Total Coverage
Test: idaholab/moose framework: 8601ad Lines: 52 54 96.3 %
Date: 2025-07-18 13:27:08 Functions: 48 110 43.6 %
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     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);

Generated by: LCOV version 1.14