diff --git a/GridKit/Model/PhasorDynamics/Bus/Bus.cpp b/GridKit/Model/PhasorDynamics/Bus/Bus.cpp index 0971e2504..ee6d3ecd8 100644 --- a/GridKit/Model/PhasorDynamics/Bus/Bus.cpp +++ b/GridKit/Model/PhasorDynamics/Bus/Bus.cpp @@ -22,6 +22,8 @@ namespace GridKit } // Available template instantiations + template class BusBase; + template class BusBase; template class Bus; template class Bus; diff --git a/GridKit/Model/PhasorDynamics/Bus/BusDependencyTracking.cpp b/GridKit/Model/PhasorDynamics/Bus/BusDependencyTracking.cpp index fe116cf7c..cd3867343 100644 --- a/GridKit/Model/PhasorDynamics/Bus/BusDependencyTracking.cpp +++ b/GridKit/Model/PhasorDynamics/Bus/BusDependencyTracking.cpp @@ -22,6 +22,8 @@ namespace GridKit } // Available template instantiations + template class BusBase; + template class BusBase; template class Bus; template class Bus; diff --git a/GridKit/Model/PhasorDynamics/Bus/BusEnzyme.cpp b/GridKit/Model/PhasorDynamics/Bus/BusEnzyme.cpp index a315622c2..06cb2054e 100644 --- a/GridKit/Model/PhasorDynamics/Bus/BusEnzyme.cpp +++ b/GridKit/Model/PhasorDynamics/Bus/BusEnzyme.cpp @@ -59,6 +59,8 @@ namespace GridKit } // Available template instantiations + template class BusBase; + template class BusBase; template class Bus; template class Bus; diff --git a/GridKit/Model/VariableMonitor.hpp b/GridKit/Model/VariableMonitor.hpp index 3aa064554..b97dbcb83 100644 --- a/GridKit/Model/VariableMonitor.hpp +++ b/GridKit/Model/VariableMonitor.hpp @@ -2,6 +2,7 @@ #include #include +#include #include #include #include @@ -11,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -24,6 +26,32 @@ namespace GridKit template class VariableMonitorController; + namespace VariableMonitorDetail + { + template + void appendReal(std::string& out, RealT value) + { + std::array buffer{}; + constexpr auto precision = std::numeric_limits::digits10 + 1; + + auto [ptr, ec] = std::to_chars(buffer.data(), + buffer.data() + buffer.size(), + value, + std::chars_format::scientific, + precision); + if (ec == std::errc{}) + { + out.append(buffer.data(), static_cast(ptr - buffer.data())); + return; + } + + std::ostringstream os; + os.precision(precision); + os << std::scientific << value; + out += os.str(); + } + } // namespace VariableMonitorDetail + /** * @enum VariableMonitorFormat * Available formats for monitor output @@ -96,13 +124,34 @@ namespace GridKit /** * @brief Print items relevant to the start of a file */ - virtual void printHeader(std::ostream&, Csv) const = 0; + virtual void printHeader(std::ostream& os, Csv csv) const + { + std::string out; + appendHeader(out, csv); + os.write(out.data(), static_cast(out.size())); + } + + virtual void printHeader(std::ostream& os, Json json) const + { + std::string out; + appendHeader(out, json); + os.write(out.data(), static_cast(out.size())); + } + + virtual void printHeader(std::ostream& os, Yaml yaml) const + { + std::string out; + appendHeader(out, yaml); + os.write(out.data(), static_cast(out.size())); + } - virtual void printHeader(std::ostream&, Json) const + virtual void appendHeader(std::string&, Csv) const = 0; + + virtual void appendHeader(std::string&, Json) const { } - virtual void printHeader(std::ostream&, Yaml) const + virtual void appendHeader(std::string&, Yaml) const { } @@ -112,9 +161,30 @@ namespace GridKit /** * @brief Print monitored variables at current state */ - virtual void print(std::ostream&, Csv) const = 0; - virtual void print(std::ostream&, Json) const = 0; - virtual void print(std::ostream&, Yaml) const = 0; + virtual void print(std::ostream& os, Csv csv) const + { + std::string out; + append(out, csv); + os.write(out.data(), static_cast(out.size())); + } + + virtual void print(std::ostream& os, Json json) const + { + std::string out; + append(out, json); + os.write(out.data(), static_cast(out.size())); + } + + virtual void print(std::ostream& os, Yaml yaml) const + { + std::string out; + append(out, yaml); + os.write(out.data(), static_cast(out.size())); + } + + virtual void append(std::string&, Csv) const = 0; + virtual void append(std::string&, Json) const = 0; + virtual void append(std::string&, Yaml) const = 0; ///@} @@ -122,15 +192,36 @@ namespace GridKit /** * @brief Print items relevant to the end of a file */ - virtual void printFooter(std::ostream&, Csv) const + virtual void printFooter(std::ostream& os, Csv csv) const + { + std::string out; + appendFooter(out, csv); + os.write(out.data(), static_cast(out.size())); + } + + virtual void printFooter(std::ostream& os, Json json) const + { + std::string out; + appendFooter(out, json); + os.write(out.data(), static_cast(out.size())); + } + + virtual void printFooter(std::ostream& os, Yaml yaml) const + { + std::string out; + appendFooter(out, yaml); + os.write(out.data(), static_cast(out.size())); + } + + virtual void appendFooter(std::string&, Csv) const { } - virtual void printFooter(std::ostream&, Json) const + virtual void appendFooter(std::string&, Json) const { } - virtual void printFooter(std::ostream&, Yaml) const + virtual void appendFooter(std::string&, Yaml) const { } diff --git a/GridKit/Model/VariableMonitorController.hpp b/GridKit/Model/VariableMonitorController.hpp index 79db154ca..578df113c 100644 --- a/GridKit/Model/VariableMonitorController.hpp +++ b/GridKit/Model/VariableMonitorController.hpp @@ -128,63 +128,146 @@ namespace GridKit /// @copydoc VariableMonitorBase::printHeader using VariableMonitorBase::printHeader; - void printHeader(std::ostream& os, Csv csv) const override + /** + * @brief Organize header output for this and all submonitors + */ + template + void printFullHeader(std::ostream& os, FormatT fmt) const { - os << "t"; - for (auto&& var : variables_) + buffer_.clear(); + appendHeader(buffer_, fmt); + buffer_ += '\n'; + os.write(buffer_.data(), static_cast(buffer_.size())); + } + + /** + * @brief Print header for all sinks + */ + void printHeader() const + { + for (auto&& sink : sinks_) { - os << csv.delim << var.label; + std::visit([this](auto&& sink) + { this->printFullHeader(*sink.os, sink.format); }, + sink); } } - void printHeader(std::ostream& os, Json) const override + /// @copydoc VariableMonitorBase::print + using VariableMonitorBase::print; + + /** + * @brief Organize variable output for this and all submonitors + */ + template + void printFull(std::ostream& os, FormatT fmt) const { - os << "[\n"; + buffer_.clear(); + append(buffer_, fmt); + buffer_ += '\n'; + os.write(buffer_.data(), static_cast(buffer_.size())); } /** - * @brief Organize header output for this and all submonitors + * @brief Print variables to each sink */ - template - void printFullHeader(std::ostream& os, FormatT fmt) const + void print() const { - this->printHeader(os, fmt); - for (auto* mon : monitors_) + for (auto&& sink : sinks_) { - mon->printHeader(os, fmt); + std::visit([this](auto&& sink) + { + this->printFull(*sink.os, sink.format); + using T = std::remove_cvref_t; + if constexpr (std::is_same_v>) + { + sink.format.after_first = true; + } }, + sink); } - os << '\n'; } + /// @copydoc VariableMonitorBase::printFooter + using VariableMonitorBase::printFooter; + /** - * @brief Print header for all sinks + * @brief Organize footer output for this and all submonitors */ - void printHeader() const + template + void printFullFooter(std::ostream& os, FormatT fmt) const + { + buffer_.clear(); + appendFooter(buffer_, fmt); + os.write(buffer_.data(), static_cast(buffer_.size())); + } + + /** + * @brief Print footer for all sinks + */ + void printFooter() const { for (auto&& sink : sinks_) { std::visit([this](auto&& sink) - { this->printFullHeader(*sink.os, sink.format); }, + { this->printFullFooter(*sink.os, sink.format); }, sink); } } - void print(std::ostream& os, Csv csv) const override + protected: + void appendHeader(std::string& out, Csv csv) const override { - os << *time_; + out += "t"; for (auto&& var : variables_) { - os << csv.delim << *var.value; + out += csv.delim; + out += var.label; } for (auto* mon : monitors_) { - mon->print(os, csv); + mon->appendHeader(out, csv); } } - void print(std::ostream& os, Json json) const override + void appendHeader(std::string& out, Json json) const override { + out += "[\n"; + for (auto* mon : monitors_) + { + mon->appendHeader(out, json); + } + } + + void appendHeader(std::string& out, Yaml yaml) const override + { + for (auto* mon : monitors_) + { + mon->appendHeader(out, yaml); + } + } + + void append(std::string& out, Csv csv) const override + { + VariableMonitorDetail::appendReal(out, *time_); + for (auto&& var : variables_) + { + out += csv.delim; + VariableMonitorDetail::appendReal(out, *var.value); + } + + for (auto* mon : monitors_) + { + mon->append(out, csv); + } + } + + void append(std::string& out, Json json) const override + { + std::ostringstream os; + os.precision(std::numeric_limits::digits10 + 1); + os << std::scientific; + std::string indent = " "; if (json.after_first) { @@ -206,17 +289,25 @@ namespace GridKit { os << ",\n"; } - mon->print(os, Json()); + std::string monitor_out; + mon->append(monitor_out, Json()); + os << monitor_out; after_first = true; } indent.erase(indent.size() - 2); os << '\n' << indent << "}"; + + out += os.str(); } - void print(std::ostream& os, Yaml) const override + void append(std::string& out, Yaml yaml) const override { + std::ostringstream os; + os.precision(std::numeric_limits::digits10 + 1); + os << std::scientific; + std::string indent = " "; os << indent << "- t: " << *time_ << '\n'; indent.append(2, ' '); @@ -227,76 +318,36 @@ namespace GridKit for (auto* mon : monitors_) { - mon->print(os, Yaml()); + std::string monitor_out; + mon->append(monitor_out, yaml); + os << monitor_out; } - } - /** - * @brief Organize variable output for this and all submonitors - */ - template - void printFull(std::ostream& os, FormatT fmt) const - { - const auto orig_prec = os.precision(); - constexpr auto max_prec = std::numeric_limits::digits10 + 1; - os.precision(max_prec); - os << std::scientific; - this->print(os, fmt); - os << '\n'; - os << std::defaultfloat; - os.precision(orig_prec); + out += os.str(); } - /** - * @brief Print variables to each sink - */ - void print() const + void appendFooter(std::string& out, Csv csv) const override { - for (auto&& sink : sinks_) + for (auto* mon : monitors_) { - std::visit([this](auto&& sink) - { - this->printFull(*sink.os, sink.format); - using T = std::remove_cvref_t; - if constexpr (std::is_same_v>) - { - sink.format.after_first = true; - } }, - sink); + mon->appendFooter(out, csv); } } - /// @copydoc VariableMonitorBase::printFooter - using VariableMonitorBase::printFooter; - - void printFooter(std::ostream& os, Json) const override - { - os << "\n]\n"; - } - - /** - * @brief Organize footer output for this and all submonitors - */ - template - void printFullFooter(std::ostream& os, FormatT format) const + void appendFooter(std::string& out, Json json) const override { for (auto* mon : monitors_) { - mon->printFooter(os, format); + mon->appendFooter(out, json); } - this->printFooter(os, format); + out += "\n]\n"; } - /** - * @brief Print footer for all sinks - */ - void printFooter() const + void appendFooter(std::string& out, Yaml yaml) const override { - for (auto&& sink : sinks_) + for (auto* mon : monitors_) { - std::visit([this](auto&& sink) - { this->printFullFooter(*sink.os, sink.format); }, - sink); + mon->appendFooter(out, yaml); } } @@ -439,6 +490,9 @@ namespace GridKit /// Collection of extra top-level monitored variables std::vector variables_; + + /// Reused output buffer + mutable std::string buffer_; }; } // namespace Model } // namespace GridKit diff --git a/GridKit/Model/VariableMonitorImpl.hpp b/GridKit/Model/VariableMonitorImpl.hpp index eda73332e..b96fac674 100644 --- a/GridKit/Model/VariableMonitorImpl.hpp +++ b/GridKit/Model/VariableMonitorImpl.hpp @@ -19,11 +19,11 @@ namespace GridKit * @brief Manage printing variables associated with a specific component or * bus * - * Implementations of the print functions print under the assumption of a - * larger context. For example, the Csv version prints the delimiter and the - * value (or label for the header) for each monitored value without a line - * break. That way other monitors can print likewise on the same line, and - * the line can be ended by the control monitor. + * Append functions assume a larger output context. For example, the CSV + * version appends the delimiter and the value (or label for the header) for + * each monitored value without a line break. That way other monitors can + * append likewise on the same line, and the line can be ended by the control + * monitor. */ template ; + + if constexpr (std::is_floating_point_v) + { + VariableMonitorDetail::appendReal(out, static_cast(f())); + } + else + { + std::ostringstream os; + os << f(); + out += os.str(); + } + } }; template @@ -125,6 +141,11 @@ namespace GridKit { os << static_cast(f()); } + + void appendValue(std::string& out) const + { + VariableMonitorDetail::appendReal(out, static_cast(f())); + } }; template @@ -138,12 +159,26 @@ namespace GridKit template ValuePrinter(FuncT f) - : impl_{ValuePrinterType(f)} { + auto printer = ValuePrinterType{f}; + impl_ = [printer](std::ostream& os) + { + printer(os); + }; + append_value_ = [printer](std::string& out) + { + printer.appendValue(out); + }; + } + + void appendValue(std::string& out) const + { + append_value_(out); } private: std::function impl_; + std::function append_value_; friend std::ostream& operator<<(std::ostream& os, const ValuePrinter& p) { @@ -154,78 +189,92 @@ namespace GridKit ///@} - void printHeader(std::ostream& os, Csv csv) const override + void appendHeader(std::string& out, Csv csv) const override { for (auto v : variables_) { - os << csv.delim << label_ << '_' << enum_name(v); + out += csv.delim; + out += label_; + out += '_'; + out += enum_name(v); } } - void print(std::ostream& os, Csv csv) const override + void append(std::string& out, Csv csv) const override { for (auto v : variables_) { - os << csv.delim << f_[static_cast(enum_integer(v))]; + out += csv.delim; + f_[static_cast(enum_integer(v))].appendValue(out); } } /** * @brief Print single variable */ - void print(std::ostream& os, VariableEnum v, Json) const + void printVariable(std::ostream& os, VariableEnum v, Json) const { os << indent_ << std::quoted(enum_name(v)) << ": " << f_[static_cast(enum_integer(v))] << ",\n"; } - void print(std::ostream& os, Json) const override + void append(std::string& out, Json) const override { if (empty()) { return; } + + std::ostringstream os; + os.precision(std::numeric_limits::digits10 + 1); + os << std::scientific; + os << indent_ << std::quoted(label_) << ": {\n"; indent_.append(2, ' '); std::ostringstream v_os; v_os.copyfmt(os); for (auto v : variables_) { - print(v_os, v, Json()); + printVariable(v_os, v, Json()); } auto vars = v_os.view(); vars.remove_suffix(2); os << vars << '\n'; indent_.erase(indent_.size() - 2); os << indent_ << "}"; - } - void printHeader(std::ostream&, Yaml) const override - { + out += os.str(); } /** * @brief Print single variable */ - void print(std::ostream& os, VariableEnum v, Yaml) const + void printVariable(std::ostream& os, VariableEnum v, Yaml) const { os << indent_ << enum_name(v) << ": " << f_[static_cast(enum_integer(v))] << '\n'; } - void print(std::ostream& os, Yaml) const override + void append(std::string& out, Yaml) const override { if (empty()) { return; } + + std::ostringstream os; + os.precision(std::numeric_limits::digits10 + 1); + os << std::scientific; + os << indent_ << label_ << ":\n"; indent_.append(2, ' '); for (auto v : variables_) { - print(os, v, Yaml()); + printVariable(os, v, Yaml()); } indent_.erase(indent_.size() - 2); + + out += os.str(); } private: diff --git a/tests/UnitTests/PhasorDynamics/SystemTests.hpp b/tests/UnitTests/PhasorDynamics/SystemTests.hpp index 28d8f3299..94bd68412 100644 --- a/tests/UnitTests/PhasorDynamics/SystemTests.hpp +++ b/tests/UnitTests/PhasorDynamics/SystemTests.hpp @@ -2,6 +2,8 @@ #include #include +#include +#include #include #include