From 5ef4b23b08c18ade251461c520d895737e24e38a Mon Sep 17 00:00:00 2001 From: Alexander Novotny Date: Tue, 6 Jan 2026 18:22:38 -0500 Subject: [PATCH 1/6] Change some Ida functions to static These functions never access object state - they should have been static anyways. This allows `const` functions in Ida to call these functions. --- GridKit/Solver/Dynamic/Ida.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/GridKit/Solver/Dynamic/Ida.hpp b/GridKit/Solver/Dynamic/Ida.hpp index 8e1715e5..982f0913 100644 --- a/GridKit/Solver/Dynamic/Ida.hpp +++ b/GridKit/Solver/Dynamic/Ida.hpp @@ -188,8 +188,8 @@ namespace AnalysisManager static void copyVec(const std::vector& x, N_Vector y); // int check_flag(void *flagvalue, const char *funcname, int opt); - inline void checkAllocation(void* v, const char* functionName); - inline void checkOutput(int retval, const char* functionName); + static void checkAllocation(void* v, const char* functionName); + static void checkOutput(int retval, const char* functionName); }; /// Simple exception to use within Ida class. From bd69d8bbd3a11339d9bf64f85c811efefa075847 Mon Sep 17 00:00:00 2001 From: Alexander Novotny Date: Tue, 6 Jan 2026 18:24:51 -0500 Subject: [PATCH 2/6] Add IdaStats --- GridKit/Solver/Dynamic/Ida.cpp | 82 ++++++++++++++++++++++++++++++++++ GridKit/Solver/Dynamic/Ida.hpp | 15 +++++++ 2 files changed, 97 insertions(+) diff --git a/GridKit/Solver/Dynamic/Ida.cpp b/GridKit/Solver/Dynamic/Ida.cpp index baa303a5..ab1a34b3 100644 --- a/GridKit/Solver/Dynamic/Ida.cpp +++ b/GridKit/Solver/Dynamic/Ida.cpp @@ -1,6 +1,7 @@ #include "Ida.hpp" +#include #include #include @@ -968,6 +969,87 @@ namespace AnalysisManager checkOutput(retval, "IDAPrintAllStats"); } + /** + * @brief Accumulate another stats object into this one, allowing for stats to be kept + * across multiple simulations with IDA. + */ + IdaStats& IdaStats::operator+=(const IdaStats& other) + { + num_steps_ += other.num_steps_; + num_residual_evals_ += other.num_residual_evals_; + num_linear_decompositions_ += other.num_linear_decompositions_; + num_error_test_fails_ += other.num_error_test_fails_; + num_linear_solves_ += other.num_linear_solves_; + num_nonlinear_convergence_fails_ += other.num_nonlinear_convergence_fails_; + + return *this; + } + + /** + * @brief Generate a string containing all of the stats in a formatted report. + * All columns are aligned. To change the width of a column, + * modify `label_width` or `stat_width`. + */ + std::string IdaStats::report() const + { + unsigned label_width = 30; + unsigned stat_width = 12; + return std::format( + "{2:>{0}} : {3:{1}}\n" // steps + "{4:>{0}} : {5:{1}}\n" // residual evals + "{6:>{0}} : {7:{1}}\n" // linear decomps + "{8:>{0}} : {9:{1}}\n" // error test fails + "{10:>{0}} : {11:{1}}\n" // linear solves + "{12:>{0}} : {13:{1}}\n", // nonlinear conv fails + label_width, + stat_width, + "Steps", + num_steps_, + "Residual evals", + num_residual_evals_, + "Linear decompositions", + num_linear_decompositions_, + "Error test failures", + num_error_test_fails_, + "Linear solves", + num_linear_solves_, + "Nonlinear convergence failures", + num_nonlinear_convergence_fails_); + } + + /** + * @brief Construct and return an `IdaStats` object containing the statistics of the current IDA workspace. + * Several statistics returned by IDA are ignored because they are about the current state of IDA, + * rather than about the simulation at large. + */ + template + IdaStats Ida::getStats() const + { + IdaStats stats; + + // Dummies for ignoring stats + int dummy; + sunrealtype dummy2; + + int retval = IDAGetIntegratorStats(solver_, + &stats.num_steps_, + &stats.num_residual_evals_, + &stats.num_linear_decompositions_, + &stats.num_error_test_fails_, + &dummy, + &dummy, + &dummy2, + &dummy2, + &dummy2, + &dummy2); + checkOutput(retval, "IDAGetIntegratorStats"); + + retval = IDAGetNonlinSolvStats(solver_, &stats.num_linear_solves_, &stats.num_nonlinear_convergence_fails_); + checkOutput(retval, "IDAGetNonlinSolvStats"); + + return stats; + } + /** * @brief Check SUNDIALS allocation * diff --git a/GridKit/Solver/Dynamic/Ida.hpp b/GridKit/Solver/Dynamic/Ida.hpp index 982f0913..4031bb1c 100644 --- a/GridKit/Solver/Dynamic/Ida.hpp +++ b/GridKit/Solver/Dynamic/Ida.hpp @@ -24,6 +24,19 @@ namespace AnalysisManager { namespace Sundials { + struct IdaStats + { + long int num_steps_ = 0; + long int num_residual_evals_ = 0; + long int num_linear_decompositions_ = 0; + long int num_error_test_fails_ = 0; + long int num_linear_solves_ = 0; + long int num_nonlinear_convergence_fails_ = 0; + + IdaStats& operator+=(const IdaStats& other); + std::string report() const; + }; + template class Ida : public DynamicSolver { @@ -115,6 +128,8 @@ namespace AnalysisManager void printSpecial(RealT t, N_Vector x); void printFinalStats(); + IdaStats getStats() const; + private: static int Residual(RealT t, N_Vector yy, From d93cca9a931d61e2e5d5c8c8925be3040ec99e4b Mon Sep 17 00:00:00 2001 From: Alexander Novotny Date: Tue, 6 Jan 2026 18:25:28 -0500 Subject: [PATCH 3/6] Report IDA stats in RLCircuit to test --- examples/PowerElectronics/RLCircuit/RLCircuit.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/PowerElectronics/RLCircuit/RLCircuit.cpp b/examples/PowerElectronics/RLCircuit/RLCircuit.cpp index dce13668..44804aa7 100644 --- a/examples/PowerElectronics/RLCircuit/RLCircuit.cpp +++ b/examples/PowerElectronics/RLCircuit/RLCircuit.cpp @@ -134,5 +134,7 @@ int main(int /* argc */, char const** /* argv */) std::cout << abs((yfinial[i] - yexact[i]) / yexact[i]) << "\n"; } + std::cerr << idas.getStats().report() << '\n'; + return 0; } From 55740ee9eb12e3efd439b6295df869eb71877fd5 Mon Sep 17 00:00:00 2001 From: Alexander Novotny Date: Tue, 6 Jan 2026 18:40:28 -0500 Subject: [PATCH 4/6] Add to CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 04aa4071..ca382a44 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,6 +40,7 @@ - Added `GridKitDocs` target for Doxygen documentation. - Added a header file defining common math functions (e.g., sigmoid) to be used throughout the code. - Added capability to print monitored variables in multiple formats, triggered from `Ida::runSimulation`. +- Added IDA statistics object which can be accumulated over multiple simulations. ## v0.1 From 2f62b81cbf04f916bdb1b146655130629c6069f3 Mon Sep 17 00:00:00 2001 From: Alexander Novotny Date: Fri, 9 Jan 2026 13:24:58 -0500 Subject: [PATCH 5/6] Use iomanip for formatting --- GridKit/Solver/Dynamic/Ida.cpp | 37 ++++++++++++---------------------- 1 file changed, 13 insertions(+), 24 deletions(-) diff --git a/GridKit/Solver/Dynamic/Ida.cpp b/GridKit/Solver/Dynamic/Ida.cpp index ab1a34b3..a2d3397c 100644 --- a/GridKit/Solver/Dynamic/Ida.cpp +++ b/GridKit/Solver/Dynamic/Ida.cpp @@ -1,9 +1,9 @@ #include "Ida.hpp" -#include #include #include +#include #include #include @@ -992,29 +992,18 @@ namespace AnalysisManager */ std::string IdaStats::report() const { - unsigned label_width = 30; - unsigned stat_width = 12; - return std::format( - "{2:>{0}} : {3:{1}}\n" // steps - "{4:>{0}} : {5:{1}}\n" // residual evals - "{6:>{0}} : {7:{1}}\n" // linear decomps - "{8:>{0}} : {9:{1}}\n" // error test fails - "{10:>{0}} : {11:{1}}\n" // linear solves - "{12:>{0}} : {13:{1}}\n", // nonlinear conv fails - label_width, - stat_width, - "Steps", - num_steps_, - "Residual evals", - num_residual_evals_, - "Linear decompositions", - num_linear_decompositions_, - "Error test failures", - num_error_test_fails_, - "Linear solves", - num_linear_solves_, - "Nonlinear convergence failures", - num_nonlinear_convergence_fails_); + int label_width = 30; + int stat_width = 12; + std::stringstream out; + + out << std::setw(label_width) << "Steps" << " : " << std::setw(stat_width) << num_residual_evals_ << '\n' + << std::setw(label_width) << "Residual evals" << " : " << std::setw(stat_width) << num_linear_decompositions_ << '\n' + << std::setw(label_width) << "Linear decompositions" << " : " << std::setw(stat_width) << num_linear_decompositions_ << '\n' + << std::setw(label_width) << "Error test failures" << " : " << std::setw(stat_width) << num_error_test_fails_ << '\n' + << std::setw(label_width) << "Linear solves" << " : " << std::setw(stat_width) << num_linear_solves_ << '\n' + << std::setw(label_width) << "Nonlinear convergence failures" << " : " << std::setw(stat_width) << num_nonlinear_convergence_fails_; + + return out.str(); } /** From 0b4113bb28141d6dae7f1e719a01cd12098a0449 Mon Sep 17 00:00:00 2001 From: Alexander Novotny Date: Fri, 9 Jan 2026 13:26:40 -0500 Subject: [PATCH 6/6] Rename lienar solves -> nonlinear iterations --- GridKit/Solver/Dynamic/Ida.cpp | 6 +++--- GridKit/Solver/Dynamic/Ida.hpp | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/GridKit/Solver/Dynamic/Ida.cpp b/GridKit/Solver/Dynamic/Ida.cpp index a2d3397c..a05ba1ca 100644 --- a/GridKit/Solver/Dynamic/Ida.cpp +++ b/GridKit/Solver/Dynamic/Ida.cpp @@ -979,7 +979,7 @@ namespace AnalysisManager num_residual_evals_ += other.num_residual_evals_; num_linear_decompositions_ += other.num_linear_decompositions_; num_error_test_fails_ += other.num_error_test_fails_; - num_linear_solves_ += other.num_linear_solves_; + num_nonlinear_iters_ += other.num_nonlinear_iters_; num_nonlinear_convergence_fails_ += other.num_nonlinear_convergence_fails_; return *this; @@ -1000,7 +1000,7 @@ namespace AnalysisManager << std::setw(label_width) << "Residual evals" << " : " << std::setw(stat_width) << num_linear_decompositions_ << '\n' << std::setw(label_width) << "Linear decompositions" << " : " << std::setw(stat_width) << num_linear_decompositions_ << '\n' << std::setw(label_width) << "Error test failures" << " : " << std::setw(stat_width) << num_error_test_fails_ << '\n' - << std::setw(label_width) << "Linear solves" << " : " << std::setw(stat_width) << num_linear_solves_ << '\n' + << std::setw(label_width) << "Nonlinear iterations" << " : " << std::setw(stat_width) << num_nonlinear_iters_ << '\n' << std::setw(label_width) << "Nonlinear convergence failures" << " : " << std::setw(stat_width) << num_nonlinear_convergence_fails_; return out.str(); @@ -1033,7 +1033,7 @@ namespace AnalysisManager &dummy2); checkOutput(retval, "IDAGetIntegratorStats"); - retval = IDAGetNonlinSolvStats(solver_, &stats.num_linear_solves_, &stats.num_nonlinear_convergence_fails_); + retval = IDAGetNonlinSolvStats(solver_, &stats.num_nonlinear_iters_, &stats.num_nonlinear_convergence_fails_); checkOutput(retval, "IDAGetNonlinSolvStats"); return stats; diff --git a/GridKit/Solver/Dynamic/Ida.hpp b/GridKit/Solver/Dynamic/Ida.hpp index 4031bb1c..650e2885 100644 --- a/GridKit/Solver/Dynamic/Ida.hpp +++ b/GridKit/Solver/Dynamic/Ida.hpp @@ -30,7 +30,7 @@ namespace AnalysisManager long int num_residual_evals_ = 0; long int num_linear_decompositions_ = 0; long int num_error_test_fails_ = 0; - long int num_linear_solves_ = 0; + long int num_nonlinear_iters_ = 0; long int num_nonlinear_convergence_fails_ = 0; IdaStats& operator+=(const IdaStats& other);