diff --git a/GridKit/Model/PhasorDynamics/Branch/Branch.hpp b/GridKit/Model/PhasorDynamics/Branch/Branch.hpp index b169cf4a7..69aefad09 100644 --- a/GridKit/Model/PhasorDynamics/Branch/Branch.hpp +++ b/GridKit/Model/PhasorDynamics/Branch/Branch.hpp @@ -10,7 +10,6 @@ #include #include -#include #include // Forward declarations. @@ -20,9 +19,6 @@ namespace GridKit { template class BusBase; - - template - struct BranchData; } // namespace PhasorDynamics } // namespace GridKit diff --git a/GridKit/Model/PhasorDynamics/Branch/BranchData.hpp b/GridKit/Model/PhasorDynamics/Branch/BranchData.hpp index 9a1c607cf..7e95f4794 100644 --- a/GridKit/Model/PhasorDynamics/Branch/BranchData.hpp +++ b/GridKit/Model/PhasorDynamics/Branch/BranchData.hpp @@ -67,21 +67,13 @@ namespace GridKit * Integer parameters are of the same type as matrix and vector indices. */ template - struct BranchData : public ComponentData - { - BranchData() = default; - - using Parameters = BranchParameters; - using Buses = BranchBuses; - using SignalInputs = BranchSignalInputs; - using SignalOutputs = BranchSignalOutputs; - using MonitorableVariables = BranchMonitorableVariables; - }; + using BranchData = + ComponentData; } // namespace PhasorDynamics } // namespace GridKit diff --git a/GridKit/Model/PhasorDynamics/BusFault/BusFault.hpp b/GridKit/Model/PhasorDynamics/BusFault/BusFault.hpp index e38074082..cb6a5a2f2 100644 --- a/GridKit/Model/PhasorDynamics/BusFault/BusFault.hpp +++ b/GridKit/Model/PhasorDynamics/BusFault/BusFault.hpp @@ -2,20 +2,10 @@ #pragma once #include +#include #include -#include #include -// Forward declaration of BusData structure -namespace GridKit -{ - namespace PhasorDynamics - { - template - struct BusFaultData; - } -} // namespace GridKit - namespace GridKit { namespace PhasorDynamics diff --git a/GridKit/Model/PhasorDynamics/BusFault/BusFaultData.hpp b/GridKit/Model/PhasorDynamics/BusFault/BusFaultData.hpp index ba74ff822..1f962ca31 100644 --- a/GridKit/Model/PhasorDynamics/BusFault/BusFaultData.hpp +++ b/GridKit/Model/PhasorDynamics/BusFault/BusFaultData.hpp @@ -57,21 +57,13 @@ namespace GridKit * Integer parameters are of the same type as matrix and vector indices. */ template - struct BusFaultData : public ComponentData - { - BusFaultData() = default; - - using Parameters = BusFaultParameters; - using Buses = BusFaultBuses; - using SignalInputs = BusFaultSignalInputs; - using SignalOutputs = BusFaultSignalOutputs; - using MonitorableVariables = BusFaultMonitorableVariables; - }; + using BusFaultData = + ComponentData; } // namespace PhasorDynamics } // namespace GridKit diff --git a/GridKit/Model/PhasorDynamics/BusToSignalAdapter/BusToSignalAdapter.hpp b/GridKit/Model/PhasorDynamics/BusToSignalAdapter/BusToSignalAdapter.hpp index 1e35bfcdd..60dcf9e2f 100644 --- a/GridKit/Model/PhasorDynamics/BusToSignalAdapter/BusToSignalAdapter.hpp +++ b/GridKit/Model/PhasorDynamics/BusToSignalAdapter/BusToSignalAdapter.hpp @@ -8,22 +8,23 @@ #pragma once #include +#include #include -#include +#include // Forward declarations namespace GridKit { namespace PhasorDynamics { - template - struct BusToSignalAdapterData; - template class BusBase; template class SignalNode; + + template + class SignalNodeSet; } // namespace PhasorDynamics } // namespace GridKit @@ -31,25 +32,15 @@ namespace GridKit { namespace PhasorDynamics { - /** - * @brief Internal variables of a `BusToSignalAdapter` - * - * @note Technically these are not owned by this component, but they must - * be classified as internal so that they can be written to signal nodes - * in `ComponentSignals` - */ + /// Internal variables of a `BusToSignalAdapter` enum class BusToSignalAdapterInternalVariables : size_t { - VREAL, - VIMAG, MAXIMUM, }; /// External variables of a `BusToSignalAdapter` enum class BusToSignalAdapterExternalVariables : size_t { - IREAL, - IIMAG, MAXIMUM, }; @@ -60,14 +51,19 @@ namespace GridKit using Component::size_; public: - using ScalarT = scalar_type; - using IdxT = index_type; - using RealT = typename Component::RealT; - using ModelDataT = BusToSignalAdapterData; - using SignalT = SignalNode; - using BusT = BusBase; + using ScalarT = scalar_type; + using IdxT = index_type; + using RealT = typename Component::RealT; + using ModelDataT = BusToSignalAdapterData; + using BusT = BusBase; + using SignalNodeT = SignalNode; + using SignalNodeSetT = SignalNodeSet; + using IOPortsT = IOPorts; BusToSignalAdapter(BusT* bus); + BusToSignalAdapter(BusT* bus, + const ModelDataT& data, + SignalNodeSetT& signal_nodes); ~BusToSignalAdapter(); int setGridKitComponentID(IdxT) override final; @@ -79,14 +75,9 @@ namespace GridKit int evaluateResidual() override final; int evaluateJacobian() override final; - /// Get the `ComponentSignals` from this `BusToSignalAdapter` - auto getSignals() - -> ComponentSignals& + IOPortsT& getPorts() { - return signals_; + return ports_; } private: @@ -94,13 +85,11 @@ namespace GridKit IdxT vr_index_{INVALID_INDEX}; IdxT vi_index_{INVALID_INDEX}; - // Signal pointers - SignalT* ir_signal_; - SignalT* ii_signal_; - BusT* bus_; + // Bus pointer + BusT* bus_; - /// Component signal extension - ComponentSignals signals_; + // Component ports + IOPortsT ports_; }; } // namespace PhasorDynamics diff --git a/GridKit/Model/PhasorDynamics/BusToSignalAdapter/BusToSignalAdapterData.hpp b/GridKit/Model/PhasorDynamics/BusToSignalAdapter/BusToSignalAdapterData.hpp index 34b83bd27..7ed246338 100644 --- a/GridKit/Model/PhasorDynamics/BusToSignalAdapter/BusToSignalAdapterData.hpp +++ b/GridKit/Model/PhasorDynamics/BusToSignalAdapter/BusToSignalAdapterData.hpp @@ -67,23 +67,14 @@ namespace GridKit * @tparam IdxT Index type (e.g., size_t) */ template - struct BusToSignalAdapterData - : public ComponentData - { - BusToSignalAdapterData() = default; - - using Parameters = BusToSignalAdapterParameters; - using Buses = BusToSignalAdapterBuses; - using SignalInputs = BusToSignalAdapterSignalInputs; - using SignalOutputs = BusToSignalAdapterSignalOutputs; - using MonitorableVariables = BusToSignalAdapterMonitorableVariables; - }; + using BusToSignalAdapterData = + ComponentData; } // namespace PhasorDynamics } // namespace GridKit diff --git a/GridKit/Model/PhasorDynamics/BusToSignalAdapter/BusToSignalAdapterImpl.hpp b/GridKit/Model/PhasorDynamics/BusToSignalAdapter/BusToSignalAdapterImpl.hpp index 4ae676ccc..4c622eb3f 100644 --- a/GridKit/Model/PhasorDynamics/BusToSignalAdapter/BusToSignalAdapterImpl.hpp +++ b/GridKit/Model/PhasorDynamics/BusToSignalAdapter/BusToSignalAdapterImpl.hpp @@ -9,7 +9,9 @@ #include #include #include +#include #include +#include #include namespace GridKit @@ -31,6 +33,25 @@ namespace GridKit size_ = 0; } + /** + * @brief Constructor for BusToSignalAdapter component + * + * @param bus Signal used for voltage + * @param data Data object + * @param signal_nodes SignalNodeSet instance for accessing system signal + * nodes + */ + template + BusToSignalAdapter::BusToSignalAdapter( + BusT* bus, + const ModelDataT& data, + SignalNodeSetT& signal_nodes) + : bus_(bus), + ports_(data, signal_nodes) + { + size_ = 0; + } + template BusToSignalAdapter::~BusToSignalAdapter() { @@ -52,19 +73,18 @@ namespace GridKit template int BusToSignalAdapter::allocate() { - static constexpr auto VREAL = BusToSignalAdapterInternalVariables::VREAL; - static constexpr auto VIMAG = BusToSignalAdapterInternalVariables::VIMAG; + using SignalOut = BusToSignalAdapterSignalOutputs; // vr_index_ and vi_index_ are both set to INVALID_INDEX. This component // simply passes voltage from bus to output signal, so indices are // ignored. - if (signals_.template isAssigned()) + if (auto vr_port = ports_.out[SignalOut::vr]) { - signals_.template getSignalNode()->set(&bus_->Vr(), &vr_index_); + vr_port.link(&bus_->Vr(), &vr_index_); } - if (signals_.template isAssigned()) + if (auto vi_port = ports_.out[SignalOut::vi]) { - signals_.template getSignalNode()->set(&bus_->Vi(), &vi_index_); + vi_port.link(&bus_->Vi(), &vi_index_); } return 0; @@ -76,27 +96,22 @@ namespace GridKit template int BusToSignalAdapter::verify() const { - static constexpr auto IREAL = BusToSignalAdapterExternalVariables::IREAL; - static constexpr auto IIMAG = BusToSignalAdapterExternalVariables::IIMAG; + using SignalIn = BusToSignalAdapterSignalInputs; int ret = 0; - if (signals_.template isAttached()) + auto ir_port = ports_.in[SignalIn::ir]; + if (ir_port.connected() && !ir_port.linked()) { - if (!signals_.template isLinked()) - { - Log::error() << "BusToSignalAdapter: Ir signal attached with no linked source\n"; - ret += 1; - } + Log::error() << "BusToSignalAdapter: Ir signal attached with no linked source\n"; + ret += 1; } - if (signals_.template isAttached()) + auto ii_port = ports_.in[SignalIn::ir]; + if (ii_port.connected() && !ii_port.linked()) { - if (!signals_.template isLinked()) - { - Log::error() << "BusToSignalAdapter: Ii signal attached with no linked source\n"; - ret += 1; - } + Log::error() << "BusToSignalAdapter: Ii signal attached with no linked source\n"; + ret += 1; } return ret; @@ -144,16 +159,15 @@ namespace GridKit template int BusToSignalAdapter::evaluateResidual() { - static constexpr auto IREAL = BusToSignalAdapterExternalVariables::IREAL; - static constexpr auto IIMAG = BusToSignalAdapterExternalVariables::IIMAG; + using SignalIn = BusToSignalAdapterSignalInputs; - if (signals_.template isAttached()) + if (auto ir_port = ports_.in[SignalIn::ir]) { - bus_->Ir() += signals_.template readExternalVariable(); + bus_->Ir() += ir_port.readSignal(); } - if (signals_.template isAttached()) + if (auto ii_port = ports_.in[SignalIn::ii]) { - bus_->Ii() += signals_.template readExternalVariable(); + bus_->Ii() += ii_port.readSignal(); } return 0; diff --git a/GridKit/Model/PhasorDynamics/CMakeLists.txt b/GridKit/Model/PhasorDynamics/CMakeLists.txt index baf76ab90..dc10defd0 100644 --- a/GridKit/Model/PhasorDynamics/CMakeLists.txt +++ b/GridKit/Model/PhasorDynamics/CMakeLists.txt @@ -13,7 +13,7 @@ gridkit_add_library( Component.hpp ComponentData.hpp ComponentLibrary.hpp - ComponentSignals.hpp + IOPorts.hpp LINK_LIBRARIES INTERFACE GridKit::definitions diff --git a/GridKit/Model/PhasorDynamics/ComponentData.hpp b/GridKit/Model/PhasorDynamics/ComponentData.hpp index bbe345230..5d0b7583c 100644 --- a/GridKit/Model/PhasorDynamics/ComponentData.hpp +++ b/GridKit/Model/PhasorDynamics/ComponentData.hpp @@ -19,16 +19,16 @@ namespace GridKit */ template - requires std::is_enum_v - && std::is_enum_v - && std::is_enum_v - && std::is_enum_v - && std::is_enum_v + typename parameters_enum, + typename buses_enum, + typename signal_inputs_enum, + typename signal_outputs_enum, + typename monitorable_variables_enum> + requires std::is_enum_v + && std::is_enum_v + && std::is_enum_v + && std::is_enum_v + && std::is_enum_v struct ComponentData { /// Real value type @@ -36,6 +36,12 @@ namespace GridKit /// Index type using IdxT = index_type; + using Parameters = parameters_enum; + using Buses = buses_enum; + using SignalInputs = signal_inputs_enum; + using SignalOutputs = signal_outputs_enum; + using MonitorableVariables = monitorable_variables_enum; + /// Class of device this is for std::string device_class; @@ -54,10 +60,8 @@ namespace GridKit /// Set of variables being monitored std::set monitored_variables; - std::string disambiguation_string; ///< Disambiguation string for this device - - protected: - ComponentData() = default; + /// Disambiguation string for this device + std::string disambiguation_string; }; } // namespace PhasorDynamics } // namespace GridKit diff --git a/GridKit/Model/PhasorDynamics/ComponentSignals.hpp b/GridKit/Model/PhasorDynamics/ComponentSignals.hpp deleted file mode 100644 index 827181346..000000000 --- a/GridKit/Model/PhasorDynamics/ComponentSignals.hpp +++ /dev/null @@ -1,220 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -#include - -namespace GridKit -{ - namespace PhasorDynamics - { - /// Dummy `Variables` type for components with no variables - enum class NoVariables : size_t - { - MAXIMUM - }; - - /// Concept requiring an enum to have a `MAXIMUM` variant and that it have - /// the underlying type of `size_t`. This does not ensure the variant is - /// the actual maximum - template - concept EnumHasMaximumValueAndIsSizeT = std::is_enum_v - && std::is_same_v, size_t> - && requires { T::MAXIMUM; }; - - /// Extension object for `Component`s adding methods and member variables - /// related to signal bus management - /// - /// This is used by adding an instance in a field to your class and - /// exposing this field to others - /// - /// @tparam scalar_type Scalar value type - /// @tparam index_type Index type - /// @tparam InternalVariables An enumeration satisfying - /// `EnumHasMaximumValueAndIsSizeT` enumerating internal variables - /// for the component - /// @tparam ExternalVariables An enumeration satisfying - /// `EnumHasMaximumValueAndIsSizeT` enumerating external variables - /// for the component - /// @invariant InternalVariables::MAXIMUM is the greatest attainable - /// integer value of the enum - /// @invariant ExternalVariables::MAXIMUM is the greatest attainable - /// integer value of the enum - template - requires EnumHasMaximumValueAndIsSizeT - && EnumHasMaximumValueAndIsSizeT - class ComponentSignals - { - public: - /// Scalar value type - using ScalarT = scalar_type; - /// Index type - using IdxT = index_type; - - /// Attaches a signal node to an external variable on this component - /// - /// @tparam variable The external variable to attach the provided - /// signal to - /// @param[in] node The signal node to attach - /// @pre The provided pointer to a signal node is not `nullptr` - /// @post The provided signal node is attached to the indicated - /// external variable - template - auto attachSignalNode(SignalNode* node) - { -#ifndef NDEBUG - if (node == nullptr) - { - throw std::logic_error("A null pointer to a signal node has been passed to attachSignalNode"); - } -#endif - - static_assert(variable < ExternalVariables::MAXIMUM); - external_variable_signals_[static_cast(variable)] = node; - } - - /// Check if a signal node has been attached to an external variable - /// - /// @tparam variable The external variable to check - template - auto isAttached() const -> bool - { - static_assert(variable < ExternalVariables::MAXIMUM); - return static_cast(external_variable_signals_[static_cast(variable)]); - } - - /// Check if a signal node has been assigned to an internal variable - /// - /// @tparam variable The internal variable to check - template - auto isAssigned() const -> bool - { - static_assert(variable < InternalVariables::MAXIMUM); - return static_cast(internal_variable_signals_[static_cast(variable)]); - } - - /// Check if a signal node has been "set" - /// - /// @tparam variable The external variable to check - template - auto isLinked() const -> bool - { - static_assert(variable < ExternalVariables::MAXIMUM); - return external_variable_signals_[static_cast(variable)].value()->linked(); - } - - /// Returns a signal node for an internal signal variable to be - /// attached to an external variable on another component - /// - /// @tparam variable The internal variable to get the assigned - /// signal node of - /// @pre A signal node has been assigned to the requested internal - /// variable - template - auto getSignalNode() -> SignalNode* - { - static_assert(variable < InternalVariables::MAXIMUM); - if (!internal_variable_signals_[static_cast(variable)]) - { - throw std::logic_error("A signal node has not been assigned to this internal variable"); - } - - return *internal_variable_signals_[static_cast(variable)]; - } - - /// Returns the value of the specified external variable - /// - /// @tparam variable The external variable to read from - /// @pre A signal node has been assigned to the requested external - /// variable - template - auto readExternalVariable() const -> ScalarT - { - static_assert(variable < ExternalVariables::MAXIMUM); - if (!external_variable_signals_[static_cast(variable)]) - { - throw std::logic_error("A signal node has not been assigned to this external variable"); - } - - return (*external_variable_signals_[static_cast(variable)])->read(); - } - - /// Returns the global index of the specified external variable - /// - /// @tparam variable The external variable to read from - /// @pre A signal node has been assigned to the requested external - /// variable - template - auto readExternalVariableIndex() const -> IdxT - { - static_assert(variable < ExternalVariables::MAXIMUM); - if (!external_variable_signals_[static_cast(variable)]) - { - throw std::logic_error("A signal node has not been assigned to this external variable"); - } - - return (*external_variable_signals_[static_cast(variable)])->getVariableIndex(); - } - - /// Writes a value to the specified external variable - /// - /// @warning This method should be used only in component initialization - /// methods. Use only if you know what you are doing. - /// - /// @tparam variable The external variable to write to - /// @param[in] value The value to write to the signal node - /// @pre A signal node has been assigned to the requested external - /// variable - /// @post The signal node of the corresponding external variable has - /// the given value written to it - template - auto writeExternalVariable(ScalarT value) - { - static_assert(variable < ExternalVariables::MAXIMUM); - if (!external_variable_signals_[static_cast(variable)]) - { - throw std::logic_error("A signal node has not been assigned to this external variable"); - } - - (*external_variable_signals_[static_cast(variable)])->init(value); - } - - /// Assigns a signal node to an internal variable on this component - /// - /// @tparam variable The internal variable to assign the signal node to - /// @param[in] node The signal node to assign - /// @pre The provided pointer to a signal node is not `nullptr` - /// @post The provided signal node is assigned to the indicated - /// internal variable - template - auto assignSignalNode(SignalNode* node) - { -#ifndef NDEBUG - if (node == nullptr) - { - throw std::logic_error("A null pointer to a signal node has been passed to assignSignalNode"); - } -#endif - - static_assert(variable < InternalVariables::MAXIMUM); - internal_variable_signals_[static_cast(variable)] = node; - } - - private: - /// Internal variables which may have a signal associated with them for - /// use elsewhere - std::array*>, - static_cast(InternalVariables::MAXIMUM)> - internal_variable_signals_{}; - - /// External variables which may have a signal associated with them for - /// use internally - std::array*>, - static_cast(ExternalVariables::MAXIMUM)> - external_variable_signals_{}; - }; - } // namespace PhasorDynamics -} // namespace GridKit diff --git a/GridKit/Model/PhasorDynamics/Exciter/IEEET1/Ieeet1.hpp b/GridKit/Model/PhasorDynamics/Exciter/IEEET1/Ieeet1.hpp index 3df983123..311d32f12 100644 --- a/GridKit/Model/PhasorDynamics/Exciter/IEEET1/Ieeet1.hpp +++ b/GridKit/Model/PhasorDynamics/Exciter/IEEET1/Ieeet1.hpp @@ -10,7 +10,8 @@ #pragma once #include -#include +#include +#include #include // Forward declarations @@ -18,18 +19,14 @@ namespace GridKit { namespace PhasorDynamics { - namespace Exciter - { - template - struct Ieeet1Data; - } // namespace Exciter - template class BusBase; template class SignalNode; + template + class SignalNodeSet; } // namespace PhasorDynamics } // namespace GridKit @@ -85,17 +82,19 @@ namespace GridKit using Component::residual_indices_; public: - using ScalarT = scalar_type; - using IdxT = index_type; - using RealT = typename Component::RealT; - using BusT = BusBase; - using ModelDataT = Ieeet1Data; - using SignalT = SignalNode; - using MonitorT = Model::VariableMonitor; + using ScalarT = scalar_type; + using IdxT = index_type; + using RealT = typename Component::RealT; + using BusT = BusBase; + using ModelDataT = Ieeet1Data; + using SignalNodeT = SignalNode; + using SignalNodeSetT = SignalNodeSet; + using IOPortsT = IOPorts; + using MonitorT = Model::VariableMonitor; Ieeet1(BusT* bus); - Ieeet1(BusT* bus, - const ModelDataT& data); + Ieeet1(BusT* bus, const ModelDataT& data); + Ieeet1(BusT* bus, const ModelDataT& data, SignalNodeSetT& signal_nodes); ~Ieeet1(); int setGridKitComponentID(IdxT) override final; @@ -107,16 +106,6 @@ namespace GridKit int evaluateResidual() override final; int evaluateJacobian() override final; - /// Get the `ComponentSignals` from this `Ieeet1` - auto getSignals() - -> ComponentSignals& - { - return signals_; - } - const Model::VariableMonitorBase* getMonitor() const override; __attribute__((always_inline)) inline int evaluateInternalResidual( @@ -155,8 +144,8 @@ namespace GridKit ScalarT vUEL_{0}; ScalarT vOEL_{0}; - /// Component signal extension - ComponentSignals signals_; + /// Component ports + IOPortsT ports_; /// Variable monitor std::unique_ptr monitor_; diff --git a/GridKit/Model/PhasorDynamics/Exciter/IEEET1/Ieeet1Data.hpp b/GridKit/Model/PhasorDynamics/Exciter/IEEET1/Ieeet1Data.hpp index d9e1f783d..c82907de2 100644 --- a/GridKit/Model/PhasorDynamics/Exciter/IEEET1/Ieeet1Data.hpp +++ b/GridKit/Model/PhasorDynamics/Exciter/IEEET1/Ieeet1Data.hpp @@ -72,22 +72,14 @@ namespace GridKit * Integer parameters are of the same type as matrix and vector indices. */ template - struct Ieeet1Data : public ComponentData - { - Ieeet1Data() = default; - - using Parameters = Ieeet1Parameters; - using Buses = Ieeet1Buses; - using SignalInputs = Ieeet1SignalInputs; - using SignalOutputs = Ieeet1SignalOutputs; - using MonitorableVariables = Ieeet1MonitorableVariables; - }; + using Ieeet1Data = + ComponentData; } // namespace Exciter } // namespace PhasorDynamics } // namespace GridKit diff --git a/GridKit/Model/PhasorDynamics/Exciter/IEEET1/Ieeet1Impl.hpp b/GridKit/Model/PhasorDynamics/Exciter/IEEET1/Ieeet1Impl.hpp index a06c788e9..e72b731b7 100644 --- a/GridKit/Model/PhasorDynamics/Exciter/IEEET1/Ieeet1Impl.hpp +++ b/GridKit/Model/PhasorDynamics/Exciter/IEEET1/Ieeet1Impl.hpp @@ -13,6 +13,8 @@ #include #include #include +#include +#include #include #include @@ -41,12 +43,35 @@ namespace GridKit * @param data Data object to store parameters */ template - Ieeet1::Ieeet1(BusT* bus, - const ModelDataT& data) + Ieeet1::Ieeet1(BusT* bus, const ModelDataT& data) : bus_(bus), monitor_(std::make_unique(data)) { + // Parse data struct into model + this->initModelParams(data); + initializeMonitor(); + + // 9 Internal Variables + size_ = 9; + } + + /** + * @brief Constructor for IEEET1 Exciter + * + * @param bus Signal used for terminal reference vmag + * @param data Data object to store parameters + * @param signal_nodes SignalNodeSet instance for accessing signal nodes + */ + template + Ieeet1::Ieeet1( + BusT* bus, + const ModelDataT& data, + SignalNodeSetT& signal_nodes) + : bus_(bus), + ports_(data, signal_nodes), + monitor_(std::make_unique(data)) + { // Parse data struct into model this->initModelParams(data); @@ -107,9 +132,9 @@ namespace GridKit } // Set output signals - if (signals_.template isAssigned()) + if (auto efd_port = ports_.out[Ieeet1SignalOutputs::efd]) { - signals_.template getSignalNode()->set(&y_[7], &(this->getVariableIndex(7))); + efd_port.link(&y_[7], &(this->getVariableIndex(7))); } return 0; @@ -121,27 +146,20 @@ namespace GridKit template int Ieeet1::verify() const { - static constexpr auto OMEGA = Ieeet1ExternalVariables::OMEGA; - static constexpr auto VS = Ieeet1ExternalVariables::VS; - int ret = 0; - if (signals_.template isAttached()) + auto speed_port = ports_.in[Ieeet1SignalInputs::speed]; + if (speed_port.connected() && !speed_port.linked()) { - if (!signals_.template isLinked()) - { - Log::error() << "Ieeet1: omega signal attached with no linked generator\n"; - ret += 1; - } + Log::error() << "Ieeet1: omega signal attached with no linked generator\n"; + ret += 1; } - if (signals_.template isAttached()) + auto vs_port = ports_.in[Ieeet1SignalInputs::vs]; + if (vs_port.connected() && !vs_port.linked()) { - if (!signals_.template isLinked()) - { - Log::error() << "Ieeet1: VS signal attached with no linked source\n"; - ret += 1; - } + Log::error() << "Ieeet1: VS signal attached with no linked source\n"; + ret += 1; } return ret; @@ -173,20 +191,20 @@ namespace GridKit // TODO: Build protections in system initialization call to // ensure Efd is initialized externally before the exciter initializes // other variables. - if (signals_.template isAssigned()) + if (ports_.out[Ieeet1SignalOutputs::efd]) { efd0 = y_[7]; ///<- generator needs to be initialized first } ScalarT omega{0}; ScalarT vs{0}; - if (signals_.template isAttached()) + if (auto speed_port = ports_.in[Ieeet1SignalInputs::speed]) { - omega = signals_.template readExternalVariable(); + omega = speed_port.readSignal(); } - if (signals_.template isAttached()) + if (auto vs_port = ports_.in[Ieeet1SignalInputs::vs]) { - vs = signals_.template readExternalVariable(); + vs = vs_port.readSignal(); } // Terminal Voltage @@ -327,19 +345,19 @@ namespace GridKit int Ieeet1::evaluateResidual() { // Set input variables. - if (signals_.template isAttached()) + if (auto speed_port = ports_.in[Ieeet1SignalInputs::speed]) { - ws_[0] = signals_.template readExternalVariable(); - ws_indices_[0] = signals_.template readExternalVariableIndex(); + ws_[0] = speed_port.readSignal(); + ws_indices_[0] = speed_port.signalVariableIndex(); } // VS signal (stabilizer output, optional) ws_[1] = 0.0; ws_indices_[1] = INVALID_INDEX; - if (signals_.template isAttached()) + if (auto vs_port = ports_.in[Ieeet1SignalInputs::vs]) { - ws_[1] = signals_.template readExternalVariable(); - ws_indices_[1] = signals_.template readExternalVariableIndex(); + ws_[1] = vs_port.readSignal(); + ws_indices_[1] = vs_port.signalVariableIndex(); } // Bus voltages diff --git a/GridKit/Model/PhasorDynamics/Exciter/SEXS-PTI/SexsPti.hpp b/GridKit/Model/PhasorDynamics/Exciter/SEXS-PTI/SexsPti.hpp index 33ae3c400..6b44ddf45 100644 --- a/GridKit/Model/PhasorDynamics/Exciter/SEXS-PTI/SexsPti.hpp +++ b/GridKit/Model/PhasorDynamics/Exciter/SEXS-PTI/SexsPti.hpp @@ -7,25 +7,22 @@ #pragma once #include -#include +#include +#include #include namespace GridKit { namespace PhasorDynamics { - namespace Exciter - { - template - struct SexsPtiData; - } // namespace Exciter - template class BusBase; template class SignalNode; + template + class SignalNodeSet; } // namespace PhasorDynamics } // namespace GridKit @@ -72,16 +69,19 @@ namespace GridKit using Component::residual_indices_; public: - using ScalarT = scalar_type; - using IdxT = index_type; - using RealT = typename Component::RealT; - using BusT = BusBase; - using ModelDataT = SexsPtiData; - using SignalT = SignalNode; - using MonitorT = Model::VariableMonitor; + using ScalarT = scalar_type; + using IdxT = index_type; + using RealT = typename Component::RealT; + using BusT = BusBase; + using ModelDataT = SexsPtiData; + using SignalNodeT = SignalNode; + using SignalNodeSetT = SignalNodeSet; + using IOPortsT = IOPorts; + using MonitorT = Model::VariableMonitor; SexsPti(BusT* bus); SexsPti(BusT* bus, const ModelDataT& data); + SexsPti(BusT* bus, const ModelDataT& data, SignalNodeSetT& signal_nodes); ~SexsPti(); int setGridKitComponentID(IdxT) override final; @@ -93,13 +93,9 @@ namespace GridKit int evaluateResidual() override final; int evaluateJacobian() override final; - auto getSignals() - -> ComponentSignals& + IOPortsT& getPorts() { - return signals_; + return ports_; } const Model::VariableMonitorBase* getMonitor() const override; @@ -123,7 +119,7 @@ namespace GridKit ScalarT vOEL_{0}; ScalarT vUEL_{0}; - ComponentSignals signals_; + IOPortsT ports_; std::unique_ptr monitor_; diff --git a/GridKit/Model/PhasorDynamics/Exciter/SEXS-PTI/SexsPtiData.hpp b/GridKit/Model/PhasorDynamics/Exciter/SEXS-PTI/SexsPtiData.hpp index db70477a5..eca3601bd 100644 --- a/GridKit/Model/PhasorDynamics/Exciter/SEXS-PTI/SexsPtiData.hpp +++ b/GridKit/Model/PhasorDynamics/Exciter/SEXS-PTI/SexsPtiData.hpp @@ -53,22 +53,14 @@ namespace GridKit }; template - struct SexsPtiData : public ComponentData - { - SexsPtiData() = default; - - using Parameters = SexsPtiParameters; - using Buses = SexsPtiBuses; - using SignalInputs = SexsPtiSignalInputs; - using SignalOutputs = SexsPtiSignalOutputs; - using MonitorableVariables = SexsPtiMonitorableVariables; - }; + using SexsPtiData = + ComponentData; } // namespace Exciter } // namespace PhasorDynamics diff --git a/GridKit/Model/PhasorDynamics/Exciter/SEXS-PTI/SexsPtiImpl.hpp b/GridKit/Model/PhasorDynamics/Exciter/SEXS-PTI/SexsPtiImpl.hpp index 273756447..0abe9631d 100644 --- a/GridKit/Model/PhasorDynamics/Exciter/SEXS-PTI/SexsPtiImpl.hpp +++ b/GridKit/Model/PhasorDynamics/Exciter/SEXS-PTI/SexsPtiImpl.hpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -32,8 +33,7 @@ namespace GridKit } template - SexsPti::SexsPti(BusT* bus, - const ModelDataT& data) + SexsPti::SexsPti(BusT* bus, const ModelDataT& data) : bus_(bus), monitor_(std::make_unique(data)) { @@ -42,6 +42,20 @@ namespace GridKit size_ = 3; } + template + SexsPti::SexsPti( + BusT* bus, + const ModelDataT& data, + SignalNodeSetT& signal_nodes) + : bus_(bus), + ports_(data, signal_nodes), + monitor_(std::make_unique(data)) + { + initModelParams(data); + initializeMonitor(); + size_ = 3; + } + template SexsPti::~SexsPti() { @@ -79,10 +93,9 @@ namespace GridKit this->setResidualIndex(j, j); } - if (signals_.template isAssigned()) + if (auto efd_port = ports_.out[SexsPtiSignalOutputs::efd]) { - signals_.template getSignalNode()->set( - &y_[1], &(this->getVariableIndex(1))); + efd_port.link(&y_[1], &(this->getVariableIndex(1))); } return 0; @@ -124,19 +137,18 @@ namespace GridKit ret += 1; } - if (!signals_.template isAssigned()) + auto efd_port = ports_.out[SexsPtiSignalOutputs::efd]; + if (!efd_port.connected() || !efd_port.linked()) { Log::error() << "SexsPti: required EFD signal is not assigned\n"; ret += 1; } - if (signals_.template isAttached()) + auto vs_port = ports_.in[SexsPtiSignalInputs::vs]; + if (vs_port.connected() && !vs_port.linked()) { - if (!signals_.template isLinked()) - { - Log::error() << "SexsPti: VS signal attached with no linked source\n"; - ret += 1; - } + Log::error() << "SexsPti: VS signal attached with no linked source\n"; + ret += 1; } return ret; @@ -146,7 +158,7 @@ namespace GridKit int SexsPti::initialize() { ScalarT efd0{0.0}; - if (signals_.template isAssigned()) + if (ports_.out[SexsPtiSignalOutputs::efd]) { efd0 = y_[1]; } @@ -231,10 +243,10 @@ namespace GridKit { ws_[0] = 0.0; ws_indices_[0] = INVALID_INDEX; - if (signals_.template isAttached()) + if (auto vs_port = ports_.in[SexsPtiSignalInputs::vs]) { - ws_[0] = signals_.template readExternalVariable(); - ws_indices_[0] = signals_.template readExternalVariableIndex(); + ws_[0] = vs_port.readSignal(); + ws_indices_[0] = vs_port.signalVariableIndex(); } wb_[0] = bus_->Vr(); diff --git a/GridKit/Model/PhasorDynamics/Governor/Tgov1/Tgov1.hpp b/GridKit/Model/PhasorDynamics/Governor/Tgov1/Tgov1.hpp index 31cc271ea..cbead60b3 100644 --- a/GridKit/Model/PhasorDynamics/Governor/Tgov1/Tgov1.hpp +++ b/GridKit/Model/PhasorDynamics/Governor/Tgov1/Tgov1.hpp @@ -10,25 +10,22 @@ #pragma once #include -#include +#include +#include // Forward declarations namespace GridKit { namespace PhasorDynamics { - namespace Governor - { - template - struct Tgov1Data; - } // namespace Governor - template class Genrou; template class SignalNode; + template + class SignalNodeSet; } // namespace PhasorDynamics } // namespace GridKit @@ -78,15 +75,17 @@ namespace GridKit using Component::residual_indices_; public: - using ScalarT = scalar_type; - using IdxT = index_type; - using RealT = typename Component::RealT; - using ModelDataT = Tgov1Data; - using SignalT = SignalNode; + using ScalarT = scalar_type; + using IdxT = index_type; + using RealT = typename Component::RealT; + using ModelDataT = Tgov1Data; + using SignalNodeT = SignalNode; + using SignalNodeSetT = SignalNodeSet; + using IOPortsT = IOPorts; Tgov1(); - Tgov1(SignalT*, SignalT*); - Tgov1(const ModelDataT&); + Tgov1(SignalNodeT*, SignalNodeT*); + Tgov1(const ModelDataT&, SignalNodeSetT&); ~Tgov1() = default; int setGridKitComponentID(IdxT) override final; @@ -100,16 +99,6 @@ namespace GridKit // Still to be implemented int evaluateJacobian() override final; - /// Get the `ComponentSignals` from this `Tgov1` - auto getSignals() - -> ComponentSignals& - { - return signals_; - } - public: __attribute__((always_inline)) inline int evaluateInternalResidual( const ScalarT*, const ScalarT*, const ScalarT*, const ScalarT*, ScalarT*); @@ -131,8 +120,8 @@ namespace GridKit // Input States (which can be parameters) ScalarT pref_{0}; - /// Component signal extension - ComponentSignals signals_; + // Component ports + IOPortsT ports_; // Parameter initialization function void initializeParameters(const ModelDataT& data); diff --git a/GridKit/Model/PhasorDynamics/Governor/Tgov1/Tgov1Data.hpp b/GridKit/Model/PhasorDynamics/Governor/Tgov1/Tgov1Data.hpp index f667443f5..fdcd167c1 100644 --- a/GridKit/Model/PhasorDynamics/Governor/Tgov1/Tgov1Data.hpp +++ b/GridKit/Model/PhasorDynamics/Governor/Tgov1/Tgov1Data.hpp @@ -79,22 +79,14 @@ namespace GridKit * @tparam index_type Index type (e.g., size_t) */ template - struct Tgov1Data : public ComponentData - { - Tgov1Data() = default; - - using Parameters = Tgov1Parameters; - using Buses = Tgov1Buses; - using SignalInputs = Tgov1SignalInputs; - using SignalOutputs = Tgov1SignalOutputs; - using MonitorableVariables = Tgov1MonitorableVariables; - }; + using Tgov1Data = + ComponentData; } // namespace Governor } // namespace PhasorDynamics diff --git a/GridKit/Model/PhasorDynamics/Governor/Tgov1/Tgov1Impl.hpp b/GridKit/Model/PhasorDynamics/Governor/Tgov1/Tgov1Impl.hpp index 7069f9ef2..55154cd92 100644 --- a/GridKit/Model/PhasorDynamics/Governor/Tgov1/Tgov1Impl.hpp +++ b/GridKit/Model/PhasorDynamics/Governor/Tgov1/Tgov1Impl.hpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #define _USE_MATH_DEFINES @@ -45,7 +46,7 @@ namespace GridKit * @param omega $\Delta_\omega$ external variable signal node */ template - Tgov1::Tgov1(SignalT* pmech, SignalT* omega) + Tgov1::Tgov1(SignalNodeT* pmech, SignalNodeT* omega) : Trate_(100.0), R_(0.05), Pvmin_(0), @@ -55,8 +56,8 @@ namespace GridKit T3_(7.5), Dt_(0) { - signals_.template assignSignalNode(pmech); - signals_.template attachSignalNode(omega); + ports_.out[Tgov1SignalOutputs::pmech].connect(pmech); + ports_.in[Tgov1SignalInputs::speed].connect(omega); // 3 internal variables size_ = 3; @@ -69,9 +70,12 @@ namespace GridKit * @param data Data to initialize the model from. */ template - Tgov1::Tgov1(const ModelDataT& data) + Tgov1::Tgov1( + const ModelDataT& data, SignalNodeSetT& signal_nodes) + : ports_(data, signal_nodes) { initializeParameters(data); + size_ = 3; setDerivedParams(); } @@ -190,9 +194,9 @@ namespace GridKit } // Set output signals - if (signals_.template isAssigned()) + if (auto pmech_port = ports_.out[Tgov1SignalOutputs::pmech]) { - signals_.template getSignalNode()->set(&y_[2], &(this->getVariableIndex(2))); + pmech_port.link(&y_[2], &(this->getVariableIndex(2))); } return 0; @@ -204,17 +208,13 @@ namespace GridKit template int Tgov1::verify() const { - static constexpr auto DELTAOMEGA = Tgov1ExternalVariables::DELTAOMEGA; - int ret = 0; - if (signals_.template isAttached()) + auto speed_port = ports_.in[Tgov1SignalInputs::speed]; + if (speed_port.connected() && !speed_port.linked()) { - if (!signals_.template isLinked()) - { - Log::error() << "Tgov1: deltaomega signal attached with no linked generator\n"; - ret += 1; - } + Log::error() << "Tgov1: deltaomega signal attached with no linked generator\n"; + ret += 1; } return ret; @@ -230,7 +230,7 @@ namespace GridKit ScalarT p0{0}; // Initial mechanical = initial electric torque - if (signals_.template isAssigned()) + if (ports_.out[Tgov1SignalOutputs::pmech]) { // System base -> governor base for governor initialization. p0 = toComponentBase(y_[2]); ///<- generator needs to be initialized first @@ -258,7 +258,6 @@ namespace GridKit template int Tgov1::tagDifferentiable() { - tag_[0] = true; // Pv tag_[1] = true; // Ptx tag_[2] = false; // Pmech @@ -331,10 +330,10 @@ namespace GridKit int Tgov1::evaluateResidual() { // Input Variables - if (signals_.template isAttached()) + if (auto speed_port = ports_.in[Tgov1SignalInputs::speed]) { - ws_[0] = signals_.template readExternalVariable(); - ws_indices_[0] = signals_.template readExternalVariableIndex(); + ws_[0] = speed_port.readSignal(); + ws_indices_[0] = speed_port.signalVariableIndex(); } evaluateInternalResidual(y_.data(), yp_.data(), wb_.data(), ws_.data(), f_.data()); diff --git a/GridKit/Model/PhasorDynamics/IOPorts.hpp b/GridKit/Model/PhasorDynamics/IOPorts.hpp new file mode 100644 index 000000000..a75c2df4f --- /dev/null +++ b/GridKit/Model/PhasorDynamics/IOPorts.hpp @@ -0,0 +1,281 @@ +#pragma once + +#include + +#include +#include +#include + +namespace GridKit +{ + namespace PhasorDynamics + { + /** + * @brief Base class representing a port (connection to a SignalNode) + * + * An input or output port may be connected to one SignalNode + */ + template + class Port + { + public: + /// Scalar type used for signals + using ScalarT = scalar_type; + /// Index type + using IdxT = index_type; + /// Instantiation of signal node type + using SignalNodeT = SignalNode; + + /** + * @brief Connect a signal node to the port + * + * @param node SignalNode to be connected + */ + void connect(SignalNodeT* node) noexcept + { + assert(node != nullptr); + signal_node_ = node; + } + + /** + * @brief Check if the port has a signal node connected + */ + operator bool() const noexcept + { + return connected(); + } + + /** + * @brief Check if the port has a signal node connected + */ + bool connected() const noexcept + { + return (signal_node_ != nullptr); + } + + /** + * @brief Check that the connected signal node is linked with a source + * signal + * + * @pre Port must be connected() to a signal node + */ + bool linked() const noexcept + { + assert(connected()); + return signal_node_->linked(); + } + + protected: + virtual void assign([[maybe_unused]] SignalNodeT*) const + { + } + + SignalNodeT* signal_node_{nullptr}; + }; + + /** + * @brief Port for sending a signal, making it available via a SignalNode + */ + template + class OutputPort : public Port + { + public: + using ScalarT = scalar_type; + using IdxT = index_type; + using SignalNodeT = SignalNode; + + /** + * @brief Link the connected signal node with the actual signal + * + * @pre Port must be connected() to a signal node + * + * @param signal_var Pointer to variable to access as signal + * @param global_index Pointer to index variable providing the global + * system index for the signal variable + */ + void link(ScalarT* signal_var, IdxT* global_index) + { + assert(this->connected()); + this->signal_node_->link(signal_var, global_index); + } + + protected: + void assign(SignalNodeT* node) const override + { + node->setAssigned(); + } + }; + + /** + * @brief Port for receiving a signal from a SignalNode + */ + template + class InputPort : public Port + { + public: + using ScalarT = scalar_type; + using IdxT = index_type; + + /** + * @brief Read the signal (value) from the connected SignalNode + * + * @pre Port must be connected() to a signal node + */ + ScalarT readSignal() const + { + assert(this->connected()); + return this->signal_node_->read(); + } + + /** + * @brief Get the global index of the variable provided as signal + */ + IdxT signalVariableIndex() const + { + assert(*this); + return this->signal_node_->getVariableIndex(); + } + + /** + * @brief Write a value back to the sending port + * + * @warning This is hazardous and should at most be used during + * initialization methods. + * + * @todo This is currently required by Genrou and Gensal components (or + * perhaps more accurately, for Tgov1 to be properly initialized), but + * it thoroughly violates the concept being implemented. We need to find a + * different way for those component types to handle the synchronization + * required at initialization. + */ + void writeValue(ScalarT value) + { + this->signal_node_->init(value); + } + }; + + /** + * @brief Represents a group of related ports + * + * This is a grouping of input or output ports. Each port is associated with + * a specific signal variable that is expected to be passed through the + * signal node connected to that port. + * + * @tparam port_type InputPort or OutputPort (with template arguments) + * @tparam An enumeration that specifies the group of ports (must satisfy + * the SizedEnum concept) + */ + template + class PortGroup + { + public: + using PortT = port_type; + using PortEnumT = port_enum_type; + using ScalarT = PortT::ScalarT; + using IdxT = PortT::IdxT; + using SignalNodeT = SignalNode; + using SignalNodeSetT = SignalNodeSet; + + PortGroup() = default; + + /** + * @brief Connect signal nodes mapped from signal ids provided in map + * + * @param id_map Map of PortEnumT variables to signal ids + * @param signal_nodes SignalNodeSet mapping signal ids to signal nodes + */ + template + PortGroup(const signal_id_map_type& id_map, SignalNodeSetT& signal_nodes) + { + for (std::size_t i = 0; i < Utilities::enum_size(); ++i) + { + auto var = static_cast(i); + if (id_map.contains(var)) + { + (*this)[var].connect(signal_nodes[id_map.at(var)]); + } + } + } + + /** + * @brief Get number or ports + */ + static constexpr std::size_t size() noexcept + { + return Utilities::enum_size(); + } + + ///@{ + /** + * @brief Access port vor specified signal variable + */ + PortT& operator[](PortEnumT var) + { + return ports_[static_cast(var)]; + } + + const PortT& operator[](PortEnumT var) const + { + return ports_[static_cast(var)]; + } + + ///@} + + private: + std::array ports_{}; + }; + + /** + * @brief Set of input and output ports for a component + * + * @tparam scalar_type Scalar value type used for signals + * @tparam model_data_type "Data" type specifying variables. This is + * expected to be (or derive from) an instantiation of ComponentData. This + * provides the SignalInputs and SignalOutputs enumeration types for + * specifying each PortGroup + */ + template + struct IOPorts + { + /// Scalar type used for signals + using ScalarT = scalar_type; + /// Model data specification type + using ModelDataT = model_data_type; + /// Index type + using IdxT = ModelDataT::IdxT; + /// Enum specifying signal variables for input + using SignalInEnumT = ModelDataT::SignalInputs; + /// Enum specifying signal variables for output + using SignalOutEnumT = ModelDataT::SignalOutputs; + /// Input port type + using InputPortT = InputPort; + /// Output port type + using OutputPortT = OutputPort; + /// SignalNode type + using SignalNodeT = SignalNode; + /// SignalNodeSet type + using SignalNodeSetT = SignalNodeSet; + + /// Grouping of input ports + PortGroup in; + /// Grouping of output ports + PortGroup out; + + IOPorts() = default; + + /** + * @brief Construct with model data and SignalNodeSet + * + * This will connect signal nodes to input and output ports according to + * the signal ids specified in the model data's `signal_inputs` and + * `signal_outputs` id maps. + */ + IOPorts(const ModelDataT& data, SignalNodeSetT& signal_nodes) + : in{data.signal_inputs, signal_nodes}, + out{data.signal_outputs, signal_nodes} + { + } + }; + + } // namespace PhasorDynamics +} // namespace GridKit diff --git a/GridKit/Model/PhasorDynamics/Load/LoadZ/LoadZ.hpp b/GridKit/Model/PhasorDynamics/Load/LoadZ/LoadZ.hpp index 2b6c36db6..8dfcbf18c 100644 --- a/GridKit/Model/PhasorDynamics/Load/LoadZ/LoadZ.hpp +++ b/GridKit/Model/PhasorDynamics/Load/LoadZ/LoadZ.hpp @@ -1,7 +1,6 @@ #pragma once #include -#include #include #include @@ -12,9 +11,6 @@ namespace GridKit { template class BusBase; - - template - struct LoadZData; } // namespace PhasorDynamics } // namespace GridKit diff --git a/GridKit/Model/PhasorDynamics/Load/LoadZ/LoadZData.hpp b/GridKit/Model/PhasorDynamics/Load/LoadZ/LoadZData.hpp index f1ca5f3ac..8fd6c1255 100644 --- a/GridKit/Model/PhasorDynamics/Load/LoadZ/LoadZData.hpp +++ b/GridKit/Model/PhasorDynamics/Load/LoadZ/LoadZData.hpp @@ -54,21 +54,13 @@ namespace GridKit * Integer parameters are of the same type as matrix and vector indices. */ template - struct LoadZData : public ComponentData - { - LoadZData() = default; - - using Parameters = LoadZParameters; - using Buses = LoadZBuses; - using SignalInputs = LoadZSignalInputs; - using SignalOutputs = LoadZSignalOutputs; - using MonitorableVariables = LoadZMonitorableVariables; - }; + using LoadZData = + ComponentData; } // namespace PhasorDynamics } // namespace GridKit diff --git a/GridKit/Model/PhasorDynamics/Load/LoadZIP/LoadZIP.hpp b/GridKit/Model/PhasorDynamics/Load/LoadZIP/LoadZIP.hpp index 4595f8ca8..f37cfa94f 100644 --- a/GridKit/Model/PhasorDynamics/Load/LoadZIP/LoadZIP.hpp +++ b/GridKit/Model/PhasorDynamics/Load/LoadZIP/LoadZIP.hpp @@ -1,7 +1,6 @@ #pragma once #include -#include #include #include @@ -12,9 +11,6 @@ namespace GridKit { template class BusBase; - - template - struct LoadZIPData; } // namespace PhasorDynamics } // namespace GridKit diff --git a/GridKit/Model/PhasorDynamics/Load/LoadZIP/LoadZIPData.hpp b/GridKit/Model/PhasorDynamics/Load/LoadZIP/LoadZIPData.hpp index 39c037d20..7d839d3c6 100644 --- a/GridKit/Model/PhasorDynamics/Load/LoadZIP/LoadZIPData.hpp +++ b/GridKit/Model/PhasorDynamics/Load/LoadZIP/LoadZIPData.hpp @@ -55,21 +55,13 @@ namespace GridKit * Integer parameters are of the same type as matrix and vector indices. */ template - struct LoadZIPData : public ComponentData - { - LoadZIPData() = default; - - using Parameters = LoadZIPParameters; - using Buses = LoadZIPBuses; - using SignalInputs = LoadZIPSignalInputs; - using SignalOutputs = LoadZIPSignalOutputs; - using MonitorableVariables = LoadZIPMonitorableVariables; - }; + using LoadZIPData = + ComponentData; } // namespace PhasorDynamics } // namespace GridKit diff --git a/GridKit/Model/PhasorDynamics/SignalNode/CMakeLists.txt b/GridKit/Model/PhasorDynamics/SignalNode/CMakeLists.txt index 7b9d3baa7..9a16feb3b 100644 --- a/GridKit/Model/PhasorDynamics/SignalNode/CMakeLists.txt +++ b/GridKit/Model/PhasorDynamics/SignalNode/CMakeLists.txt @@ -7,7 +7,7 @@ gridkit_add_library( phasor_dynamics_signal SOURCES SignalNode.cpp - HEADERS SignalNode.hpp SignalNodeData.hpp) + HEADERS SignalNode.hpp SignalNodeData.hpp SignalNodeSet.hpp) gridkit_add_library(phasor_dynamics_signal_dependency_tracking SOURCES SignalNodeDependencyTracking.cpp) diff --git a/GridKit/Model/PhasorDynamics/SignalNode/README.md b/GridKit/Model/PhasorDynamics/SignalNode/README.md index c05d85a0d..d98109655 100644 --- a/GridKit/Model/PhasorDynamics/SignalNode/README.md +++ b/GridKit/Model/PhasorDynamics/SignalNode/README.md @@ -2,7 +2,7 @@ Signal nodes provide scalar connection points between phasor-dynamics components. Components attach external inputs to signal nodes and assign -internal outputs to signal nodes through `ComponentSignals`. +internal outputs to signal nodes through `IOPorts`. A linked signal node stores a pointer to the component variable that owns the signal value, so other connected components can read or initialize that value diff --git a/GridKit/Model/PhasorDynamics/SignalNode/SignalNode.hpp b/GridKit/Model/PhasorDynamics/SignalNode/SignalNode.hpp index e86e973fa..854f9ff5f 100644 --- a/GridKit/Model/PhasorDynamics/SignalNode/SignalNode.hpp +++ b/GridKit/Model/PhasorDynamics/SignalNode/SignalNode.hpp @@ -19,6 +19,17 @@ namespace GridKit /*! * @brief SignalNode model implementation base class. * + * A signal node conceptually carries a signal (scalar value) from an + * output port of one component to an input port of another component (or + * multiple components). + * + * (Component):[OutputPort] -> {SignalNode} -> [InputPort]:(Component) + * + * A SignalNode can be "connected" to a Port. When that port is an + * OutputPort, the SignalNode is considered `assigned()` since it can be + * connected to only one OutputPort. The SignalNode is considered `linked()` + * when the actual signal (scalar variable) has been made available. + * */ template class SignalNode @@ -33,34 +44,20 @@ namespace GridKit virtual ~SignalNode() = default; - void set(ScalarT* signal_in, IdxT* global_index); - bool linked() const; - ScalarT read() const; - void init(ScalarT signal_in); - - const IdxT signalId() const - { - return signal_id_; - } - - IdxT getVariableIndex() const - { - return *variable_index_; - } - - virtual const IdxT busID() const - { - return bus_id_; - } + IdxT signalId() const noexcept; + void setAssigned() noexcept; + bool assigned() const noexcept; + void link(ScalarT* signal_in, IdxT* global_index) noexcept; + bool linked() const noexcept; + ScalarT read() const noexcept; + IdxT getVariableIndex() const noexcept; + void init(ScalarT signal_in) noexcept; private: ScalarT* signal_{nullptr}; IdxT signal_id_{0}; - - protected: - const IdxT bus_id_{INVALID_INDEX}; - - IdxT* variable_index_{nullptr}; + IdxT* variable_index_{nullptr}; + bool assigned_{false}; }; } // namespace PhasorDynamics diff --git a/GridKit/Model/PhasorDynamics/SignalNode/SignalNodeImpl.hpp b/GridKit/Model/PhasorDynamics/SignalNode/SignalNodeImpl.hpp index c3eba4e2f..cd32c84ed 100644 --- a/GridKit/Model/PhasorDynamics/SignalNode/SignalNodeImpl.hpp +++ b/GridKit/Model/PhasorDynamics/SignalNode/SignalNodeImpl.hpp @@ -1,6 +1,9 @@ /** * @file SignalNode model implementation. */ + +#include + #include #include @@ -20,27 +23,55 @@ namespace GridKit } template - void SignalNode::set(ScalarT* signal, IdxT* variable_index) + index_type SignalNode::signalId() const noexcept + { + return signal_id_; + } + + template + void SignalNode::setAssigned() noexcept + { + assert(!assigned_); + assigned_ = true; + } + + template + bool SignalNode::assigned() const noexcept + { + return assigned_; + } + + template + void SignalNode::link(ScalarT* signal, IdxT* variable_index) noexcept { signal_ = signal; variable_index_ = variable_index; } template - bool SignalNode::linked() const + bool SignalNode::linked() const noexcept { return (signal_) && (variable_index_); } template - scalar_type SignalNode::read() const + scalar_type SignalNode::read() const noexcept { + assert(signal_); return *signal_; } template - void SignalNode::init(ScalarT signal) + index_type SignalNode::getVariableIndex() const noexcept + { + assert(variable_index_); + return *variable_index_; + } + + template + void SignalNode::init(ScalarT signal) noexcept { + assert(signal_); *signal_ = signal; } } // namespace PhasorDynamics diff --git a/GridKit/Model/PhasorDynamics/SignalNode/SignalNodeSet.hpp b/GridKit/Model/PhasorDynamics/SignalNode/SignalNodeSet.hpp new file mode 100644 index 000000000..7e347c429 --- /dev/null +++ b/GridKit/Model/PhasorDynamics/SignalNode/SignalNodeSet.hpp @@ -0,0 +1,82 @@ +#pragma once + +#include +#include +#include +#include + +#include +#include + +namespace GridKit +{ + namespace PhasorDynamics + { + /** + * @brief Manage a collection of signal nodes specifed for a system model + * + * This class manages the memory and access of signal nodes for a + * SystemModel. A SignalNode can be accessed either by id or name. + */ + template + class SignalNodeSet + { + public: + using SignalNodeT = signal_node_type; + using ScalarT = SignalNodeT::ScalarT; + using IdxT = SignalNodeT::IdxT; + using RealT = SignalNodeT::RealT; + using SignalNodeDataT = SignalNodeData; + + SignalNodeSet() = default; + + ~SignalNodeSet() + { + for (auto node : signal_nodes_) + { + delete node; + } + } + + /** + * @brief Create a new node based on the input data + */ + void add(const SignalNodeDataT& node_data) + { + auto node = new SignalNodeT(node_data); + signal_nodes_.push_back(node); + signal_node_id_map_[node_data.signal_id] = node; + signal_node_name_map_[node_data.name] = node; + } + + /** + * @brief Access a SignalNode using its id + */ + SignalNodeT* operator[](IdxT signal_id) + { + assert(signal_node_id_map_.count(signal_id)); + auto* node = signal_node_id_map_[signal_id]; + assert(node->signalId() == signal_id); + return node; + } + + /** + * @brief Access a SignalNode using its name + */ + SignalNodeT* operator[](const std::string& signal_name) + { + assert(signal_node_name_map_.count(signal_name)); + auto* node = signal_node_name_map_[signal_name]; + return node; + } + + private: + /// Vector of nodes + std::vector signal_nodes_; + /// Map of ids to nodes + std::unordered_map signal_node_id_map_; + /// Map of names to nodes + std::unordered_map signal_node_name_map_; + }; + } // namespace PhasorDynamics +} // namespace GridKit diff --git a/GridKit/Model/PhasorDynamics/SignalSource/ConstantSignalSource.hpp b/GridKit/Model/PhasorDynamics/SignalSource/ConstantSignalSource.hpp index 21cc5fbe7..ba7a39d01 100644 --- a/GridKit/Model/PhasorDynamics/SignalSource/ConstantSignalSource.hpp +++ b/GridKit/Model/PhasorDynamics/SignalSource/ConstantSignalSource.hpp @@ -1,21 +1,16 @@ #pragma once #include -#include +#include +#include namespace GridKit { namespace PhasorDynamics { - // Forward declaration - template - struct ConstantSignalSourceData; - /// Internal variables for ConstantSignalSource enum class ConstantSignalSourceInternalVariables : size_t { - SREAL, - SIMAG, MAXIMUM, }; @@ -38,13 +33,17 @@ namespace GridKit using Component::size_; public: - using ScalarT = scalar_type; - using IdxT = index_type; - using RealT = typename Component::RealT; - using ModelDataT = ConstantSignalSourceData; + using ScalarT = scalar_type; + using IdxT = index_type; + using RealT = typename Component::RealT; + using ModelDataT = ConstantSignalSourceData; + using SignalNodeT = SignalNode; + using SignalNodeSetT = SignalNodeSet; + using IOPortsT = IOPorts; ConstantSignalSource(); ConstantSignalSource(const ModelDataT& data); + ConstantSignalSource(const ModelDataT& data, SignalNodeSetT& signal_nodes); ~ConstantSignalSource(); int setGridKitComponentID(IdxT) override final; @@ -56,16 +55,6 @@ namespace GridKit int evaluateResidual() override final; int evaluateJacobian() override final; - /// Get the `ComponentSignals` from this component - auto getSignals() - -> ComponentSignals& - { - return signals_; - } - private: /// Real part of source value ScalarT s_real_{0.0}; @@ -76,8 +65,8 @@ namespace GridKit IdxT sr_index_{INVALID_INDEX}; IdxT si_index_{INVALID_INDEX}; - /// Component signals - ComponentSignals signals_; + /// Component ports + IOPortsT ports_; // Parameter initialization function void initializeParameters(const ModelDataT& data); diff --git a/GridKit/Model/PhasorDynamics/SignalSource/ConstantSignalSourceData.hpp b/GridKit/Model/PhasorDynamics/SignalSource/ConstantSignalSourceData.hpp index 83aaab86d..f55b46a97 100644 --- a/GridKit/Model/PhasorDynamics/SignalSource/ConstantSignalSourceData.hpp +++ b/GridKit/Model/PhasorDynamics/SignalSource/ConstantSignalSourceData.hpp @@ -35,22 +35,14 @@ namespace GridKit }; template - struct ConstantSignalSourceData : public ComponentData - { - ConstantSignalSourceData() = default; - - using Parameters = ConstantSignalSourceParameters; - using Buses = ConstantSignalSourceBuses; - using SignalInputs = ConstantSignalSourceSignalInputs; - using SignalOutputs = ConstantSignalSourceSignalOutputs; - using MonitorableVariables = ConstantSignalSourceMonitorableVariables; - }; + using ConstantSignalSourceData = + ComponentData; } // namespace PhasorDynamics } // namespace GridKit diff --git a/GridKit/Model/PhasorDynamics/SignalSource/ConstantSignalSourceImpl.hpp b/GridKit/Model/PhasorDynamics/SignalSource/ConstantSignalSourceImpl.hpp index 5babc146b..175103b3c 100644 --- a/GridKit/Model/PhasorDynamics/SignalSource/ConstantSignalSourceImpl.hpp +++ b/GridKit/Model/PhasorDynamics/SignalSource/ConstantSignalSourceImpl.hpp @@ -1,4 +1,6 @@ +#include +#include #include #include #include @@ -28,6 +30,18 @@ namespace GridKit size_ = 0; } + /** + * @brief Construct with values from input data with signal nodes access + */ + template + ConstantSignalSource::ConstantSignalSource( + const ModelDataT& data, SignalNodeSetT& signal_nodes) + : ports_(data, signal_nodes) + { + initializeParameters(data); + size_ = 0; + } + template ConstantSignalSource::~ConstantSignalSource() { @@ -66,16 +80,15 @@ namespace GridKit template int ConstantSignalSource::allocate() { - static constexpr auto SREAL = ConstantSignalSourceInternalVariables::SREAL; - static constexpr auto SIMAG = ConstantSignalSourceInternalVariables::SIMAG; + using SignalOut = ConstantSignalSourceSignalOutputs; - if (signals_.template isAssigned()) + if (auto sr_port = ports_.out[SignalOut::sr]) { - signals_.template getSignalNode()->set(&s_real_, &sr_index_); + sr_port.link(&s_real_, &sr_index_); } - if (signals_.template isAssigned()) + if (auto si_port = ports_.out[SignalOut::si]) { - signals_.template getSignalNode()->set(&s_imag_, &si_index_); + si_port.link(&s_imag_, &si_index_); } return 0; diff --git a/GridKit/Model/PhasorDynamics/Stabilizer/IEEEST/Ieeest.hpp b/GridKit/Model/PhasorDynamics/Stabilizer/IEEEST/Ieeest.hpp index 4ef2dcb37..3b35fb11f 100644 --- a/GridKit/Model/PhasorDynamics/Stabilizer/IEEEST/Ieeest.hpp +++ b/GridKit/Model/PhasorDynamics/Stabilizer/IEEEST/Ieeest.hpp @@ -7,22 +7,19 @@ #pragma once #include -#include +#include +#include #include namespace GridKit { namespace PhasorDynamics { - namespace Stabilizer - { - template - struct IeeestData; - } // namespace Stabilizer - template class SignalNode; + template + class SignalNodeSet; } // namespace PhasorDynamics } // namespace GridKit @@ -79,15 +76,18 @@ namespace GridKit using Component::residual_indices_; public: - using ScalarT = scalar_type; - using IdxT = index_type; - using RealT = typename Component::RealT; - using ModelDataT = IeeestData; - using SignalT = SignalNode; - using MonitorT = Model::VariableMonitor; + using ScalarT = scalar_type; + using IdxT = index_type; + using RealT = typename Component::RealT; + using ModelDataT = IeeestData; + using SignalNodeT = SignalNode; + using SignalNodeSetT = SignalNodeSet; + using IOPortsT = IOPorts; + using MonitorT = Model::VariableMonitor; Ieeest(); Ieeest(const ModelDataT& data); + Ieeest(const ModelDataT& data, SignalNodeSetT& signal_nodes); ~Ieeest(); int setGridKitComponentID(IdxT) override final; @@ -99,14 +99,9 @@ namespace GridKit int evaluateResidual() override final; int evaluateJacobian() override final; - /// Get the `ComponentSignals` from this `Ieeest` - auto getSignals() - -> ComponentSignals& + IOPortsT& getPorts() { - return signals_; + return ports_; } const Model::VariableMonitorBase* getMonitor() const override; @@ -160,7 +155,7 @@ namespace GridKit RealT use_T6_block_{1}; RealT bypass_T6_block_{0}; - ComponentSignals signals_; + IOPortsT ports_; std::unique_ptr monitor_; diff --git a/GridKit/Model/PhasorDynamics/Stabilizer/IEEEST/IeeestData.hpp b/GridKit/Model/PhasorDynamics/Stabilizer/IEEEST/IeeestData.hpp index 139e928af..753549e05 100644 --- a/GridKit/Model/PhasorDynamics/Stabilizer/IEEEST/IeeestData.hpp +++ b/GridKit/Model/PhasorDynamics/Stabilizer/IEEEST/IeeestData.hpp @@ -80,22 +80,14 @@ namespace GridKit * @tparam index_type Integer parameter data type */ template - struct IeeestData : public ComponentData - { - IeeestData() = default; - - using Parameters = IeeestParameters; - using Buses = IeeestBuses; - using SignalInputs = IeeestSignalInputs; - using SignalOutputs = IeeestSignalOutputs; - using MonitorableVariables = IeeestMonitorableVariables; - }; + using IeeestData = + ComponentData; } // namespace Stabilizer } // namespace PhasorDynamics diff --git a/GridKit/Model/PhasorDynamics/Stabilizer/IEEEST/IeeestImpl.hpp b/GridKit/Model/PhasorDynamics/Stabilizer/IEEEST/IeeestImpl.hpp index 302e5bc63..b9985da49 100644 --- a/GridKit/Model/PhasorDynamics/Stabilizer/IEEEST/IeeestImpl.hpp +++ b/GridKit/Model/PhasorDynamics/Stabilizer/IEEEST/IeeestImpl.hpp @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -38,6 +39,17 @@ namespace GridKit size_ = 12; } + template + Ieeest::Ieeest( + const ModelDataT& data, SignalNodeSetT& signal_nodes) + : ports_(data, signal_nodes), + monitor_(std::make_unique(data)) + { + initializeParameters(data); + initializeMonitor(); + size_ = 12; + } + template Ieeest::~Ieeest() { @@ -177,10 +189,9 @@ namespace GridKit this->setResidualIndex(j, j); } - if (signals_.template isAssigned()) + if (auto output_port = ports_.out[IeeestSignalOutputs::output]) { - signals_.template getSignalNode()->set( - &y_[11], &(this->getVariableIndex(11))); + output_port.link(&y_[11], &(this->getVariableIndex(11))); } return 0; @@ -191,17 +202,15 @@ namespace GridKit { int ret = 0; - if (signals_.template isAttached()) + auto input_port = ports_.in[IeeestSignalInputs::input]; + if (!input_port.connected()) { - if (!signals_.template isLinked()) - { - Log::error() << "Ieeest: input signal U attached with no linked source\n"; - ret += 1; - } + Log::error() << "Ieeest: required input signal U is not attached\n"; + ret += 1; } - else + if (input_port.connected() && !input_port.linked()) { - Log::error() << "Ieeest: required input signal U is not attached\n"; + Log::error() << "Ieeest: input signal U attached with no linked source\n"; ret += 1; } @@ -316,10 +325,10 @@ namespace GridKit template int Ieeest::evaluateResidual() { - if (signals_.template isAttached()) + if (auto input_port = ports_.in[IeeestSignalInputs::input]) { - ws_[0] = signals_.template readExternalVariable(); - ws_indices_[0] = signals_.template readExternalVariableIndex(); + ws_[0] = input_port.readSignal(); + ws_indices_[0] = input_port.signalVariableIndex(); } evaluateInternalResidual(y_.data(), yp_.data(), wb_.data(), ws_.data(), f_.data()); diff --git a/GridKit/Model/PhasorDynamics/SynchronousMachine/GENROU/Genrou.hpp b/GridKit/Model/PhasorDynamics/SynchronousMachine/GENROU/Genrou.hpp index 0ba969043..dc59f5c92 100644 --- a/GridKit/Model/PhasorDynamics/SynchronousMachine/GENROU/Genrou.hpp +++ b/GridKit/Model/PhasorDynamics/SynchronousMachine/GENROU/Genrou.hpp @@ -9,7 +9,7 @@ #pragma once #include -#include +#include #include #include @@ -24,8 +24,8 @@ namespace GridKit template class SignalNode; - template - struct GenrouData; + template + class SignalNodeSet; } // namespace PhasorDynamics } // namespace GridKit @@ -90,25 +90,31 @@ namespace GridKit using Component::residual_indices_; public: - using ScalarT = scalar_type; - using IdxT = index_type; - using RealT = typename Component::RealT; - using BusT = BusBase; - using ModelDataT = GenrouData; - using SignalT = SignalNode; - using MonitorT = Model::VariableMonitor; + using ScalarT = scalar_type; + using IdxT = index_type; + using RealT = typename Component::RealT; + using BusT = BusBase; + using ModelDataT = GenrouData; + using SignalNodeT = SignalNode; + using SignalNodeSetT = SignalNodeSet; + using IOPortsT = IOPorts; + using MonitorT = Model::VariableMonitor; Genrou(BusT* bus); Genrou(BusT* bus, - SignalT* omega, - SignalT* pmech, + SignalNodeT* omega, + SignalNodeT* pmech, const ModelDataT& data); Genrou(BusT* bus, - SignalT* omega, - SignalT* pmech, - SignalT* efd, + SignalNodeT* omega, + SignalNodeT* pmech, + SignalNodeT* efd, const ModelDataT& data); - Genrou(BusT* bus, const ModelDataT& data); + Genrou(BusT* bus, + const ModelDataT& data); + Genrou(BusT* bus, + const ModelDataT& data, + SignalNodeSetT& signal_nodes); Genrou(BusT* bus, RealT p0, RealT q0, @@ -141,16 +147,6 @@ namespace GridKit // Still to be implemented int evaluateJacobian() override final; - /// Get the `ComponentSignals` from this `Genrou` - auto getSignals() - -> ComponentSignals& - { - return signals_; - } - const Model::VariableMonitorBase* getMonitor() const override; private: @@ -210,8 +206,8 @@ namespace GridKit BusT* bus_; IdxT bus_id_{0}; - /// Component signal extension - ComponentSignals signals_; + /* Component ports */ + IOPortsT ports_; /* Initial terminal conditions */ RealT p0_{0.0}; diff --git a/GridKit/Model/PhasorDynamics/SynchronousMachine/GENROU/GenrouData.hpp b/GridKit/Model/PhasorDynamics/SynchronousMachine/GENROU/GenrouData.hpp index c50f70d9f..66661e6d5 100644 --- a/GridKit/Model/PhasorDynamics/SynchronousMachine/GENROU/GenrouData.hpp +++ b/GridKit/Model/PhasorDynamics/SynchronousMachine/GENROU/GenrouData.hpp @@ -44,6 +44,10 @@ namespace GridKit }; /// Signal inputs for a Genrou generator model + /// + /// @todo Genrou (and likely other components) would need to name multiple + /// signal inlets and outlets. For now we have only speed out and + /// mechanical power in. enum class GenrouSignalInputs : size_t { pmech, ///< Unique ID of the signal providing mechanical power @@ -79,21 +83,13 @@ namespace GridKit * Integer parameters are of the same type as matrix and vector indices. */ template - struct GenrouData : public ComponentData - { - GenrouData() = default; - - using Parameters = GenrouParameters; - using Buses = GenrouBuses; - using SignalInputs = GenrouSignalInputs; - using SignalOutputs = GenrouSignalOutputs; - using MonitorableVariables = GenrouMonitorableVariables; - }; + using GenrouData = + ComponentData; } // namespace PhasorDynamics } // namespace GridKit diff --git a/GridKit/Model/PhasorDynamics/SynchronousMachine/GENROU/GenrouImpl.hpp b/GridKit/Model/PhasorDynamics/SynchronousMachine/GENROU/GenrouImpl.hpp index 523a7563a..3fec6a1b9 100644 --- a/GridKit/Model/PhasorDynamics/SynchronousMachine/GENROU/GenrouImpl.hpp +++ b/GridKit/Model/PhasorDynamics/SynchronousMachine/GENROU/GenrouImpl.hpp @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -107,7 +108,8 @@ namespace GridKit * @brief Constructor for a GENROU generator model with saturation */ template - Genrou::Genrou(BusT* bus, const ModelDataT& data) + Genrou::Genrou(BusT* bus, + const ModelDataT& data) : bus_(bus), monitor_(std::make_unique(data)) { @@ -122,12 +124,12 @@ namespace GridKit * @brief Constructor for a GENROU generator model with saturation */ template - Genrou::Genrou(BusT* bus, SignalT* omega, SignalT* pmech, const ModelDataT& data) + Genrou::Genrou( + BusT* bus, const ModelDataT& data, SignalNodeSetT& signal_nodes) : bus_(bus), + ports_(data, signal_nodes), monitor_(std::make_unique(data)) { - signals_.template attachSignalNode(pmech); - signals_.template assignSignalNode(omega); initializeParameters(data); initializeMonitor(); @@ -139,13 +141,30 @@ namespace GridKit * @brief Constructor for a GENROU generator model with saturation */ template - Genrou::Genrou(BusT* bus, SignalT* omega, SignalT* pmech, SignalT* efd, const ModelDataT& data) + Genrou::Genrou(BusT* bus, SignalNodeT* omega, SignalNodeT* pmech, const ModelDataT& data) : bus_(bus), monitor_(std::make_unique(data)) { - signals_.template attachSignalNode(pmech); - signals_.template assignSignalNode(omega); - signals_.template attachSignalNode(efd); + ports_.in[GenrouSignalInputs::pmech].connect(pmech); + ports_.out[GenrouSignalOutputs::speed].connect(omega); + initializeParameters(data); + initializeMonitor(); + + size_ = 19; + setDerivedParams(); + } + + /** + * @brief Constructor for a GENROU generator model with saturation + */ + template + Genrou::Genrou(BusT* bus, SignalNodeT* omega, SignalNodeT* pmech, SignalNodeT* efd, const ModelDataT& data) + : bus_(bus), + monitor_(std::make_unique(data)) + { + ports_.in[GenrouSignalInputs::pmech].connect(pmech); + ports_.in[GenrouSignalInputs::efd].connect(efd); + ports_.out[GenrouSignalOutputs::speed].connect(omega); initializeParameters(data); initializeMonitor(); @@ -351,9 +370,9 @@ namespace GridKit } // Set output signals - if (signals_.template isAssigned()) + if (auto speed_port = ports_.out[GenrouSignalOutputs::speed]) { - signals_.template getSignalNode()->set(&y_[1], &(this->getVariableIndex(1))); + speed_port.link(&y_[1], &(this->getVariableIndex(1))); } return 0; @@ -365,27 +384,20 @@ namespace GridKit template int Genrou::verify() const { - static constexpr auto PM = GenrouExternalVariables::PM; - static constexpr auto EFD = GenrouExternalVariables::EFD; - int ret = 0; - if (signals_.template isAttached()) + auto pmech_port = ports_.in[GenrouSignalInputs::pmech]; + if (pmech_port.connected() && !pmech_port.linked()) { - if (!signals_.template isLinked()) - { - Log::error() << "Genrou: pmech signal attached with no linked governor\n"; - ret += 1; - } + Log::error() << "Genrou: pmech signal attached with no linked governor\n"; + ret += 1; } - if (signals_.template isAttached()) + auto efd_port = ports_.in[GenrouSignalInputs::efd]; + if (efd_port.connected() && !efd_port.linked()) { - if (!signals_.template isLinked()) - { - Log::error() << "Genrou: efd signal attached with no linked exciter\n"; - ret += 1; - } + Log::error() << "Genrou: efd signal attached with no linked exciter\n"; + ret += 1; } return ret; @@ -489,17 +501,21 @@ namespace GridKit + G_ * (vd * -std::cos(delta) + vq * std::sin(delta)); ScalarT Te = y_[12]; + + // TODO: Writing to an input signal seems very wrong. Check to see if this + // could be handled differently + // Convert Te to system base for governor PM signal. pmech_set_ = toSystemBase(Te); - if (signals_.template isAttached()) + if (auto pmech_port = ports_.in[GenrouSignalInputs::pmech]) { - signals_.template writeExternalVariable(pmech_set_); + pmech_port.writeValue(pmech_set_); } efd_set_ = Eqp + Xd1_ * (id + Xd3_ * (Eqp - psidp - Xd2_ * id)) + psidpp * ksat; - if (signals_.template isAttached()) + if (auto efd_port = ports_.in[GenrouSignalInputs::efd]) { - signals_.template writeExternalVariable(efd_set_); + efd_port.writeValue(efd_set_); } for (IdxT i = 0; i < size_; ++i) @@ -650,18 +666,18 @@ namespace GridKit { // Mechanical Power ws_[0] = pmech_set_; - if (signals_.template isAttached()) + if (auto pmech_port = ports_.in[GenrouSignalInputs::pmech]) { - ws_[0] = signals_.template readExternalVariable(); - ws_indices_[0] = signals_.template readExternalVariableIndex(); + ws_[0] = pmech_port.readSignal(); + ws_indices_[0] = pmech_port.signalVariableIndex(); } // Exciter Efield ws_[1] = efd_set_; - if (signals_.template isAttached()) + if (auto efd_port = ports_.in[GenrouSignalInputs::efd]) { - ws_[1] = signals_.template readExternalVariable(); - ws_indices_[1] = signals_.template readExternalVariableIndex(); + ws_[1] = efd_port.readSignal(); + ws_indices_[1] = efd_port.signalVariableIndex(); } // Bus voltages diff --git a/GridKit/Model/PhasorDynamics/SynchronousMachine/GENSAL/Gensal.hpp b/GridKit/Model/PhasorDynamics/SynchronousMachine/GENSAL/Gensal.hpp index 5c17f4a3c..d4c4bcd8e 100644 --- a/GridKit/Model/PhasorDynamics/SynchronousMachine/GENSAL/Gensal.hpp +++ b/GridKit/Model/PhasorDynamics/SynchronousMachine/GENSAL/Gensal.hpp @@ -8,7 +8,7 @@ #pragma once #include -#include +#include #include #include @@ -20,8 +20,8 @@ namespace GridKit template class BusBase; - template - struct GensalData; + template + class SignalNodeSet; } // namespace PhasorDynamics } // namespace GridKit @@ -85,14 +85,18 @@ namespace GridKit using Component::residual_indices_; public: - using ScalarT = scalar_type; - using IdxT = index_type; - using RealT = typename Component::RealT; - using BusT = BusBase; - using ModelDataT = GensalData; - using MonitorT = Model::VariableMonitor; + using ScalarT = scalar_type; + using IdxT = index_type; + using RealT = typename Component::RealT; + using BusT = BusBase; + using ModelDataT = GensalData; + using SignalNodeT = SignalNode; + using SignalNodeSetT = SignalNodeSet; + using IOPortsT = IOPorts; + using MonitorT = Model::VariableMonitor; Gensal(BusT* bus, const ModelDataT& data); + Gensal(BusT* bus, const ModelDataT& data, SignalNodeSetT& signal_nodes); ~Gensal(); int setGridKitComponentID(IdxT) override final; @@ -106,16 +110,6 @@ namespace GridKit // Still to be implemented int evaluateJacobian() override final; - /// Get the `ComponentSignals` from this `Gensal` - auto getSignals() - -> ComponentSignals& - { - return signals_; - } - const Model::VariableMonitorBase* getMonitor() const override; private: @@ -174,8 +168,8 @@ namespace GridKit /* Identification */ BusT* bus_; - /// Component signal extension - ComponentSignals signals_; + /* Component ports */ + IOPortsT ports_; /* Initial terminal conditions */ RealT p0_{0.0}; diff --git a/GridKit/Model/PhasorDynamics/SynchronousMachine/GENSAL/GensalData.hpp b/GridKit/Model/PhasorDynamics/SynchronousMachine/GENSAL/GensalData.hpp index 35555665f..aa0e7d5a4 100644 --- a/GridKit/Model/PhasorDynamics/SynchronousMachine/GENSAL/GensalData.hpp +++ b/GridKit/Model/PhasorDynamics/SynchronousMachine/GENSAL/GensalData.hpp @@ -85,21 +85,13 @@ namespace GridKit * Integer parameters are of the same type as matrix and vector indices. */ template - struct GensalData : public ComponentData - { - GensalData() = default; - - using Parameters = GensalParameters; - using Buses = GensalBuses; - using SignalInputs = GensalSignalInputs; - using SignalOutputs = GensalSignalOutputs; - using MonitorableVariables = GensalMonitorableVariables; - }; + using GensalData = + ComponentData; } // namespace PhasorDynamics } // namespace GridKit diff --git a/GridKit/Model/PhasorDynamics/SynchronousMachine/GENSAL/GensalImpl.hpp b/GridKit/Model/PhasorDynamics/SynchronousMachine/GENSAL/GensalImpl.hpp index 318759aaf..63e570291 100644 --- a/GridKit/Model/PhasorDynamics/SynchronousMachine/GENSAL/GensalImpl.hpp +++ b/GridKit/Model/PhasorDynamics/SynchronousMachine/GENSAL/GensalImpl.hpp @@ -5,6 +5,9 @@ #include #include +#include +#include +#include #include #include #include @@ -31,6 +34,23 @@ namespace GridKit setDerivedParams(); } + /** + * @brief Constructor for a GENSAL generator model with saturation + */ + template + Gensal::Gensal( + BusT* bus, const ModelDataT& data, SignalNodeSetT& signal_nodes) + : bus_(bus), + ports_(data, signal_nodes), + monitor_(std::make_unique(data)) + { + initializeParameters(data); + initializeMonitor(); + + size_ = 16; + setDerivedParams(); + } + template Gensal::~Gensal() { @@ -226,9 +246,9 @@ namespace GridKit } // Set output signals - if (signals_.template isAssigned()) + if (auto speed_port = ports_.out[GensalSignalOutputs::speed]) { - signals_.template getSignalNode()->set(&y_[1], &(this->getVariableIndex(1))); + speed_port.link(&y_[1], &(this->getVariableIndex(1))); } return 0; @@ -240,27 +260,20 @@ namespace GridKit template int Gensal::verify() const { - static constexpr auto PM = GensalExternalVariables::PM; - static constexpr auto EFD = GensalExternalVariables::EFD; - int ret = 0; - if (signals_.template isAttached()) + auto pmech_port = ports_.in[GensalSignalInputs::pmech]; + if (pmech_port.connected() && !pmech_port.linked()) { - if (!signals_.template isLinked()) - { - Log::error() << "Gensal: pmech signal attached with no linked governor\n"; - ret += 1; - } + Log::error() << "Gensal: pmech signal attached with no linked governor\n"; + ret += 1; } - if (signals_.template isAttached()) + auto efd_port = ports_.in[GensalSignalInputs::efd]; + if (efd_port.connected() && !efd_port.linked()) { - if (!signals_.template isLinked()) - { - Log::error() << "Gensal: efd signal attached with no linked exciter\n"; - ret += 1; - } + Log::error() << "Gensal: efd signal attached with no linked exciter\n"; + ret += 1; } return ret; @@ -320,15 +333,15 @@ namespace GridKit // Convert Te to system base for governor PM signal. pmech_set_ = toSystemBase(Te); - if (signals_.template isAttached()) + if (auto pmech_port = ports_.in[GensalSignalInputs::pmech]) { - signals_.template writeExternalVariable(pmech_set_); + pmech_port.writeValue(pmech_set_); } efd_set_ = Eqp + Xd1_ * (id + Xd3_ * (Eqp - psidp - Xd2_ * id)) + ksat; - if (signals_.template isAttached()) + if (auto efd_port = ports_.in[GensalSignalInputs::efd]) { - signals_.template writeExternalVariable(efd_set_); + efd_port.writeValue(efd_set_); } for (IdxT i = 0; i < size_; ++i) @@ -472,18 +485,18 @@ namespace GridKit { // Mechanical Power ws_[0] = pmech_set_; - if (signals_.template isAttached()) + if (auto pmech_port = ports_.in[GensalSignalInputs::pmech]) { - ws_[0] = signals_.template readExternalVariable(); - ws_indices_[0] = signals_.template readExternalVariableIndex(); + ws_[0] = pmech_port.readSignal(); + ws_indices_[0] = pmech_port.signalVariableIndex(); } // Exciter Efield ws_[1] = efd_set_; - if (signals_.template isAttached()) + if (auto efd_port = ports_.in[GensalSignalInputs::efd]) { - ws_[1] = signals_.template readExternalVariable(); - ws_indices_[1] = signals_.template readExternalVariableIndex(); + ws_[1] = efd_port.readSignal(); + ws_indices_[1] = efd_port.signalVariableIndex(); } // Bus voltages diff --git a/GridKit/Model/PhasorDynamics/SynchronousMachine/GenClassical/GenClassical.hpp b/GridKit/Model/PhasorDynamics/SynchronousMachine/GenClassical/GenClassical.hpp index b528dca34..3304d03bc 100644 --- a/GridKit/Model/PhasorDynamics/SynchronousMachine/GenClassical/GenClassical.hpp +++ b/GridKit/Model/PhasorDynamics/SynchronousMachine/GenClassical/GenClassical.hpp @@ -8,7 +8,6 @@ #pragma once #include -#include #include #include @@ -19,9 +18,6 @@ namespace GridKit { template class BusBase; - - template - struct GenClassicalData; } // namespace PhasorDynamics } // namespace GridKit diff --git a/GridKit/Model/PhasorDynamics/SynchronousMachine/GenClassical/GenClassicalData.hpp b/GridKit/Model/PhasorDynamics/SynchronousMachine/GenClassical/GenClassicalData.hpp index 38d68e646..03be7a74d 100644 --- a/GridKit/Model/PhasorDynamics/SynchronousMachine/GenClassical/GenClassicalData.hpp +++ b/GridKit/Model/PhasorDynamics/SynchronousMachine/GenClassical/GenClassicalData.hpp @@ -36,7 +36,7 @@ namespace GridKit /// @warning GenClassical signal support is incomplete. These legacy signal /// names are not wired by SystemModel today; the intended refactor is to /// align this model with Genrou/Gensal by supporting `pmech`, `speed`, and - /// `efd` signals through ComponentSignals. + /// `efd` signals. enum class GenClassicalSignalInputs : size_t { exciter_signal, ///< Unique ID of the bus providing the exciter signal @@ -72,21 +72,13 @@ namespace GridKit * @todo Decide on naming scheme for model parameters. */ template - struct GenClassicalData : public ComponentData - { - GenClassicalData() = default; - - using Parameters = GenClassicalParameters; - using Buses = GenClassicalBuses; - using SignalInputs = GenClassicalSignalInputs; - using SignalOutputs = GenClassicalSignalOutputs; - using MonitorableVariables = GenClassicalMonitorableVariables; - }; + using GenClassicalData = + ComponentData; } // namespace PhasorDynamics } // namespace GridKit diff --git a/GridKit/Model/PhasorDynamics/SystemModel.hpp b/GridKit/Model/PhasorDynamics/SystemModel.hpp index 2f0524535..06f3a9956 100644 --- a/GridKit/Model/PhasorDynamics/SystemModel.hpp +++ b/GridKit/Model/PhasorDynamics/SystemModel.hpp @@ -5,6 +5,7 @@ #include #include +#include namespace GridKit { @@ -57,15 +58,15 @@ namespace GridKit using Component::map_to_csr_; public: - using ScalarT = scalar_type; - using IdxT = index_type; - using RealT = typename Model::Evaluator::RealT; - using CsrMatrixT = typename Model::Evaluator::CsrMatrixT; - using CooMatrixT = typename Model::Evaluator::CooMatrixT; - using BusT = BusBase; - using SignalT = SignalNode; - using ComponentT = Component; - using MonitorT = Model::VariableMonitorController; + using ScalarT = scalar_type; + using IdxT = index_type; + using RealT = typename Model::Evaluator::RealT; + using CsrMatrixT = typename Model::Evaluator::CsrMatrixT; + using CooMatrixT = typename Model::Evaluator::CooMatrixT; + using BusT = BusBase; + using SignalNodeT = SignalNode; + using ComponentT = Component; + using MonitorT = Model::VariableMonitorController; SystemModel(); @@ -96,25 +97,23 @@ namespace GridKit void updateTime(RealT t, RealT a) override; void addBus(BusT* bus); - void addSignal(SignalT* signal); void addComponent(ComponentT* component); void addFault(ComponentT* component); void setSystemBase(RealT freq_system_base, RealT va_system_base); BusT* getBus(IdxT bus_id); - SignalT* getSignal(IdxT signal_id); + SignalNodeT* getSignalNode(IdxT signal_id); ComponentT* getComponent(IdxT gridkit_component_id); BusFault* getBusFault(IdxT fault_id); private: - std::vector buses_; - std::vector signals_; - std::vector components_; + std::vector buses_; + SignalNodeSet signal_nodes_; + std::vector components_; - std::map gridkit_bus_indices_; ///< Map between gridkit_bus_id and bus_id - std::map gridkit_signal_indices_; ///< Map between gridkit_signal_id and signal_id - std::map gridkit_fault_indices_; ///< Map between fault_id and component_id + std::map gridkit_bus_indices_; ///< Map between gridkit_bus_id and bus_id + std::map gridkit_fault_indices_; ///< Map between fault_id and component_id bool owns_components_{false}; diff --git a/GridKit/Model/PhasorDynamics/SystemModelImpl.hpp b/GridKit/Model/PhasorDynamics/SystemModelImpl.hpp index b020f8f72..8720c474d 100644 --- a/GridKit/Model/PhasorDynamics/SystemModelImpl.hpp +++ b/GridKit/Model/PhasorDynamics/SystemModelImpl.hpp @@ -55,12 +55,10 @@ namespace GridKit addBus(bus); } - /// @todo Signal data needs to be populated by the parser. - /// See TODOs in SystemModelDataJSONParser + // Add signal nodes for (const auto& signaldata : data.signal) { - SignalNode* signal = new SignalNode(signaldata); - addSignal(signal); + signal_nodes_.add(signaldata); } // Add bus-to-signal adapters @@ -72,36 +70,9 @@ namespace GridKit bus_index = adapterdata.buses.at(BusToSignalAdapterBuses::bus); } - auto* adapter = new BusToSignalAdapter(getBus(bus_index)); - - if (adapterdata.signal_outputs.contains(BusToSignalAdapterSignalOutputs::vr)) - { - IdxT vr = adapterdata.signal_outputs.at(BusToSignalAdapterSignalOutputs::vr); - constexpr auto VREAL = BusToSignalAdapterInternalVariables::VREAL; - adapter->getSignals().template assignSignalNode(getSignal(vr)); - } - - if (adapterdata.signal_outputs.contains(BusToSignalAdapterSignalOutputs::vi)) - { - IdxT vi = adapterdata.signal_outputs.at(BusToSignalAdapterSignalOutputs::vi); - constexpr auto VIMAG = BusToSignalAdapterInternalVariables::VIMAG; - adapter->getSignals().template assignSignalNode(getSignal(vi)); - } - - if (adapterdata.signal_inputs.contains(BusToSignalAdapterSignalInputs::ir)) - { - IdxT ir = adapterdata.signal_inputs.at(BusToSignalAdapterSignalInputs::ir); - constexpr auto IREAL = BusToSignalAdapterExternalVariables::IREAL; - adapter->getSignals().template attachSignalNode(getSignal(ir)); - } - - if (adapterdata.signal_inputs.contains(BusToSignalAdapterSignalInputs::ii)) - { - IdxT ii = adapterdata.signal_inputs.at(BusToSignalAdapterSignalInputs::ii); - constexpr auto IIMAG = BusToSignalAdapterExternalVariables::IIMAG; - adapter->getSignals().template attachSignalNode(getSignal(ii)); - } - + auto* adapter = new BusToSignalAdapter(getBus(bus_index), + adapterdata, + signal_nodes_); addComponent(adapter); } @@ -120,8 +91,9 @@ namespace GridKit bus2_index = branchdata.buses.at(BranchBuses::bus2); } - auto* branch = new Branch( - getBus(bus1_index), getBus(bus2_index), branchdata); + auto* branch = new Branch(getBus(bus1_index), + getBus(bus2_index), + branchdata); addComponent(branch); } @@ -147,7 +119,8 @@ namespace GridKit { bus_index = loadzipdata.buses.at(LoadZIPBuses::bus); } - auto* loadzip = new LoadZIP(getBus(bus_index), loadzipdata); + auto* loadzip = new LoadZIP(getBus(bus_index), + loadzipdata); addComponent(loadzip); } @@ -159,33 +132,9 @@ namespace GridKit { bus_index = gendata.buses.at(GenrouBuses::bus); } - - auto* gen = new Genrou(getBus(bus_index), gendata); - - /// @todo Genrou (and likely other components) would need to name multiple - /// signal inlets and outlets. For now we have only speed out and mechanical - /// power in. - if (gendata.signal_outputs.contains(GenrouSignalOutputs::speed)) - { - IdxT speed = gendata.signal_outputs.at(GenrouSignalOutputs::speed); - constexpr auto OMEGA = GenrouInternalVariables::OMEGA; - gen->getSignals().template assignSignalNode(getSignal(speed)); - } - - if (gendata.signal_inputs.contains(GenrouSignalInputs::pmech)) - { - IdxT pmech = gendata.signal_inputs.at(GenrouSignalInputs::pmech); - constexpr auto PM = GenrouExternalVariables::PM; - gen->getSignals().template attachSignalNode(getSignal(pmech)); - } - - if (gendata.signal_inputs.contains(GenrouSignalInputs::efd)) - { - IdxT efd = gendata.signal_inputs.at(GenrouSignalInputs::efd); - constexpr auto EFD = GenrouExternalVariables::EFD; - gen->getSignals().template attachSignalNode(getSignal(efd)); - } - + auto* gen = new Genrou(getBus(bus_index), + gendata, + signal_nodes_); addComponent(gen); } @@ -198,29 +147,9 @@ namespace GridKit bus_index = gendata.buses.at(GensalBuses::bus); } - auto* gen = new Gensal(getBus(bus_index), gendata); - - if (gendata.signal_outputs.contains(GensalSignalOutputs::speed)) - { - IdxT speed = gendata.signal_outputs.at(GensalSignalOutputs::speed); - constexpr auto OMEGA = GensalInternalVariables::OMEGA; - gen->getSignals().template assignSignalNode(getSignal(speed)); - } - - if (gendata.signal_inputs.contains(GensalSignalInputs::pmech)) - { - IdxT pmech = gendata.signal_inputs.at(GensalSignalInputs::pmech); - constexpr auto PM = GensalExternalVariables::PM; - gen->getSignals().template attachSignalNode(getSignal(pmech)); - } - - if (gendata.signal_inputs.contains(GensalSignalInputs::efd)) - { - IdxT efd = gendata.signal_inputs.at(GensalSignalInputs::efd); - constexpr auto EFD = GensalExternalVariables::EFD; - gen->getSignals().template attachSignalNode(getSignal(efd)); - } - + auto* gen = new Gensal(getBus(bus_index), + gendata, + signal_nodes_); addComponent(gen); } @@ -239,22 +168,7 @@ namespace GridKit // Add Tgov1 governors for (const auto& govdata : data.gov) { - auto* gov = new Tgov1(govdata); - - if (govdata.signal_inputs.contains(Tgov1SignalInputs::speed)) - { - IdxT speed = govdata.signal_inputs.at(Tgov1SignalInputs::speed); - constexpr auto DELTAOMEGA = Tgov1ExternalVariables::DELTAOMEGA; - gov->getSignals().template attachSignalNode(getSignal(speed)); - } - - if (govdata.signal_outputs.contains(Tgov1SignalOutputs::pmech)) - { - IdxT pmech = govdata.signal_outputs.at(Tgov1SignalOutputs::pmech); - constexpr auto PM = Tgov1InternalVariables::PM; - gov->getSignals().template assignSignalNode(getSignal(pmech)); - } - + auto* gov = new Tgov1(govdata, signal_nodes_); addComponent(gov); } @@ -266,29 +180,9 @@ namespace GridKit bus_index = excitedata.buses.at(Ieeet1Buses::bus); } - auto* exciter = new Ieeet1(getBus(bus_index), excitedata); - - if (excitedata.signal_inputs.contains(Ieeet1SignalInputs::speed)) - { - IdxT speed = excitedata.signal_inputs.at(Ieeet1SignalInputs::speed); - constexpr auto OMEGA = Ieeet1ExternalVariables::OMEGA; - exciter->getSignals().template attachSignalNode(getSignal(speed)); - } - - if (excitedata.signal_outputs.contains(Ieeet1SignalOutputs::efd)) - { - IdxT efd = excitedata.signal_outputs.at(Ieeet1SignalOutputs::efd); - constexpr auto EFD = Ieeet1InternalVariables::EFD; - exciter->getSignals().template assignSignalNode(getSignal(efd)); - } - - if (excitedata.signal_inputs.contains(Ieeet1SignalInputs::vs)) - { - IdxT vs = excitedata.signal_inputs.at(Ieeet1SignalInputs::vs); - constexpr auto VS = Ieeet1ExternalVariables::VS; - exciter->getSignals().template attachSignalNode(getSignal(vs)); - } - + auto* exciter = new Ieeet1(getBus(bus_index), + excitedata, + signal_nodes_); addComponent(exciter); } @@ -300,66 +194,24 @@ namespace GridKit bus_index = excitedata.buses.at(SexsPtiBuses::bus); } - auto* exciter = new SexsPti(getBus(bus_index), excitedata); - - if (excitedata.signal_outputs.contains(SexsPtiSignalOutputs::efd)) - { - IdxT efd = excitedata.signal_outputs.at(SexsPtiSignalOutputs::efd); - constexpr auto EFD = SexsPtiInternalVariables::EFD; - exciter->getSignals().template assignSignalNode(getSignal(efd)); - } - - if (excitedata.signal_inputs.contains(SexsPtiSignalInputs::vs)) - { - IdxT vs = excitedata.signal_inputs.at(SexsPtiSignalInputs::vs); - constexpr auto VS = SexsPtiExternalVariables::VS; - exciter->getSignals().template attachSignalNode(getSignal(vs)); - } - + auto* exciter = new SexsPti(getBus(bus_index), + excitedata, + signal_nodes_); addComponent(exciter); } // Add IEEEST stabilizers for (const auto& stabdata : data.stabilizer) { - auto* stabilizer = new Ieeest(stabdata); - - if (stabdata.signal_inputs.contains(IeeestSignalInputs::input)) - { - IdxT input = stabdata.signal_inputs.at(IeeestSignalInputs::input); - constexpr auto U = IeeestExternalVariables::U; - stabilizer->getSignals().template attachSignalNode(getSignal(input)); - } - - if (stabdata.signal_outputs.contains(IeeestSignalOutputs::output)) - { - IdxT output = stabdata.signal_outputs.at(IeeestSignalOutputs::output); - constexpr auto VSS = IeeestInternalVariables::VSS; - stabilizer->getSignals().template assignSignalNode(getSignal(output)); - } - + auto* stabilizer = new Ieeest(stabdata, signal_nodes_); addComponent(stabilizer); } // Add constant signal sources for (const auto& srcdata : data.constant_source) { - auto* source = new ConstantSignalSource(srcdata); - - using SignalOutputs = ConstantSignalSourceSignalOutputs; - if (srcdata.signal_outputs.contains(SignalOutputs::sr)) - { - IdxT sr = srcdata.signal_outputs.at(SignalOutputs::sr); - constexpr auto SREAL = ConstantSignalSourceInternalVariables::SREAL; - source->getSignals().template assignSignalNode(getSignal(sr)); - } - if (srcdata.signal_outputs.contains(SignalOutputs::si)) - { - IdxT si = srcdata.signal_outputs.at(SignalOutputs::si); - constexpr auto SIMAG = ConstantSignalSourceInternalVariables::SIMAG; - source->getSignals().template assignSignalNode(getSignal(si)); - } - + auto* source = new ConstantSignalSource(srcdata, + signal_nodes_); addComponent(source); } @@ -401,11 +253,6 @@ namespace GridKit { delete bus; } - - for (auto signal : signals_) - { - delete signal; - } } } @@ -1032,21 +879,6 @@ namespace GridKit buses_.push_back(bus); } - /** - * @brief Add signal - * - * Add signal at the end of the signals array and map signal ID with - * GridKit's ID for the signal - * - */ - template - void SystemModel::addSignal(SignalT* signal) - { - IdxT gridkit_signal_id = static_cast(signals_.size()); - gridkit_signal_indices_[signal->signalId()] = gridkit_signal_id; - signals_.push_back(signal); - } - /** * @brief Add component * @@ -1119,13 +951,10 @@ namespace GridKit * */ template - SystemModel::SignalT* - SystemModel::getSignal(IdxT signal_id) + SystemModel::SignalNodeT* + SystemModel::getSignalNode(IdxT signal_id) { - // Should fail if user-provided signal_id is incorrect - IdxT gridkit_signal_id = gridkit_signal_indices_.at(signal_id); - assert((signals_[gridkit_signal_id])->signalId() == signal_id); - return signals_[gridkit_signal_id]; + return signal_nodes_[signal_id]; } /** diff --git a/GridKit/Utilities/CMakeLists.txt b/GridKit/Utilities/CMakeLists.txt index 7a94598a6..08f33dadf 100644 --- a/GridKit/Utilities/CMakeLists.txt +++ b/GridKit/Utilities/CMakeLists.txt @@ -11,7 +11,10 @@ add_subdirectory(CliArgs) install(TARGETS Utilities EXPORT gridkit-targets) install( FILES Colors.hpp + Enum.hpp Errors.hpp FileIO.hpp MapFromCsr.hpp + Stream.hpp + String.hpp DESTINATION include/GridKit/Utilities) diff --git a/GridKit/Utilities/Enum.hpp b/GridKit/Utilities/Enum.hpp new file mode 100644 index 000000000..bde4ce73a --- /dev/null +++ b/GridKit/Utilities/Enum.hpp @@ -0,0 +1,21 @@ +#pragma once + +namespace GridKit +{ + namespace Utilities + { + template + concept SizedEnum = std::is_enum_v && requires { T::SIZE; }; + + enum class EmptyEnum : std::size_t + { + SIZE = 0 + }; + + template + constexpr std::size_t enum_size() + { + return static_cast(enum_type::SIZE); + } + } // namespace Utilities +} // namespace GridKit diff --git a/tests/UnitTests/PhasorDynamics/BusToSignalAdapterTests.hpp b/tests/UnitTests/PhasorDynamics/BusToSignalAdapterTests.hpp index ae4a8fafd..e9f793884 100644 --- a/tests/UnitTests/PhasorDynamics/BusToSignalAdapterTests.hpp +++ b/tests/UnitTests/PhasorDynamics/BusToSignalAdapterTests.hpp @@ -67,20 +67,18 @@ namespace GridKit IdxT ir_index{INVALID_INDEX}; IdxT ii_index{INVALID_INDEX}; - ir_sig.set(&Ir, &ir_index); - ii_sig.set(&Ii, &ii_index); + ir_sig.link(&Ir, &ir_index); + ii_sig.link(&Ii, &ii_index); using namespace GridKit::PhasorDynamics; - constexpr auto VREAL = BusToSignalAdapterInternalVariables::VREAL; - constexpr auto VIMAG = BusToSignalAdapterInternalVariables::VIMAG; - constexpr auto IREAL = BusToSignalAdapterExternalVariables::IREAL; - constexpr auto IIMAG = BusToSignalAdapterExternalVariables::IIMAG; + using SignalIn = BusToSignalAdapterSignalInputs; + using SignalOut = BusToSignalAdapterSignalOutputs; auto adapter = AdapterT(&bus); - adapter.getSignals().template assignSignalNode(&vr_sig); - adapter.getSignals().template assignSignalNode(&vi_sig); - adapter.getSignals().template attachSignalNode(&ir_sig); - adapter.getSignals().template attachSignalNode(&ii_sig); + adapter.getPorts().out[SignalOut::vr].connect(&vr_sig); + adapter.getPorts().out[SignalOut::vi].connect(&vi_sig); + adapter.getPorts().in[SignalIn::ir].connect(&ir_sig); + adapter.getPorts().in[SignalIn::ii].connect(&ii_sig); adapter.allocate(); success *= (adapter.verify() == 0); success *= (vr_sig.read() == Vr); diff --git a/tests/UnitTests/PhasorDynamics/ExciterSexsPtiTests.hpp b/tests/UnitTests/PhasorDynamics/ExciterSexsPtiTests.hpp index 6aa61e04a..104f5e19d 100644 --- a/tests/UnitTests/PhasorDynamics/ExciterSexsPtiTests.hpp +++ b/tests/UnitTests/PhasorDynamics/ExciterSexsPtiTests.hpp @@ -61,11 +61,13 @@ namespace GridKit PhasorDynamics::SignalNode efd_node; ScalarT efd_value{0.0}; IdxT efd_index = INVALID_INDEX; - efd_node.set(&efd_value, &efd_index); + efd_node.link(&efd_value, &efd_index); - auto data = makeTestData(); - PhasorDynamics::Exciter::SexsPti exciter(&bus, data); - exciter.getSignals().template assignSignalNode(&efd_node); + using namespace GridKit::PhasorDynamics::Exciter; + + auto data = makeTestData(); + auto exciter = SexsPti(&bus, data); + exciter.getPorts().out[SexsPtiSignalOutputs::efd].connect(&efd_node); exciter.allocate(); efd_node.init(1.2); @@ -107,13 +109,15 @@ namespace GridKit ScalarT vs_value{0.25}; IdxT efd_index = INVALID_INDEX; IdxT vs_index = 4; - efd_node.set(&efd_value, &efd_index); - vs_node.set(&vs_value, &vs_index); + efd_node.link(&efd_value, &efd_index); + vs_node.link(&vs_value, &vs_index); - auto data = makeTestData(); - PhasorDynamics::Exciter::SexsPti exciter(&bus, data); - exciter.getSignals().template assignSignalNode(&efd_node); - exciter.getSignals().template attachSignalNode(&vs_node); + using namespace GridKit::PhasorDynamics::Exciter; + + auto data = makeTestData(); + auto exciter = SexsPti(&bus, data); + exciter.getPorts().out[SexsPtiSignalOutputs::efd].connect(&efd_node); + exciter.getPorts().in[SexsPtiSignalInputs::vs].connect(&vs_node); exciter.allocate(); efd_node.init(1.2); diff --git a/tests/UnitTests/PhasorDynamics/StabilizerIeeestTests.hpp b/tests/UnitTests/PhasorDynamics/StabilizerIeeestTests.hpp index 15b64876f..f5d40f117 100644 --- a/tests/UnitTests/PhasorDynamics/StabilizerIeeestTests.hpp +++ b/tests/UnitTests/PhasorDynamics/StabilizerIeeestTests.hpp @@ -58,15 +58,17 @@ namespace GridKit IdxT vss_index = INVALID_INDEX; // Link signal nodes to backing storage - u_node.set(&u_value, &u_index); - vss_node.set(&vss_value, &vss_index); + u_node.link(&u_value, &u_index); + vss_node.link(&vss_value, &vss_index); - auto data = makeTestData(); - PhasorDynamics::Stabilizer::Ieeest stab(data); + using namespace GridKit::PhasorDynamics::Stabilizer; + + auto data = makeTestData(); + auto stab = Ieeest(data); // Wire: stabilizer reads u_node as input, writes vss_node as output - stab.getSignals().template attachSignalNode(&u_node); - stab.getSignals().template assignSignalNode(&vss_node); + stab.getPorts().in[IeeestSignalInputs::input].connect(&u_node); + stab.getPorts().out[IeeestSignalOutputs::output].connect(&vss_node); stab.allocate(); success *= (stab.verify() == 0); @@ -109,14 +111,16 @@ namespace GridKit ScalarT vss_value{0.0}; IdxT vss_index = INVALID_INDEX; - u_node.set(&u_value, &u_index); - vss_node.set(&vss_value, &vss_index); + u_node.link(&u_value, &u_index); + vss_node.link(&vss_value, &vss_index); + + using namespace GridKit::PhasorDynamics::Stabilizer; - auto data = makeTestData(); - PhasorDynamics::Stabilizer::Ieeest stab(data); + auto data = makeTestData(); + auto stab = Ieeest(data); - stab.getSignals().template attachSignalNode(&u_node); - stab.getSignals().template assignSignalNode(&vss_node); + stab.getPorts().in[IeeestSignalInputs::input].connect(&u_node); + stab.getPorts().out[IeeestSignalOutputs::output].connect(&vss_node); stab.allocate(); stab.initialize(); @@ -201,12 +205,14 @@ namespace GridKit DepVar vss_value{0.0}; IdxT vss_index = INVALID_INDEX; - u_node.set(&u_value, &u_index); - vss_node.set(&vss_value, &vss_index); + u_node.link(&u_value, &u_index); + vss_node.link(&vss_value, &vss_index); + + using namespace GridKit::PhasorDynamics::Stabilizer; - PhasorDynamics::Stabilizer::Ieeest stab(ieeestdata); - stab.getSignals().template attachSignalNode(&u_node); - stab.getSignals().template assignSignalNode(&vss_node); + auto stab = Ieeest(ieeestdata); + stab.getPorts().in[IeeestSignalInputs::input].connect(&u_node); + stab.getPorts().out[IeeestSignalOutputs::output].connect(&vss_node); stab.allocate(); stab.initialize(); @@ -292,12 +298,14 @@ namespace GridKit ScalarT vss_value{0.0}; IdxT vss_index = INVALID_INDEX; - u_node.set(&u_value, &u_index); - vss_node.set(&vss_value, &vss_index); + u_node.link(&u_value, &u_index); + vss_node.link(&vss_value, &vss_index); + + using namespace GridKit::PhasorDynamics::Stabilizer; - PhasorDynamics::Stabilizer::Ieeest stab(ieeestdata); - stab.getSignals().template attachSignalNode(&u_node); - stab.getSignals().template assignSignalNode(&vss_node); + auto stab = Ieeest(ieeestdata); + stab.getPorts().in[IeeestSignalInputs::input].connect(&u_node); + stab.getPorts().out[IeeestSignalOutputs::output].connect(&vss_node); stab.allocate(); stab.initialize();