Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
ee0b1b1
Add BusPartitionInterface to define partition boundaries
abdourahmanbarry Aug 26, 2026
ca6fe44
Add SubsystemModel for partition evaluation
abdourahmanbarry Aug 26, 2026
c315040
Add HIRES test for SubsystemModel
abdourahmanbarry Aug 26, 2026
ac8769e
Add protected access to SystemModelPowerElectronics
abdourahmanbarry Aug 26, 2026
6c76011
Change external data vector to use GridKit vectors
abdourahmanbarry Aug 28, 2026
a67eef2
Updated SystemModelPowerElectronics.hpp
abdourahmanbarry Aug 28, 2026
3d36c47
Update README.md for Hires problem
abdourahmanbarry Aug 28, 2026
35af962
Minor update
abdourahmanbarry Aug 28, 2026
239fbdd
Apply pre-commit fixes
abdourahmanbarry Sep 3, 2026
4b4c8b6
Address leak in test
abdourahmanbarry Sep 8, 2026
50c8f26
Change neg1 to INVALID_INDEX
abdourahmanbarry Sep 12, 2026
ac21a74
Simplified SubsystemModel code
abdourahmanbarry Sep 19, 2026
e37d476
Update BusPartitionInterface constructor and documentation
abdourahmanbarry Sep 19, 2026
ed86caa
Update test case
abdourahmanbarry Sep 19, 2026
daba0ee
Bug fix
abdourahmanbarry Oct 12, 2026
ceb7c5c
Update SubsystemModel
abdourahmanbarry Oct 12, 2026
bc769a9
Add BusLinePartitionInterface
abdourahmanbarry Oct 12, 2026
88043c4
Update CMake
abdourahmanbarry Oct 12, 2026
ccfaa9f
Forward time updates to enclosed component in BusPartitionInterface
abdourahmanbarry Oct 18, 2026
3de0eba
Update SubsystemModel
abdourahmanbarry Oct 18, 2026
768622f
Remove cloned component and move connection index initialization to c…
abdourahmanbarry Oct 18, 2026
b023eb3
Remove Hires Test problem
abdourahmanbarry Oct 18, 2026
39f879c
Add partition PowerElectronics Test problem
abdourahmanbarry Oct 18, 2026
73869f1
Update CMake file in tests
abdourahmanbarry Oct 18, 2026
489655c
Remove BusPartitionInterface as requested during code review
abdourahmanbarry Oct 18, 2026
3c0aa34
change "counter" to "index"
abdourahmanbarry Oct 18, 2026
4a1235e
Set f_ext_data_ to zero
abdourahmanbarry Oct 18, 2026
b6581b6
Updated SystemModels
abdourahmanbarry Oct 30, 2026
4f1fa56
Update Interfaces
abdourahmanbarry Oct 30, 2026
cde9d74
Update tests
abdourahmanbarry Oct 30, 2026
c96a613
Apply pre-commit fixes
abdourahmanbarry Oct 30, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions GridKit/Model/PowerElectronics/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,13 @@ add_subdirectory(TransmissionLine)
add_subdirectory(MicrogridLoad)
add_subdirectory(MicrogridLine)
add_subdirectory(MicrogridBusDQ)
add_subdirectory(PartitionInterface)

install(
FILES Component.hpp
Node.hpp
SystemModel.hpp
NodeBase.hpp
ExternalConnection.hpp
SubsystemModel.hpp
DESTINATION include/GridKit/Model/PowerElectronics)
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@

#include "BusLinePartitionInterface.hpp"

#include <cstddef>
#include <format>

#include <GridKit/Model/PowerElectronics/Bus/MicrogridBus.hpp>
#include <GridKit/Model/PowerElectronics/ExternalConnection.hpp>
#include <GridKit/Model/PowerElectronics/MicrogridLine/MicrogridLine.hpp>

namespace GridKit
{

namespace PowerElectronics
{

/**
* @brief Construct a partition interface between a bus and a line.
*
* The interface consists of four external connections: two bus variables
* and two line current variables. During partitioning, these connections
* are mapped to the appropriate subsystem storage.
*
* The line currents contribute to the bus residual equations, with signs
* determined by the orientation of the line relative to the bus.
*
* @pre The bus and line connection indices have been allocated.
*
* @param bus Bus associated with the partition interface.
* @param component Line component across the partition boundary.
* @param id Unique identifier for the partition interface.
*/
template <class ScalarT, typename IdxT>
BusLinePartitionInterface<ScalarT, IdxT>::BusLinePartitionInterface(node_type* bus, component_type* component, IdxT id)

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.

Yes, this is much simpler and easier to understand than BusPartitionInterface. I recommend adding a unit test for this approach or updating the existing test. My preference would be to only keep BusLinePartitionInterface for now and only introduce BusPartitionInterface if/when the need arises.

Also, I believe we don't need clone() in this approach. We can directly compute and store the relevant indices and signs in the constructor.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Since the BusLinePartitionInterface is specific to the connection between a bus and a line, removing BusPartitionInterface means the Hires Test problem can no longer be partitioned as in the current design, since it does not contain a line.

I plan to keep the BusPartitionInterface and the Hires test problem in a separate branch for order testing of my cosims methods, without any plans of merging to develop. For now I have removed the Hires test and added a new Power Electronics test to validate the partitioning implementation.

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.

Since Hires is not specific to PowerElectronics, I recommend a custom evaluator with the partitions in the unit test, similar to what's being done in #614. A PR with Hires testing the (higer-order) co-simulation methods is welcome.

{
if (dynamic_cast<MicrogridBus<ScalarT, IdxT>*>(bus) == nullptr)
{
throw std::invalid_argument("BusLinePartitionInterface: Requires a MicrogridBus.");
}

if (dynamic_cast<MicrogridLine<ScalarT, IdxT>*>(component) == nullptr)
{
throw std::invalid_argument("BusLinePartitionInterface: Requires a MicrogridLine.");
}

if (!bus->isAllocated() or !component->isAllocated())
{
throw std::invalid_argument("BusLinePartitionInterface: Malformed network. Ensure components are allocated before partitioning.");
}

size_ = 4;
n_intern_ = 0;
n_extern_ = 4;
idc_ = id;
extern_indices_ = {0, 1, 2, 3};
nnz_ = 2;

// Global indices associated with the local bus.
auto bus_idx_i = bus->getNodeConnection(0).idx_;
auto bus_idx_j = bus->getNodeConnection(1).idx_;

const bool is_left_connection = bus_idx_i == component->getNodeConnection(1) && bus_idx_j == component->getNodeConnection(2);
const bool is_right_connection = bus_idx_i == component->getNodeConnection(3) && bus_idx_j == component->getNodeConnection(4);

if (is_left_connection == is_right_connection)
{
throw std::invalid_argument(
std::format(
"Invalid partition interface detected. Bus(ID={}), Component(ID={}). "
"Malformed topology or no partitionable connection was found.",
bus->busID(),
component->getIDcomponent()));
}

sign_ = is_left_connection ? -1.0 : 1.0;
connection_indices_.resize(static_cast<size_t>(size_));

connection_indices_[0] = component->getNodeConnection(is_left_connection ? 1 : 3);
connection_indices_[1] = component->getNodeConnection(is_left_connection ? 2 : 4);
connection_indices_[2] = component->getNodeConnection(5);
connection_indices_[3] = component->getNodeConnection(6);
}

template <class ScalarT, typename IdxT>
int BusLinePartitionInterface<ScalarT, IdxT>::allocate()
{
Component<ScalarT, IdxT>::allocate();

this->setConnectionNodes(0, connection_indices_[0]);
this->setConnectionNodes(1, connection_indices_[1]);
this->setConnectionNodes(2, connection_indices_[2]);
this->setConnectionNodes(3, connection_indices_[3]);

return 0;
}

template <class ScalarT, typename IdxT>
int BusLinePartitionInterface<ScalarT, IdxT>::setAbsoluteTolerance(RealT abs_tol)
{
abs_tol_.setToConst(static_cast<ScalarT>(abs_tol));
return 0;
}

/**
* @brief Initialize the partition interface.
*
* @return 0 on success.
*/
template <class ScalarT, typename IdxT>
int BusLinePartitionInterface<ScalarT, IdxT>::initialize()
{
return 0;
}

/**
* @brief Identify differential variables
*/
template <class ScalarT, typename IdxT>
int BusLinePartitionInterface<ScalarT, IdxT>::tagDifferentiable()
{
return 0;
}

/**
* @brief Evaluates the internal residual.
*
* All variables are external from the interface's point of view, so there
* are no internal residual contributions to evaluate.
*
* @return 0 on success.
*/
template <class ScalarT, typename IdxT>
int BusLinePartitionInterface<ScalarT, IdxT>::evaluateInternalResidual()
{
return 0;
}

/**
* @brief Evaluate the line contribution to the external bus residual.
*
* Adds the two line current residual contributions to the corresponding
* bus residual equations. The sign of each contribution is determined by
* whether the bus is connected to the left or right end of the line,
* consistent with the line current orientation.
*
* @return 0 on success.
*/
template <class ScalarT, typename IdxT>
int BusLinePartitionInterface<ScalarT, IdxT>::evaluateExternalResidual()
{
*f_ext_[0] += sign_ * *y_ext_[2];
*f_ext_[1] += sign_ * *y_ext_[3];

return 0;
}

/**
* @brief Evaluate the Jacobian contributions associated with the bus.
*
* Evaluates the Jacobian of the entries whose residual rows correspond
* to variables belonging to the connected bus.
*
* @note Some of these entries may not be required to form the Jacobian of the
* subsystem, but may be useful for parallel Jacobian assembly.
*
* @return 0 on success.
*/
template <class ScalarT, typename IdxT>
int BusLinePartitionInterface<ScalarT, IdxT>::evaluateJacobian()
{
this->zeroJacMatrix();

// Jacobian entries corresponding to the two transferred line contributions.
std::vector<IdxT> rtemp{0, 1};
std::vector<IdxT> ctemp{2, 3};
std::vector<RealT> valtemp{sign_, sign_};

this->setJacValues(rtemp, ctemp, valtemp);

return 0;
}

template <class ScalarT, typename IdxT>
int BusLinePartitionInterface<ScalarT, IdxT>::evaluateIntegrand()
{
return 0;
}

template <class ScalarT, typename IdxT>
int BusLinePartitionInterface<ScalarT, IdxT>::initializeAdjoint()
{
return 0;
}

template <class ScalarT, typename IdxT>
int BusLinePartitionInterface<ScalarT, IdxT>::evaluateAdjointResidual()
{
return 0;
}

template <class ScalarT, typename IdxT>
int BusLinePartitionInterface<ScalarT, IdxT>::evaluateAdjointIntegrand()
{
return 0;
}

// Available template instantiations
template class BusLinePartitionInterface<double, long int>;
template class BusLinePartitionInterface<double, size_t>;
template class BusLinePartitionInterface<DependencyTracking::Variable, long int>;
template class BusLinePartitionInterface<DependencyTracking::Variable, size_t>;
} // namespace PowerElectronics
} // namespace GridKit
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/**
* @file BusLinePartitionInterface.hpp
* @author Abdourahman Barry (abdourahman@vt.edu)
*
*/

#pragma once

#include <GridKit/Model/PowerElectronics/Component.hpp>
#include <GridKit/Model/PowerElectronics/NodeBase.hpp>
#include <GridKit/Model/PowerElectronics/PartitionInterface/PartitionInterface.hpp>

namespace GridKit
{
namespace PowerElectronics
{
/**
* @brief Partition interface between a bus and a line.
*
* The BusLinePartitionInterface provides a mechanism for a partition to access
* the current injection it requires from a line located in a neighboring
* partition, without evaluating the line within the local partition.
*
* The interface tracks four variables associated with the bus-line connection.
* The first two variables belong to the bus participating in the partition
* boundary, while the last two belong to the line connected to that bus. All
* four variables are treated as external variables by the interface.
*
* During partitioning, the interface is added to the partition containing the
* bus. Since the first two interface variables belong to components within the
* partition, the subsystem maps them to its local variables. The last two
* variables belong to the line in the neighboring partition and are therefore
* identified by the subsystem as external variables.
*
* During subsystem evaluation, the interface uses these external line variables
* to provide the corresponding current injection to the bus equations. The
* current contribution is added to or subtracted from the bus residual according
* to which end of the line is connected to the bus.
*
* @tparam ScalarT Scalar type used for state and residual values.
* @tparam IdxT Index type used for component and variable indices.
*/
template <class ScalarT, typename IdxT>
class BusLinePartitionInterface : public PartitionInterface<ScalarT, IdxT>
{

using component_type = Component<ScalarT, IdxT>;
using node_type = typename PowerElectronics::NodeBase<ScalarT, IdxT>;
using RealT = typename Component<ScalarT, IdxT>::RealT;

using Component<ScalarT, IdxT>::size_;
using Component<ScalarT, IdxT>::nnz_;
using Component<ScalarT, IdxT>::time_;
using Component<ScalarT, IdxT>::alpha_;
using Component<ScalarT, IdxT>::y_ext_;
using Component<ScalarT, IdxT>::y_int_;
using Component<ScalarT, IdxT>::yp_ext_;
using Component<ScalarT, IdxT>::yp_int_;
using Component<ScalarT, IdxT>::tag_;
using Component<ScalarT, IdxT>::abs_tol_;
using Component<ScalarT, IdxT>::f_ext_;
using Component<ScalarT, IdxT>::f_int_;
using Component<ScalarT, IdxT>::g_;
using Component<ScalarT, IdxT>::yB_;
using Component<ScalarT, IdxT>::ypB_;
using Component<ScalarT, IdxT>::fB_;
using Component<ScalarT, IdxT>::gB_;
using Component<ScalarT, IdxT>::param_;
using Component<ScalarT, IdxT>::idc_;

using Component<ScalarT, IdxT>::extern_indices_;
using Component<ScalarT, IdxT>::n_extern_;
using Component<ScalarT, IdxT>::n_intern_;
using Component<ScalarT, IdxT>::connection_nodes_;

public:
BusLinePartitionInterface(node_type* bus, component_type* component, IdxT id);
~BusLinePartitionInterface() override = default;

int allocate() final;
int initialize() final;
int tagDifferentiable() final;
int setAbsoluteTolerance(RealT) final;
int evaluateInternalResidual() final;
int evaluateExternalResidual() final;
int evaluateJacobian() final;
int evaluateIntegrand() final;

int initializeAdjoint() final;
int evaluateAdjointResidual() final;
// int evaluateAdjointJacobian();
Comment thread
superwhiskers marked this conversation as resolved.
Resolved
int evaluateAdjointIntegrand() final;

private:
/**
* @brief Global connection indices for the bus voltages and line currents
* across the partition boundary.
*/
std::vector<IdxT> connection_indices_;

/**
* @brief Sign of the line current contribution to the bus residual.
*
* -1 for the left end of the line and +1 for the right end.
*/
RealT sign_;
};
} // namespace PowerElectronics
} // namespace GridKit
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
gridkit_add_library(
power_elec_bus_line_interface
SOURCES BusLinePartitionInterface.cpp
HEADERS BusLinePartitionInterface.hpp PartitionInterface.hpp
LINK_LIBRARIES
PUBLIC
GridKit::dense_vector
GridKit::utilities_logger
GridKit::power_elec_microline)
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@


#pragma once

#include <GridKit/AutomaticDifferentiation/DependencyTracking/Variable.hpp>
#include <GridKit/Model/Evaluator.hpp>
#include <GridKit/Model/PowerElectronics/Component.hpp>

namespace GridKit
{
namespace PowerElectronics
{

/*!
* @brief Base class for partition interface components.
*
* A partition interface is simply a component that is designed to aid in
* marking partition boundary to facilitate partion evaluation and
* Jacobian assembly. All partition interface that will be implemented
* in the future should inherit from this class and all common features
* should be factored here.
*/
template <class ScalarT, typename IdxT>
class PartitionInterface : public Component<ScalarT, IdxT>
{
public:
PartitionInterface() = default;

~PartitionInterface() = default;
};
} // namespace PowerElectronics
} // namespace GridKit
Loading
Loading