From cb6e49f16d6d516c03b76b47ce7e685ca51bbb1b Mon Sep 17 00:00:00 2001 From: lukelowry Date: Thu, 28 May 2026 18:57:30 -0500 Subject: [PATCH] Add GASTPTI phasor dynamics governor --- CHANGELOG.md | 1 + .../Model/PhasorDynamics/ComponentLibrary.hpp | 1 + .../PhasorDynamics/Governor/CMakeLists.txt | 1 + .../Governor/GASTPTI/CMakeLists.txt | 54 ++ .../Governor/GASTPTI/GastPti.cpp | 27 + .../Governor/GASTPTI/GastPti.hpp | 131 ++++ .../Governor/GASTPTI/GastPtiData.hpp | 68 +++ .../GASTPTI/GastPtiDependencyTracking.cpp | 27 + .../Governor/GASTPTI/GastPtiEnzyme.cpp | 75 +++ .../Governor/GASTPTI/GastPtiImpl.hpp | 349 +++++++++++ .../PhasorDynamics/Governor/GASTPTI/README.md | 221 +++++++ .../Model/PhasorDynamics/Governor/README.md | 1 + GridKit/Model/PhasorDynamics/INPUT_FORMAT.md | 1 + GridKit/Model/PhasorDynamics/SystemModel.hpp | 27 + .../Model/PhasorDynamics/SystemModelData.hpp | 3 + .../SystemModelDataJSONParser.hpp | 6 + .../PhasorDynamics/GASTPTI_diagram.png | Bin 0 -> 95673 bytes tests/UnitTests/PhasorDynamics/CMakeLists.txt | 10 + .../PhasorDynamics/GovernorGastPtiTests.hpp | 572 ++++++++++++++++++ .../runGovernorGastPtiTests.cpp | 23 + 20 files changed, 1598 insertions(+) create mode 100644 GridKit/Model/PhasorDynamics/Governor/GASTPTI/CMakeLists.txt create mode 100644 GridKit/Model/PhasorDynamics/Governor/GASTPTI/GastPti.cpp create mode 100644 GridKit/Model/PhasorDynamics/Governor/GASTPTI/GastPti.hpp create mode 100644 GridKit/Model/PhasorDynamics/Governor/GASTPTI/GastPtiData.hpp create mode 100644 GridKit/Model/PhasorDynamics/Governor/GASTPTI/GastPtiDependencyTracking.cpp create mode 100644 GridKit/Model/PhasorDynamics/Governor/GASTPTI/GastPtiEnzyme.cpp create mode 100644 GridKit/Model/PhasorDynamics/Governor/GASTPTI/GastPtiImpl.hpp create mode 100644 GridKit/Model/PhasorDynamics/Governor/GASTPTI/README.md create mode 100644 docs/Figures/PhasorDynamics/GASTPTI_diagram.png create mode 100644 tests/UnitTests/PhasorDynamics/GovernorGastPtiTests.hpp create mode 100644 tests/UnitTests/PhasorDynamics/runGovernorGastPtiTests.cpp diff --git a/CHANGELOG.md b/CHANGELOG.md index 066786e09..ffe21010b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -62,6 +62,7 @@ - Added cmake-format hooks, including in pre-commit. - Added off-nominal tap ratio and phase shift support to the PhasorDynamics `Branch` model. - Added portable Vector class to GridKit +- Added `GASTPTI` governor model implementation for PhasorDynamics. ## v0.1 diff --git a/GridKit/Model/PhasorDynamics/ComponentLibrary.hpp b/GridKit/Model/PhasorDynamics/ComponentLibrary.hpp index e48a8a9c5..0b574fb75 100644 --- a/GridKit/Model/PhasorDynamics/ComponentLibrary.hpp +++ b/GridKit/Model/PhasorDynamics/ComponentLibrary.hpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include diff --git a/GridKit/Model/PhasorDynamics/Governor/CMakeLists.txt b/GridKit/Model/PhasorDynamics/Governor/CMakeLists.txt index 7c7269784..2ec5aaa55 100644 --- a/GridKit/Model/PhasorDynamics/Governor/CMakeLists.txt +++ b/GridKit/Model/PhasorDynamics/Governor/CMakeLists.txt @@ -4,3 +4,4 @@ # ]] add_subdirectory(Tgov1) +add_subdirectory(GASTPTI) diff --git a/GridKit/Model/PhasorDynamics/Governor/GASTPTI/CMakeLists.txt b/GridKit/Model/PhasorDynamics/Governor/GASTPTI/CMakeLists.txt new file mode 100644 index 000000000..41dacae76 --- /dev/null +++ b/GridKit/Model/PhasorDynamics/Governor/GASTPTI/CMakeLists.txt @@ -0,0 +1,54 @@ +# [[ +# Author(s): +# - Luke Lowery +# ]] + +set(_install_headers GastPti.hpp GastPtiData.hpp) + +if(GRIDKIT_ENABLE_ENZYME) + gridkit_add_library( + phasor_dynamics_governor_gastpti + SOURCES GastPtiEnzyme.cpp + HEADERS ${_install_headers} + INCLUDE_DIRECTORIES PRIVATE ${GRIDKIT_THIRD_PARTY_DIR}/magic-enum/include + LINK_LIBRARIES + PUBLIC + GridKit::phasor_dynamics_core + PUBLIC + GridKit::phasor_dynamics_signal + PRIVATE + ClangEnzymeFlags + COMPILE_OPTIONS + PRIVATE + -mllvm + -enzyme-auto-sparsity=1 + -fno-math-errno) +else() + gridkit_add_library( + phasor_dynamics_governor_gastpti + SOURCES GastPti.cpp + HEADERS ${_install_headers} + INCLUDE_DIRECTORIES PRIVATE ${GRIDKIT_THIRD_PARTY_DIR}/magic-enum/include + LINK_LIBRARIES + PUBLIC + GridKit::phasor_dynamics_core + PUBLIC + GridKit::phasor_dynamics_signal) +endif() + +gridkit_add_library( + phasor_dynamics_governor_gastpti_dependency_tracking + SOURCES GastPtiDependencyTracking.cpp + INCLUDE_DIRECTORIES PRIVATE ${GRIDKIT_THIRD_PARTY_DIR}/magic-enum/include + LINK_LIBRARIES + PUBLIC + GridKit::phasor_dynamics_core + PUBLIC + GridKit::phasor_dynamics_signal_dependency_tracking) + +target_link_libraries( + phasor_dynamics_components + INTERFACE GridKit::phasor_dynamics_governor_gastpti) +target_link_libraries( + phasor_dynamics_components_dependency_tracking + INTERFACE GridKit::phasor_dynamics_governor_gastpti_dependency_tracking) diff --git a/GridKit/Model/PhasorDynamics/Governor/GASTPTI/GastPti.cpp b/GridKit/Model/PhasorDynamics/Governor/GASTPTI/GastPti.cpp new file mode 100644 index 000000000..e0eabfe22 --- /dev/null +++ b/GridKit/Model/PhasorDynamics/Governor/GASTPTI/GastPti.cpp @@ -0,0 +1,27 @@ +/** + * @file GastPti.cpp + * @author Luke Lowery (lukel@tamu.edu) + * @brief Non-Enzyme instantiation for the GASTPTI governor model. + */ + +#include "GastPtiImpl.hpp" + +namespace GridKit +{ + namespace PhasorDynamics + { + namespace Governor + { + template + int GastPti::evaluateJacobian() + { + Log::misc() << "Evaluate Jacobian for GastPti..." << std::endl; + Log::misc() << "Jacobian evaluation is not implemented!" << std::endl; + return 0; + } + + template class GastPti; + template class GastPti; + } // namespace Governor + } // namespace PhasorDynamics +} // namespace GridKit diff --git a/GridKit/Model/PhasorDynamics/Governor/GASTPTI/GastPti.hpp b/GridKit/Model/PhasorDynamics/Governor/GASTPTI/GastPti.hpp new file mode 100644 index 000000000..8bb9aeebc --- /dev/null +++ b/GridKit/Model/PhasorDynamics/Governor/GASTPTI/GastPti.hpp @@ -0,0 +1,131 @@ +/** + * @file GastPti.hpp + * @author Luke Lowery (lukel@tamu.edu) + * @brief Declaration of the GASTPTI governor model. + */ + +#pragma once + +#include +#include +#include + +#include +#include +#include +#include + +namespace GridKit +{ + namespace PhasorDynamics + { + template + class SignalNode; + + namespace Governor + { + /// Internal variables of a `GastPti`. + enum class GastPtiInternalVariables : size_t + { + XVALVE, ///< Fuel-valve state + XFLOW, ///< Fuel-flow state + XTEMP, ///< Exhaust-temperature feedback state + VLOAD, ///< Speed/load fuel demand + VTEMP, ///< Temperature-limit fuel demand + VLV, ///< Low-value gate output + PMECH, ///< Mechanical-power output + MAXIMUM, + }; + + /// External variables of a `GastPti`. + enum class GastPtiExternalVariables : size_t + { + OMEGA, ///< Machine speed deviation + PREF, ///< Active-power/load reference + MAXIMUM, + }; + + template + class GastPti : public Component + { + using Component::alpha_; + using Component::f_; + using Component::gridkit_component_id_; + using Component::J_; + using Component::J_cols_buffer_; + using Component::J_rows_buffer_; + using Component::J_vals_buffer_; + using Component::residual_indices_; + using Component::size_; + using Component::tag_; + using Component::va_system_base_; + using Component::variable_indices_; + using Component::wb_; + using Component::y_; + using Component::yp_; + + public: + using ScalarT = scalar_type; + using IdxT = index_type; + using RealT = typename Component::RealT; + using ModelDataT = GastPtiData; + using SignalT = SignalNode; + using MonitorT = Model::VariableMonitor; + + GastPti(); + GastPti(const ModelDataT& data); + ~GastPti() override; + + int setGridKitComponentID(IdxT) override final; + int allocate() override final; + int verify() const override final; + int initialize() override final; + int tagDifferentiable() override final; + int evaluateResidual() override final; + int evaluateJacobian() override final; + + auto getSignals() + -> ComponentSignals& + { + return signals_; + } + + const Model::VariableMonitorBase* getMonitor() const override; + + __attribute__((always_inline)) inline int evaluateInternalResidual( + ScalarT*, ScalarT*, ScalarT*, ScalarT*, ScalarT*); + + private: + void initModelParams(const ModelDataT& data); + void initializeMonitor(); + ScalarT toComponentBase(ScalarT value) const; + ScalarT toSystemBase(ScalarT value) const; + + RealT R_{0}; + RealT T1_{0}; + RealT T2_{0}; + RealT T3_{0}; + RealT At_{0}; + RealT Kt_{0}; + RealT Vmax_{0}; + RealT Vmin_{0}; + RealT Dturb_{0}; + RealT Trate_{0}; + RealT va_component_base_{0}; + + int parameter_error_count_{0}; + + ScalarT pref_set_{0}; + + ComponentSignals signals_; + std::unique_ptr monitor_; + + std::vector ws_; + std::vector ws_indices_; + }; + } // namespace Governor + } // namespace PhasorDynamics +} // namespace GridKit diff --git a/GridKit/Model/PhasorDynamics/Governor/GASTPTI/GastPtiData.hpp b/GridKit/Model/PhasorDynamics/Governor/GASTPTI/GastPtiData.hpp new file mode 100644 index 000000000..1ac6ca6f7 --- /dev/null +++ b/GridKit/Model/PhasorDynamics/Governor/GASTPTI/GastPtiData.hpp @@ -0,0 +1,68 @@ +/** + * @file GastPtiData.hpp + * @author Luke Lowery (lukel@tamu.edu) + * @brief Modeling data for the GASTPTI governor model. + */ + +#pragma once + +#include + +namespace GridKit +{ + namespace PhasorDynamics + { + namespace Governor + { + /// Parameter keys for the GASTPTI governor model. + enum class GastPtiParameters + { + R, ///< Permanent droop + T1, ///< Fuel-valve time constant + T2, ///< Fuel-flow time constant + T3, ///< Exhaust-temperature time constant + At, ///< Ambient-temperature load limit + Kt, ///< Exhaust-temperature feedback gain + Vmax, ///< Maximum fuel-valve/turbine-power limit + Vmin, ///< Minimum fuel-valve/turbine-power limit + Dturb, ///< Turbine damping coefficient + Trate ///< Turbine-rating power base + }; + + /// Ports for the GASTPTI governor model. + enum class GastPtiPorts + { + bus, ///< Optional terminal bus ID for case-format compatibility + speed, ///< Machine speed-deviation signal ID + pmech, ///< Mechanical-power output signal ID + pref ///< Optional active-power/load reference signal ID + }; + + /// Variables available through the monitor interface. + enum class GastPtiMonitorableVariables + { + pmech, ///< Mechanical power output + fuelvalve, ///< Fuel-valve state + fuelflow, ///< Fuel-flow state + exhausttemp, ///< Exhaust-temperature feedback state + vload, ///< Speed/load fuel demand + vtemp, ///< Temperature-limit fuel demand + vlv ///< Low-value gate output + }; + + template + struct GastPtiData : public ComponentData + { + GastPtiData() = default; + + using Parameters = GastPtiParameters; + using Ports = GastPtiPorts; + using MonitorableVariables = GastPtiMonitorableVariables; + }; + } // namespace Governor + } // namespace PhasorDynamics +} // namespace GridKit diff --git a/GridKit/Model/PhasorDynamics/Governor/GASTPTI/GastPtiDependencyTracking.cpp b/GridKit/Model/PhasorDynamics/Governor/GASTPTI/GastPtiDependencyTracking.cpp new file mode 100644 index 000000000..8c588e794 --- /dev/null +++ b/GridKit/Model/PhasorDynamics/Governor/GASTPTI/GastPtiDependencyTracking.cpp @@ -0,0 +1,27 @@ +/** + * @file GastPtiDependencyTracking.cpp + * @author Luke Lowery (lukel@tamu.edu) + * @brief Dependency-tracking instantiations for the GASTPTI governor model. + */ + +#include "GastPtiImpl.hpp" + +namespace GridKit +{ + namespace PhasorDynamics + { + namespace Governor + { + template + int GastPti::evaluateJacobian() + { + Log::misc() << "Evaluate Jacobian for GastPti..." << std::endl; + Log::misc() << "Jacobian evaluation is not implemented!" << std::endl; + return 0; + } + + template class GastPti; + template class GastPti; + } // namespace Governor + } // namespace PhasorDynamics +} // namespace GridKit diff --git a/GridKit/Model/PhasorDynamics/Governor/GASTPTI/GastPtiEnzyme.cpp b/GridKit/Model/PhasorDynamics/Governor/GASTPTI/GastPtiEnzyme.cpp new file mode 100644 index 000000000..10a7313d1 --- /dev/null +++ b/GridKit/Model/PhasorDynamics/Governor/GASTPTI/GastPtiEnzyme.cpp @@ -0,0 +1,75 @@ +/** + * @file GastPtiEnzyme.cpp + * @author Luke Lowery (lukel@tamu.edu) + * @brief Enzyme sparse Jacobian for the GASTPTI governor model. + */ + +#include + +#include "GastPtiImpl.hpp" + +namespace GridKit +{ + namespace PhasorDynamics + { + namespace Governor + { + template + int GastPti::evaluateJacobian() + { + Log::misc() << "Evaluate Jacobian for GastPti..." << std::endl; + Log::misc() << "Jacobian evaluation is experimental!" << std::endl; + + J_.zeroMatrix(); + if (J_rows_buffer_ == nullptr) + { + J_rows_buffer_ = new IdxT[static_cast(size_) * static_cast(size_)]; + J_cols_buffer_ = new IdxT[static_cast(size_) * static_cast(size_)]; + J_vals_buffer_ = new RealT[static_cast(size_) * static_cast(size_)]; + } + + using ModelT = GridKit::PhasorDynamics::Governor::GastPti; + using Fn = GridKit::Enzyme::Sparse::MemberFunctions; + + GridKit::Enzyme::Sparse::DfDy::eval(this, + f_.size(), + y_.size(), + (this->getResidualIndices()).data(), + (this->getVariableIndices()).data(), + y_.data(), + yp_.data(), + wb_.data(), + ws_.data(), + alpha_, + J_rows_buffer_, + J_cols_buffer_, + J_vals_buffer_, + J_); + + GridKit::Enzyme::Sparse::DfDws::eval(this, + f_.size(), + ws_.size(), + (this->getResidualIndices()).data(), + ws_indices_.data(), + y_.data(), + yp_.data(), + wb_.data(), + ws_.data(), + J_rows_buffer_, + J_cols_buffer_, + J_vals_buffer_, + J_); + return 0; + } + + template class GastPti; + template class GastPti; + } // namespace Governor + } // namespace PhasorDynamics +} // namespace GridKit diff --git a/GridKit/Model/PhasorDynamics/Governor/GASTPTI/GastPtiImpl.hpp b/GridKit/Model/PhasorDynamics/Governor/GASTPTI/GastPtiImpl.hpp new file mode 100644 index 000000000..594ea6aa8 --- /dev/null +++ b/GridKit/Model/PhasorDynamics/Governor/GASTPTI/GastPtiImpl.hpp @@ -0,0 +1,349 @@ +/** + * @file GastPtiImpl.hpp + * @author Luke Lowery (lukel@tamu.edu) + * @brief Definition of the GASTPTI governor model. + */ + +#pragma once + +#include +#include + +#include +#include +#include +#include +#include + +namespace GridKit +{ + namespace PhasorDynamics + { + namespace Governor + { + using Log = ::GridKit::Utilities::Logger; + + template + GastPti::GastPti() + { + size_ = static_cast(GastPtiInternalVariables::MAXIMUM); + } + + template + GastPti::GastPti(const ModelDataT& data) + : monitor_(std::make_unique(data)) + { + initModelParams(data); + initializeMonitor(); + size_ = static_cast(GastPtiInternalVariables::MAXIMUM); + } + + template + GastPti::~GastPti() + { + } + + template + scalar_type GastPti::toComponentBase(scalar_type value) const + { + return value * va_system_base_ / va_component_base_; + } + + template + scalar_type GastPti::toSystemBase(scalar_type value) const + { + return value / toComponentBase(static_cast(ONE)); + } + + template + void GastPti::initModelParams(const ModelDataT& data) + { + using Params = typename ModelDataT::Parameters; + + parameter_error_count_ = 0; + + auto load_required_real = [&](auto key, RealT& target, const char* name) + { + if (!data.parameters.contains(key)) + { + Log::error() << "GastPti: missing required parameter '" << name << "'\n"; + ++parameter_error_count_; + return; + } + + const auto& value = data.parameters.at(key); + if (const auto* real_value = std::get_if(&value)) + { + target = *real_value; + } + else if (const auto* index_value = std::get_if(&value)) + { + target = static_cast(*index_value); + } + else + { + Log::error() << "GastPti: parameter '" << name << "' must be numeric\n"; + ++parameter_error_count_; + } + }; + + load_required_real(Params::R, R_, "R"); + load_required_real(Params::T1, T1_, "T1"); + load_required_real(Params::T2, T2_, "T2"); + load_required_real(Params::T3, T3_, "T3"); + load_required_real(Params::At, At_, "At"); + load_required_real(Params::Kt, Kt_, "Kt"); + load_required_real(Params::Vmax, Vmax_, "Vmax"); + load_required_real(Params::Vmin, Vmin_, "Vmin"); + load_required_real(Params::Dturb, Dturb_, "Dturb"); + load_required_real(Params::Trate, Trate_, "Trate"); + + va_component_base_ = Trate_ * static_cast(1.0e6); + } + + template + const Model::VariableMonitorBase* GastPti::getMonitor() const + { + return monitor_.get(); + } + + template + void GastPti::initializeMonitor() + { + using Variable = typename ModelDataT::MonitorableVariables; + auto index = [](GastPtiInternalVariables variable) + { + return static_cast(variable); + }; + + monitor_->set(Variable::pmech, [this, index] + { return y_[index(GastPtiInternalVariables::PMECH)]; }); + monitor_->set(Variable::fuelvalve, [this, index] + { return y_[index(GastPtiInternalVariables::XVALVE)]; }); + monitor_->set(Variable::fuelflow, [this, index] + { return y_[index(GastPtiInternalVariables::XFLOW)]; }); + monitor_->set(Variable::exhausttemp, [this, index] + { return y_[index(GastPtiInternalVariables::XTEMP)]; }); + monitor_->set(Variable::vload, [this, index] + { return y_[index(GastPtiInternalVariables::VLOAD)]; }); + monitor_->set(Variable::vtemp, [this, index] + { return y_[index(GastPtiInternalVariables::VTEMP)]; }); + monitor_->set(Variable::vlv, [this, index] + { return y_[index(GastPtiInternalVariables::VLV)]; }); + } + + template + int GastPti::setGridKitComponentID(IdxT component_id) + { + gridkit_component_id_ = component_id; + return 0; + } + + template + int GastPti::allocate() + { + size_ = static_cast(GastPtiInternalVariables::MAXIMUM); + auto size = static_cast(size_); + + f_.assign(size, ScalarT{0}); + y_.assign(size, ScalarT{0}); + yp_.assign(size, ScalarT{0}); + tag_.assign(size, false); + variable_indices_.resize(size); + residual_indices_.resize(size); + + wb_.clear(); + + auto signal_size = static_cast(GastPtiExternalVariables::MAXIMUM); + ws_.assign(signal_size, ScalarT{0}); + ws_indices_.assign(signal_size, INVALID_INDEX); + + for (IdxT j = 0; j < size_; ++j) + { + this->setVariableIndex(j, j); + this->setResidualIndex(j, j); + } + + if (signals_.template isAssigned()) + { + signals_.template getSignalNode()->set( + &y_[static_cast(GastPtiInternalVariables::PMECH)], + &(this->getVariableIndex(static_cast(GastPtiInternalVariables::PMECH)))); + } + + return 0; + } + + template + int GastPti::verify() const + { + int ret = parameter_error_count_; + + auto check = [&](bool condition, const char* message) + { + if (!condition) + { + Log::error() << "GastPti: " << message << '\n'; + ret += 1; + } + }; + + check(R_ > ZERO, "R must be positive"); + check(T1_ >= ZERO, "T1 must be non-negative"); + check(T2_ >= ZERO, "T2 must be non-negative"); + check(T3_ >= ZERO, "T3 must be non-negative"); + check(At_ >= ZERO, "At must be non-negative"); + check(Kt_ >= ZERO, "Kt must be non-negative"); + check(Vmin_ <= Vmax_, "Vmin must be less than or equal to Vmax"); + check(Dturb_ >= ZERO, "Dturb must be non-negative"); + check(Trate_ > ZERO, "Trate must be positive"); + + if (signals_.template isAttached() + && !signals_.template isLinked()) + { + Log::error() << "GastPti: omega signal attached with no linked source\n"; + ret += 1; + } + + if (signals_.template isAttached() + && !signals_.template isLinked()) + { + Log::error() << "GastPti: pref signal attached with no linked source\n"; + ret += 1; + } + + return ret; + } + + template + int GastPti::initialize() + { + if (parameter_error_count_ > 0 || verify() > 0) + { + Log::error() << "GastPti: cannot initialize with invalid configuration\n"; + return 1; + } + + const auto XVALVE = static_cast(GastPtiInternalVariables::XVALVE); + const auto XFLOW = static_cast(GastPtiInternalVariables::XFLOW); + const auto XTEMP = static_cast(GastPtiInternalVariables::XTEMP); + const auto VLOAD = static_cast(GastPtiInternalVariables::VLOAD); + const auto VTEMP = static_cast(GastPtiInternalVariables::VTEMP); + const auto VLV = static_cast(GastPtiInternalVariables::VLV); + const auto PMECH = static_cast(GastPtiInternalVariables::PMECH); + + ScalarT omega0{ZERO}; + if (signals_.template isAttached()) + { + omega0 = signals_.template readExternalVariable(); + } + + // Machine initialization seeds the assigned PMECH signal before + // governor initialization reads it. + const ScalarT pmech0 = toComponentBase(y_[PMECH]); + const ScalarT xflow0 = pmech0 + Dturb_ * omega0; + + y_[XFLOW] = xflow0; + y_[XVALVE] = xflow0; + y_[XTEMP] = xflow0; + y_[VTEMP] = At_ + Kt_ * (At_ - y_[XTEMP]); + y_[VLV] = y_[XVALVE]; + y_[VLOAD] = y_[XVALVE]; + y_[PMECH] = toSystemBase(pmech0); + + pref_set_ = y_[VLOAD] + omega0 / R_; + if (signals_.template isAttached()) + { + pref_set_ = signals_.template readExternalVariable(); + y_[VLOAD] = pref_set_ - omega0 / R_; + } + + std::fill(yp_.begin(), yp_.end(), ZERO); + return 0; + } + + template + int GastPti::tagDifferentiable() + { + std::fill(tag_.begin(), tag_.end(), false); + tag_[static_cast(GastPtiInternalVariables::XVALVE)] = (T1_ > ZERO); + tag_[static_cast(GastPtiInternalVariables::XFLOW)] = (T2_ > ZERO); + tag_[static_cast(GastPtiInternalVariables::XTEMP)] = (T3_ > ZERO); + return 0; + } + + template + __attribute__((always_inline)) inline int GastPti::evaluateInternalResidual( + ScalarT* y, + ScalarT* yp, + [[maybe_unused]] ScalarT* wb, + ScalarT* ws, + ScalarT* f) + { + const auto XVALVE = static_cast(GastPtiInternalVariables::XVALVE); + const auto XFLOW = static_cast(GastPtiInternalVariables::XFLOW); + const auto XTEMP = static_cast(GastPtiInternalVariables::XTEMP); + const auto VLOAD = static_cast(GastPtiInternalVariables::VLOAD); + const auto VTEMP = static_cast(GastPtiInternalVariables::VTEMP); + const auto VLV = static_cast(GastPtiInternalVariables::VLV); + const auto PMECH = static_cast(GastPtiInternalVariables::PMECH); + + const auto OMEGA = static_cast(GastPtiExternalVariables::OMEGA); + const auto PREF = static_cast(GastPtiExternalVariables::PREF); + + const ScalarT xvalve = y[XVALVE]; + const ScalarT xflow = y[XFLOW]; + const ScalarT xtemp = y[XTEMP]; + const ScalarT vload = y[VLOAD]; + const ScalarT vtemp = y[VTEMP]; + const ScalarT vlv = y[VLV]; + const ScalarT pmech = y[PMECH]; + + const ScalarT omega = ws[OMEGA]; + const ScalarT pref = ws[PREF]; + + const ScalarT fvalve = vlv - xvalve; + + f[XVALVE] = -T1_ * yp[XVALVE] + Math::antiwindup(xvalve, fvalve, Vmin_, Vmax_); + f[XFLOW] = -T2_ * yp[XFLOW] - xflow + xvalve; + f[XTEMP] = -T3_ * yp[XTEMP] - xtemp + xflow; + f[VLOAD] = -omega + R_ * (pref - vload); + f[VTEMP] = -vtemp + At_ + Kt_ * (At_ - xtemp); + f[VLV] = -vlv + Math::min(vload, vtemp); + const RealT system_base = va_system_base_ / static_cast(1.0e6); + f[PMECH] = -system_base * pmech + Trate_ * (xflow - Dturb_ * omega); + + return 0; + } + + template + int GastPti::evaluateResidual() + { + const auto OMEGA = static_cast(GastPtiExternalVariables::OMEGA); + const auto PREF = static_cast(GastPtiExternalVariables::PREF); + + ws_[OMEGA] = ZERO; + ws_[PREF] = pref_set_; + ws_indices_[OMEGA] = INVALID_INDEX; + ws_indices_[PREF] = INVALID_INDEX; + + if (signals_.template isAttached()) + { + ws_[OMEGA] = signals_.template readExternalVariable(); + ws_indices_[OMEGA] = + signals_.template readExternalVariableIndex(); + } + + if (signals_.template isAttached()) + { + ws_[PREF] = signals_.template readExternalVariable(); + ws_indices_[PREF] = + signals_.template readExternalVariableIndex(); + } + + evaluateInternalResidual(y_.data(), yp_.data(), wb_.data(), ws_.data(), f_.data()); + return 0; + } + } // namespace Governor + } // namespace PhasorDynamics +} // namespace GridKit diff --git a/GridKit/Model/PhasorDynamics/Governor/GASTPTI/README.md b/GridKit/Model/PhasorDynamics/Governor/GASTPTI/README.md new file mode 100644 index 000000000..e898d70d5 --- /dev/null +++ b/GridKit/Model/PhasorDynamics/Governor/GASTPTI/README.md @@ -0,0 +1,221 @@ +# **Gas Turbine-Governor Model (GASTPTI)** + +GASTPTI is a gas turbine-governor model for thermal generating units. In +GridKit it is represented as a governor model that reads machine speed +deviation and supplies mechanical power to the machine through a fuel-valve, +fuel-flow, and exhaust-temperature limiting chain. + +Notes: +- GASTPTI uses `Trate` as its turbine-power component base. +- The shared `pmech` signal is treated as system-base power. GridKit converts + between system base and `Trate` at the GASTPTI signal boundary. +- The diagram shows the GASTD speed deadband block (`dbL`/`dbH`). That + block is only used by GASTD; GASTPTI uses the speed-deviation input + $\omega$ directly. + +## Block Diagram + +Standard GASTPTI block diagram. + +
+ + + Figure 1: Governor GASTPTI model. Figure courtesy of [PowerWorld](https://www.powerworld.com/WebHelp/) +
+ +## Model Parameters + +Symbol | Units | JSON | Description | Typical Value | Note +--------------------------------|----------|----------|----------------------------------------------|---------------|------ +$R$ | [p.u.] | `R` | Permanent droop | 0.05 | Required nonzero +$T_1$ | [sec] | `T1` | Fuel-valve time constant | 0.4 | If zero, $x_{\mathrm{valve}}$ is algebraic +$T_2$ | [sec] | `T2` | Fuel-flow time constant | 0.1 | If zero, $x_{\mathrm{flow}}$ is algebraic +$T_3$ | [sec] | `T3` | Exhaust-temperature time constant | 3.0 | If zero, $x_{\mathrm{temp}}$ is algebraic +$A_{\mathrm{T}}$ | [p.u.] | `At` | Ambient-temperature load limit | 1.0 | +$K_{\mathrm{T}}$ | [p.u.] | `Kt` | Exhaust-temperature feedback gain | 2.0 | +$V^{\max}$ | [p.u.] | `Vmax` | Maximum fuel-valve/turbine-power limit | 1.0 | +$V^{\min}$ | [p.u.] | `Vmin` | Minimum fuel-valve/turbine-power limit | 0.0 | +$D_{\mathrm{turb}}$ | [p.u.] | `Dturb` | Turbine damping coefficient | 0.0 | Multiplies speed deviation +$T_{\mathrm{rate}}$ | [MW] | `Trate` | Turbine-rating power base | 100.0 | Required positive value + +### Parameter Validation + +Invalid GASTPTI parameter sets are rejected by the following checks. + +The required checks are: + +```math +\begin{aligned} + &R > 0 \\ + &T_1, T_2, T_3 \ge 0 \\ + &A_{\mathrm{T}} \ge 0 \\ + &K_{\mathrm{T}} \ge 0 \\ + &V^{\min} \le V^{\max} \\ + &D_{\mathrm{turb}} \ge 0 \\ + &T_{\mathrm{rate}} > 0 +\end{aligned} +``` + +### Model Derived Parameters + +The system power base $S_{\mathrm{sys}}$ is derived from the system model. + +## Model Variables + +### Internal Variables + +#### Differential + +Symbol | Units | Description | Note +------------------------|--------|------------------------------------|------ +$x_{\mathrm{valve}}$ | [p.u.] | Fuel-valve state | State 1 in Fig. 1; source label: `Fuel Valve`; algebraic when $T_1 = 0$ +$x_{\mathrm{flow}}$ | [p.u.] | Fuel-flow state | State 2 in Fig. 1; source label: `Fuel Flow`; algebraic when $T_2 = 0$ +$x_{\mathrm{temp}}$ | [p.u.] | Exhaust-temperature feedback state | State 3 in Fig. 1; source label: `Exhaust Temperature`; algebraic when $T_3 = 0$ + +#### Algebraic + +Symbol | Units | Description | Note +--------------------------------|--------|--------------------------------------|------ +$V_{\mathrm{load}}$ | [p.u.] | Speed/load fuel demand | $P_{\mathrm{ref}}$ less droop feedback; LV gate input +$V_{\mathrm{temp}}$ | [p.u.] | Temperature-limit fuel demand | Exhaust-temperature branch; LV gate input +$V_{\mathrm{LV}}$ | [p.u.] | LV gate output | Lesser of $V_{\mathrm{load}}$ and $V_{\mathrm{temp}}$; drives the fuel-valve lag +$P_{\text{m}}$ | [p.u.] | Mechanical power to generator | On system base + +### External Variables + +#### Differential + +None. + +#### Algebraic + +Symbol | Units | Description | Note +------------------------|--------|-----------------------------|------ +$\omega$ | [p.u.] | Machine speed deviation | Read from a machine model +$P_{\mathrm{ref}}$ | [p.u.] | Active-power/load reference | On `Trate` component base; source label: `Pref` + +## Model Equations + +### Differential Equations + +The lag residuals are written in descriptor form. When $T_1$, $T_2$, or +$T_3$ is zero, the corresponding residual becomes algebraic. + +```math +\begin{aligned} + 0 &= + -T_1 \dot x_{\mathrm{valve}} + + \text{antiwindup}( + x_{\mathrm{valve}}, + V_{\mathrm{LV}} - x_{\mathrm{valve}}, + V^{\min}, + V^{\max} + ) \\ + 0 &= -T_2 \dot x_{\mathrm{flow}} - x_{\mathrm{flow}} + x_{\mathrm{valve}} \\ + 0 &= -T_3 \dot x_{\mathrm{temp}} - x_{\mathrm{temp}} + x_{\mathrm{flow}} +\end{aligned} +``` + +CommonMath defines the [Anti-Windup](../../../../CommonMath.md#anti-windup-indicator) +target and smooth approximation. + +### Algebraic Equations + +The algebraic targets form the two LV gate inputs, select between them with the +low-value gate, and assemble the mechanical power output. + +```math +\begin{aligned} + 0 &= -\omega + R(P_{\mathrm{ref}} - V_{\mathrm{load}}) \\ + 0 &= -V_{\mathrm{temp}} + + A_{\mathrm{T}} + + K_{\mathrm{T}}(A_{\mathrm{T}} - x_{\mathrm{temp}}) \\ + 0 &= -V_{\mathrm{LV}} + \min(V_{\mathrm{load}}, V_{\mathrm{temp}}) \\ + 0 &= -S_{\mathrm{sys}} P_{\text{m}} + + T_{\mathrm{rate}}(x_{\mathrm{flow}} - D_{\mathrm{turb}}\omega) +\end{aligned} +``` + +CommonMath defines the helper targets and smooth approximations for +[min](../../../../CommonMath.md#derived-functions). + +## Initialization + +Initialization is performed by evaluating the steady-state residuals in +dependency order. Let subscript $0$ denote initial values and set all internal +derivatives to zero. A standard power-flow start uses synchronous machine +speed: + +```math +\begin{aligned} + \omega_0 &= 0 +\end{aligned} +``` + +The machine initializes mechanical power first; GASTPTI reads it as +$P_{\text{m},0}$ on the system base and converts it to the `Trate` +component base: + +```math +\begin{aligned} + S_{\mathrm{sys}} P_{\text{m},0} + &= T_{\mathrm{rate}}P^{\mathrm{gastpti}}_{\text{m},0} +\end{aligned} +``` + +The output residual fixes the fuel-flow state, and the two plain lag residuals +pass that value through to the fuel valve and exhaust-temperature states: + +```math +\begin{aligned} + x_{\mathrm{flow},0} &= + P^{\mathrm{gastpti}}_{\mathrm{mech},0} + + D_{\mathrm{turb}}\omega_0 \\ + x_{\mathrm{valve},0} &= x_{\mathrm{flow},0} \\ + x_{\mathrm{temp},0} &= x_{\mathrm{flow},0} +\end{aligned} +``` + +Then evaluate the temperature-limit branch and the LV-gate target: + +```math +\begin{aligned} + V_{\mathrm{temp},0} + &= A_{\mathrm{T}} + + K_{\mathrm{T}}(A_{\mathrm{T}} - x_{\mathrm{temp},0}) \\ + V_{\mathrm{LV},0} &= x_{\mathrm{valve},0} +\end{aligned} +``` + +For an unsaturated start where the speed/load demand is selected by the LV +gate, choose or verify: + +```math +\begin{aligned} + V_{\mathrm{load},0} &= x_{\mathrm{valve},0} \\ + P_{\mathrm{ref},0} + &= V_{\mathrm{load},0} + \dfrac{\omega_0}{R} +\end{aligned} +``` + +This start requires $V^{\min} \le x_{\mathrm{valve},0} \le V^{\max}$ and +$V_{\mathrm{temp},0} \ge V_{\mathrm{load},0}$ so the LV gate selects the +speed/load demand and the fuel-valve anti-windup residual holds. If the +supplied $P_{\mathrm{ref}}$ is connected, use its initial value and verify the +same residuals. + +Starts that bind the fuel-valve limit or the temperature gate are outside these +closed-form initialization formulas. The zero-derivative lag relations still +require $x_{\mathrm{temp},0}=x_{\mathrm{flow},0}=x_{\mathrm{valve},0}$. + +## Model Outputs + +Output | Units | Description | Note +---------------|--------|------------------------------------|------ +`pmech` | [p.u.] | Mechanical power output | On system base; oriented as mechanical input to the machine +`fuelvalve` | [p.u.] | Fuel-valve state | State 1 +`fuelflow` | [p.u.] | Fuel-flow state | State 2 +`exhausttemp` | [p.u.] | Exhaust-temperature feedback state | State 3 +`vload` | [p.u.] | Speed/load fuel demand | LV gate input +`vtemp` | [p.u.] | Temperature-limit fuel demand | LV gate input +`vlv` | [p.u.] | LV gate output | Selected fuel demand diff --git a/GridKit/Model/PhasorDynamics/Governor/README.md b/GridKit/Model/PhasorDynamics/Governor/README.md index 8e6305aa8..b0a5c5a40 100644 --- a/GridKit/Model/PhasorDynamics/Governor/README.md +++ b/GridKit/Model/PhasorDynamics/Governor/README.md @@ -9,3 +9,4 @@ A governor models the control system that regulates the output power of a machin There are a few standard Governor models - Turbine Governor (See [TGOV1](Tgov1/README.md)) +- Gas Turbine Governor (See [GASTPTI](GASTPTI/README.md)) diff --git a/GridKit/Model/PhasorDynamics/INPUT_FORMAT.md b/GridKit/Model/PhasorDynamics/INPUT_FORMAT.md index 32df36cdc..40925c7e9 100644 --- a/GridKit/Model/PhasorDynamics/INPUT_FORMAT.md +++ b/GridKit/Model/PhasorDynamics/INPUT_FORMAT.md @@ -145,6 +145,7 @@ are specified: `Gensal` | 5th order salient-pole machine model | `bus`, `pmech`\*, `speed`\*, `efd`\* | `p0`, `q0`, `H`, `D`, `Ra`, `Tdop`, `Tdopp`, `Tqopp`, `Xd`, `Xdp`, `Xdpp`, `Xq`, `Xl`, `S10`, `S12`, `mva` | `ir`, `ii`, `p`, `q`, `delta`, `omega`, `speed`, `Eqp`, `psidp`, `psiqpp`, `psidpp`, `vd`, `vq`, `te`, `id`, `iq` `GenClassical` | the classical machine model | `bus`, `pmech`\*, `speed`\*, `efd`\* | `p0`, `q0`, `H`, `D`, `Ra`, `Xdp`, `mva` | `ir`, `ii`, `p`, `q`, `delta`, `omega` `Tgov1 ` | the TGOV1 governor model | `pmech`, `speed` | `R`, `T1`, `T2`, `T3`, `Pvmax`, `Pvmin`, `Dt` | `none` + `GastPti` | the GASTPTI gas turbine-governor model | `pmech`, `speed`\*, `pref`\* | `R`, `T1`, `T2`, `T3`, `At`, `Kt`, `Vmax`, `Vmin`, `Dturb`, `Trate` | `pmech`, `fuelvalve`, `fuelflow`, `exhausttemp`, `vload`, `vtemp`, `vlv` `Ieeet1` | the IEEET1 exciter model | `bus`, `speed`, `efd`, `vs`\* | `Tr`, `Ka`, `Ta`, `Ke`, `Te`, `Kf`, `Tf`, `Vrmin`, `Vrmax`, `E1`, `E2`, `Se1`, `Se2`, `Ispdlim` | `efd`, `ksat` `SexsPti` | the SEXS-PTI simplified exciter model | `bus`, `efd`, `vs`\* | `Ta`, `Tb`, `Te`, `K`, `Efdmax`, `Efdmin` | `efd` `Ieeest` | the IEEEST stabilizer model | `input`, `output` | `A1`, `A2`, `A3`, `A4`, `A5`, `A6`, `T1`, `T2`, `T3`, `T4`, `T5`, `T6`, `Ks`, `Lsmin`, `Lsmax`, `Vcl`, `Vcu`, `Tdelay` | `vss` diff --git a/GridKit/Model/PhasorDynamics/SystemModel.hpp b/GridKit/Model/PhasorDynamics/SystemModel.hpp index d7b4cc9d7..449c3c2da 100644 --- a/GridKit/Model/PhasorDynamics/SystemModel.hpp +++ b/GridKit/Model/PhasorDynamics/SystemModel.hpp @@ -297,6 +297,33 @@ namespace GridKit addComponent(gov); } + // Add GASTPTI governors + for (const auto& govdata : data.gastpti) + { + auto* gov = new GastPti(govdata); + + if (govdata.ports.contains(GastPtiData::Ports::speed)) + { + IdxT speed = govdata.ports.at(GastPtiData::Ports::speed); + gov->getSignals().template attachSignalNode(getSignal(speed)); + } + + if (govdata.ports.contains(GastPtiData::Ports::pmech)) + { + IdxT pmech = govdata.ports.at(GastPtiData::Ports::pmech); + gov->getSignals().template assignSignalNode(getSignal(pmech)); + } + + if (govdata.ports.contains(GastPtiData::Ports::pref)) + { + IdxT pref = govdata.ports.at(GastPtiData::Ports::pref); + gov->getSignals().template attachSignalNode( + getSignal(pref)); + } + + addComponent(gov); + } + for (const auto& excitedata : data.exciter) { IdxT bus_index = 0; diff --git a/GridKit/Model/PhasorDynamics/SystemModelData.hpp b/GridKit/Model/PhasorDynamics/SystemModelData.hpp index d4deef66e..e415f5102 100644 --- a/GridKit/Model/PhasorDynamics/SystemModelData.hpp +++ b/GridKit/Model/PhasorDynamics/SystemModelData.hpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -41,6 +42,7 @@ namespace GridKit using BusToSignalAdapterDataT = BusToSignalAdapterData; using BusFaultDataT = BusFaultData; using Tgov1DataT = Governor::Tgov1Data; + using GastPtiDataT = Governor::GastPtiData; using Ieeet1DataT = Exciter::Ieeet1Data; using SexsPtiDataT = Exciter::SexsPtiData; using IeeestDataT = Stabilizer::IeeestData; @@ -99,6 +101,7 @@ namespace GridKit std::vector load; ///< Loads within the model std::vector loadzip; ///< Loads within the model std::vector gov; ///< Governors within the model + std::vector gastpti; ///< GASTPTI governors within the model std::vector exciter; ///< Exciters within the model std::vector sexspti; ///< SEXS-PTI exciters within the model std::vector stabilizer; ///< Stabilizers within the model diff --git a/GridKit/Model/PhasorDynamics/SystemModelDataJSONParser.hpp b/GridKit/Model/PhasorDynamics/SystemModelDataJSONParser.hpp index 00a6278c2..7d50cc498 100644 --- a/GridKit/Model/PhasorDynamics/SystemModelDataJSONParser.hpp +++ b/GridKit/Model/PhasorDynamics/SystemModelDataJSONParser.hpp @@ -142,6 +142,12 @@ namespace GridKit raw_component.get_to(gov); sm.gov.push_back(gov); } + else if (kind == "GastPti") + { + typename SystemModelData::GastPtiDataT gov; + raw_component.get_to(gov); + sm.gastpti.push_back(gov); + } else if (kind == "Ieeet1") { typename SystemModelData::Ieeet1DataT exciter; diff --git a/docs/Figures/PhasorDynamics/GASTPTI_diagram.png b/docs/Figures/PhasorDynamics/GASTPTI_diagram.png new file mode 100644 index 0000000000000000000000000000000000000000..3bc194bf78efe6aab608c258516933c5cef891e1 GIT binary patch literal 95673 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 +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace GridKit +{ + namespace Testing + { + template + class GovernorGastPtiTests + { + public: + using ScalarT = scalar_type; + using IdxT = index_type; + using RealT = typename PhasorDynamics::Component::RealT; + using Gov = PhasorDynamics::Governor::GastPti; + using Data = PhasorDynamics::Governor::GastPtiData; + using Var = PhasorDynamics::Governor::GastPtiInternalVariables; + using Ext = PhasorDynamics::Governor::GastPtiExternalVariables; + using Params = PhasorDynamics::Governor::GastPtiParameters; + using Mon = PhasorDynamics::Governor::GastPtiMonitorableVariables; + + GovernorGastPtiTests() = default; + ~GovernorGastPtiTests() = default; + + TestOutcome constructor() + { + TestStatus success = true; + + Gov model(makeTestData()); + + const auto* monitor = model.getMonitor(); + success *= (model.size() == static_cast(Var::MAXIMUM)); + success *= (monitor != nullptr); + if (monitor != nullptr) + { + success *= (!monitor->empty()); + } + success *= (model.verify() == 0); + + return success.report(__func__); + } + + TestOutcome zeroInitialResidual() + { + TestStatus success = true; + + Gov model(makeTestData()); + + PhasorDynamics::SignalNode pmech_node; + PhasorDynamics::SignalNode omega_node; + const ScalarT pmech0 = scalar(kInitialPmech); + ScalarT pmech_value{0.0}; + ScalarT omega_value = scalar(kInitialOmega); + IdxT pmech_index = INVALID_INDEX; + IdxT omega_index = 9; + pmech_node.set(&pmech_value, &pmech_index); + omega_node.set(&omega_value, &omega_index); + + model.getSignals().template assignSignalNode(&pmech_node); + model.getSignals().template attachSignalNode(&omega_node); + + success *= (model.allocate() == 0); + pmech_node.init(pmech0); + success *= pmech_node.linked(); + success *= (pmech_node.getVariableIndex() == static_cast(Var::PMECH)); + + success *= (model.verify() == 0); + success *= (model.initialize() == 0); + success *= (model.tagDifferentiable() == 0); + success *= (model.evaluateResidual() == 0); + + const ScalarT xflow0 = pmech0 + scalar(kDturb) * omega_value; + const ScalarT vtemp0 = scalar(kAt) + scalar(kKt) * (scalar(kAt) - xflow0); + + success *= isEqual(model.y()[index(Var::XVALVE)], xflow0, scalar(kTolerance)); + success *= isEqual(model.y()[index(Var::XFLOW)], xflow0, scalar(kTolerance)); + success *= isEqual(model.y()[index(Var::XTEMP)], xflow0, scalar(kTolerance)); + success *= isEqual(model.y()[index(Var::VLOAD)], xflow0, scalar(kTolerance)); + success *= isEqual(model.y()[index(Var::VTEMP)], vtemp0, scalar(kTolerance)); + success *= isEqual(model.y()[index(Var::VLV)], xflow0, scalar(kTolerance)); + success *= (model.tag()[index(Var::XVALVE)] == true); + success *= (model.tag()[index(Var::XFLOW)] == true); + success *= (model.tag()[index(Var::XTEMP)] == true); + + checkZeroResidual(model, success); + + return success.report(__func__); + } + + TestOutcome baseConversion() + { + TestStatus success = true; + + auto data = makeTestData(); + data.parameters[Params::Trate] = static_cast(kConversionTrate); + Gov model(data); + model.setSystemBase(static_cast(kSystemFrequency), + static_cast(kConversionSystemBase * 1.0e6)); + + PhasorDynamics::SignalNode pmech_node; + PhasorDynamics::SignalNode omega_node; + const ScalarT pmech0 = scalar(kConversionInitialPmech); + ScalarT pmech_value{0.0}; + ScalarT omega_value = scalar(kInitialOmega); + IdxT pmech_index = INVALID_INDEX; + IdxT omega_index = 9; + pmech_node.set(&pmech_value, &pmech_index); + omega_node.set(&omega_value, &omega_index); + + model.getSignals().template assignSignalNode(&pmech_node); + model.getSignals().template attachSignalNode(&omega_node); + + success *= (model.allocate() == 0); + pmech_node.init(pmech0); + success *= (model.verify() == 0); + success *= (model.initialize() == 0); + success *= (model.evaluateResidual() == 0); + + const ScalarT pmech_component = + pmech0 * scalar(kConversionSystemBase / kConversionTrate); + const ScalarT xflow0 = pmech_component + scalar(kDturb) * omega_value; + const ScalarT vtemp0 = scalar(kAt) + scalar(kKt) * (scalar(kAt) - xflow0); + + success *= isEqual(model.y()[index(Var::XVALVE)], xflow0, scalar(kTolerance)); + success *= isEqual(model.y()[index(Var::XFLOW)], xflow0, scalar(kTolerance)); + success *= isEqual(model.y()[index(Var::XTEMP)], xflow0, scalar(kTolerance)); + success *= isEqual(model.y()[index(Var::VLOAD)], xflow0, scalar(kTolerance)); + success *= isEqual(model.y()[index(Var::VTEMP)], vtemp0, scalar(kTolerance)); + success *= isEqual(model.y()[index(Var::VLV)], xflow0, scalar(kTolerance)); + success *= isEqual(model.y()[index(Var::PMECH)], pmech0, scalar(kTolerance)); + + checkZeroResidual(model, success); + + return success.report(__func__); + } + + TestOutcome prefSignal() + { + TestStatus success = true; + + Gov model(makeTestData()); + + PhasorDynamics::SignalNode pmech_node; + PhasorDynamics::SignalNode omega_node; + PhasorDynamics::SignalNode pref_node; + const ScalarT pmech0 = scalar(kInitialPmech); + ScalarT pmech_value{0.0}; + ScalarT omega_value = scalar(kInitialOmega); + ScalarT pref_value = prefForInitialPoint(pmech0, omega_value); + IdxT pmech_index = INVALID_INDEX; + IdxT omega_index = 9; + IdxT pref_index = 10; + pmech_node.set(&pmech_value, &pmech_index); + omega_node.set(&omega_value, &omega_index); + pref_node.set(&pref_value, &pref_index); + + model.getSignals().template assignSignalNode(&pmech_node); + model.getSignals().template attachSignalNode(&omega_node); + model.getSignals().template attachSignalNode(&pref_node); + + success *= (model.allocate() == 0); + pmech_node.init(pmech0); + success *= (model.verify() == 0); + success *= (model.initialize() == 0); + success *= (model.evaluateResidual() == 0); + checkZeroResidual(model, success); + + pref_value += scalar(kPrefStep); + success *= (model.evaluateResidual() == 0); + success *= isEqual(model.getResidual()[index(Var::VLOAD)], + scalar(kR * kPrefStep), + scalar(kTolerance)); + + return success.report(__func__); + } + + TestOutcome residual() + { + TestStatus success = true; + + Gov model(makeTestData()); + + PhasorDynamics::SignalNode omega_node; + PhasorDynamics::SignalNode pref_node; + ScalarT omega_value = scalar(kResidualOmega); + ScalarT pref_value = scalar(kResidualPref); + IdxT omega_index = 7; + IdxT pref_index = 8; + omega_node.set(&omega_value, &omega_index); + pref_node.set(&pref_value, &pref_index); + + model.getSignals().template attachSignalNode(&omega_node); + model.getSignals().template attachSignalNode(&pref_node); + + success *= (model.allocate() == 0); + + const ScalarT xvalve = scalar(kResidualXvalve); + const ScalarT xflow = scalar(kResidualXflow); + const ScalarT xtemp = scalar(kResidualXtemp); + const ScalarT vload = scalar(kResidualVload); + const ScalarT vtemp = scalar(kResidualVtemp); + const ScalarT vlv = scalar(kResidualVlv); + const ScalarT pmech = scalar(kResidualPmech); + const ScalarT xvalve_dot = scalar(kResidualXvalveDot); + const ScalarT xflow_dot = scalar(kResidualXflowDot); + const ScalarT xtemp_dot = scalar(kResidualXtempDot); + + model.y()[index(Var::XVALVE)] = xvalve; + model.y()[index(Var::XFLOW)] = xflow; + model.y()[index(Var::XTEMP)] = xtemp; + model.y()[index(Var::VLOAD)] = vload; + model.y()[index(Var::VTEMP)] = vtemp; + model.y()[index(Var::VLV)] = vlv; + model.y()[index(Var::PMECH)] = pmech; + + model.yp()[index(Var::XVALVE)] = xvalve_dot; + model.yp()[index(Var::XFLOW)] = xflow_dot; + model.yp()[index(Var::XTEMP)] = xtemp_dot; + + success *= (model.verify() == 0); + success *= (model.evaluateResidual() == 0); + + const ScalarT valve_target = vlv - xvalve; + const ScalarT selected_vlv = vload; + const std::vector expected = { + -scalar(kT1) * xvalve_dot + valve_target, + -scalar(kT2) * xflow_dot - xflow + xvalve, + -scalar(kT3) * xtemp_dot - xtemp + xflow, + -omega_value + scalar(kR) * (pref_value - vload), + -vtemp + scalar(kAt) + scalar(kKt) * (scalar(kAt) - xtemp), + -vlv + selected_vlv, + -scalar(kSystemBase) * pmech + scalar(kTrate) * (xflow - scalar(kDturb) * omega_value), + }; + + checkResidual(model, expected, success); + + return success.report(__func__); + } + + TestOutcome antiWindupLimiter() + { + TestStatus success = true; + + Gov model(makeTestData()); + success *= (model.allocate() == 0); + + auto check_valve = [&](ScalarT xvalve, ScalarT vlv, ScalarT expected) + { + std::fill(model.y().begin(), model.y().end(), ScalarT{0}); + std::fill(model.yp().begin(), model.yp().end(), ScalarT{0}); + model.y()[index(Var::XVALVE)] = xvalve; + model.y()[index(Var::VLV)] = vlv; + success *= (model.evaluateResidual() == 0); + success *= isEqual(model.getResidual()[index(Var::XVALVE)], + expected, + scalar(kSmoothTolerance)); + }; + + check_valve(scalar(2.2), scalar(3.2), scalar(0.0)); + check_valve(scalar(2.2), scalar(1.2), scalar(-1.0)); + check_valve(scalar(-1.0), scalar(-2.0), scalar(0.0)); + check_valve(scalar(-1.0), scalar(0.0), scalar(1.0)); + + return success.report(__func__); + } + + TestOutcome timeConstantTags() + { + TestStatus success = true; + + auto data = makeTestData(); + data.parameters[Params::T1] = static_cast(0.0); + data.parameters[Params::T2] = static_cast(kT2); + data.parameters[Params::T3] = static_cast(0.0); + + Gov model(data); + success *= (model.allocate() == 0); + success *= (model.tagDifferentiable() == 0); + success *= (model.tag()[index(Var::XVALVE)] == false); + success *= (model.tag()[index(Var::XFLOW)] == true); + success *= (model.tag()[index(Var::XTEMP)] == false); + + return success.report(__func__); + } + + TestOutcome parameterValidation() + { + TestStatus success = true; + + auto missing = makeTestData(); + missing.parameters.erase(Params::R); + Gov missing_model(missing); + success *= (missing_model.verify() > 0); + + auto missing_trate = makeTestData(); + missing_trate.parameters.erase(Params::Trate); + Gov missing_trate_model(missing_trate); + success *= (missing_trate_model.verify() > 0); + + auto negative_time = makeTestData(); + negative_time.parameters[Params::T2] = static_cast(-kT2); + Gov negative_time_model(negative_time); + success *= (negative_time_model.verify() > 0); + + auto invalid_limits = makeTestData(); + invalid_limits.parameters[Params::Vmin] = static_cast(kVmax + 0.1); + invalid_limits.parameters[Params::Vmax] = static_cast(kVmax); + Gov invalid_limits_model(invalid_limits); + success *= (invalid_limits_model.verify() > 0); + + auto invalid_trate = makeTestData(); + invalid_trate.parameters[Params::Trate] = true; + Gov invalid_trate_model(invalid_trate); + success *= (invalid_trate_model.verify() > 0); + + auto zero_trate = makeTestData(); + zero_trate.parameters[Params::Trate] = static_cast(0.0); + Gov zero_trate_model(zero_trate); + success *= (zero_trate_model.verify() > 0); + + return success.report(__func__); + } + + TestOutcome signalValidation() + { + TestStatus success = true; + + PhasorDynamics::SignalNode omega_node; + Gov omega_model(makeTestData()); + omega_model.getSignals().template attachSignalNode(&omega_node); + success *= (omega_model.verify() > 0); + + PhasorDynamics::SignalNode pref_node; + Gov pref_model(makeTestData()); + pref_model.getSignals().template attachSignalNode(&pref_node); + success *= (pref_model.verify() > 0); + + return success.report(__func__); + } + +#ifdef GRIDKIT_ENABLE_ENZYME + TestOutcome jacobian() + { + TestStatus success = true; + + auto dependency_tracking_jacobian = dependencyTrackingJacobian(); + auto enzyme_jacobian = enzymeJacobian(); + + success *= (dependency_tracking_jacobian.size() == enzyme_jacobian.size()); + for (size_t i = 0; i < dependency_tracking_jacobian.size(); ++i) + { + success *= isEqual(dependency_tracking_jacobian[i], enzyme_jacobian[i], kTolerance); + } + + return success.report(__func__); + } +#endif + + private: + static constexpr RealT kTolerance = 100.0 * std::numeric_limits::epsilon(); + static constexpr RealT kSmoothTolerance = 1.0e-2; + + static constexpr RealT kR = 0.05; + static constexpr RealT kT1 = 0.4; + static constexpr RealT kT2 = 0.5; + static constexpr RealT kT3 = 0.25; + static constexpr RealT kAt = 2.0; + static constexpr RealT kKt = 0.3; + static constexpr RealT kVmax = 1.2; + static constexpr RealT kVmin = 0.0; + static constexpr RealT kDturb = 0.1; + static constexpr RealT kTrate = 100.0; + static constexpr RealT kSystemBase = 100.0; + + static constexpr RealT kInitialPmech = 0.75; + static constexpr RealT kInitialOmega = 0.02; + static constexpr RealT kPrefStep = 0.1; + + static constexpr RealT kSystemFrequency = 60.0; + static constexpr RealT kConversionTrate = 50.0; + static constexpr RealT kConversionSystemBase = 100.0; + static constexpr RealT kConversionInitialPmech = 0.40; + + static constexpr RealT kResidualOmega = 0.02; + static constexpr RealT kResidualPref = 1.25; + static constexpr RealT kResidualXvalve = 0.7; + static constexpr RealT kResidualXflow = 0.6; + static constexpr RealT kResidualXtemp = 0.5; + static constexpr RealT kResidualVload = 0.9; + static constexpr RealT kResidualVtemp = 2.5; + static constexpr RealT kResidualVlv = 0.8; + static constexpr RealT kResidualPmech = 0.55; + static constexpr RealT kResidualXvalveDot = 0.05; + static constexpr RealT kResidualXflowDot = -0.1; + static constexpr RealT kResidualXtempDot = 0.2; + + static ScalarT scalar(RealT value) + { + return static_cast(value); + } + + template + static value_type value(RealT value) + { + return value_type{value}; + } + + static size_t index(Var variable) + { + return static_cast(variable); + } + + template + static value_type prefForInitialPoint(const value_type& pmech, const value_type& omega) + { + return pmech + value(kDturb) * omega + omega / value(kR); + } + + void checkResidual(const Gov& model, + const std::vector& expected, + TestStatus& success) const + { + const auto& residual = model.getResidual(); + for (size_t i = 0; i < expected.size(); ++i) + { + if (!isEqual(residual[i], expected[i], scalar(kTolerance))) + { + std::cout << "Unexpected GASTPTI residual at index " << i << ": " + << residual[i] << " != " << expected[i] << "\n"; + success = false; + } + } + } + + void checkZeroResidual(const Gov& model, TestStatus& success) const + { + std::vector expected(static_cast(Var::MAXIMUM), ScalarT{0}); + checkResidual(model, expected, success); + } + + Data makeTestData() + { + Data data; + data.device_class = "GastPti"; + data.disambiguation_string = "gastpti_test"; + data.monitored_variables.insert(Mon::pmech); + data.monitored_variables.insert(Mon::fuelvalve); + + data.parameters[Params::R] = static_cast(kR); + data.parameters[Params::T1] = static_cast(kT1); + data.parameters[Params::T2] = static_cast(kT2); + data.parameters[Params::T3] = static_cast(kT3); + data.parameters[Params::At] = static_cast(kAt); + data.parameters[Params::Kt] = static_cast(kKt); + data.parameters[Params::Vmax] = static_cast(kVmax); + data.parameters[Params::Vmin] = static_cast(kVmin); + data.parameters[Params::Dturb] = static_cast(kDturb); + data.parameters[Params::Trate] = static_cast(kTrate); + + return data; + } + +#ifdef GRIDKIT_ENABLE_ENZYME + using DependencyMap = DependencyTracking::Variable::DependencyMap; + + std::vector dependencyTrackingJacobian() + { + using ADScalarT = DependencyTracking::Variable; + using ADGov = PhasorDynamics::Governor::GastPti; + + ADGov model(makeTestData()); + + PhasorDynamics::SignalNode omega_node; + PhasorDynamics::SignalNode pref_node; + ADScalarT omega_value{kResidualOmega}; + ADScalarT pref_value{kResidualPref}; + IdxT omega_index = static_cast(Var::MAXIMUM); + IdxT pref_index = omega_index + 1; + omega_node.set(&omega_value, &omega_index); + pref_node.set(&pref_value, &pref_index); + + model.getSignals().template attachSignalNode(&omega_node); + model.getSignals().template attachSignalNode(&pref_node); + model.allocate(); + model.updateTime(0.0, 1.0); + + model.y()[index(Var::XVALVE)] = ADScalarT{kResidualXvalve}; + model.y()[index(Var::XFLOW)] = ADScalarT{kResidualXflow}; + model.y()[index(Var::XTEMP)] = ADScalarT{kResidualXtemp}; + model.y()[index(Var::VLOAD)] = ADScalarT{kResidualVload}; + model.y()[index(Var::VTEMP)] = ADScalarT{kResidualVtemp}; + model.y()[index(Var::VLV)] = ADScalarT{kResidualVlv}; + model.y()[index(Var::PMECH)] = ADScalarT{kResidualPmech}; + + model.yp()[index(Var::XVALVE)] = ADScalarT{kResidualXvalveDot}; + model.yp()[index(Var::XFLOW)] = ADScalarT{kResidualXflowDot}; + model.yp()[index(Var::XTEMP)] = ADScalarT{kResidualXtempDot}; + + for (size_t i = 0; i < model.y().size(); ++i) + { + model.y()[i].setVariableNumber(i); + model.yp()[i].setVariableNumber(i); + } + omega_value.setVariableNumber(static_cast(omega_index)); + pref_value.setVariableNumber(static_cast(pref_index)); + + model.evaluateResidual(); + + std::vector dependencies(model.getResidual().size()); + for (size_t i = 0; i < dependencies.size(); ++i) + { + dependencies[i] = model.getResidual()[i].getDependencies(); + } + + return dependencies; + } + + std::vector enzymeJacobian() + { + Gov model(makeTestData()); + + PhasorDynamics::SignalNode omega_node; + PhasorDynamics::SignalNode pref_node; + ScalarT omega_value = scalar(kResidualOmega); + ScalarT pref_value = scalar(kResidualPref); + IdxT omega_index = static_cast(Var::MAXIMUM); + IdxT pref_index = omega_index + 1; + omega_node.set(&omega_value, &omega_index); + pref_node.set(&pref_value, &pref_index); + + model.getSignals().template attachSignalNode(&omega_node); + model.getSignals().template attachSignalNode(&pref_node); + model.allocate(); + model.updateTime(0.0, 1.0); + + model.y()[index(Var::XVALVE)] = scalar(kResidualXvalve); + model.y()[index(Var::XFLOW)] = scalar(kResidualXflow); + model.y()[index(Var::XTEMP)] = scalar(kResidualXtemp); + model.y()[index(Var::VLOAD)] = scalar(kResidualVload); + model.y()[index(Var::VTEMP)] = scalar(kResidualVtemp); + model.y()[index(Var::VLV)] = scalar(kResidualVlv); + model.y()[index(Var::PMECH)] = scalar(kResidualPmech); + + model.yp()[index(Var::XVALVE)] = scalar(kResidualXvalveDot); + model.yp()[index(Var::XFLOW)] = scalar(kResidualXflowDot); + model.yp()[index(Var::XTEMP)] = scalar(kResidualXtempDot); + + model.evaluateResidual(); + model.evaluateJacobian(); + + auto& model_jacobian = model.getJacobian(); + model_jacobian.deduplicate(); + + return MapFromCOO(model_jacobian); + } +#endif + }; + } // namespace Testing +} // namespace GridKit diff --git a/tests/UnitTests/PhasorDynamics/runGovernorGastPtiTests.cpp b/tests/UnitTests/PhasorDynamics/runGovernorGastPtiTests.cpp new file mode 100644 index 000000000..3932dd4a8 --- /dev/null +++ b/tests/UnitTests/PhasorDynamics/runGovernorGastPtiTests.cpp @@ -0,0 +1,23 @@ +#include "GovernorGastPtiTests.hpp" + +int main() +{ + GridKit::Testing::TestingResults result; + + GridKit::Testing::GovernorGastPtiTests test; + + result += test.constructor(); + result += test.zeroInitialResidual(); + result += test.baseConversion(); + result += test.prefSignal(); + result += test.residual(); + result += test.antiWindupLimiter(); + result += test.timeConstantTags(); + result += test.parameterValidation(); + result += test.signalValidation(); +#ifdef GRIDKIT_ENABLE_ENZYME + result += test.jacobian(); +#endif + + return result.summary(); +}