Skip to content

Signal Bus Implementation#173

Closed
lukelowry wants to merge 91 commits intodevelopfrom
lukel/signal_bus_complete_dev
Closed

Signal Bus Implementation#173
lukelowry wants to merge 91 commits intodevelopfrom
lukel/signal_bus_complete_dev

Conversation

@lukelowry
Copy link
Copy Markdown
Collaborator

@lukelowry lukelowry commented Jul 4, 2025

Description

A preliminary BusSignal implementation that is a stepping point to a fully developed Bus Hierarchy. Supported signals can be specified by the Data structure input.

Closes Issue #154

An exciter model implementation and validated example (see branch IEEET1) are ready for PR once this is merged

Proposed changes

  • BusSignal Class inherits directly from BusBase, which still has virtual voltage/current functions. This is retained only because creating a seperate base grows the scope of this PR beyond what it was intended for. We can segment these models in the next PR.
  • Signal connections can be specified in (Model)Data struct.
  • SystemModel now auto implements governor.
  • Component class has helper functions that implement 'safe' versions of signal read/write/init to make model implementation easier.
  • Removes the GovernorBase class which was only added as a temporary solution.

Checklist

  • All tests pass.
  • Code compiles cleanly with flags -Wall -Wpedantic -Wconversion -Wextra.
  • The new code follows GridKit™ style guidelines.
  • There are unit tests for the new code.
  • The new code is documented.
  • The feature branch is rebased with respect to the target branch.

Further comments

The cmake of many filles had to be changed to include the governor program since I added it to the SystemModel. Is there a better way to have files that use SystemModel to not require all the same programs?

Future PR will need to create seperate bases: BusElectricalBase and BusSignalBase. This is a temporary solutions before these new bases are formed, because that task is large to undertake.

Forgive the large quantity of commits. I have a VM in addition to local WSL, so I am making frequent commits to the origin.

Copy link
Copy Markdown
Collaborator

@pelesh pelesh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this first cut I suggest we implement signal bus as a standalone class inheriting directly from Model::Evaluator.

Comment thread src/Model/PhasorDynamics/Bus/BusSignal.cpp
Comment thread src/Model/PhasorDynamics/Bus/BusSignal.hpp Outdated
Comment thread src/Model/PhasorDynamics/Bus/BusSignal.hpp Outdated
@lukelowry
Copy link
Copy Markdown
Collaborator Author

Okay, I have performed the following:

  • Rebased the branch
  • Refactor 'send' to 'write'
  • Use the new Port data structure to input the BusSignal index

After this merge, I will make the following Issues:

  • Electrical and Signal Bus Base Classes
  • Implement Governor with new Data Structure (it is currently using old struct)

Seeking further review @pelesh and @abirchfield . Hoping we unlock doors with this PR!

@lukelowry lukelowry requested a review from pelesh July 10, 2025 21:42
@pelesh pelesh requested a review from nkoukpaizan July 10, 2025 21:43
Copy link
Copy Markdown
Collaborator

@superwhiskers superwhiskers left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

some minor suggestions. i don't think i have any major comments.

Comment on lines +51 to +58
// if(is_initialized_)
//{
// std::cout << "ERROR!";
// }
// else{
// y_[0] = value;
// is_initialized_ = true;
// }
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is this commented out? it appears to implement the intended behavior but instead we always set y_[0] to the value provided.

Comment on lines +41 to +47
/**
* @brief A one-time initialization function
* that can be called by any.
*
* @return state value of signal
*/
void initial_value(ScalarT value) override
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this method should be called initialValue to follow the naming conventions we have.

Comment on lines +31 to +79
/**
* @brief A helper function to easily init
* a signal value wiithout needing to check if initialized
*
* @param signal The signal bus object pointer
* @param value The value to send to the equality constraint
*/
void safeInit(bus_type* signal, ScalarT value)
{
if (signal)
{
signal->initial_value(value);
}
}

/**
* @brief A helper function to easily init
* a signal value wiithout needing to check if initialized
*
* @param signal The signal bus object pointer
* @param value The value to send to the equality constraint
*/
void safeWrite(bus_type* signal, ScalarT value)
{
if (signal)
{
signal->write(value);
}
}

/**
* @brief A helper function to easily read
* the state of the signal without checking nullptr
*
*
* @param signal The signal bus object pointer
* @param default_val The value to return if no signal
*/
ScalarT safeRead(bus_type* signal, ScalarT default_val)
{
if (signal)
{
return signal->read();
}
else
{
return default_val;
}
}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'm not sure these would belong on the Component class to be totally honest. they seem to exclusively be helper functions with no need to access any private data present on a Component; i think they would make more sense as standalone functions in a Utility.hpp file or something present within src/Model/PhasorDynamics.

{
bus, ///< Unique ID of the connecting bus
pmech_signal, ///< Unique ID of the governor Pmech signal.
speed_signal, ///< Unique ID of the speed signal of the machine.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should also modify src/Model/PhasorDynamics/INPUT_FORMAT.md if we're adding new ports

Comment on lines +128 to 158
IdxT pmech_index = 0;
IdxT speed_index = 0;

bool has_bus = gendata.ports.contains(GenrouData<ScalarT, IdxT>::Ports::bus);
bool has_pmech_signal = gendata.ports.contains(GenrouData<ScalarT, IdxT>::Ports::pmech_signal);
bool has_speed_signal = gendata.ports.contains(GenrouData<ScalarT, IdxT>::Ports::speed_signal);

if (has_bus && has_pmech_signal && has_speed_signal)
{

bus_index = gendata.ports.at(GenrouData<ScalarT, IdxT>::Ports::bus);
pmech_index = gendata.ports.at(GenrouData<ScalarT, IdxT>::Ports::pmech_signal);
speed_index = gendata.ports.at(GenrouData<ScalarT, IdxT>::Ports::speed_signal);

auto* gen = new Genrou<ScalarT, IdxT>(
getBus(bus_index),
getBus(pmech_index),
getBus(speed_index),
gendata);
addComponent(gen);
}
else{
bus_index = gendata.ports.at(GenrouData<ScalarT, IdxT>::Ports::bus);

auto* gen = new Genrou<ScalarT, IdxT>(
getBus(bus_index),
nullptr,
nullptr,
gendata);
addComponent(gen);
}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
IdxT pmech_index = 0;
IdxT speed_index = 0;
bool has_bus = gendata.ports.contains(GenrouData<ScalarT, IdxT>::Ports::bus);
bool has_pmech_signal = gendata.ports.contains(GenrouData<ScalarT, IdxT>::Ports::pmech_signal);
bool has_speed_signal = gendata.ports.contains(GenrouData<ScalarT, IdxT>::Ports::speed_signal);
if (has_bus && has_pmech_signal && has_speed_signal)
{
bus_index = gendata.ports.at(GenrouData<ScalarT, IdxT>::Ports::bus);
pmech_index = gendata.ports.at(GenrouData<ScalarT, IdxT>::Ports::pmech_signal);
speed_index = gendata.ports.at(GenrouData<ScalarT, IdxT>::Ports::speed_signal);
auto* gen = new Genrou<ScalarT, IdxT>(
getBus(bus_index),
getBus(pmech_index),
getBus(speed_index),
gendata);
addComponent(gen);
}
else{
bus_index = gendata.ports.at(GenrouData<ScalarT, IdxT>::Ports::bus);
auto* gen = new Genrou<ScalarT, IdxT>(
getBus(bus_index),
nullptr,
nullptr,
gendata);
addComponent(gen);
}
bus_type* bus = nullptr;
bus_type* pmech = nullptr;
bus_type* speed = nullptr;
if (gendata.ports.contains(GenrouData<ScalarT, IdxT>::Ports::bus))
{
bus = getBus(gendata.ports.at(GenrouData<ScalarT, IdxT>::Ports::bus));
}
if (gendata.ports.contains(GenrouData<ScalarT, IdxT>::Ports::pmech_signal))
{
pmech = getBus(gendata.ports.at(GenrouData<ScalarT, IdxT>::Ports::pmech_signal));
}
if (gendata.ports.contains(GenrouData<ScalarT, IdxT>::Ports::speed_signal))
{
speed = getBus(gendata.ports.at(GenrouData<ScalarT, IdxT>::Ports::speed_signal));
}
auto* gen = new Genrou<ScalarT, IdxT>(bus, pmech, speed, gendata);
addComponent(gen);

you can make this much cleaner

Comment on lines +177 to +181
/* Need to add signals to gendata class */
auto* gov = new Governor::Tgov1<ScalarT, IdxT>(
getBus(govdata.signal_pmech),
getBus(govdata.signal_speed),
govdata);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it the case with Tgov1 that these signals are non-optional? (that sounds correct to me but i want to make sure as it is not documented in the input format)

@pelesh
Copy link
Copy Markdown
Collaborator

pelesh commented Jul 18, 2025

Closing this for now, but we can revisit this implementation approach later.

@pelesh pelesh closed this Jul 18, 2025
@lukelowry lukelowry deleted the lukel/signal_bus_complete_dev branch August 23, 2025 02:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add signal bus model to phasor dynamics family

4 participants