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 : // STL includes 13 : #include <vector> 14 : #include <string> 15 : #include <fstream> 16 : 17 : #include "libmesh/parallel.h" 18 : 19 : // MOOSE includes 20 : #include "MooseEnum.h" 21 : #include "MooseTypes.h" 22 : 23 : namespace MooseUtils 24 : { 25 : 26 : /** 27 : * Utility class for reading delimited data (e.g., CSV data). 28 : * @param filename A string for the filename to read. 29 : * @param comm A pointer to a Communicator object (see below). 30 : * 31 : * This class assumes that all data is numeric and can be converted to a C++ double. If a 32 : * Communicator is provide then it will only read on processor 0 and broadcast the data to all 33 : * processors. If not provided it will read on all processors. 34 : */ 35 : template <typename T> 36 : class DelimitedFileReaderTempl 37 : { 38 : public: 39 : enum class HeaderFlag 40 : { 41 : OFF = 0, 42 : ON = 1, 43 : AUTO = 2 44 : }; 45 : 46 : enum class FormatFlag 47 : { 48 : COLUMNS = 0, 49 : ROWS = 1 50 : }; 51 : 52 : const std::size_t INVALID_SIZE = std::numeric_limits<std::size_t>::max(); 53 : 54 : DelimitedFileReaderTempl(const std::string & filename, 55 : const libMesh::Parallel::Communicator * comm = nullptr); 56 : 57 : /** 58 : * Perform the actual data reading. 59 : * 60 : * This is a separate method to allow for the filename to be read multiple times. 61 : */ 62 : void read(); 63 : 64 : /** 65 : * Get the total number of entries in the file 66 : * @returns number of entries in file 67 : */ 68 : std::size_t numEntries() const; 69 : 70 : ///@{ 71 : /** 72 : * Set/Get methods for file format controls. 73 : * IgnoreEmptyLines: When true all empty lines are ignored, when false an error is produced. 74 : * FormatFlag: Set the file format (rows vs. columns). 75 : * Delimiter: Set the file delimiter (if unset it will be detected). 76 : * HeaderFlag: Set the header flag (TRUE used the first row has header, FALSE assumes no 77 : * header, and AUTO will attempt to determine if a header exists). 78 : * Comment: Set the comment character, by default no comment character is used. 79 : */ 80 199 : void setIgnoreEmptyLines(bool value) { _ignore_empty_lines = value; } 81 0 : bool getIgnoreEmptyLines() const { return _ignore_empty_lines; } 82 : 83 895 : void setFormatFlag(FormatFlag value) { _format_flag = value; } 84 0 : FormatFlag getFormatFlag() const { return _format_flag; } 85 : 86 37 : void setDelimiter(const std::string & value) { _delimiter = value; } 87 0 : const std::string & setDelimiter() const { return _delimiter; } 88 : 89 91 : void setHeaderFlag(HeaderFlag value) { _header_flag = value; } 90 0 : HeaderFlag getHeaderFlag() const { return _header_flag; } 91 : 92 255 : void setComment(const std::string & value) { _row_comment = value; } 93 0 : const std::string & getComment() const { return _row_comment; } 94 : ///@} 95 : 96 : /// Set the file name, used to change the file to read from 97 : /// We also reset the column/row names as a second read might have different names 98 22 : void setFileName(const std::string & new_file) 99 : { 100 22 : _filename = new_file; 101 22 : _names.clear(); 102 22 : } 103 : 104 : /** 105 : * Return the column/row names. 106 : */ 107 : const std::vector<std::string> & getNames() const; 108 : 109 : /** 110 : * Return the rows/columns of data. 111 : * 112 : * The outer vector is column and the inner the rows. 113 : */ 114 : const std::vector<std::vector<T>> & getData() const; 115 : 116 : /** 117 : * Get the data in Point format. This performs checks that the data 118 : * is of valid dimensions to do so. 119 : */ 120 : const std::vector<Point> getDataAsPoints() const; 121 : 122 : ///@{ 123 : /** 124 : * Return the row/column of data for a specified header entry 125 : */ 126 : const std::vector<T> & getData(const std::string & name) const; 127 : const std::vector<T> & getData(std::size_t index) const; 128 : ///@} 129 : 130 : protected: 131 : /// The supplied filename. 132 : std::string _filename; 133 : 134 : /// Flag indicating if the file contains a header. 135 : HeaderFlag _header_flag; 136 : 137 : /// The delimiter separating the supplied data entires. 138 : std::string _delimiter; 139 : 140 : /// Flag for ignoring empty lines 141 : bool _ignore_empty_lines; 142 : 143 : /// Storage for the read or generated column names. 144 : std::vector<std::string> _names; 145 : 146 : /// Storage for the read data columns. 147 : std::vector<std::vector<T>> _data; 148 : 149 : /// Communicator 150 : const libMesh::Parallel::Communicator * const _communicator; 151 : 152 : /// Format "rows" vs "columns" 153 : FormatFlag _format_flag; 154 : 155 : /// Row offsets (only used with _format == "rows") 156 : std::vector<std::size_t> _row_offsets; 157 : 158 : /// Hide row comments 159 : std::string _row_comment; 160 : 161 : private: 162 : ///@{ 163 : /** 164 : * Read the numeric data as rows or columns into a single vector. 165 : */ 166 : void readColumnData(std::ifstream & stream_data, std::vector<T> & output); 167 : void readRowData(std::ifstream & stream_data, std::vector<T> & output); 168 : ///@} 169 : 170 : /** 171 : * Populate supplied vector with content from line. 172 : * @param line The line to extract data from. 173 : * @param row The vector to populate. 174 : * @param num The current line number. 175 : */ 176 : void processLine(const std::string & line, std::vector<T> & row, const unsigned int & num); 177 : 178 : /** 179 : * Check the content of the line and if it should be skipped. 180 : * @param line Complete line being read. 181 : * @param num The current line number. 182 : * @returns True if the line should be skipped. 183 : */ 184 : bool preprocessLine(std::string & line, const unsigned int & num); 185 : 186 : /** 187 : * Determine the delimiter. 188 : * 189 : * If the setDelimiter method is not called the data is inspected, if a ',' is found it is assumed 190 : * to be the delimiter as is the case for \t. Otherwise a space is used. 191 : */ 192 : const std::string & delimiter(const std::string & line); 193 : 194 : /** 195 : * Return the header flag, if it is set to AUTO attempt to determine if a header exists in line. 196 : */ 197 : bool header(const std::string & line); 198 : }; 199 : 200 : typedef DelimitedFileReaderTempl<double> DelimitedFileReader; 201 : typedef DelimitedFileReaderTempl<std::string> DelimitedFileOfStringReader; 202 : }