From 547cac8b2a835b4676dffb444a1e8a8d08ea0ea2 Mon Sep 17 00:00:00 2001 From: abdourahmanbarry Date: Tue, 21 Apr 2026 18:11:16 +0000 Subject: [PATCH 01/52] Work in progress code for cosim integration --- GridKit/Model/PartitionEvaluator.hpp | 282 ++++++++++++++++++ .../Solver/Dynamic/MultiStageCosimulation.cpp | 124 ++++++++ .../Solver/Dynamic/MultiStageCosimulation.hpp | 133 +++++++++ 3 files changed, 539 insertions(+) create mode 100644 GridKit/Model/PartitionEvaluator.hpp create mode 100644 GridKit/Solver/Dynamic/MultiStageCosimulation.cpp create mode 100644 GridKit/Solver/Dynamic/MultiStageCosimulation.hpp diff --git a/GridKit/Model/PartitionEvaluator.hpp b/GridKit/Model/PartitionEvaluator.hpp new file mode 100644 index 000000000..96d906ea3 --- /dev/null +++ b/GridKit/Model/PartitionEvaluator.hpp @@ -0,0 +1,282 @@ +#pragma once + +#include + +#include "Evaluator.hpp" + +namespace GridKit +{ + namespace Model + { + + template + class PartitionEvaluator : public Evaluator + { + public: + using Base = Evaluator; + using RealT = typename Base::RealT; + using MatrixT = typename Base::MatrixT; + using CsrMatrixT = typename Base::CsrMatrixT; + + using ForcingFunc = std::function; + + private: + std::vector y_; + std::vector yp_; + std::vector yB_; + std::vector ypB_; + std::vector param_; + std::vector param_up_; + std::vector param_lo_; + std::vector residual_; + std::vector integrand_; + std::vector adj_integrand_; + std::vector adj_residual_; + std::vector tag_; + + MatrixT jacobian_; + + std::vector coupling_; + std::vector forcing_; + + public: + int allocate() override + { + return 0; + } + + int initialize() override + { + return 0; + } + + int tagDifferentiable() override + { + return 0; + } + + int evaluateResidual() override + { + return 0; + } + + int evaluateJacobian() override + { + return 0; + } + + int evaluateIntegrand() override + { + return 0; + } + + int initializeAdjoint() override + { + return 0; + } + + int evaluateAdjointResidual() override + { + return 0; + } + + int evaluateAdjointIntegrand() override + { + return 0; + } + + IdxT size() override + { + return 0; + } + + IdxT nnz() override + { + return 0; + } + + bool hasJacobian() override + { + return false; + } + + IdxT sizeQuadrature() override + { + return 0; + } + + IdxT sizeParams() override + { + return 0; + } + + void updateTime(RealT, RealT) override + { + } + + void setTolerances(RealT&, RealT&) const override + { + } + + void setMaxSteps(IdxT&) const override + { + } + + std::vector& y() override + { + return y_; + } + + const std::vector& y() const override + { + return y_; + } + + std::vector& yp() override + { + return yp_; + } + + const std::vector& yp() const override + { + return yp_; + } + + std::vector& tag() override + { + return tag_; + } + + const std::vector& tag() const override + { + return tag_; + } + + std::vector& yB() override + { + return yB_; + } + + const std::vector& yB() const override + { + return yB_; + } + + std::vector& ypB() override + { + return ypB_; + } + + const std::vector& ypB() const override + { + return ypB_; + } + + std::vector& param() override + { + return param_; + } + + const std::vector& param() const override + { + return param_; + } + + std::vector& param_up() override + { + return param_up_; + } + + const std::vector& param_up() const override + { + return param_up_; + } + + std::vector& param_lo() override + { + return param_lo_; + } + + const std::vector& param_lo() const override + { + return param_lo_; + } + + std::vector& getResidual() override + { + return residual_; + } + + const std::vector& getResidual() const override + { + return residual_; + } + + MatrixT& getJacobian() override + { + return jacobian_; + } + + const MatrixT& getJacobian() const override + { + return jacobian_; + } + + std::vector& getIntegrand() override + { + return integrand_; + } + + const std::vector& getIntegrand() const override + { + return integrand_; + } + + std::vector& getAdjointResidual() override + { + return adj_residual_; + } + + const std::vector& getAdjointResidual() const override + { + return adj_residual_; + } + + std::vector& getAdjointIntegrand() override + { + return adj_integrand_; + } + + const std::vector& getAdjointIntegrand() const override + { + return adj_integrand_; + } + + void addForcing(const ForcingFunc& f) + { + forcing_.push_back(f); + } + + IdxT getCouplingSize() const + { + return static_cast(coupling_.size()); + } + + IdxT getStateSize() const + { + return static_cast(y_.size()); + } + + std::vector& getCoupling() + { + return coupling_; + } + + const std::vector& getCoupling() const + { + return coupling_; + } + }; + + } // namespace Model +} // namespace GridKit \ No newline at end of file diff --git a/GridKit/Solver/Dynamic/MultiStageCosimulation.cpp b/GridKit/Solver/Dynamic/MultiStageCosimulation.cpp new file mode 100644 index 000000000..390eb8d03 --- /dev/null +++ b/GridKit/Solver/Dynamic/MultiStageCosimulation.cpp @@ -0,0 +1,124 @@ + +#include "MultiStageCosimulation.hpp"; + +#include + +#include +#include +#include + +namespace Integrator +{ + + // TODO: Complete implementation + template + int MultiStageCosimulation::allocate() + { + + for (size_t p = 0; p < num_partitions_; ++p) + { + auto* part = partitions_[p]; + + int num_coupling = part->getCouplingSize(); + int num_states = part->getStateSize(); + + x0_local_[p] = State(num_states); + coupling_mat_[p] = State(num_coupling, num_stages_); + + x0_local_[p].allocate(memspace_); + coupling_mat_[p].allocate(memspace_); + } + + return 0; + } + + template + int MultiStageCosimulation::distributeLocal(const State& global_y) + { + + auto* global_data = global_y.getData(memspace_); + + for (size_t p = 0; p < num_partitions_; ++p) + { + auto* part = partitions_[p]; + auto& internal = part->getInternalIndecies(); // TODO: add this function to partition evaluate + size_t n = part->size(); + + auto* local_data = x0_local_[p].getData(memspace_); + + for (size_t i = 0; i < n; ++i) + { + local_data[i] = global_data[internal[i]]; + } + } + + return 0; + } + + template + int MultiStageCosimulation::distributeCoupling(const State& global_y, int stage) + { + + auto* global_data = global_y.getData(memspace_); + + for (size_t p = 0; p < num_partitions_; ++p) + { + auto* part = partitions_[p]; + auto& external = part->getExternalIndices(); // TODO: add this function to partition evaluate + size_t n = part->size(); + + auto* local_data = coupling_mat_[p].getData(stage, memspace_); + + for (size_t i = 0; i < n; ++i) + { + local_data[i] = global_data[external[i]]; + } + } + + return 0; + } + + // TODO: Complete implementation + template + int MultiStageCosimulation::timestep(const State& global_y) + { + return 0; + } + + // TODO: Complete implementation + template + int MultiStageCosimulation::partitionSolve() + { + + std::vector output_time = output_times; + + for (size_t i = 0; i < max_iterations_; i++) + { + for (auto* partitionEvaluator : partitions_) + { + + // TODO: add a coupling function to the partition + partitionEvaluator->addForcing(); + + if (i + 1 < max_iterations_) + { + solver.integrate({t0}, partition_solution_.getData(stage)); + } + else + { + solver.integrate(output_time, partition_solution_); + } + } + + updateCoupling(stage + 1) + + if (stage == 0) + { + break; + } + } + + return 0; + } + +} // namespace Integrator \ No newline at end of file diff --git a/GridKit/Solver/Dynamic/MultiStageCosimulation.hpp b/GridKit/Solver/Dynamic/MultiStageCosimulation.hpp new file mode 100644 index 000000000..ddc3ceb6a --- /dev/null +++ b/GridKit/Solver/Dynamic/MultiStageCosimulation.hpp @@ -0,0 +1,133 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace Integrator +{ + + template + class MultiStageCosimulation + { + using State = ReSolve::vector::Vector; + using RealT = typename GridKit::ScalarTraits::RealT; + using Rosenbrock = Integrator::Rosenbrock; + using PartitionEvaluator = GridKit::Model::PartitionEvaluator; + + public: + int initializeSimulation(RealT t0); + + int runSimulation(RealT tn, IdxT nout, std::optional> step_callback = {}); + + int partitionSolve(); + + int timestepper(const std::vector& out_times, + std::optional> out_cb = {}); + + int distributeLocal(const State& global_y); + + int distributeCoupling(const State& global_y, int stage); + + int allocate(); + + private: + /** + * @brief initial time + */ + RealT t0_; + /** + * @brief Maximum number of iterations per stage + */ + size_t max_iterations_; + + /** + * + * @brief Number of partitions in the problem + */ + size_t num_partitions_; + + /** + * + * @brief Number of stages used by the method + */ + size_t num_stages_; + + /** + * + * @brief Memory space used + */ + ReSolve::memory::MemorySpace memspace_; + + /** + * + * @brief Handles for linear algebra operations + * + */ + ReSolve::VectorHandler vector_handler_; + + /** + * + * @brief linear solver for the sub-integrator + * + */ + std::vector> lin_solver_; + + /** + * + * @brief Contains vectors of initial conditions for every step + * + */ + std::vector x0_local_; + + /** + * + * @brief Vectors of coupling matrices for each partition + * + */ + std::vector coupling_mat_; + + /** + * + * @brief Vector of stages + * + */ + State stages_; + + /** + * + * @brief partition solution multi-dimensional vectors + * + */ + State partition_solution_; + + /** + * + *@brief Vector of partitions + */ + std::vector partitions_; + + /** + * + *@brief Vector of solvers + */ + std::vector> solvers_; + }; +} // namespace Integrator From 74633002f61d2a8552d8649867c4cdf691dc3b8b Mon Sep 17 00:00:00 2001 From: abdourahmanbarry Date: Mon, 1 Jun 2026 15:28:33 +0000 Subject: [PATCH 02/52] Added partition interfaces to power electronics and updated cmake files. During partitioning: - BusPartitionInterface connects to the bus partition - ComponentPartitionInterface connects to the component partition --- GridKit/Model/PowerElectronics/CMakeLists.txt | 1 + .../BusPartitionInterface.cpp | 182 ++++++++++++++++++ .../BusPartitionInterface.hpp | 70 +++++++ .../PartitionInterface/CMakeLists.txt | 8 + .../ComponentPartitionInterface.cpp | 176 +++++++++++++++++ .../ComponentPartitionInterface.hpp | 75 ++++++++ .../PartitionInterface/PartitionInterface.hpp | 74 +++++++ examples/PowerElectronics/CMakeLists.txt | 1 + .../PowerElectronics/Partition/CMakeLists.txt | 4 + .../PowerElectronics/Partition/HiresBus.hpp | 104 ++++++++++ .../Partition/HiresComponent1.hpp | 109 +++++++++++ .../Partition/HiresComponent2.hpp | 109 +++++++++++ .../PowerElectronics/Partition/Partition.cpp | 114 +++++++++++ 13 files changed, 1027 insertions(+) create mode 100644 GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.cpp create mode 100644 GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.hpp create mode 100644 GridKit/Model/PowerElectronics/PartitionInterface/CMakeLists.txt create mode 100644 GridKit/Model/PowerElectronics/PartitionInterface/ComponentPartitionInterface.cpp create mode 100644 GridKit/Model/PowerElectronics/PartitionInterface/ComponentPartitionInterface.hpp create mode 100644 GridKit/Model/PowerElectronics/PartitionInterface/PartitionInterface.hpp create mode 100644 examples/PowerElectronics/Partition/CMakeLists.txt create mode 100644 examples/PowerElectronics/Partition/HiresBus.hpp create mode 100644 examples/PowerElectronics/Partition/HiresComponent1.hpp create mode 100644 examples/PowerElectronics/Partition/HiresComponent2.hpp create mode 100644 examples/PowerElectronics/Partition/Partition.cpp diff --git a/GridKit/Model/PowerElectronics/CMakeLists.txt b/GridKit/Model/PowerElectronics/CMakeLists.txt index c985d69d6..d0b4f0080 100644 --- a/GridKit/Model/PowerElectronics/CMakeLists.txt +++ b/GridKit/Model/PowerElectronics/CMakeLists.txt @@ -21,6 +21,7 @@ add_subdirectory(TransmissionLine) add_subdirectory(MicrogridLoad) add_subdirectory(MicrogridLine) add_subdirectory(MicrogridBusDQ) +add_subdirectory(PartitionInterface) install( FILES CircuitComponent.hpp diff --git a/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.cpp b/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.cpp new file mode 100644 index 000000000..9b1e47e40 --- /dev/null +++ b/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.cpp @@ -0,0 +1,182 @@ + +#include "BusPartitionInterface.hpp" + +#include +#include +#include + +namespace GridKit +{ + + /*! + * @brief Construct a bus-level partition interface. + * + * This interface exposes only the two bus variables associated with a + * component that has been separated from the rest of the system during + * partitioning. Internally, it maintains a copy of the original component + * and acts as a proxy between the co-simulation algorithm and the component + * model. + * + * The bus variables become the internal variables of this interface, + * while all remaining component variables are treated as external data + * supplied by neighboring partitions. + * + * @param component Circuit component associated with this interface. + * @param id Unique component identifier. + */ + template + BusPartitionInterface::BusPartitionInterface(CircuitComponent& component, IdxT id) + : component_(component) + { + size_ = 2; + n_intern_ = 0; + n_extern_ = 2; + extern_indices_ = {0, 1}; + idc_ = id; + } + + template + BusPartitionInterface::~BusPartitionInterface() + { + } + + /*! + * @brief allocate method computes sparsity pattern of the Jacobian. + */ + template + int BusPartitionInterface::allocate() + { + y_.resize(static_cast(size_)); + yp_.resize(static_cast(size_)); + f_.resize(static_cast(size_)); + + size_t interface_extern_size = static_cast(component_.size() - size_); + interface_partition_externals_.resize(interface_extern_size); + external_data_y_.resize(interface_extern_size, 0); + external_data_yp_.resize(interface_extern_size, 0); + + component_.allocate(); + + auto bus_i = this->getNodeConnection(0); + auto bus_j = this->getNodeConnection(1); + + size_t counter = 0; + for (size_t i = 0; i < static_cast(component_.size()); i++) + { + if (bus_i == component_.getNodeConnection(static_cast(i))) + { + bus_port_i_ = i; + } + else if (bus_j == component_.getNodeConnection(static_cast(i))) + { + bus_port_j_ = i; + } + else + { + interface_partition_externals_[counter] = component_.getNodeConnection(static_cast(i)); + counter++; + } + } + + return 0; + } + + /** + * Initialization of the grid model + */ + template + int BusPartitionInterface::initialize() + { + return 0; + } + + /* + * \brief Identify differential variables + */ + template + int BusPartitionInterface::tagDifferentiable() + { + return 0; + } + + /** + * @brief Eval Micro Load + */ + template + int BusPartitionInterface::evaluateResidual() + { + auto& y = component_.y(); + auto& yp = component_.yp(); + + y[bus_port_i_] = y_[0]; + y[bus_port_j_] = y_[1]; + + yp[bus_port_i_] = yp_[0]; + yp[bus_port_j_] = yp_[1]; + + size_t counter = 0; + for (size_t i = 0; i < static_cast(component_.size()); i++) + { + if (bus_port_i_ != i && bus_port_j_ != i) + { + y[i] = external_data_y_[counter]; + counter++; + } + } + + component_.evaluateResidual(); + + auto& f = component_.getResidual(); + + f_[0] = f[bus_port_i_]; + f_[1] = f[bus_port_j_]; + + return 0; + } + + /** + * @brief Generate Jacobian for Micro Load + * + * @tparam ScalarT + * @tparam IdxT + * @return int + */ + template + int BusPartitionInterface::evaluateJacobian() + { + jac_.zeroMatrix(); + + return 0; + } + + template + int BusPartitionInterface::evaluateIntegrand() + { + return 0; + } + + template + int BusPartitionInterface::initializeAdjoint() + { + return 0; + } + + template + int BusPartitionInterface::evaluateAdjointResidual() + { + return 0; + } + + template + int BusPartitionInterface::evaluateAdjointIntegrand() + { + return 0; + } + + // Available template instantiations + template class BusPartitionInterface; + template class BusPartitionInterface; + template class BusPartitionInterface; + template class BusPartitionInterface; + +} // namespace GridKit diff --git a/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.hpp b/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.hpp new file mode 100644 index 000000000..7e4fbef6c --- /dev/null +++ b/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.hpp @@ -0,0 +1,70 @@ + + +#pragma once + +#include + +#include + +#include "GridKit/Model/PowerElectronics/PartitionInterface/PartitionInterface.hpp" + +namespace GridKit +{ + /*! + * @brief Declaration of a passive BusPartitionInterface class. + * + */ + template + class BusPartitionInterface : public PartitionInterface + { + using RealT = typename CircuitComponent::RealT; + using MatrixT = typename CircuitComponent::MatrixT; + + using PartitionInterface::external_data_y_; + using PartitionInterface::external_data_yp_; + using PartitionInterface::interface_partition_externals_; + + using CircuitComponent::size_; + using CircuitComponent::nnz_; + using CircuitComponent::time_; + using CircuitComponent::alpha_; + using CircuitComponent::y_; + using CircuitComponent::yp_; + using CircuitComponent::tag_; + using CircuitComponent::f_; + using CircuitComponent::g_; + using CircuitComponent::yB_; + using CircuitComponent::ypB_; + using CircuitComponent::fB_; + using CircuitComponent::gB_; + using CircuitComponent::jac_; + using CircuitComponent::param_; + using CircuitComponent::idc_; + + using CircuitComponent::extern_indices_; + using CircuitComponent::n_extern_; + using CircuitComponent::n_intern_; + + public: + BusPartitionInterface(CircuitComponent& component, IdxT id); + virtual ~BusPartitionInterface(); + + int allocate(); + int initialize(); + int tagDifferentiable(); + int evaluateResidual(); + int evaluateJacobian(); + int evaluateIntegrand(); + + int initializeAdjoint(); + int evaluateAdjointResidual(); + // int evaluateAdjointJacobian(); + int evaluateAdjointIntegrand(); + + private: + size_t bus_port_i_; + size_t bus_port_j_; + + CircuitComponent& component_; + }; +} // namespace GridKit diff --git a/GridKit/Model/PowerElectronics/PartitionInterface/CMakeLists.txt b/GridKit/Model/PowerElectronics/PartitionInterface/CMakeLists.txt new file mode 100644 index 000000000..28fb804b0 --- /dev/null +++ b/GridKit/Model/PowerElectronics/PartitionInterface/CMakeLists.txt @@ -0,0 +1,8 @@ +gridkit_add_library(power_elec_partition_interfaces + SOURCES + BusPartitionInterface.cpp + ComponentPartitionInterface.cpp + HEADERS + BusPartitionInterface.hpp + ComponentPartitionInterface.hpp + PartitionInterface.hpp) diff --git a/GridKit/Model/PowerElectronics/PartitionInterface/ComponentPartitionInterface.cpp b/GridKit/Model/PowerElectronics/PartitionInterface/ComponentPartitionInterface.cpp new file mode 100644 index 000000000..bc33b102f --- /dev/null +++ b/GridKit/Model/PowerElectronics/PartitionInterface/ComponentPartitionInterface.cpp @@ -0,0 +1,176 @@ + + +#include "ComponentPartitionInterface.hpp" + +#include +#include +#include +#include + +namespace GridKit +{ + + /*! + * @brief Constructor for a constant MicrogridLoad model + * + * Calls default ModelEvaluatorImpl constructor. + * + * + * + */ + + template + ComponentPartitionInterface::ComponentPartitionInterface(CircuitComponent* component, IdxT bus_i, IdxT bus_j, IdxT id) + : component_(component), + bus_i_(bus_i), + bus_j_(bus_j) + { + // internals [id, iq] + // externals [\omegaref, vbd_out, vbq_out] + size_ = component_->size(); + n_intern_ = component_->getInternalSize(); + n_extern_ = component_->getExternSize(); + extern_indices_ = component_->getExternIndices(); + idc_ = id; + } + + template + ComponentPartitionInterface::~ComponentPartitionInterface() + { + } + + /*! + * @brief allocate method computes sparsity pattern of the Jacobian. + */ + template + int ComponentPartitionInterface::allocate() + { + + size_t bus_size = 2; + + y_.resize(static_cast(size_)); + yp_.resize(static_cast(size_)); + f_.resize(static_cast(size_)); + interface_partition_externals_.resize(bus_size); + external_data_y_.resize(bus_size, 0); + external_data_yp_.resize(bus_size, 0); + + component_->allocate(); + + for (size_t i = 0; i < static_cast(size_); i++) + { + auto index = component_->getNodeConnection(static_cast(i)); + if (bus_i_ == index) + { + bus_port_i_ = i; + } + else if (bus_j_ == index) + { + bus_port_j_ = i; + } + else + { + this->setExternalConnectionNodes(static_cast(i), index); + } + } + + this->setExternalConnectionNodes(static_cast(bus_port_i_), static_cast(-1)); + this->setExternalConnectionNodes(static_cast(bus_port_j_), static_cast(-1)); + + interface_partition_externals_[0] = bus_i_; + interface_partition_externals_[1] = bus_j_; + + return 0; + } + + /** + * Initialization of the grid model + */ + template + int ComponentPartitionInterface::initialize() + { + return 0; + } + + /* + * \brief Identify differential variables + */ + template + int ComponentPartitionInterface::tagDifferentiable() + { + return 0; + } + + /** + * @brief Eval Micro Load + */ + template + int ComponentPartitionInterface::evaluateResidual() + { + + auto& y = component_->y(); + auto& yp = component_->yp(); + + std::copy(y_.begin(), y_.end(), y.begin()); + std::copy(yp_.begin(), yp_.end(), yp.begin()); + + y[bus_port_i_] = external_data_y_[0]; + y[bus_port_j_] = external_data_y_[1]; + + yp[bus_port_i_] = external_data_yp_[0]; + yp[bus_port_j_] = external_data_yp_[1]; + + component_->evaluateResidual(); + auto& f = component_->getResidual(); + + std::copy(f.begin(), f.end(), f_.begin()); + + return 0; + } + + /** + * @brief Generate Jacobian for Micro Load + * + * @tparam ScalarT + * @tparam IdxT + * @return int + */ + template + int ComponentPartitionInterface::evaluateJacobian() + { + jac_.zeroMatrix(); + + return 0; + } + + template + int ComponentPartitionInterface::evaluateIntegrand() + { + return 0; + } + + template + int ComponentPartitionInterface::initializeAdjoint() + { + return 0; + } + + template + int ComponentPartitionInterface::evaluateAdjointResidual() + { + return 0; + } + + template + int ComponentPartitionInterface::evaluateAdjointIntegrand() + { + return 0; + } + + // Available template instantiations + template class ComponentPartitionInterface; + template class ComponentPartitionInterface; + template class ComponentPartitionInterface; + template class ComponentPartitionInterface; + +} // namespace GridKit diff --git a/GridKit/Model/PowerElectronics/PartitionInterface/ComponentPartitionInterface.hpp b/GridKit/Model/PowerElectronics/PartitionInterface/ComponentPartitionInterface.hpp new file mode 100644 index 000000000..d7d1b3926 --- /dev/null +++ b/GridKit/Model/PowerElectronics/PartitionInterface/ComponentPartitionInterface.hpp @@ -0,0 +1,75 @@ +#pragma once + +#include + +#include + +#include "GridKit/Model/PowerElectronics/PartitionInterface/PartitionInterface.hpp" + +namespace GridKit +{ + template + class BaseBus; +} + +namespace GridKit +{ + /*! + * @brief Declaration of a passive ComponentPartitionInterface class. + * + */ + template + class ComponentPartitionInterface : public PartitionInterface + { + using RealT = typename CircuitComponent::RealT; + using MatrixT = typename CircuitComponent::MatrixT; + + using PartitionInterface::external_data_y_; + using PartitionInterface::external_data_yp_; + using PartitionInterface::interface_partition_externals_; + + using CircuitComponent::size_; + using CircuitComponent::nnz_; + using CircuitComponent::time_; + using CircuitComponent::alpha_; + using CircuitComponent::y_; + using CircuitComponent::yp_; + using CircuitComponent::tag_; + using CircuitComponent::f_; + using CircuitComponent::g_; + using CircuitComponent::yB_; + using CircuitComponent::ypB_; + using CircuitComponent::fB_; + using CircuitComponent::gB_; + using CircuitComponent::jac_; + using CircuitComponent::param_; + using CircuitComponent::idc_; + using CircuitComponent::extern_indices_; + using CircuitComponent::n_extern_; + using CircuitComponent::n_intern_; + + public: + ComponentPartitionInterface(CircuitComponent* component, IdxT bus_i, IdxT bus_j, IdxT id); + virtual ~ComponentPartitionInterface(); + + int allocate(); + int initialize(); + int tagDifferentiable(); + int evaluateResidual(); + int evaluateJacobian(); + int evaluateIntegrand(); + + int initializeAdjoint(); + int evaluateAdjointResidual(); + // int evaluateAdjointJacobian(); + int evaluateAdjointIntegrand(); + + private: + CircuitComponent* component_; + + size_t bus_port_i_; + size_t bus_port_j_; + IdxT bus_i_; + IdxT bus_j_; + }; +} // namespace GridKit diff --git a/GridKit/Model/PowerElectronics/PartitionInterface/PartitionInterface.hpp b/GridKit/Model/PowerElectronics/PartitionInterface/PartitionInterface.hpp new file mode 100644 index 000000000..96202da2e --- /dev/null +++ b/GridKit/Model/PowerElectronics/PartitionInterface/PartitionInterface.hpp @@ -0,0 +1,74 @@ + + +#pragma once + +#include +#include + +#include +#include +#include + +namespace GridKit +{ + /*! + * @brief Base class for partition interface components. + * + * PartitionInterface provides the infrastructure needed by components + * that exchange information across partition boundaries during + * co-simulation. It stores the values of external variables received + * from neighboring partitions together with the corresponding global + * indices in the original unpartitioned system. + * + * Derived classes are responsible for implementing the specific + * interface behavior associated with buses, circuit components, or + * other partition boundary entities. + */ + template + class PartitionInterface : public CircuitComponent + { + public: + using RealT = typename Model::Evaluator::RealT; + using MatrixT = typename Model::Evaluator::MatrixT; + using CsrMatrixT = typename Model::Evaluator::CsrMatrixT; + + PartitionInterface() = default; + + ~PartitionInterface() = default; + + int setExternalDataY(const std::vector& data) + { + std::copy(data.begin(), data.end(), external_data_y_.begin()); + + return 0; + } + + int setExternalDataYP(const std::vector& data) + { + std::copy(data.begin(), data.end(), external_data_yp_.begin()); + + return 0; + } + + std::vector& getExternalDataY() + { + return external_data_y_; + } + + std::vector& getExternalDataYP() + { + return external_data_yp_; + } + + std::vector& getPartitionExternalIndices() + { + return interface_partition_externals_; + } + + protected: + std::vector external_data_y_; + std::vector external_data_yp_; + std::vector interface_partition_externals_; + }; + +} // namespace GridKit diff --git a/examples/PowerElectronics/CMakeLists.txt b/examples/PowerElectronics/CMakeLists.txt index 2bf311e22..bdf85eb07 100644 --- a/examples/PowerElectronics/CMakeLists.txt +++ b/examples/PowerElectronics/CMakeLists.txt @@ -10,5 +10,6 @@ if(TARGET SUNDIALS::idas) add_subdirectory(RLCircuit) add_subdirectory(Microgrid) add_subdirectory(ScaleMicrogrid) + add_subdirectory(Partition) endif() endif() diff --git a/examples/PowerElectronics/Partition/CMakeLists.txt b/examples/PowerElectronics/Partition/CMakeLists.txt new file mode 100644 index 000000000..a5d192a9d --- /dev/null +++ b/examples/PowerElectronics/Partition/CMakeLists.txt @@ -0,0 +1,4 @@ +add_executable(partition Partition.cpp) +target_link_libraries(partition GridKit::solvers_dyn + GridKit::power_elec_partition_interfaces) + diff --git a/examples/PowerElectronics/Partition/HiresBus.hpp b/examples/PowerElectronics/Partition/HiresBus.hpp new file mode 100644 index 000000000..a01152ac0 --- /dev/null +++ b/examples/PowerElectronics/Partition/HiresBus.hpp @@ -0,0 +1,104 @@ +#pragma once + +#include + +namespace GridKit +{ + /*! + * @brief Declaration of a MicrogridLine class. + * + */ + template + class HiresBus : public CircuitComponent + { + using RealT = typename CircuitComponent::RealT; + using MatrixT = typename CircuitComponent::MatrixT; + + using CircuitComponent::size_; + using CircuitComponent::nnz_; + using CircuitComponent::time_; + using CircuitComponent::alpha_; + using CircuitComponent::y_; + using CircuitComponent::yp_; + using CircuitComponent::tag_; + using CircuitComponent::f_; + using CircuitComponent::g_; + using CircuitComponent::yB_; + using CircuitComponent::ypB_; + using CircuitComponent::fB_; + using CircuitComponent::gB_; + using CircuitComponent::jac_; + using CircuitComponent::param_; + using CircuitComponent::idc_; + + using CircuitComponent::extern_indices_; + using CircuitComponent::n_extern_; + using CircuitComponent::n_intern_; + + public: + HiresBus(IdxT id) + { + size_ = 2; + n_intern_ = 2; + n_extern_ = 0; + extern_indices_ = {}; + idc_ = id; + } + + ~HiresBus() + { + } + + int allocate() + { + y_.resize(static_cast(size_)); + yp_.resize(static_cast(size_)); + f_.resize(static_cast(size_)); + + return 0; + } + + int initialize() + { + return 0; + } + + int tagDifferentiable() + { + return 0; + } + + int evaluateResidual() + { + f_[0] = -y_[0]; + f_[1] = -y_[1]; + + return 0; + } + + int evaluateJacobian() + { + return 0; + } + + int evaluateIntegrand() + { + return 0; + } + + int initializeAdjoint() + { + return 0; + } + + int evaluateAdjointResidual() + { + return 0; + } + + int evaluateAdjointIntegrand() + { + return 0; + } + }; +} // namespace GridKit \ No newline at end of file diff --git a/examples/PowerElectronics/Partition/HiresComponent1.hpp b/examples/PowerElectronics/Partition/HiresComponent1.hpp new file mode 100644 index 000000000..7314264b0 --- /dev/null +++ b/examples/PowerElectronics/Partition/HiresComponent1.hpp @@ -0,0 +1,109 @@ + + +#pragma once + +#include + +namespace GridKit +{ + /*! + * @brief Declaration of a HiresComponent1 class. + * + */ + template + class HiresComponent1 : public CircuitComponent + { + using RealT = typename CircuitComponent::RealT; + using MatrixT = typename CircuitComponent::MatrixT; + + using CircuitComponent::size_; + using CircuitComponent::nnz_; + using CircuitComponent::time_; + using CircuitComponent::alpha_; + using CircuitComponent::y_; + using CircuitComponent::yp_; + using CircuitComponent::tag_; + using CircuitComponent::f_; + using CircuitComponent::g_; + using CircuitComponent::yB_; + using CircuitComponent::ypB_; + using CircuitComponent::fB_; + using CircuitComponent::gB_; + using CircuitComponent::jac_; + using CircuitComponent::param_; + using CircuitComponent::idc_; + + using CircuitComponent::extern_indices_; + using CircuitComponent::n_extern_; + using CircuitComponent::n_intern_; + + public: + HiresComponent1(IdxT id) + { + size_ = 5; + n_intern_ = 3; + n_extern_ = 2; + extern_indices_ = {3, 4}; + idc_ = id; + } + + ~HiresComponent1() + { + } + + int allocate() + { + y_.resize(static_cast(size_)); + yp_.resize(static_cast(size_)); + f_.resize(static_cast(size_)); + + return 0; + } + + int initialize() + { + return 0; + } + + int tagDifferentiable() + { + return 0; + } + + int evaluateResidual() + { + f_[0] = -1.71 * y_[0] + 0.43 * y_[1] + 8.32 * y_[2] + 0.0007; + f_[1] = 1.71 * y_[0] - 8.75 * y_[1]; + f_[2] = -10.03 * y_[2] + 0.43 * y_[3] + 0.035 * y_[4]; + f_[3] = 8.32 * y_[1] + 1.71 * y_[2] - 0.1 * y_[3]; + f_[4] = -0.7 * y_[4]; + + return 0; + } + + int evaluateJacobian() + { + return 0; + } + + int evaluateIntegrand() + { + return 0; + } + + int initializeAdjoint() + { + return 0; + } + + int evaluateAdjointResidual() + { + return 0; + } + + int evaluateAdjointIntegrand() + { + return 0; + } + }; +} // namespace GridKit diff --git a/examples/PowerElectronics/Partition/HiresComponent2.hpp b/examples/PowerElectronics/Partition/HiresComponent2.hpp new file mode 100644 index 000000000..c1787e51a --- /dev/null +++ b/examples/PowerElectronics/Partition/HiresComponent2.hpp @@ -0,0 +1,109 @@ + + +#pragma once + +#include + +namespace GridKit +{ + /*! + * @brief Declaration of a HiresComponent1 class. + * + */ + template + class HiresComponent2 : public CircuitComponent + { + using RealT = typename CircuitComponent::RealT; + using MatrixT = typename CircuitComponent::MatrixT; + + using CircuitComponent::size_; + using CircuitComponent::nnz_; + using CircuitComponent::time_; + using CircuitComponent::alpha_; + using CircuitComponent::y_; + using CircuitComponent::yp_; + using CircuitComponent::tag_; + using CircuitComponent::f_; + using CircuitComponent::g_; + using CircuitComponent::yB_; + using CircuitComponent::ypB_; + using CircuitComponent::fB_; + using CircuitComponent::gB_; + using CircuitComponent::jac_; + using CircuitComponent::param_; + using CircuitComponent::idc_; + + using CircuitComponent::extern_indices_; + using CircuitComponent::n_extern_; + using CircuitComponent::n_intern_; + + public: + HiresComponent2(IdxT id) + { + size_ = 5; + n_intern_ = 3; + n_extern_ = 2; + extern_indices_ = {3, 4}; + idc_ = id; + } + + ~HiresComponent2() + { + } + + int allocate() + { + y_.resize(static_cast(size_)); + yp_.resize(static_cast(size_)); + f_.resize(static_cast(size_)); + + return 0; + } + + int initialize() + { + return 0; + } + + int tagDifferentiable() + { + return 0; + } + + int evaluateResidual() + { + f_[0] = -280 * y_[0] * y_[2] + 0.69 * y_[3] + 1.71 * y_[4] - 0.43 * y_[0] + 0.69 * y_[1]; + f_[1] = 280 * y_[0] * y_[2] - 1.81 * y_[1]; + f_[2] = -280 * y_[0] * y_[2] + 1.81 * y_[1]; + f_[3] = -0.02 * y_[3]; + f_[4] = -0.045 * y_[4] + 0.43 * y_[0] + 0.43 * y_[1]; + + return 0; + } + + int evaluateJacobian() + { + return 0; + } + + int evaluateIntegrand() + { + return 0; + } + + int initializeAdjoint() + { + return 0; + } + + int evaluateAdjointResidual() + { + return 0; + } + + int evaluateAdjointIntegrand() + { + return 0; + } + }; +} // namespace GridKit diff --git a/examples/PowerElectronics/Partition/Partition.cpp b/examples/PowerElectronics/Partition/Partition.cpp new file mode 100644 index 000000000..67afcd264 --- /dev/null +++ b/examples/PowerElectronics/Partition/Partition.cpp @@ -0,0 +1,114 @@ +#include + +#define _USE_MATH_DEFINES +#include +#include +#include + +#include +#include +#include + +#include "HiresBus.hpp" +#include "HiresComponent1.hpp" +#include "HiresComponent2.hpp" + +std::vector hires(std::vector y); + +int main(int /* argc */, char const** /* argv */) +{ + + double abs_tol = 1.0e-5; + double rel_tol = 1.0e-5; + size_t max_step_number = 3000; + bool use_jac = true; + + auto* partition1 = new GridKit::PowerElectronicsModel(rel_tol, abs_tol, use_jac, max_step_number); + auto* partition2 = new GridKit::PowerElectronicsModel(rel_tol, abs_tol, use_jac, max_step_number); + + GridKit::HiresComponent1* comp1 = new GridKit::HiresComponent1(1); + GridKit::HiresBus* bus1 = new GridKit::HiresBus(2); + GridKit::HiresComponent2* comp2 = new GridKit::HiresComponent2(3); + + comp1->setExternalConnectionNodes(0, 0); + comp1->setExternalConnectionNodes(1, 1); + comp1->setExternalConnectionNodes(2, 2); + comp1->setExternalConnectionNodes(3, 3); + comp1->setExternalConnectionNodes(4, 4); + + bus1->setExternalConnectionNodes(0, 3); + bus1->setExternalConnectionNodes(1, 4); + + comp2->setExternalConnectionNodes(0, 5); + comp2->setExternalConnectionNodes(1, 6); + comp2->setExternalConnectionNodes(2, 7); + comp2->setExternalConnectionNodes(3, 3); + comp2->setExternalConnectionNodes(4, 4); + + GridKit::HiresComponent2 comp2copy(*comp2); + GridKit::BusPartitionInterface* busInterface = new GridKit::BusPartitionInterface(comp2copy, 10); + GridKit::ComponentPartitionInterface* compInterface = new GridKit::ComponentPartitionInterface(comp2, 3, 4, 9); + + busInterface->setExternalConnectionNodes(0, 3); + busInterface->setExternalConnectionNodes(1, 4); + + partition1->addComponent(comp1); + partition1->addComponent(bus1); + partition1->addComponent(busInterface); + + partition2->addComponent(compInterface); + + size_t sys_size = 8; + + partition1->allocate(sys_size); + partition2->allocate(sys_size); + + busInterface->setExternalDataY({6, 7, 8}); + compInterface->setExternalDataY({4, 5}); + + for (size_t i = 0; i < sys_size; i++) + { + + partition1->y()[i] = static_cast(i) + 1; + partition1->yp()[i] = static_cast(i) + 1; + + partition2->y()[i] = static_cast(i) + 1; + partition2->yp()[i] = static_cast(i) + 1; + } + + partition1->evaluateResidual(); + partition2->evaluateResidual(); + + auto ref = hires({1, 2, 3, 4, 5, 6, 7, 8}); + + std::cout << "------------- Partition 1 -----------" << std::endl; + for (size_t i = 0; i < sys_size; i++) + { + printf("%-10.5g ---------- %10.5g\n", partition1->getResidual()[i], ref[i]); + } + + std::cout << "\n------------- Partition 2 -----------" << std::endl; + for (size_t i = 0; i < sys_size; i++) + { + printf("%-10.5g ---------- %10.5g\n", partition2->getResidual()[i], ref[i]); + } + + delete partition1; + delete partition2; +} + +std::vector hires(std::vector y) +{ + std::vector f(8); + + f[0] = -1.71 * y[0] + 0.43 * y[1] + 8.32 * y[2] + 0.0007; + f[1] = 1.71 * y[0] - 8.75 * y[1]; + f[2] = -10.03 * y[2] + 0.43 * y[3] + 0.035 * y[4]; + f[3] = 8.32 * y[1] + 1.71 * y[2] - 1.12 * y[3]; + f[4] = -1.745 * y[4] + 0.43 * y[5] + 0.43 * y[6]; + f[5] = -280 * y[5] * y[7] + 0.69 * y[3] + 1.71 * y[4] - 0.43 * y[5] + 0.69 * y[6]; + f[6] = 280 * y[5] * y[7] - 1.81 * y[6]; + f[7] = -280 * y[5] * y[7] + 1.81 * y[6]; + + return f; +} From ea84ec56c01e033a611cf4ca7ae730beec6d68ba Mon Sep 17 00:00:00 2001 From: abdourahmanbarry Date: Wed, 3 Jun 2026 05:24:24 +0000 Subject: [PATCH 03/52] Made minor changes to partition interface code --- .../PartitionInterface/BusPartitionInterface.cpp | 15 +++++++++------ .../PartitionInterface/BusPartitionInterface.hpp | 9 +++++---- .../ComponentPartitionInterface.cpp | 7 ++++--- .../ComponentPartitionInterface.hpp | 9 ++++----- .../PartitionInterface/PartitionInterface.hpp | 5 +++++ 5 files changed, 27 insertions(+), 18 deletions(-) diff --git a/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.cpp b/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.cpp index 9b1e47e40..b2c806b26 100644 --- a/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.cpp +++ b/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.cpp @@ -25,7 +25,7 @@ namespace GridKit * @param id Unique component identifier. */ template - BusPartitionInterface::BusPartitionInterface(CircuitComponent& component, IdxT id) + BusPartitionInterface::BusPartitionInterface(CircuitComponent& component, IdxT bus_i, IdxT bus_j, IdxT id) : component_(component) { size_ = 2; @@ -33,6 +33,9 @@ namespace GridKit n_extern_ = 2; extern_indices_ = {0, 1}; idc_ = id; + + bus_i_ = bus_i; + bus_j_ = bus_j; } template @@ -57,17 +60,14 @@ namespace GridKit component_.allocate(); - auto bus_i = this->getNodeConnection(0); - auto bus_j = this->getNodeConnection(1); - size_t counter = 0; for (size_t i = 0; i < static_cast(component_.size()); i++) { - if (bus_i == component_.getNodeConnection(static_cast(i))) + if (bus_i_ == component_.getNodeConnection(static_cast(i))) { bus_port_i_ = i; } - else if (bus_j == component_.getNodeConnection(static_cast(i))) + else if (bus_j_ == component_.getNodeConnection(static_cast(i))) { bus_port_j_ = i; } @@ -78,6 +78,9 @@ namespace GridKit } } + this->setExternalConnectionNodes(0, bus_i_); + this->setExternalConnectionNodes(1, bus_j_); + return 0; } diff --git a/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.hpp b/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.hpp index 7e4fbef6c..1b1d9e4bd 100644 --- a/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.hpp +++ b/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.hpp @@ -23,6 +23,10 @@ namespace GridKit using PartitionInterface::external_data_y_; using PartitionInterface::external_data_yp_; using PartitionInterface::interface_partition_externals_; + using PartitionInterface::bus_port_i_; + using PartitionInterface::bus_port_j_; + using PartitionInterface::bus_i_; + using PartitionInterface::bus_j_; using CircuitComponent::size_; using CircuitComponent::nnz_; @@ -46,7 +50,7 @@ namespace GridKit using CircuitComponent::n_intern_; public: - BusPartitionInterface(CircuitComponent& component, IdxT id); + BusPartitionInterface(CircuitComponent& component, IdxT bus_i, IdxT bus_j, IdxT id); virtual ~BusPartitionInterface(); int allocate(); @@ -62,9 +66,6 @@ namespace GridKit int evaluateAdjointIntegrand(); private: - size_t bus_port_i_; - size_t bus_port_j_; - CircuitComponent& component_; }; } // namespace GridKit diff --git a/GridKit/Model/PowerElectronics/PartitionInterface/ComponentPartitionInterface.cpp b/GridKit/Model/PowerElectronics/PartitionInterface/ComponentPartitionInterface.cpp index bc33b102f..3c88c4dee 100644 --- a/GridKit/Model/PowerElectronics/PartitionInterface/ComponentPartitionInterface.cpp +++ b/GridKit/Model/PowerElectronics/PartitionInterface/ComponentPartitionInterface.cpp @@ -21,9 +21,7 @@ namespace GridKit template ComponentPartitionInterface::ComponentPartitionInterface(CircuitComponent* component, IdxT bus_i, IdxT bus_j, IdxT id) - : component_(component), - bus_i_(bus_i), - bus_j_(bus_j) + : component_(component) { // internals [id, iq] // externals [\omegaref, vbd_out, vbq_out] @@ -32,6 +30,9 @@ namespace GridKit n_extern_ = component_->getExternSize(); extern_indices_ = component_->getExternIndices(); idc_ = id; + + bus_i_ = bus_i; + bus_j_ = bus_j; } template diff --git a/GridKit/Model/PowerElectronics/PartitionInterface/ComponentPartitionInterface.hpp b/GridKit/Model/PowerElectronics/PartitionInterface/ComponentPartitionInterface.hpp index d7d1b3926..a46231f6d 100644 --- a/GridKit/Model/PowerElectronics/PartitionInterface/ComponentPartitionInterface.hpp +++ b/GridKit/Model/PowerElectronics/PartitionInterface/ComponentPartitionInterface.hpp @@ -27,6 +27,10 @@ namespace GridKit using PartitionInterface::external_data_y_; using PartitionInterface::external_data_yp_; using PartitionInterface::interface_partition_externals_; + using PartitionInterface::bus_port_i_; + using PartitionInterface::bus_port_j_; + using PartitionInterface::bus_i_; + using PartitionInterface::bus_j_; using CircuitComponent::size_; using CircuitComponent::nnz_; @@ -66,10 +70,5 @@ namespace GridKit private: CircuitComponent* component_; - - size_t bus_port_i_; - size_t bus_port_j_; - IdxT bus_i_; - IdxT bus_j_; }; } // namespace GridKit diff --git a/GridKit/Model/PowerElectronics/PartitionInterface/PartitionInterface.hpp b/GridKit/Model/PowerElectronics/PartitionInterface/PartitionInterface.hpp index 96202da2e..85599404c 100644 --- a/GridKit/Model/PowerElectronics/PartitionInterface/PartitionInterface.hpp +++ b/GridKit/Model/PowerElectronics/PartitionInterface/PartitionInterface.hpp @@ -69,6 +69,11 @@ namespace GridKit std::vector external_data_y_; std::vector external_data_yp_; std::vector interface_partition_externals_; + + size_t bus_port_i_; + size_t bus_port_j_; + IdxT bus_i_; + IdxT bus_j_; }; } // namespace GridKit From 2dd8fe8ad69490c15d9a38ade983f9f3ff9736ad Mon Sep 17 00:00:00 2001 From: abdourahmanbarry Date: Wed, 3 Jun 2026 05:26:09 +0000 Subject: [PATCH 04/52] Updated Hires test problem to implicit form --- .../PowerElectronics/Partition/HiresBus.hpp | 6 +-- .../Partition/HiresComponent1.hpp | 12 ++--- ...iresComponent2.hpp => HiresComponent3.hpp} | 18 +++---- .../PowerElectronics/Partition/Partition.cpp | 48 +++++++++---------- 4 files changed, 42 insertions(+), 42 deletions(-) rename examples/PowerElectronics/Partition/{HiresComponent2.hpp => HiresComponent3.hpp} (81%) diff --git a/examples/PowerElectronics/Partition/HiresBus.hpp b/examples/PowerElectronics/Partition/HiresBus.hpp index a01152ac0..585d25ce0 100644 --- a/examples/PowerElectronics/Partition/HiresBus.hpp +++ b/examples/PowerElectronics/Partition/HiresBus.hpp @@ -5,7 +5,7 @@ namespace GridKit { /*! - * @brief Declaration of a MicrogridLine class. + * @brief Declaration of a Hires Bus Component. * */ template @@ -70,8 +70,8 @@ namespace GridKit int evaluateResidual() { - f_[0] = -y_[0]; - f_[1] = -y_[1]; + f_[0] = yp_[0] + y_[0]; + f_[1] = yp_[1] + y_[1]; return 0; } diff --git a/examples/PowerElectronics/Partition/HiresComponent1.hpp b/examples/PowerElectronics/Partition/HiresComponent1.hpp index 7314264b0..71f4268f5 100644 --- a/examples/PowerElectronics/Partition/HiresComponent1.hpp +++ b/examples/PowerElectronics/Partition/HiresComponent1.hpp @@ -7,7 +7,7 @@ namespace GridKit { /*! - * @brief Declaration of a HiresComponent1 class. + * @brief Declaration of a Hires Component 1 class. * */ template @@ -72,11 +72,11 @@ namespace GridKit int evaluateResidual() { - f_[0] = -1.71 * y_[0] + 0.43 * y_[1] + 8.32 * y_[2] + 0.0007; - f_[1] = 1.71 * y_[0] - 8.75 * y_[1]; - f_[2] = -10.03 * y_[2] + 0.43 * y_[3] + 0.035 * y_[4]; - f_[3] = 8.32 * y_[1] + 1.71 * y_[2] - 0.1 * y_[3]; - f_[4] = -0.7 * y_[4]; + f_[0] = yp_[0] + 1.71 * y_[0] - 0.43 * y_[1] - 8.32 * y_[2] - 0.0007; + f_[1] = yp_[1] - 1.71 * y_[0] + 8.75 * y_[1]; + f_[2] = yp_[2] + 10.03 * y_[2] - 0.43 * y_[3] - 0.035 * y_[4]; + f_[3] = -8.32 * y_[1] - 1.71 * y_[2] + 0.1 * y_[3]; + f_[4] = +0.7 * y_[4]; return 0; } diff --git a/examples/PowerElectronics/Partition/HiresComponent2.hpp b/examples/PowerElectronics/Partition/HiresComponent3.hpp similarity index 81% rename from examples/PowerElectronics/Partition/HiresComponent2.hpp rename to examples/PowerElectronics/Partition/HiresComponent3.hpp index c1787e51a..a396a5e82 100644 --- a/examples/PowerElectronics/Partition/HiresComponent2.hpp +++ b/examples/PowerElectronics/Partition/HiresComponent3.hpp @@ -7,11 +7,11 @@ namespace GridKit { /*! - * @brief Declaration of a HiresComponent1 class. + * @brief Declaration of a Hires Component 3 class. * */ template - class HiresComponent2 : public CircuitComponent + class HiresComponent3 : public CircuitComponent { using RealT = typename CircuitComponent::RealT; using MatrixT = typename CircuitComponent::MatrixT; @@ -38,7 +38,7 @@ namespace GridKit using CircuitComponent::n_intern_; public: - HiresComponent2(IdxT id) + HiresComponent3(IdxT id) { size_ = 5; n_intern_ = 3; @@ -47,7 +47,7 @@ namespace GridKit idc_ = id; } - ~HiresComponent2() + ~HiresComponent3() { } @@ -72,11 +72,11 @@ namespace GridKit int evaluateResidual() { - f_[0] = -280 * y_[0] * y_[2] + 0.69 * y_[3] + 1.71 * y_[4] - 0.43 * y_[0] + 0.69 * y_[1]; - f_[1] = 280 * y_[0] * y_[2] - 1.81 * y_[1]; - f_[2] = -280 * y_[0] * y_[2] + 1.81 * y_[1]; - f_[3] = -0.02 * y_[3]; - f_[4] = -0.045 * y_[4] + 0.43 * y_[0] + 0.43 * y_[1]; + f_[0] = yp_[0] + 280 * y_[0] * y_[2] - 0.69 * y_[3] - 1.71 * y_[4] + 0.43 * y_[0] - 0.69 * y_[1]; + f_[1] = yp_[1] - 280 * y_[0] * y_[2] + 1.81 * y_[1]; + f_[2] = yp_[2] + 280 * y_[0] * y_[2] - 1.81 * y_[1]; + f_[3] = 0.02 * y_[3]; + f_[4] = 0.045 * y_[4] - 0.43 * y_[0] - 0.43 * y_[1]; return 0; } diff --git a/examples/PowerElectronics/Partition/Partition.cpp b/examples/PowerElectronics/Partition/Partition.cpp index 67afcd264..b02285fbd 100644 --- a/examples/PowerElectronics/Partition/Partition.cpp +++ b/examples/PowerElectronics/Partition/Partition.cpp @@ -11,9 +11,9 @@ #include "HiresBus.hpp" #include "HiresComponent1.hpp" -#include "HiresComponent2.hpp" +#include "HiresComponent3.hpp" -std::vector hires(std::vector y); +std::vector hires(const std::vector& y, const std::vector& yp); int main(int /* argc */, char const** /* argv */) { @@ -28,7 +28,7 @@ int main(int /* argc */, char const** /* argv */) GridKit::HiresComponent1* comp1 = new GridKit::HiresComponent1(1); GridKit::HiresBus* bus1 = new GridKit::HiresBus(2); - GridKit::HiresComponent2* comp2 = new GridKit::HiresComponent2(3); + GridKit::HiresComponent3* comp3 = new GridKit::HiresComponent3(3); comp1->setExternalConnectionNodes(0, 0); comp1->setExternalConnectionNodes(1, 1); @@ -39,18 +39,15 @@ int main(int /* argc */, char const** /* argv */) bus1->setExternalConnectionNodes(0, 3); bus1->setExternalConnectionNodes(1, 4); - comp2->setExternalConnectionNodes(0, 5); - comp2->setExternalConnectionNodes(1, 6); - comp2->setExternalConnectionNodes(2, 7); - comp2->setExternalConnectionNodes(3, 3); - comp2->setExternalConnectionNodes(4, 4); + comp3->setExternalConnectionNodes(0, 5); + comp3->setExternalConnectionNodes(1, 6); + comp3->setExternalConnectionNodes(2, 7); + comp3->setExternalConnectionNodes(3, 3); + comp3->setExternalConnectionNodes(4, 4); - GridKit::HiresComponent2 comp2copy(*comp2); - GridKit::BusPartitionInterface* busInterface = new GridKit::BusPartitionInterface(comp2copy, 10); - GridKit::ComponentPartitionInterface* compInterface = new GridKit::ComponentPartitionInterface(comp2, 3, 4, 9); - - busInterface->setExternalConnectionNodes(0, 3); - busInterface->setExternalConnectionNodes(1, 4); + GridKit::HiresComponent3 comp2copy(*comp3); + GridKit::BusPartitionInterface* busInterface = new GridKit::BusPartitionInterface(comp2copy, 3, 4, 4); + GridKit::ComponentPartitionInterface* compInterface = new GridKit::ComponentPartitionInterface(comp3, 3, 4, 5); partition1->addComponent(comp1); partition1->addComponent(bus1); @@ -64,7 +61,10 @@ int main(int /* argc */, char const** /* argv */) partition2->allocate(sys_size); busInterface->setExternalDataY({6, 7, 8}); + busInterface->setExternalDataYP({6, 7, 8}); + compInterface->setExternalDataY({4, 5}); + compInterface->setExternalDataYP({4, 5}); for (size_t i = 0; i < sys_size; i++) { @@ -79,7 +79,7 @@ int main(int /* argc */, char const** /* argv */) partition1->evaluateResidual(); partition2->evaluateResidual(); - auto ref = hires({1, 2, 3, 4, 5, 6, 7, 8}); + auto ref = hires({1, 2, 3, 4, 5, 6, 7, 8}, {1, 2, 3, 4, 5, 6, 7, 8}); std::cout << "------------- Partition 1 -----------" << std::endl; for (size_t i = 0; i < sys_size; i++) @@ -97,18 +97,18 @@ int main(int /* argc */, char const** /* argv */) delete partition2; } -std::vector hires(std::vector y) +std::vector hires(const std::vector& y, const std::vector& yp) { std::vector f(8); - f[0] = -1.71 * y[0] + 0.43 * y[1] + 8.32 * y[2] + 0.0007; - f[1] = 1.71 * y[0] - 8.75 * y[1]; - f[2] = -10.03 * y[2] + 0.43 * y[3] + 0.035 * y[4]; - f[3] = 8.32 * y[1] + 1.71 * y[2] - 1.12 * y[3]; - f[4] = -1.745 * y[4] + 0.43 * y[5] + 0.43 * y[6]; - f[5] = -280 * y[5] * y[7] + 0.69 * y[3] + 1.71 * y[4] - 0.43 * y[5] + 0.69 * y[6]; - f[6] = 280 * y[5] * y[7] - 1.81 * y[6]; - f[7] = -280 * y[5] * y[7] + 1.81 * y[6]; + f[0] = yp[0] + 1.71 * y[0] - 0.43 * y[1] - 8.32 * y[2] - 0.0007; + f[1] = yp[1] - 1.71 * y[0] + 8.75 * y[1]; + f[2] = yp[2] + 10.03 * y[2] - 0.43 * y[3] - 0.035 * y[4]; + f[3] = yp[3] - 8.32 * y[1] - 1.71 * y[2] + 1.12 * y[3]; + f[4] = yp[4] + 1.745 * y[4] - 0.43 * y[5] - 0.43 * y[6]; + f[5] = yp[5] + 280 * y[5] * y[7] - 0.69 * y[3] - 1.71 * y[4] + 0.43 * y[5] - 0.69 * y[6]; + f[6] = yp[6] - 280 * y[5] * y[7] + 1.81 * y[6]; + f[7] = yp[7] + 280 * y[5] * y[7] - 1.81 * y[6]; return f; } From a0b7a5078c1e7b228b994a2f9cf2744f13d4db66 Mon Sep 17 00:00:00 2001 From: abdourahmanbarry Date: Wed, 3 Jun 2026 19:53:32 +0000 Subject: [PATCH 05/52] Change comments in partition interfaces --- .../PartitionInterface/BusPartitionInterface.cpp | 2 +- .../PartitionInterface/BusPartitionInterface.hpp | 1 - .../ComponentPartitionInterface.cpp | 13 +++++-------- examples/PowerElectronics/Partition/Partition.cpp | 4 ++-- 4 files changed, 8 insertions(+), 12 deletions(-) diff --git a/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.cpp b/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.cpp index b2c806b26..e275c6b39 100644 --- a/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.cpp +++ b/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.cpp @@ -44,7 +44,7 @@ namespace GridKit } /*! - * @brief allocate method computes sparsity pattern of the Jacobian. + * @brief allocate method */ template int BusPartitionInterface::allocate() diff --git a/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.hpp b/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.hpp index 1b1d9e4bd..0c8b2e902 100644 --- a/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.hpp +++ b/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.hpp @@ -5,7 +5,6 @@ #include #include - #include "GridKit/Model/PowerElectronics/PartitionInterface/PartitionInterface.hpp" namespace GridKit diff --git a/GridKit/Model/PowerElectronics/PartitionInterface/ComponentPartitionInterface.cpp b/GridKit/Model/PowerElectronics/PartitionInterface/ComponentPartitionInterface.cpp index 3c88c4dee..c5a06ae66 100644 --- a/GridKit/Model/PowerElectronics/PartitionInterface/ComponentPartitionInterface.cpp +++ b/GridKit/Model/PowerElectronics/PartitionInterface/ComponentPartitionInterface.cpp @@ -11,9 +11,8 @@ namespace GridKit { /*! - * @brief Constructor for a constant MicrogridLoad model + * @brief Constructor for the ComponentPartitionInterface * - * Calls default ModelEvaluatorImpl constructor. * * * @@ -23,8 +22,6 @@ namespace GridKit ComponentPartitionInterface::ComponentPartitionInterface(CircuitComponent* component, IdxT bus_i, IdxT bus_j, IdxT id) : component_(component) { - // internals [id, iq] - // externals [\omegaref, vbd_out, vbq_out] size_ = component_->size(); n_intern_ = component_->getInternalSize(); n_extern_ = component_->getExternSize(); @@ -41,7 +38,7 @@ namespace GridKit } /*! - * @brief allocate method computes sparsity pattern of the Jacobian. + * @brief allocate method. */ template int ComponentPartitionInterface::allocate() @@ -85,7 +82,7 @@ namespace GridKit } /** - * Initialization of the grid model + * Initialization */ template int ComponentPartitionInterface::initialize() @@ -94,7 +91,7 @@ namespace GridKit } /* - * \brief Identify differential variables + * */ template int ComponentPartitionInterface::tagDifferentiable() @@ -103,7 +100,7 @@ namespace GridKit } /** - * @brief Eval Micro Load + * @brief */ template int ComponentPartitionInterface::evaluateResidual() diff --git a/examples/PowerElectronics/Partition/Partition.cpp b/examples/PowerElectronics/Partition/Partition.cpp index b02285fbd..92f2d60ff 100644 --- a/examples/PowerElectronics/Partition/Partition.cpp +++ b/examples/PowerElectronics/Partition/Partition.cpp @@ -45,8 +45,8 @@ int main(int /* argc */, char const** /* argv */) comp3->setExternalConnectionNodes(3, 3); comp3->setExternalConnectionNodes(4, 4); - GridKit::HiresComponent3 comp2copy(*comp3); - GridKit::BusPartitionInterface* busInterface = new GridKit::BusPartitionInterface(comp2copy, 3, 4, 4); + GridKit::HiresComponent3 comp3copy(*comp3); + GridKit::BusPartitionInterface* busInterface = new GridKit::BusPartitionInterface(comp3copy, 3, 4, 4); GridKit::ComponentPartitionInterface* compInterface = new GridKit::ComponentPartitionInterface(comp3, 3, 4, 5); partition1->addComponent(comp1); From 82c0efc99984345ed017146f48f7ff9179ac9f9d Mon Sep 17 00:00:00 2001 From: abdourahmanbarry Date: Mon, 15 Jun 2026 01:36:06 +0000 Subject: [PATCH 06/52] Added a subsystem model for evaluating partitions --- GridKit/Model/PowerElectronics/CMakeLists.txt | 2 + .../Model/PowerElectronics/SubsystemModel.hpp | 452 ++++++++++++++++++ .../PowerElectronics/Partition/CMakeLists.txt | 8 + .../Partition/HiresComponent1.hpp | 16 +- .../Partition/HiresComponent3.hpp | 15 +- .../PowerElectronics/Partition/Partition.cpp | 110 +++-- 6 files changed, 548 insertions(+), 55 deletions(-) create mode 100644 GridKit/Model/PowerElectronics/SubsystemModel.hpp diff --git a/GridKit/Model/PowerElectronics/CMakeLists.txt b/GridKit/Model/PowerElectronics/CMakeLists.txt index d0b4f0080..a45284ee7 100644 --- a/GridKit/Model/PowerElectronics/CMakeLists.txt +++ b/GridKit/Model/PowerElectronics/CMakeLists.txt @@ -30,4 +30,6 @@ install( SystemModelPowerElectronics.hpp NodeBase.hpp ExternalConnection.hpp + SubsystemModel.hpp DESTINATION include/GridKit/Model/PowerElectronics) + diff --git a/GridKit/Model/PowerElectronics/SubsystemModel.hpp b/GridKit/Model/PowerElectronics/SubsystemModel.hpp new file mode 100644 index 000000000..af3d9dc9f --- /dev/null +++ b/GridKit/Model/PowerElectronics/SubsystemModel.hpp @@ -0,0 +1,452 @@ + + +#pragma once + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "GridKit/Model/PowerElectronics/MicrogridBusDQ/MicrogridBusDQ.hpp" + +namespace GridKit +{ + template + class SubsystemModel : public CircuitComponent + { + using RealT = typename CircuitComponent::RealT; + using MatrixT = typename CircuitComponent::MatrixT; + using CsrMatrixT = typename CircuitComponent::CsrMatrixT; + using component_type = CircuitComponent; + using node_type = CircuitNode; + + using CircuitComponent::size_; + using CircuitComponent::n_extern_; + using CircuitComponent::n_intern_; + using CircuitComponent::nnz_; + using CircuitComponent::time_; + using CircuitComponent::alpha_; + using CircuitComponent::y_; + using CircuitComponent::yp_; + using CircuitComponent::f_; + using CircuitComponent::jac_; + using CircuitComponent::rel_tol_; + using CircuitComponent::abs_tol_; + + public: + /** + * @brief Default constructor for the system model + * + * @post System model parameters set as default + */ + SubsystemModel(IdxT refframe_index = neg1_) + { + // Set system model parameters as default + rel_tol_ = 1e-4; + abs_tol_ = 1e-4; + this->max_steps_ = 2000; + // By default don't use the jacobian + use_jac_ = false; + refframe_index_ = refframe_index; + } + + virtual ~SubsystemModel() + { + for (auto comp : this->components_) + { + delete comp; + } + delete csr_jac_; + delete[] map_to_csr_; + } + + bool hasJacobian() final + { + if (!this->use_jac_) + return false; + + for (const auto& component : components_) + { + if (!component->hasJacobian()) + { + return false; + } + } + return true; + } + + int allocate() override + { + // Allocate all components + size_ = 0; + n_extern_ = 0; + n_intern_ = 0; + + size_t counter = 0; + + // First pass: Map global component indices to local subsystem indices. + for (const auto& component : components_) + { + component->allocate(); + + for (IdxT i = component->getExternSize(); i < component->size(); i++) + { + + IdxT index = component->getNodeConnection(i); + + if (index != neg1_) + { + internal_map_[index] = counter; + n_intern_++; + counter++; + } + } + + // TODO : To be removed once MicrogridBusDQ owns its internals + auto* comp = dynamic_cast*>(component); + + if (comp != nullptr) + { + IdxT index1 = component->getNodeConnection(0); + IdxT index2 = component->getNodeConnection(1); + + internal_map_[index1] = counter; + counter++; + internal_map_[index2] = counter; + counter++; + n_intern_ += 2; + } + } + + if (refframe_index_ != neg1_) + { + internal_map_[refframe_index_] = counter; + n_intern_++; + counter++; + } + + // Second pass: Identify variables required by this partition but owned by + // another partition. + for (const auto& component : components_) + { + for (IdxT i = 0; i < component->getExternSize(); i++) + { + IdxT index = component->getNodeConnection(i); + + if (internal_map_.count(index) < 1 && external_map_.count(index) < 1 && index != neg1_) + { + external_map_[index] = counter; + + counter++; + n_extern_++; + } + } + } + + size_ = n_intern_ + n_extern_; + + this->mapGlobalToLocal(); + + // Allocate subsystem vectors. + y_.resize(n_intern_); + yp_.resize(n_intern_); + f_.resize(n_intern_); + + // Allocate vectors associated with external coupling variables. + y_ext_.resize(n_extern_); + yp_ext_.resize(n_extern_); + f_ext_.resize(n_extern_); + external_indices_.resize(n_extern_); + + // Store the mapping from local subsystem indices back to their global system indices + for (const auto& [global_idx, local_idx] : internal_map_) + { + this->setExternalConnectionNodes(local_idx, global_idx); + } + + // Store the global indices of all external coupling variables. + for (const auto& [global_idx, local_idx] : external_map_) + { + external_indices_[local_idx - n_intern_] = global_idx; + } + + return 0; + } + + /** + * @brief Set intial y and y' of each component + * + * @return int 0 if successful, positive if there's a recoverable error, negative if unrecoverable + */ + int initialize() final + { + // Initialize components + for (const auto& component : components_) + { + component->initialize(); + } + this->distributeVectors(); + + return 0; + } + + /** + * @brief Distribute y and y' to each component based of node connection graph + * + * @post Each component has y and y' set + * + * @return int 0 if successful, positive if there's a recoverable error, negative if unrecoverable + */ + int distributeVectors() + { + for (const auto& component : components_) + { + IdxT size = component->size(); + std::vector& y = component->y(); + std::vector& yp = component->yp(); + + for (IdxT j = 0; j < size; ++j) + { + if (component->getNodeConnection(j) == neg1_) + { + y[j] = 0.0; + yp[j] = 0.0; + } + else if (component->getNodeConnection(j) < this->getInternalSize()) + { + y[j] = y_[component->getNodeConnection(j)]; + yp[j] = yp_[component->getNodeConnection(j)]; + } + else + { + y[j] = y_ext_[component->getNodeConnection(j) - this->getInternalSize()]; + yp[j] = yp_ext_[component->getNodeConnection(j) - this->getInternalSize()]; + } + } + } + return 0; + } + + int tagDifferentiable() final + { + return 0; + } + + /** + * @brief Evaluate Residuals at each component then collect them + * + * @return int 0 if successful, positive if there's a recoverable error, negative if unrecoverable + */ + int evaluateResidual() final + { + for (IdxT i = 0; i < this->f_.size(); i++) + { + f_[i] = 0.0; + } + + this->distributeVectors(); + + // Update system residual vector + for (const auto& component : components_) + { + // TODO:check return type + component->evaluateResidual(); + + IdxT size = component->size(); + const std::vector& residual = component->getResidual(); + for (IdxT j = 0; j < size; ++j) + { + //@todo should do a different grounding check + if (component->getNodeConnection(j) != neg1_ && component->getNodeConnection(j) < this->getInternalSize()) + { + f_[component->getNodeConnection(j)] += residual[j]; + } + } + } + + return 0; + } + + /** + * @brief Creates the system Jacobian representing \alpha dF/dy' + dF/dy + * + * Updates the CSR Jacobian values using the per-component mappings + * computed during allocate(). + * + * @return int 0 if successful, positive if there's a recoverable error, negative if unrecoverable + */ + int evaluateJacobian() final + { + return 0; + } + + /** + * @brief Evaluate integrands for the system quadratures. + */ + int evaluateIntegrand() final + { + return 0; + } + + /** + * @brief Initialize system adjoint. + * + * Updates variables and optimization parameters, then initializes + * adjoints locally and copies them to the system adjoint vector. + */ + int initializeAdjoint() final + { + return 0; + } + + /** + * @brief Compute adjoint residual for the system model. + * + * + */ + int evaluateAdjointResidual() final + { + return 0; + } + + /** + * @brief Evaluate adjoint integrand for the system model. + * + * + */ + int evaluateAdjointIntegrand() final + { + return 0; + } + + /** + * @brief Distribute time and time scaling for each component + * + * @param t + * @param a + */ + void updateTime(RealT t, RealT a) final + { + for (const auto& component : components_) + { + component->updateTime(t, a); + } + time_ = t; + alpha_ = a; + } + + /** + * @brief print the system residual in COO format + * + * @param[in] filename + * @param[in] title + */ + void printResidualMatrixMarket(std::string filename, std::string title) + { + writeVectorToMatrixMarket(f_, filename, title); + } + + /** + * @brief print the system Jacobian in COO format + * + * @param[in] filename + * @param[in] title + */ + void printJacobianMatrixMarket(std::string filename, std::string title) + { + jac_.printMatrixMarket(filename, title); + } + + CsrMatrixT* getCsrJacobian() const override + { + return csr_jac_; + } + + void addComponent(component_type* component) + { + components_.push_back(component); + } + + std::unordered_map& getInternalMap() + { + return internal_map_; + } + + std::unordered_map& getExternalMap() + { + return external_map_; + } + + int mapGlobalToLocal() + { + for (const auto& component : components_) + { + + for (IdxT i = 0; i < component->size(); i++) + { + IdxT index = component->getNodeConnection(i); + + if (index == neg1_) + { + continue; + } + else if (internal_map_.count(index) > 0) + { + component->setExternalConnectionNodes(i, internal_map_.at(index)); + } + else + { + component->setExternalConnectionNodes(i, external_map_.at(index)); + } + } + } + + return 0; + } + + int mapLocalToGlobal() + { + return 0; + } + + std::vector& getExternalDataY() + { + return y_ext_; + } + + std::vector& getExternalDataYP() + { + return yp_ext_; + } + + std::vector& getExternalIndices() + { + return external_indices_; + } + + private: + static constexpr IdxT neg1_ = INVALID_INDEX; + + std::vector components_; + std::unordered_map internal_map_; + std::unordered_map external_map_; + std::vector external_indices_; + std::vector y_ext_; + std::vector yp_ext_; + std::vector f_ext_; + IdxT refframe_index_{neg1_}; + + IdxT* map_to_csr_{nullptr}; + CsrMatrixT* csr_jac_{nullptr}; + + int jac_call_count_{0}; + bool use_jac_; + + }; // class SubsystemModel + +} // namespace GridKit diff --git a/examples/PowerElectronics/Partition/CMakeLists.txt b/examples/PowerElectronics/Partition/CMakeLists.txt index a5d192a9d..2bc268e4f 100644 --- a/examples/PowerElectronics/Partition/CMakeLists.txt +++ b/examples/PowerElectronics/Partition/CMakeLists.txt @@ -2,3 +2,11 @@ add_executable(partition Partition.cpp) target_link_libraries(partition GridKit::solvers_dyn GridKit::power_elec_partition_interfaces) + +add_executable(partitionMicrogrid PartitionMicrogrid.cpp) +target_link_libraries(partitionMicrogrid GridKit::power_elec_disgen + GridKit::power_elec_microline + GridKit::power_elec_microload + GridKit::solvers_dyn + GridKit::power_elec_microbusdq + GridKit::power_elec_partition_interfaces) \ No newline at end of file diff --git a/examples/PowerElectronics/Partition/HiresComponent1.hpp b/examples/PowerElectronics/Partition/HiresComponent1.hpp index 71f4268f5..1d6c51fc8 100644 --- a/examples/PowerElectronics/Partition/HiresComponent1.hpp +++ b/examples/PowerElectronics/Partition/HiresComponent1.hpp @@ -43,7 +43,7 @@ namespace GridKit size_ = 5; n_intern_ = 3; n_extern_ = 2; - extern_indices_ = {3, 4}; + extern_indices_ = {0, 1}; idc_ = id; } @@ -72,11 +72,15 @@ namespace GridKit int evaluateResidual() { - f_[0] = yp_[0] + 1.71 * y_[0] - 0.43 * y_[1] - 8.32 * y_[2] - 0.0007; - f_[1] = yp_[1] - 1.71 * y_[0] + 8.75 * y_[1]; - f_[2] = yp_[2] + 10.03 * y_[2] - 0.43 * y_[3] - 0.035 * y_[4]; - f_[3] = -8.32 * y_[1] - 1.71 * y_[2] + 0.1 * y_[3]; - f_[4] = +0.7 * y_[4]; + + // outputs + f_[0] = -8.32 * y_[3] - 1.71 * y_[4] + 0.1 * y_[0]; + f_[1] = +0.7 * y_[1]; + + // Internals + f_[2] = yp_[2] + 1.71 * y_[2] - 0.43 * y_[3] - 8.32 * y_[4] - 0.0007; + f_[3] = yp_[3] - 1.71 * y_[2] + 8.75 * y_[3]; + f_[4] = yp_[4] + 10.03 * y_[4] - 0.43 * y_[0] - 0.035 * y_[1]; return 0; } diff --git a/examples/PowerElectronics/Partition/HiresComponent3.hpp b/examples/PowerElectronics/Partition/HiresComponent3.hpp index a396a5e82..d0b71f5c1 100644 --- a/examples/PowerElectronics/Partition/HiresComponent3.hpp +++ b/examples/PowerElectronics/Partition/HiresComponent3.hpp @@ -43,7 +43,7 @@ namespace GridKit size_ = 5; n_intern_ = 3; n_extern_ = 2; - extern_indices_ = {3, 4}; + extern_indices_ = {0, 1}; idc_ = id; } @@ -72,11 +72,14 @@ namespace GridKit int evaluateResidual() { - f_[0] = yp_[0] + 280 * y_[0] * y_[2] - 0.69 * y_[3] - 1.71 * y_[4] + 0.43 * y_[0] - 0.69 * y_[1]; - f_[1] = yp_[1] - 280 * y_[0] * y_[2] + 1.81 * y_[1]; - f_[2] = yp_[2] + 280 * y_[0] * y_[2] - 1.81 * y_[1]; - f_[3] = 0.02 * y_[3]; - f_[4] = 0.045 * y_[4] - 0.43 * y_[0] - 0.43 * y_[1]; + // Outputs + f_[0] = 0.02 * y_[0]; + f_[1] = 0.045 * y_[1] - 0.43 * y_[2] - 0.43 * y_[3]; + + // Internals + f_[2] = yp_[2] + 280 * y_[2] * y_[4] - 0.69 * y_[0] - 1.71 * y_[1] + 0.43 * y_[2] - 0.69 * y_[3]; + f_[3] = yp_[3] - 280 * y_[2] * y_[4] + 1.81 * y_[3]; + f_[4] = yp_[4] + 280 * y_[2] * y_[4] - 1.81 * y_[3]; return 0; } diff --git a/examples/PowerElectronics/Partition/Partition.cpp b/examples/PowerElectronics/Partition/Partition.cpp index 92f2d60ff..319e4dc00 100644 --- a/examples/PowerElectronics/Partition/Partition.cpp +++ b/examples/PowerElectronics/Partition/Partition.cpp @@ -4,10 +4,10 @@ #include #include #include +#include #include -#include -#include +#include #include "HiresBus.hpp" #include "HiresComponent1.hpp" @@ -18,79 +18,103 @@ std::vector hires(const std::vector& y, const std::vector(rel_tol, abs_tol, use_jac, max_step_number); - auto* partition2 = new GridKit::PowerElectronicsModel(rel_tol, abs_tol, use_jac, max_step_number); + GridKit::SubsystemModel* partition1 = new GridKit::SubsystemModel(); + GridKit::SubsystemModel* partition2 = new GridKit::SubsystemModel(); GridKit::HiresComponent1* comp1 = new GridKit::HiresComponent1(1); GridKit::HiresBus* bus1 = new GridKit::HiresBus(2); GridKit::HiresComponent3* comp3 = new GridKit::HiresComponent3(3); - comp1->setExternalConnectionNodes(0, 0); - comp1->setExternalConnectionNodes(1, 1); - comp1->setExternalConnectionNodes(2, 2); - comp1->setExternalConnectionNodes(3, 3); - comp1->setExternalConnectionNodes(4, 4); + comp1->setExternalConnectionNodes(0, 3); + comp1->setExternalConnectionNodes(1, 4); + comp1->setExternalConnectionNodes(2, 0); + comp1->setExternalConnectionNodes(3, 1); + comp1->setExternalConnectionNodes(4, 2); bus1->setExternalConnectionNodes(0, 3); bus1->setExternalConnectionNodes(1, 4); - comp3->setExternalConnectionNodes(0, 5); - comp3->setExternalConnectionNodes(1, 6); - comp3->setExternalConnectionNodes(2, 7); - comp3->setExternalConnectionNodes(3, 3); - comp3->setExternalConnectionNodes(4, 4); + comp3->setExternalConnectionNodes(0, 3); + comp3->setExternalConnectionNodes(1, 4); + comp3->setExternalConnectionNodes(2, 5); + comp3->setExternalConnectionNodes(3, 6); + comp3->setExternalConnectionNodes(4, 7); - GridKit::HiresComponent3 comp3copy(*comp3); - GridKit::BusPartitionInterface* busInterface = new GridKit::BusPartitionInterface(comp3copy, 3, 4, 4); - GridKit::ComponentPartitionInterface* compInterface = new GridKit::ComponentPartitionInterface(comp3, 3, 4, 5); + GridKit::HiresComponent3 comp3copy(*comp3); + GridKit::BusPartitionInterface* busInterface = new GridKit::BusPartitionInterface(comp3copy, 3, 4, 4); partition1->addComponent(comp1); partition1->addComponent(bus1); partition1->addComponent(busInterface); - partition2->addComponent(compInterface); - - size_t sys_size = 8; + partition2->addComponent(comp3); - partition1->allocate(sys_size); - partition2->allocate(sys_size); + partition1->allocate(); + partition2->allocate(); - busInterface->setExternalDataY({6, 7, 8}); - busInterface->setExternalDataYP({6, 7, 8}); + std::vector y = {1, 2, 3, 4, 5, 6, 7, 8}; + std::vector yp = {1, 2, 3, 4, 5, 6, 7, 8}; - compInterface->setExternalDataY({4, 5}); - compInterface->setExternalDataYP({4, 5}); + // Distribute externals to partition 1 + for (size_t i = 0; i < partition1->getExternSize(); i++) + { + partition1->getExternalDataY()[i] = y[partition1->getExternalIndices()[i]]; + partition1->getExternalDataYP()[i] = yp[partition1->getExternalIndices()[i]]; + } - for (size_t i = 0; i < sys_size; i++) + // Distribute externals to partition 2 + for (size_t i = 0; i < partition2->getExternSize(); i++) { + partition2->getExternalDataY()[i] = y[partition2->getExternalIndices()[i]]; + partition2->getExternalDataYP()[i] = yp[partition2->getExternalIndices()[i]]; + } - partition1->y()[i] = static_cast(i) + 1; - partition1->yp()[i] = static_cast(i) + 1; + // Distribute internals to partition 1 + for (size_t i = 0; i < partition1->getInternalSize(); i++) + { + partition1->y()[i] = static_cast(1 + i); + partition1->yp()[i] = static_cast(1 + i); + } - partition2->y()[i] = static_cast(i) + 1; - partition2->yp()[i] = static_cast(i) + 1; + // Distribute internals to partition 2 + for (size_t i = 0; i < partition2->getInternalSize(); i++) + { + partition2->y()[i] = static_cast(6 + i); + partition2->yp()[i] = static_cast(6 + i); } + // Evaluate Residuals for each partition partition1->evaluateResidual(); partition2->evaluateResidual(); - auto ref = hires({1, 2, 3, 4, 5, 6, 7, 8}, {1, 2, 3, 4, 5, 6, 7, 8}); + auto printTitle = [](std::string msg) -> void + { + std::cout << "\n------------- " << msg << " -----------" << std::endl; + printf("%-10s ---------- %10s\n", "Res Values", "Comp. Index"); + }; + + // Print Residuals from partition 1 + printTitle("Partition 1"); + for (size_t i = 0; i < partition1->getInternalSize(); i++) + { + auto com_index = partition1->getNodeConnection(static_cast(i)); + printf("%-10.5g ---------- %10zu\n", partition1->getResidual()[i], com_index); + } - std::cout << "------------- Partition 1 -----------" << std::endl; - for (size_t i = 0; i < sys_size; i++) + // Print Residuals from partition 2 + printTitle("Partition 2"); + for (size_t i = 0; i < partition2->getInternalSize(); i++) { - printf("%-10.5g ---------- %10.5g\n", partition1->getResidual()[i], ref[i]); + auto com_index = partition2->getNodeConnection(static_cast(i)); + printf("%-10.5g ---------- %10zu\n", partition2->getResidual()[i], com_index); } - std::cout << "\n------------- Partition 2 -----------" << std::endl; - for (size_t i = 0; i < sys_size; i++) + auto ref = hires(y, yp); + + printTitle("Reference Solution"); + for (size_t i = 0; i < 8; i++) { - printf("%-10.5g ---------- %10.5g\n", partition2->getResidual()[i], ref[i]); + printf("%-10.5g ---------- %10zu\n", ref[i], i); } delete partition1; From d5d84c588f835a1caa6d4aaf0c02d3181e4f320a Mon Sep 17 00:00:00 2001 From: abdourahmanbarry Date: Mon, 15 Jun 2026 01:43:51 +0000 Subject: [PATCH 07/52] Added a partitioned microgrid test problem --- .../Partition/PartitionMicrogrid.cpp | 392 ++++++++++++++++++ 1 file changed, 392 insertions(+) create mode 100644 examples/PowerElectronics/Partition/PartitionMicrogrid.cpp diff --git a/examples/PowerElectronics/Partition/PartitionMicrogrid.cpp b/examples/PowerElectronics/Partition/PartitionMicrogrid.cpp new file mode 100644 index 000000000..4a961caa8 --- /dev/null +++ b/examples/PowerElectronics/Partition/PartitionMicrogrid.cpp @@ -0,0 +1,392 @@ +#include + +#define _USE_MATH_DEFINES +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +int main() +{ + /// @todo Needs to be modified. Some components are small relative to others thus + /// there error is high (or could be matlab vector issue) + double abs_tol = 1.0e-8; + double rel_tol = 1.0e-8; + size_t max_step_number = 3000; + bool use_jac = true; + + auto* sysmodel = new GridKit::PowerElectronicsModel(rel_tol, abs_tol, use_jac, max_step_number); + + // Modeled after the problem in the paper + double RN = 1.0e4; + + // DG Params + GridKit::DistributedGeneratorParameters parms1; + parms1.wb_ = 2.0 * M_PI * 50.0; + parms1.wc_ = 31.41; + parms1.mp_ = 9.4e-5; + parms1.Vn_ = 380.0; + parms1.nq_ = 1.3e-3; + parms1.F_ = 0.75; + parms1.Kiv_ = 420.0; + parms1.Kpv_ = 0.1; + parms1.Kic_ = 2.0e4; + parms1.Kpc_ = 15.0; + parms1.Cf_ = 5.0e-5; + parms1.rLf_ = 0.1; + parms1.Lf_ = 1.35e-3; + parms1.rLc_ = 0.03; + parms1.Lc_ = 0.35e-3; + + GridKit::DistributedGeneratorParameters parms2; + // Parameters from MATLAB Microgrid code for first DG + parms2.wb_ = 2.0 * M_PI * 50.0; + parms2.wc_ = 31.41; + parms2.mp_ = 12.5e-5; + parms2.Vn_ = 380.0; + parms2.nq_ = 1.5e-3; + parms2.F_ = 0.75; + parms2.Kiv_ = 390.0; + parms2.Kpv_ = 0.05; + parms2.Kic_ = 16.0e3; + parms2.Kpc_ = 10.5; + parms2.Cf_ = 50.0e-6; + parms2.rLf_ = 0.1; + parms2.Lf_ = 1.35e-3; + parms2.rLc_ = 0.03; + parms2.Lc_ = 0.35e-3; + + // Line params + double rline1 = 0.23; + double Lline1 = 0.1 / (2.0 * M_PI * 50.0); + + double rline2 = 0.35; + double Lline2 = 0.58 / (2.0 * M_PI * 50.0); + + double rline3 = 0.23; + double Lline3 = 0.1 / (2.0 * M_PI * 50.0); + + // load parms + double rload1 = 3.0; + double Lload1 = 2.0 / (2.0 * M_PI * 50.0); + + double rload2 = 2.0; + double Lload2 = 1.0 / (2.0 * M_PI * 50.0); + + // indexing sets + size_t Nsize = 2; + // DGs + - refframe Lines + Loads + size_t vec_size_internals = 13 * (2 * Nsize) - 1 + (2 + 4 * (Nsize - 1)) + 2 * Nsize; + // \omegaref + BusDQ + size_t vec_size_externals = 1 + 2 * (2 * Nsize); + size_t dqbus1 = vec_size_internals + 1; + size_t dqbus2 = vec_size_internals + 3; + size_t dqbus3 = vec_size_internals + 5; + size_t dqbus4 = vec_size_internals + 7; + + size_t vec_size_total = vec_size_internals + vec_size_externals; + + size_t indexv = 0; + + // dg 1 + GridKit::DistributedGenerator* dg1 = new GridKit::DistributedGenerator(0, parms1, true); + // ref motor + dg1->setExternalConnectionNodes(0, vec_size_internals); + // outputs + dg1->setExternalConnectionNodes(1, dqbus1); + dg1->setExternalConnectionNodes(2, dqbus1 + 1); + //"grounding" of the difference + dg1->setExternalConnectionNodes(3, static_cast(-1)); + // internal connections + for (size_t i = 0; i < 12; i++) + { + + dg1->setExternalConnectionNodes(4 + i, indexv + i); + } + indexv += 12; + + // dg 2 + GridKit::DistributedGenerator* dg2 = new GridKit::DistributedGenerator(1, parms1, false); + // ref motor + dg2->setExternalConnectionNodes(0, vec_size_internals); + // outputs + dg2->setExternalConnectionNodes(1, dqbus2); + dg2->setExternalConnectionNodes(2, dqbus2 + 1); + // internal connections + for (size_t i = 0; i < 13; i++) + { + + dg2->setExternalConnectionNodes(3 + i, indexv + i); + } + indexv += 13; + + // dg 3 + GridKit::DistributedGenerator* dg3 = new GridKit::DistributedGenerator(2, parms2, false); + // ref motor + dg3->setExternalConnectionNodes(0, vec_size_internals); + // outputs + dg3->setExternalConnectionNodes(1, dqbus3); + dg3->setExternalConnectionNodes(2, dqbus3 + 1); + // internal connections + for (size_t i = 0; i < 13; i++) + { + + dg3->setExternalConnectionNodes(3 + i, indexv + i); + } + indexv += 13; + + // dg 4 + GridKit::DistributedGenerator* dg4 = new GridKit::DistributedGenerator(3, parms2, false); + // ref motor + dg4->setExternalConnectionNodes(0, vec_size_internals); + // outputs + dg4->setExternalConnectionNodes(1, dqbus4); + dg4->setExternalConnectionNodes(2, dqbus4 + 1); + + // internal connections + for (size_t i = 0; i < 13; i++) + { + + dg4->setExternalConnectionNodes(3 + i, indexv + i); + } + indexv += 13; + + // Lines + + // line 1 + GridKit::MicrogridLine* l1 = new GridKit::MicrogridLine(4, rline1, Lline1); + // ref motor + l1->setExternalConnectionNodes(0, vec_size_internals); + // input connections + l1->setExternalConnectionNodes(1, dqbus1); + l1->setExternalConnectionNodes(2, dqbus1 + 1); + // output connections + l1->setExternalConnectionNodes(3, dqbus2); + l1->setExternalConnectionNodes(4, dqbus2 + 1); + // internal connections + for (size_t i = 0; i < 2; i++) + { + + l1->setExternalConnectionNodes(5 + i, indexv + i); + } + indexv += 2; + + // line 2 + GridKit::MicrogridLine* l2 = new GridKit::MicrogridLine(5, rline2, Lline2); + // ref motor + l2->setExternalConnectionNodes(0, vec_size_internals); + // input connections + l2->setExternalConnectionNodes(1, dqbus2); + l2->setExternalConnectionNodes(2, dqbus2 + 1); + // output connections + l2->setExternalConnectionNodes(3, dqbus3); + l2->setExternalConnectionNodes(4, dqbus3 + 1); + // internal connections + for (size_t i = 0; i < 2; i++) + { + + l2->setExternalConnectionNodes(5 + i, indexv + i); + } + indexv += 2; + + // line 3 + GridKit::MicrogridLine* l3 = new GridKit::MicrogridLine(6, rline3, Lline3); + // ref motor + l3->setExternalConnectionNodes(0, vec_size_internals); + // input connections + l3->setExternalConnectionNodes(1, dqbus3); + l3->setExternalConnectionNodes(2, dqbus3 + 1); + // output connections + l3->setExternalConnectionNodes(3, dqbus4); + l3->setExternalConnectionNodes(4, dqbus4 + 1); + // internal connections + for (size_t i = 0; i < 2; i++) + { + + l3->setExternalConnectionNodes(5 + i, indexv + i); + } + indexv += 2; + + // loads + + // load 1 + GridKit::MicrogridLoad* load1 = new GridKit::MicrogridLoad(7, rload1, Lload1); + // ref motor + load1->setExternalConnectionNodes(0, vec_size_internals); + // input connections + load1->setExternalConnectionNodes(1, dqbus1); + load1->setExternalConnectionNodes(2, dqbus1 + 1); + // internal connections + for (size_t i = 0; i < 2; i++) + { + + load1->setExternalConnectionNodes(3 + i, indexv + i); + } + indexv += 2; + + // load 2 + GridKit::MicrogridLoad* load2 = new GridKit::MicrogridLoad(8, rload2, Lload2); + // ref motor + load2->setExternalConnectionNodes(0, vec_size_internals); + // input connections + load2->setExternalConnectionNodes(1, dqbus3); + load2->setExternalConnectionNodes(2, dqbus3 + 1); + // internal connections + for (size_t i = 0; i < 2; i++) + { + + load2->setExternalConnectionNodes(3 + i, indexv + i); + } + indexv += 2; + + // Virtual PQ Buses + GridKit::MicrogridBusDQ* bus1 = new GridKit::MicrogridBusDQ(9, RN); + + bus1->setExternalConnectionNodes(0, dqbus1); + bus1->setExternalConnectionNodes(1, dqbus1 + 1); + + GridKit::MicrogridBusDQ* bus2 = new GridKit::MicrogridBusDQ(10, RN); + + bus2->setExternalConnectionNodes(0, dqbus2); + bus2->setExternalConnectionNodes(1, dqbus2 + 1); + + GridKit::MicrogridBusDQ* bus3 = new GridKit::MicrogridBusDQ(11, RN); + + bus3->setExternalConnectionNodes(0, dqbus3); + bus3->setExternalConnectionNodes(1, dqbus3 + 1); + + GridKit::MicrogridBusDQ* bus4 = new GridKit::MicrogridBusDQ(12, RN); + + bus4->setExternalConnectionNodes(0, dqbus4); + bus4->setExternalConnectionNodes(1, dqbus4 + 1); + + GridKit::SubsystemModel* partition1 = new GridKit::SubsystemModel(vec_size_internals); + GridKit::SubsystemModel* partition2 = new GridKit::SubsystemModel(); + + GridKit::MicrogridLine comp3copy(*l2); + GridKit::BusPartitionInterface* busInterface = new GridKit::BusPartitionInterface(comp3copy, dqbus2, dqbus2 + 1, 14); + + partition1->addComponent(dg1); + partition1->addComponent(dg2); + partition1->addComponent(l1); + partition1->addComponent(bus1); + partition1->addComponent(bus2); + partition1->addComponent(load1); + partition1->addComponent(busInterface); + + partition2->addComponent(dg3); + partition2->addComponent(dg4); + partition2->addComponent(l2); + partition2->addComponent(bus3); + partition2->addComponent(bus4); + partition2->addComponent(l3); + partition2->addComponent(load2); + + partition1->allocate(); + partition2->allocate(); + + std::vector y; + std::vector yp; + + for (size_t i = 0; i < vec_size_total; i++) + { + y.push_back(static_cast(i + 1)); + yp.push_back(static_cast(i + 1)); + } + + // Distribute externals to partition 1 + for (size_t i = 0; i < partition1->getExternSize(); i++) + { + partition1->getExternalDataY()[i] = y[partition1->getExternalIndices()[i]]; + partition1->getExternalDataYP()[i] = yp[partition1->getExternalIndices()[i]]; + } + + // Distribute externals to partition 2 + for (size_t i = 0; i < partition2->getExternSize(); i++) + { + partition2->getExternalDataY()[i] = y[partition2->getExternalIndices()[i]]; + partition2->getExternalDataYP()[i] = yp[partition2->getExternalIndices()[i]]; + } + + // Distribute internals to partition 1 + for (size_t i = 0; i < partition1->getInternalSize(); i++) + { + partition1->y()[i] = y[partition1->getNodeConnection(i)]; + partition1->yp()[i] = yp[partition1->getNodeConnection(i)]; + } + + // Distribute internals to partition 2 + for (size_t i = 0; i < partition2->getInternalSize(); i++) + { + partition2->y()[i] = y[partition2->getNodeConnection(i)]; + partition2->yp()[i] = yp[partition2->getNodeConnection(i)]; + } + + // Evaluate Residuals for each partition + partition1->evaluateResidual(); + partition2->evaluateResidual(); + + auto printTitle = [](std::string msg) -> void + { + std::cout << "\n------------- " << msg << " -----------" << std::endl; + printf("%-10s ---------- %10s\n", "Res Values", "Comp. Index"); + }; + + // Print Residuals from partition 1 + printTitle("Partition 1"); + for (size_t i = 0; i < partition1->getInternalSize(); i++) + { + auto com_index = partition1->getNodeConnection(static_cast(i)); + printf("%-10.5g ----------%zu %10zu\n", partition1->getResidual()[i], i, com_index); + } + + // Print Residuals from partition 2 + printTitle("Partition 2"); + for (size_t i = 0; i < partition2->getInternalSize(); i++) + { + auto com_index = partition2->getNodeConnection(static_cast(i)); + printf("%-10.5g ----------%zu %10zu\n", partition2->getResidual()[i], i, com_index); + } + + // sysmodel->addComponent(dg1); + // sysmodel->addComponent(dg2); + // sysmodel->addComponent(dg3); + // sysmodel->addComponent(dg4); + // sysmodel->addComponent(l1); + // sysmodel->addComponent(l2); + // sysmodel->addComponent(l3); + // sysmodel->addComponent(bus1); + // sysmodel->addComponent(bus2); + // sysmodel->addComponent(bus3); + // sysmodel->addComponent(bus4); + // sysmodel->addComponent(load1); + // sysmodel->addComponent(load2); + + // sysmodel->allocate(vec_size_total); + + // for (size_t i = 0; i < vec_size_total; i++) + // { + // sysmodel->y()[i] = y[i]; + // sysmodel->yp()[i] = yp[i]; + // } + + // sysmodel->evaluateResidual(); + + // printTitle("Reference Solution"); + // for (size_t i = 0; i < vec_size_total; i++) + // { + // printf("%-10.5g ---------- %10zu\n", sysmodel->getResidual()[i], i); + // } + + delete partition1; + delete partition2; + delete sysmodel; + + return 0; +} \ No newline at end of file From 1a8ba042685f09b1ad42426b8a3f63c3949e2332 Mon Sep 17 00:00:00 2001 From: abdourahmanbarry Date: Mon, 15 Jun 2026 01:45:13 +0000 Subject: [PATCH 08/52] Updated bus partition interface to work with new SubsystemModel --- .../BusPartitionInterface.cpp | 55 ++++++------------- .../BusPartitionInterface.hpp | 1 + 2 files changed, 18 insertions(+), 38 deletions(-) diff --git a/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.cpp b/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.cpp index e275c6b39..2e04e558d 100644 --- a/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.cpp +++ b/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.cpp @@ -1,6 +1,7 @@ #include "BusPartitionInterface.hpp" +#include #include #include #include @@ -28,11 +29,15 @@ namespace GridKit BusPartitionInterface::BusPartitionInterface(CircuitComponent& component, IdxT bus_i, IdxT bus_j, IdxT id) : component_(component) { - size_ = 2; - n_intern_ = 0; - n_extern_ = 2; - extern_indices_ = {0, 1}; - idc_ = id; + size_ = component.size(); + n_intern_ = 0; + n_extern_ = static_cast(component.size()); + idc_ = id; + + for (IdxT i = 0; i < size_; i++) + { + extern_indices_.insert(i); + } bus_i_ = bus_i; bus_j_ = bus_j; @@ -53,14 +58,10 @@ namespace GridKit yp_.resize(static_cast(size_)); f_.resize(static_cast(size_)); - size_t interface_extern_size = static_cast(component_.size() - size_); - interface_partition_externals_.resize(interface_extern_size); - external_data_y_.resize(interface_extern_size, 0); - external_data_yp_.resize(interface_extern_size, 0); + std::fill(f_.begin(), f_.end(), 0); component_.allocate(); - size_t counter = 0; for (size_t i = 0; i < static_cast(component_.size()); i++) { if (bus_i_ == component_.getNodeConnection(static_cast(i))) @@ -71,16 +72,10 @@ namespace GridKit { bus_port_j_ = i; } - else - { - interface_partition_externals_[counter] = component_.getNodeConnection(static_cast(i)); - counter++; - } + IdxT global_idx = component_.getNodeConnection(static_cast(i)); + this->setExternalConnectionNodes(static_cast(i), global_idx); } - this->setExternalConnectionNodes(0, bus_i_); - this->setExternalConnectionNodes(1, bus_j_); - return 0; } @@ -108,31 +103,15 @@ namespace GridKit template int BusPartitionInterface::evaluateResidual() { - auto& y = component_.y(); - auto& yp = component_.yp(); - - y[bus_port_i_] = y_[0]; - y[bus_port_j_] = y_[1]; - - yp[bus_port_i_] = yp_[0]; - yp[bus_port_j_] = yp_[1]; - - size_t counter = 0; - for (size_t i = 0; i < static_cast(component_.size()); i++) - { - if (bus_port_i_ != i && bus_port_j_ != i) - { - y[i] = external_data_y_[counter]; - counter++; - } - } + std::copy(y_.begin(), y_.end(), component_.y().begin()); + std::copy(yp_.begin(), yp_.end(), component_.yp().begin()); component_.evaluateResidual(); auto& f = component_.getResidual(); - f_[0] = f[bus_port_i_]; - f_[1] = f[bus_port_j_]; + f_[bus_port_i_] = f[bus_port_i_]; + f_[bus_port_j_] = f[bus_port_j_]; return 0; } diff --git a/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.hpp b/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.hpp index 0c8b2e902..1b1d9e4bd 100644 --- a/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.hpp +++ b/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.hpp @@ -5,6 +5,7 @@ #include #include + #include "GridKit/Model/PowerElectronics/PartitionInterface/PartitionInterface.hpp" namespace GridKit From c9c64f27bbce2e8ba10877e8f3818e6f3888b09e Mon Sep 17 00:00:00 2001 From: abdourahmanbarry Date: Mon, 15 Jun 2026 01:47:12 +0000 Subject: [PATCH 09/52] Work in progress cosimulation implementation: in rought shape --- .../Solver/Dynamic/MultiStageCosimulation.cpp | 138 +++++++++++++----- .../Solver/Dynamic/MultiStageCosimulation.hpp | 70 +++++++-- 2 files changed, 161 insertions(+), 47 deletions(-) diff --git a/GridKit/Solver/Dynamic/MultiStageCosimulation.cpp b/GridKit/Solver/Dynamic/MultiStageCosimulation.cpp index 390eb8d03..9ce53ab3c 100644 --- a/GridKit/Solver/Dynamic/MultiStageCosimulation.cpp +++ b/GridKit/Solver/Dynamic/MultiStageCosimulation.cpp @@ -5,52 +5,99 @@ #include #include +#include #include +#include "GridKit/Solver/Dynamic/Interpolation.hpp" +#include "GridKit/Solver/Dynamic/Rosenbrock.hpp" + namespace Integrator { + template + MultiStageCosimulation::MultiStageCosimulation( + std::vector partitions, + std::size_t num_stages, + MemorySpace memspace) + : num_partitions_(partitions.size()), + num_stages_(num_stages), + memspace_(memspace), + partitions_(std::move(partitions)) + { + if (num_partitions_ == 0) + { + throw std::invalid_argument("MultiStageCosimulation requires at least one partition."); + } + + if (num_stages_ == 0) + { + throw std::invalid_argument("MultiStageCosimulation requires at least one stage."); + } + + for (auto* partition : partitions_) + { + if (partition == nullptr) + { + throw std::invalid_argument("Partition pointer cannot be null."); + } + } + } + // TODO: Complete implementation template int MultiStageCosimulation::allocate() { + num_partitions_ = partitions_.size(); + + x0_local_.resize(num_partitions_); + coupling_mat_.resize(num_partitions_); + linear_workspaces_.resize(num_partitions_); + lin_solver_.resize(num_partitions_); + solvers_.resize(num_partitions_); for (size_t p = 0; p < num_partitions_; ++p) { auto* part = partitions_[p]; + assert(part != nullptr); + + const IdxT num_states = part->getStateSize(); + const IdxT num_coupling = part->getCouplingSize(); - int num_coupling = part->getCouplingSize(); - int num_states = part->getStateSize(); + x0_local_[p] = std::make_unique(num_states); + x0_local_[p]->allocate(memspace_); - x0_local_[p] = State(num_states); - coupling_mat_[p] = State(num_coupling, num_stages_); + coupling_mat_[p] = std::make_unique(num_coupling); + coupling_mat_[p]->allocate(memspace_); - x0_local_[p].allocate(memspace_); - coupling_mat_[p].allocate(memspace_); + linear_workspaces_[p] = std::make_unique(); + + lin_solver_[p] = std::make_unique( + linear_workspaces_[p].get(), + "klu", + "klu", + "klu"); + + lin_solver_[p]->initialize(); + + solvers_[p] = std::make_unique( + Rosenbrock::Tableau::rodas5p(), + part, + lin_solver_[p].get(), + &vector_handler_); + + solvers_[p]->allocate(); } return 0; } template - int MultiStageCosimulation::distributeLocal(const State& global_y) + int MultiStageCosimulation::distributeLocal(const State& global_y, PartitionEvaluator* partition) { + auto& internal = partition->getInternalIndecies(); + size_t n = partition->size(); - auto* global_data = global_y.getData(memspace_); - - for (size_t p = 0; p < num_partitions_; ++p) - { - auto* part = partitions_[p]; - auto& internal = part->getInternalIndecies(); // TODO: add this function to partition evaluate - size_t n = part->size(); - - auto* local_data = x0_local_[p].getData(memspace_); - - for (size_t i = 0; i < n; ++i) - { - local_data[i] = global_data[internal[i]]; - } - } + matrix_handler_.matvec(); return 0; } @@ -87,32 +134,53 @@ namespace Integrator // TODO: Complete implementation template - int MultiStageCosimulation::partitionSolve() + int MultiStageCosimulation::partitionStageSolve(int stage, double t0, double dt) { - std::vector output_time = output_times; + std::vector out_times; - for (size_t i = 0; i < max_iterations_; i++) + for (size_t iter = 0; iter < max_iterations_; iter++) { - for (auto* partitionEvaluator : partitions_) + for (size_t part = 0; part < num_partitions_; part++) { - // TODO: add a coupling function to the partition - partitionEvaluator->addForcing(); + auto* partitionEvaluator = partitions_[part]; + auto* partition_solver = solvers_[part]; - if (i + 1 < max_iterations_) + if (stage == 0 && iter == 0) { - solver.integrate({t0}, partition_solution_.getData(stage)); + distributeLocal(y_, partitionEvaluator); } - else + + auto& coupling_stage_values = coupling_mat_[part]; + + GridKit::LagrangeInterpolant interp(coupling_stage_values, t0, dt); + partitionEvaluator->addForcing(interp); + + for (size_t i = 0; i < num_partitions_; i++) { - solver.integrate(output_time, partition_solution_); + partition_solution_[i]->setToZero(memspace_); } - } - updateCoupling(stage + 1) + int counter = 1; + + auto sol_out = [&](double tt, ReSolve::vector::Vector yp) -> void + { + auto* internal_csr = partition_internal_csr_[part].get(); + auto* internal_sol = partition_solution_[counter].get(); + + double alpha = 1.0; + double beta = 1.0; + + matrix_handler_.matvec(internal_csr, &yp, internal_sol, &alpha, &beta, memspace_); + + counter++; + }; + + partition_solver->integrate(out_times, stepController_, params_, sol_out); + } - if (stage == 0) + if (stage == 0) { break; } diff --git a/GridKit/Solver/Dynamic/MultiStageCosimulation.hpp b/GridKit/Solver/Dynamic/MultiStageCosimulation.hpp index ddc3ceb6a..ab6aabf08 100644 --- a/GridKit/Solver/Dynamic/MultiStageCosimulation.hpp +++ b/GridKit/Solver/Dynamic/MultiStageCosimulation.hpp @@ -11,15 +11,18 @@ #include #include +#include #include #include #include #include #include +#include #include #include #include +#include namespace Integrator { @@ -31,28 +34,41 @@ namespace Integrator using RealT = typename GridKit::ScalarTraits::RealT; using Rosenbrock = Integrator::Rosenbrock; using PartitionEvaluator = GridKit::Model::PartitionEvaluator; + using MemorySpace = ReSolve::memory::MemorySpace; public: + MultiStageCosimulation(std::vector partitions, + size_t num_stages = 2, + size_t max_iterations = 2, + MemorySpace memspace = MemorySpace::HOST); + int initializeSimulation(RealT t0); int runSimulation(RealT tn, IdxT nout, std::optional> step_callback = {}); - int partitionSolve(); + int partitionStageSolve(int, double, double); - int timestepper(const std::vector& out_times, - std::optional> out_cb = {}); + int timestepper(const std::vector&, + std::optional>); - int distributeLocal(const State& global_y); + int distributeLocal(const State&); - int distributeCoupling(const State& global_y, int stage); + int distributeCoupling(const State&, int); int allocate(); private: + /** + * @brief + * + */ + std::vector> linear_workspaces_; + /** * @brief initial time */ - RealT t0_; + RealT t0_; + /** * @brief Maximum number of iterations per stage */ @@ -70,6 +86,22 @@ namespace Integrator */ size_t num_stages_; + /** + * + * @brief Number of stages used by the method + */ + size_t num_states_; + + /** + * + * @brief The global state of the system + */ + State y_; + + Rosenbrock::Parameters params_; + + Integrator::AdaptiveStep stepController_; + /** * * @brief Memory space used @@ -78,11 +110,18 @@ namespace Integrator /** * - * @brief Handles for linear algebra operations + * @brief Handles vector operations * */ ReSolve::VectorHandler vector_handler_; + /** + * + * @brief Handles matrix linear algebra operations + * + */ + ReSolve::MatrixHandler matrix_handler_; + /** * * @brief linear solver for the sub-integrator @@ -95,28 +134,35 @@ namespace Integrator * @brief Contains vectors of initial conditions for every step * */ - std::vector x0_local_; + std::vector> x0_local_; /** * * @brief Vectors of coupling matrices for each partition * */ - std::vector coupling_mat_; + std::vector>> coupling_mat_; /** * - * @brief Vector of stages + * @brief partition solution multi-dimensional vectors + * + */ + std::vector> partition_solution_; + + /** + * + * @brief partition solution multi-dimensional vectors * */ - State stages_; + std::vector partition_external_csr_; /** * * @brief partition solution multi-dimensional vectors * */ - State partition_solution_; + std::vector> partition_internal_csr_; /** * From 9692e5713db4b09d97af39fcb5fac38f11cb71fe Mon Sep 17 00:00:00 2001 From: abdourahmanbarry Date: Mon, 15 Jun 2026 05:56:14 +0000 Subject: [PATCH 10/52] Cleaned up code in subsystem and made the partition microgrid test example more rebust --- .../Model/PowerElectronics/SubsystemModel.hpp | 20 +--- .../Partition/PartitionMicrogrid.cpp | 97 ++++++++++++------- 2 files changed, 69 insertions(+), 48 deletions(-) diff --git a/GridKit/Model/PowerElectronics/SubsystemModel.hpp b/GridKit/Model/PowerElectronics/SubsystemModel.hpp index af3d9dc9f..4db68eddd 100644 --- a/GridKit/Model/PowerElectronics/SubsystemModel.hpp +++ b/GridKit/Model/PowerElectronics/SubsystemModel.hpp @@ -82,11 +82,6 @@ namespace GridKit int allocate() override { - // Allocate all components - size_ = 0; - n_extern_ = 0; - n_intern_ = 0; - size_t counter = 0; // First pass: Map global component indices to local subsystem indices. @@ -102,7 +97,6 @@ namespace GridKit if (index != neg1_) { internal_map_[index] = counter; - n_intern_++; counter++; } } @@ -119,19 +113,17 @@ namespace GridKit counter++; internal_map_[index2] = counter; counter++; - n_intern_ += 2; } } + // If this subsystem contains the reference generator, add the rotor motor as an internal if (refframe_index_ != neg1_) { internal_map_[refframe_index_] = counter; - n_intern_++; counter++; } - // Second pass: Identify variables required by this partition but owned by - // another partition. + // Second pass: Identify variables required by this partition but owned by another partition. for (const auto& component : components_) { for (IdxT i = 0; i < component->getExternSize(); i++) @@ -141,14 +133,14 @@ namespace GridKit if (internal_map_.count(index) < 1 && external_map_.count(index) < 1 && index != neg1_) { external_map_[index] = counter; - counter++; - n_extern_++; } } } - size_ = n_intern_ + n_extern_; + n_intern_ = internal_map_.size(); + n_extern_ = external_map_.size(); + size_ = n_intern_ + n_extern_; this->mapGlobalToLocal(); @@ -275,8 +267,6 @@ namespace GridKit /** * @brief Creates the system Jacobian representing \alpha dF/dy' + dF/dy * - * Updates the CSR Jacobian values using the per-component mappings - * computed during allocate(). * * @return int 0 if successful, positive if there's a recoverable error, negative if unrecoverable */ diff --git a/examples/PowerElectronics/Partition/PartitionMicrogrid.cpp b/examples/PowerElectronics/Partition/PartitionMicrogrid.cpp index 4a961caa8..8bc6b00c0 100644 --- a/examples/PowerElectronics/Partition/PartitionMicrogrid.cpp +++ b/examples/PowerElectronics/Partition/PartitionMicrogrid.cpp @@ -1,3 +1,4 @@ +#include #include #define _USE_MATH_DEFINES @@ -288,8 +289,23 @@ int main() partition2->addComponent(l3); partition2->addComponent(load2); + sysmodel->addComponent(new GridKit::DistributedGenerator(*dg1)); + sysmodel->addComponent(new GridKit::DistributedGenerator(*dg2)); + sysmodel->addComponent(new GridKit::DistributedGenerator(*dg3)); + sysmodel->addComponent(new GridKit::DistributedGenerator(*dg4)); + sysmodel->addComponent(new GridKit::MicrogridLine(*l1)); + sysmodel->addComponent(new GridKit::MicrogridLine(*l2)); + sysmodel->addComponent(new GridKit::MicrogridLine(*l3)); + sysmodel->addComponent(new GridKit::MicrogridBusDQ(*bus1)); + sysmodel->addComponent(new GridKit::MicrogridBusDQ(*bus2)); + sysmodel->addComponent(new GridKit::MicrogridBusDQ(*bus3)); + sysmodel->addComponent(new GridKit::MicrogridBusDQ(*bus4)); + sysmodel->addComponent(new GridKit::MicrogridLoad(*load1)); + sysmodel->addComponent(new GridKit::MicrogridLoad(*load2)); + partition1->allocate(); partition2->allocate(); + sysmodel->allocate(vec_size_total); std::vector y; std::vector yp; @@ -334,8 +350,8 @@ int main() auto printTitle = [](std::string msg) -> void { - std::cout << "\n------------- " << msg << " -----------" << std::endl; - printf("%-10s ---------- %10s\n", "Res Values", "Comp. Index"); + std::cout << "\n--------------- " << msg << " -------------" << std::endl; + printf("%-12s ---------- %12s\n", "Res Values", "Comp. Index"); }; // Print Residuals from partition 1 @@ -343,7 +359,7 @@ int main() for (size_t i = 0; i < partition1->getInternalSize(); i++) { auto com_index = partition1->getNodeConnection(static_cast(i)); - printf("%-10.5g ----------%zu %10zu\n", partition1->getResidual()[i], i, com_index); + printf("%-12.5g ---------- %7zu\n", partition1->getResidual()[i], com_index); } // Print Residuals from partition 2 @@ -351,38 +367,53 @@ int main() for (size_t i = 0; i < partition2->getInternalSize(); i++) { auto com_index = partition2->getNodeConnection(static_cast(i)); - printf("%-10.5g ----------%zu %10zu\n", partition2->getResidual()[i], i, com_index); + printf("%-12.5g ---------- %7zu\n", partition2->getResidual()[i], com_index); + } + + for (size_t i = 0; i < vec_size_total; i++) + { + sysmodel->y()[i] = y[i]; + sysmodel->yp()[i] = yp[i]; + } + + sysmodel->evaluateResidual(); + + printTitle("Reference Solution"); + for (size_t i = 0; i < vec_size_total; i++) + { + printf("%-12.5g ---------- %7zu\n", sysmodel->getResidual()[i], i); + } + + std::vector f(vec_size_total, 0.0); + std::vector error(vec_size_total, 1.0); + + // Get internal residuals from partition 1 + for (size_t i = 0; i < partition1->getInternalSize(); i++) + { + f[partition1->getNodeConnection(i)] = partition1->getResidual()[i]; + } + + // Get internal residuals from partition 2 + for (size_t i = 0; i < partition2->getInternalSize(); i++) + { + f[partition2->getNodeConnection(i)] = partition2->getResidual()[i]; + } + + for (size_t i = 0; i < vec_size_total; i++) + { + error[i] = sysmodel->getResidual()[i] - f[i]; + } + + double max_error = 0; + for (size_t i = 0; i < vec_size_total; i++) + { + if (max_error < std::abs(error[i])) + { + max_error = std::abs(error[i]); + } } - // sysmodel->addComponent(dg1); - // sysmodel->addComponent(dg2); - // sysmodel->addComponent(dg3); - // sysmodel->addComponent(dg4); - // sysmodel->addComponent(l1); - // sysmodel->addComponent(l2); - // sysmodel->addComponent(l3); - // sysmodel->addComponent(bus1); - // sysmodel->addComponent(bus2); - // sysmodel->addComponent(bus3); - // sysmodel->addComponent(bus4); - // sysmodel->addComponent(load1); - // sysmodel->addComponent(load2); - - // sysmodel->allocate(vec_size_total); - - // for (size_t i = 0; i < vec_size_total; i++) - // { - // sysmodel->y()[i] = y[i]; - // sysmodel->yp()[i] = yp[i]; - // } - - // sysmodel->evaluateResidual(); - - // printTitle("Reference Solution"); - // for (size_t i = 0; i < vec_size_total; i++) - // { - // printf("%-10.5g ---------- %10zu\n", sysmodel->getResidual()[i], i); - // } + std::cout << "\nMax Error of Reference and Partition Evaluation: " << max_error << std::endl; delete partition1; delete partition2; From 9950483e54916926c16667842fdd0dd46d836d34 Mon Sep 17 00:00:00 2001 From: abdourahmanbarry Date: Wed, 24 Jun 2026 15:25:27 +0000 Subject: [PATCH 11/52] First attempt at arbitrary partitioning --- .../Partition/PartitionScaleMicrogrid.cpp | 392 ++++++++++++++++++ 1 file changed, 392 insertions(+) create mode 100644 examples/PowerElectronics/Partition/PartitionScaleMicrogrid.cpp diff --git a/examples/PowerElectronics/Partition/PartitionScaleMicrogrid.cpp b/examples/PowerElectronics/Partition/PartitionScaleMicrogrid.cpp new file mode 100644 index 000000000..178ad0a47 --- /dev/null +++ b/examples/PowerElectronics/Partition/PartitionScaleMicrogrid.cpp @@ -0,0 +1,392 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using index_type = size_t; +using real_type = double; + +int printMicrogridSystems(index_type N_size); + +/** + * @brief Run Scale Microgrid for a specific N given by the user. + * + * @param[in] argc should be 1 if no N given, 2 if N given + * @param[in] argv can contain the N value (argv[1]) + * @return int + */ +int main(int argc, char const* argv[]) +{ + // Default value + index_type N_size = 2; + + // Parse command line arguments if provided + if (argc > 1) + { + try + { + N_size = static_cast(std::stoi(argv[1])); + } + catch (const std::exception& e) + { + std::cerr << "Error parsing grid size argument: " << e.what() << std::endl; + std::cerr << "Using default grid size = " << N_size << std::endl; + } + } + + return printMicrogridSystems(N_size); +} + +/** + * @brief Tests network of distributed generators. + * + * @param[in] N_size - The number of DG line load cobinations to generate for scale + * @return int returns 0 if successful, >0 otherwise + */ +int printMicrogridSystems(index_type N_size) +{ + using namespace GridKit; + + const size_t num_ibrs = 2 * N_size; + const size_t num_partitions = 4; + + assert(num_partitions <= num_ibrs); + + bool use_jac = true; + + real_type rel_tol = 1e-5; + real_type abs_tol = 1e-5; + + // Create circuit model + auto* sys_model = new PowerElectronicsModel(rel_tol, + abs_tol, + use_jac, + 2000); + + // Ensure minimum size requirement + if (N_size < 1) + { + std::cout << "N_size must be at least 1.\n"; + return 1; + } + + // Modeled after the problem in the paper + // Every Bus has the same virtual resistance. This is due to numerical stability as mentioned in the paper. + real_type RN = 1.0e4; + + // DG Params Vector + // All DGs have the same set of parameters except for the first two. + GridKit::DistributedGeneratorParameters DG_parms1; + DG_parms1.wb_ = 2.0 * M_PI * 50.0; + DG_parms1.wc_ = 31.41; + DG_parms1.mp_ = 9.4e-5; + DG_parms1.Vn_ = 380.0; + DG_parms1.nq_ = 1.3e-3; + DG_parms1.F_ = 0.75; + DG_parms1.Kiv_ = 420.0; + DG_parms1.Kpv_ = 0.1; + DG_parms1.Kic_ = 2.0e4; + DG_parms1.Kpc_ = 15.0; + DG_parms1.Cf_ = 5.0e-5; + DG_parms1.rLf_ = 0.1; + DG_parms1.Lf_ = 1.35e-3; + DG_parms1.rLc_ = 0.03; + DG_parms1.Lc_ = 0.35e-3; + + GridKit::DistributedGeneratorParameters DG_parms2; + DG_parms2.wb_ = 2.0 * M_PI * 50.0; + DG_parms2.wc_ = 31.41; + DG_parms2.mp_ = 12.5e-5; + DG_parms2.Vn_ = 380.0; + DG_parms2.nq_ = 1.5e-3; + DG_parms2.F_ = 0.75; + DG_parms2.Kiv_ = 390.0; + DG_parms2.Kpv_ = 0.05; + DG_parms2.Kic_ = 16.0e3; + DG_parms2.Kpc_ = 10.5; + DG_parms2.Cf_ = 50.0e-6; + DG_parms2.rLf_ = 0.1; + DG_parms2.Lf_ = 1.35e-3; + DG_parms2.rLc_ = 0.03; + DG_parms2.Lc_ = 0.35e-3; + + std::vector> DGParams_list(2 * N_size, DG_parms2); + + // First two generators use parameters 1 + if (DGParams_list.size() >= 1) + DGParams_list[0] = DG_parms1; + if (DGParams_list.size() >= 2) + DGParams_list[1] = DG_parms1; + + // line vector params + // Every odd line has the same parameters and every even line has the same parameters + real_type rline1 = 0.23; + real_type Lline1 = 0.1 / (2.0 * M_PI * 50.0); + real_type rline2 = 0.35; + real_type Lline2 = 0.58 / (2.0 * M_PI * 50.0); + std::vector rline_list(2 * N_size - 1, 0.0); + std::vector Lline_list(2 * N_size - 1, 0.0); + for (index_type i = 0; i < rline_list.size(); i++) + { + rline_list[i] = (i % 2) ? rline2 : rline1; + Lline_list[i] = (i % 2) ? Lline2 : Lline1; + } + + // load parms + // Only the first load has the same paramaters. + real_type rload1 = 3.0; + real_type Lload1 = 2.0 / (2.0 * M_PI * 50.0); + real_type rload2 = 2.0; + real_type Lload2 = 1.0 / (2.0 * M_PI * 50.0); + + std::vector rload_list(N_size, rload2); + std::vector Lload_list(N_size, Lload2); + if (rload_list.size() >= 1) + { + rload_list[0] = rload1; + Lload_list[0] = Lload1; + } + + using SignalNode = GridKit::PowerElectronics::SignalNode; + using Bus = GridKit::PowerElectronics::MicrogridBus; + using BusDQ = MicrogridBusDQ; + using Generator = DistributedGenerator; + using Line = MicrogridLine; + using Load = MicrogridLoad; + + std::vector buses(num_ibrs, nullptr); + std::vector busesDQ(num_ibrs, nullptr); + std::vector generators(num_ibrs, nullptr); + std::vector lines(num_ibrs, nullptr); + std::vector loads(num_ibrs, nullptr); + + SignalNode dg_signal; + sys_model->addNode(&dg_signal); + + for (size_t i = 0; i < 2 * N_size; i++) + { + buses[i] = new Bus(); + sys_model->addNode(buses[i]); + } + + // Create the reference DG + auto* dg_ref = new DistributedGenerator(0, + DGParams_list[0], + true, + &dg_signal, + buses[0]); + sys_model->addComponent(dg_ref); + + generators[0] = dg_ref; + + // Keep track of models and index location + index_type model_id = 1; + // Add all other DGs + for (index_type i = 1; i < 2 * N_size; i++) + { + // current DG to add + auto* dg = new DistributedGenerator(model_id++, + DGParams_list[i], + false, + &dg_signal, + buses[i]); + + generators[i] = dg; + sys_model->addComponent(dg); + } + + // Load all the Line components + for (index_type i = 0; i < 2 * N_size - 1; i++) + { + // line + auto* line_model = new MicrogridLine(model_id++, + rline_list[i], + Lline_list[i], + &dg_signal, + buses[i], + buses[i + 1]); + lines[i + 1] = line_model; + sys_model->addComponent(line_model); + } + + // Load all the Load components + for (index_type i = 0; i < N_size; i++) + { + auto* load_model = new MicrogridLoad(model_id++, + rload_list[i], + Lload_list[i], + &dg_signal, + buses[2 * i]); + loads[2 * i] = load_model; + sys_model->addComponent(load_model); + } + + // Add all the microgrid Virtual DQ Buses + for (index_type i = 0; i < 2 * N_size; i++) + { + auto* virDQbus_model = new MicrogridBusDQ(model_id++, RN, buses[i]); + + busesDQ[i] = virDQbus_model; + sys_model->addComponent(virDQbus_model); + } + + sys_model->allocate(); + + std::vector y; + std::vector yp; + + for (size_t i = 0; i < sys_model->size(); i++) + { + y.push_back(static_cast(i + 1)); + yp.push_back(static_cast(i + 1)); + } + + for (size_t i = 0; i < sys_model->size(); i++) + { + sys_model->y()[i] = y[i]; + sys_model->yp()[i] = yp[i]; + } + + sys_model->evaluateResidual(); + std::vector f_sysmodel = sys_model->getResidual(); + + std::vector*> subsystems(num_partitions); + + // Partition the system + index_type q = (num_ibrs) / num_partitions; + index_type r = (num_ibrs) % num_partitions; + index_type index = 0; + for (index_type j = 0; j < num_partitions; j++) + { + auto* partition = new SubsystemModel(false); + + // add Reference rotor to the first partition + if (j == 0) + { + partition->addNode(&dg_signal); + } + + std::cout << "Partition " << j << std::endl; + + index_type part_size = q + (j < r ? 1 : 0); + index_type end = std::min(index + part_size, num_ibrs); + + // Add all components belonging to this partition + for (; index < end; ++index) + { + partition->addComponent(generators[index]); + std::cout << "Comp Gen" << index << std::endl; + partition->addComponent(busesDQ[index]); + std::cout << "Comp BDQ" << index << std::endl; + + if (loads[index] != nullptr) + { + partition->addComponent(loads[index]); + std::cout << "Comp Load" << index << std::endl; + } + + if (lines[index] != nullptr) + { + partition->addComponent(lines[index]); + std::cout << "Comp Line" << index << std::endl; + } + + std::cout << "Comp Bus " << index << std::endl; + + partition->addNode(buses[index]); + } + + // Add partition interface at a partition point + if (index < num_ibrs) + { + auto* linecopy = new GridKit::MicrogridLine(*lines[index]); + + auto* busInterface = new GridKit::BusPartitionInterface(*buses[index - 1], *linecopy, model_id++); + busInterface->allocate(); + + std::cout << "Interface " << "(" << index - 1 << ", " << index << ")" << std::endl; + + partition->addComponent(busInterface); + } + + subsystems[j] = partition; + } + + for (auto* partition : subsystems) + { + partition->toString(); + partition->allocate(); + } + + // Distribute externals to partition 1 + for (auto* partition : subsystems) + { + for (size_t i = 0; i < partition->getExternSize(); i++) + { + partition->getExternalDataY()[i] = y[partition->getExternalIndices()[i]]; + partition->getExternalDataYP()[i] = yp[partition->getExternalIndices()[i]]; + } + } + + for (auto* partition : subsystems) + { + for (size_t i = 0; i < partition->getInternalSize(); i++) + { + partition->y()[i] = y[partition->getNodeConnection(i)]; + partition->yp()[i] = yp[partition->getNodeConnection(i)]; + } + } + + for (auto* partition : subsystems) + { + partition->evaluateResidual(); + } + + std::vector f(sys_model->size(), 0.0); + std::vector error(sys_model->size(), 1.0); + + // Get internal residuals from partition 1 + for (auto* partition : subsystems) + { + for (size_t i = 0; i < partition->getInternalSize(); i++) + { + f[partition->getNodeConnection(i)] = partition->getResidual()[i]; + } + } + + for (size_t i = 0; i < sys_model->size(); i++) + { + error[i] = f_sysmodel[i] - f[i]; + std::cout << i << " " << error[i] << " ---------- " << i << std::endl; + } + + double max_error = 0; + for (size_t i = 0; i < sys_model->size(); i++) + { + if (max_error < std::abs(error[i])) + { + max_error = std::abs(error[i]); + } + } + + std::cout << "\nMax Error of Reference and Partition Evaluation: " << max_error << std::endl; + + return 0; +} From 3609340ea571e93ef9e759efb0f93b2bb603e84a Mon Sep 17 00:00:00 2001 From: abdourahmanbarry Date: Wed, 24 Jun 2026 19:05:53 +0000 Subject: [PATCH 12/52] Added microgrid with four partitions --- .../Partition/PartitionMicrogrid.cpp | 383 +++++++----------- 1 file changed, 136 insertions(+), 247 deletions(-) diff --git a/examples/PowerElectronics/Partition/PartitionMicrogrid.cpp b/examples/PowerElectronics/Partition/PartitionMicrogrid.cpp index 8bc6b00c0..f9e00c37b 100644 --- a/examples/PowerElectronics/Partition/PartitionMicrogrid.cpp +++ b/examples/PowerElectronics/Partition/PartitionMicrogrid.cpp @@ -1,10 +1,13 @@ #include #include +#include #define _USE_MATH_DEFINES #include #include +#include +#include #include #include #include @@ -12,6 +15,8 @@ #include #include #include +#include +#include int main() { @@ -22,6 +27,7 @@ int main() size_t max_step_number = 3000; bool use_jac = true; + // Create model auto* sysmodel = new GridKit::PowerElectronicsModel(rel_tol, abs_tol, use_jac, max_step_number); // Modeled after the problem in the paper @@ -80,274 +86,174 @@ int main() double rload2 = 2.0; double Lload2 = 1.0 / (2.0 * M_PI * 50.0); - // indexing sets - size_t Nsize = 2; - // DGs + - refframe Lines + Loads - size_t vec_size_internals = 13 * (2 * Nsize) - 1 + (2 + 4 * (Nsize - 1)) + 2 * Nsize; - // \omegaref + BusDQ - size_t vec_size_externals = 1 + 2 * (2 * Nsize); - size_t dqbus1 = vec_size_internals + 1; - size_t dqbus2 = vec_size_internals + 3; - size_t dqbus3 = vec_size_internals + 5; - size_t dqbus4 = vec_size_internals + 7; + using SignalNode = GridKit::PowerElectronics::SignalNode; + SignalNode dg_signal; - size_t vec_size_total = vec_size_internals + vec_size_externals; + sysmodel->addNode(&dg_signal); - size_t indexv = 0; + using Bus = GridKit::PowerElectronics::MicrogridBus; + Bus bus1; + Bus bus2; + Bus bus3; + Bus bus4; - // dg 1 - GridKit::DistributedGenerator* dg1 = new GridKit::DistributedGenerator(0, parms1, true); - // ref motor - dg1->setExternalConnectionNodes(0, vec_size_internals); - // outputs - dg1->setExternalConnectionNodes(1, dqbus1); - dg1->setExternalConnectionNodes(2, dqbus1 + 1); - //"grounding" of the difference - dg1->setExternalConnectionNodes(3, static_cast(-1)); - // internal connections - for (size_t i = 0; i < 12; i++) - { + sysmodel->addNode(&bus1); + sysmodel->addNode(&bus2); + sysmodel->addNode(&bus3); + sysmodel->addNode(&bus4); - dg1->setExternalConnectionNodes(4 + i, indexv + i); - } - indexv += 12; + // dg 1 + GridKit::DistributedGenerator* dg1 = new GridKit::DistributedGenerator( + 0, parms1, true, &dg_signal, &bus1); + sysmodel->addComponent(dg1); // dg 2 - GridKit::DistributedGenerator* dg2 = new GridKit::DistributedGenerator(1, parms1, false); - // ref motor - dg2->setExternalConnectionNodes(0, vec_size_internals); - // outputs - dg2->setExternalConnectionNodes(1, dqbus2); - dg2->setExternalConnectionNodes(2, dqbus2 + 1); - // internal connections - for (size_t i = 0; i < 13; i++) - { - - dg2->setExternalConnectionNodes(3 + i, indexv + i); - } - indexv += 13; + GridKit::DistributedGenerator* dg2 = new GridKit::DistributedGenerator( + 1, parms1, false, &dg_signal, &bus2); + sysmodel->addComponent(dg2); // dg 3 - GridKit::DistributedGenerator* dg3 = new GridKit::DistributedGenerator(2, parms2, false); - // ref motor - dg3->setExternalConnectionNodes(0, vec_size_internals); - // outputs - dg3->setExternalConnectionNodes(1, dqbus3); - dg3->setExternalConnectionNodes(2, dqbus3 + 1); - // internal connections - for (size_t i = 0; i < 13; i++) - { - - dg3->setExternalConnectionNodes(3 + i, indexv + i); - } - indexv += 13; + GridKit::DistributedGenerator* dg3 = new GridKit::DistributedGenerator( + 2, parms2, false, &dg_signal, &bus3); + sysmodel->addComponent(dg3); // dg 4 - GridKit::DistributedGenerator* dg4 = new GridKit::DistributedGenerator(3, parms2, false); - // ref motor - dg4->setExternalConnectionNodes(0, vec_size_internals); - // outputs - dg4->setExternalConnectionNodes(1, dqbus4); - dg4->setExternalConnectionNodes(2, dqbus4 + 1); - - // internal connections - for (size_t i = 0; i < 13; i++) - { - - dg4->setExternalConnectionNodes(3 + i, indexv + i); - } - indexv += 13; + GridKit::DistributedGenerator* dg4 = new GridKit::DistributedGenerator( + 3, parms2, false, &dg_signal, &bus4); + sysmodel->addComponent(dg4); // Lines // line 1 - GridKit::MicrogridLine* l1 = new GridKit::MicrogridLine(4, rline1, Lline1); - // ref motor - l1->setExternalConnectionNodes(0, vec_size_internals); - // input connections - l1->setExternalConnectionNodes(1, dqbus1); - l1->setExternalConnectionNodes(2, dqbus1 + 1); - // output connections - l1->setExternalConnectionNodes(3, dqbus2); - l1->setExternalConnectionNodes(4, dqbus2 + 1); - // internal connections - for (size_t i = 0; i < 2; i++) - { - - l1->setExternalConnectionNodes(5 + i, indexv + i); - } - indexv += 2; + GridKit::MicrogridLine* l1 = new GridKit::MicrogridLine( + 4, rline1, Lline1, &dg_signal, &bus1, &bus2); + sysmodel->addComponent(l1); // line 2 - GridKit::MicrogridLine* l2 = new GridKit::MicrogridLine(5, rline2, Lline2); - // ref motor - l2->setExternalConnectionNodes(0, vec_size_internals); - // input connections - l2->setExternalConnectionNodes(1, dqbus2); - l2->setExternalConnectionNodes(2, dqbus2 + 1); - // output connections - l2->setExternalConnectionNodes(3, dqbus3); - l2->setExternalConnectionNodes(4, dqbus3 + 1); - // internal connections - for (size_t i = 0; i < 2; i++) - { - - l2->setExternalConnectionNodes(5 + i, indexv + i); - } - indexv += 2; + GridKit::MicrogridLine* l2 = new GridKit::MicrogridLine( + 5, rline2, Lline2, &dg_signal, &bus2, &bus3); + sysmodel->addComponent(l2); // line 3 - GridKit::MicrogridLine* l3 = new GridKit::MicrogridLine(6, rline3, Lline3); - // ref motor - l3->setExternalConnectionNodes(0, vec_size_internals); - // input connections - l3->setExternalConnectionNodes(1, dqbus3); - l3->setExternalConnectionNodes(2, dqbus3 + 1); - // output connections - l3->setExternalConnectionNodes(3, dqbus4); - l3->setExternalConnectionNodes(4, dqbus4 + 1); - // internal connections - for (size_t i = 0; i < 2; i++) - { - - l3->setExternalConnectionNodes(5 + i, indexv + i); - } - indexv += 2; + GridKit::MicrogridLine* l3 = new GridKit::MicrogridLine( + 6, rline3, Lline3, &dg_signal, &bus3, &bus4); + sysmodel->addComponent(l3); // loads // load 1 - GridKit::MicrogridLoad* load1 = new GridKit::MicrogridLoad(7, rload1, Lload1); - // ref motor - load1->setExternalConnectionNodes(0, vec_size_internals); - // input connections - load1->setExternalConnectionNodes(1, dqbus1); - load1->setExternalConnectionNodes(2, dqbus1 + 1); - // internal connections - for (size_t i = 0; i < 2; i++) - { - - load1->setExternalConnectionNodes(3 + i, indexv + i); - } - indexv += 2; + GridKit::MicrogridLoad* load1 = new GridKit::MicrogridLoad(7, rload1, Lload1, &dg_signal, &bus1); + sysmodel->addComponent(load1); // load 2 - GridKit::MicrogridLoad* load2 = new GridKit::MicrogridLoad(8, rload2, Lload2); - // ref motor - load2->setExternalConnectionNodes(0, vec_size_internals); - // input connections - load2->setExternalConnectionNodes(1, dqbus3); - load2->setExternalConnectionNodes(2, dqbus3 + 1); - // internal connections - for (size_t i = 0; i < 2; i++) - { - - load2->setExternalConnectionNodes(3 + i, indexv + i); - } - indexv += 2; + GridKit::MicrogridLoad* load2 = new GridKit::MicrogridLoad(8, rload2, Lload2, &dg_signal, &bus3); + sysmodel->addComponent(load2); // Virtual PQ Buses - GridKit::MicrogridBusDQ* bus1 = new GridKit::MicrogridBusDQ(9, RN); - - bus1->setExternalConnectionNodes(0, dqbus1); - bus1->setExternalConnectionNodes(1, dqbus1 + 1); + GridKit::MicrogridBusDQ* bus_para_1 = new GridKit::MicrogridBusDQ(9, RN, &bus1); + sysmodel->addComponent(bus_para_1); - GridKit::MicrogridBusDQ* bus2 = new GridKit::MicrogridBusDQ(10, RN); + GridKit::MicrogridBusDQ* bus_para_2 = new GridKit::MicrogridBusDQ(10, RN, &bus2); + sysmodel->addComponent(bus_para_2); - bus2->setExternalConnectionNodes(0, dqbus2); - bus2->setExternalConnectionNodes(1, dqbus2 + 1); + GridKit::MicrogridBusDQ* bus_para_3 = new GridKit::MicrogridBusDQ(11, RN, &bus3); + sysmodel->addComponent(bus_para_3); - GridKit::MicrogridBusDQ* bus3 = new GridKit::MicrogridBusDQ(11, RN); + GridKit::MicrogridBusDQ* bus_para_4 = new GridKit::MicrogridBusDQ(12, RN, &bus4); + sysmodel->addComponent(bus_para_4); - bus3->setExternalConnectionNodes(0, dqbus3); - bus3->setExternalConnectionNodes(1, dqbus3 + 1); + sysmodel->allocate(); - GridKit::MicrogridBusDQ* bus4 = new GridKit::MicrogridBusDQ(12, RN); + GridKit::SubsystemModel* partition1 = new GridKit::SubsystemModel(false); + GridKit::SubsystemModel* partition2 = new GridKit::SubsystemModel(false); + GridKit::SubsystemModel* partition3 = new GridKit::SubsystemModel(false); + GridKit::SubsystemModel* partition4 = new GridKit::SubsystemModel(false); - bus4->setExternalConnectionNodes(0, dqbus4); - bus4->setExternalConnectionNodes(1, dqbus4 + 1); + GridKit::MicrogridLine l1copy(*l1); + GridKit::MicrogridLine l2copy(*l2); + GridKit::MicrogridLine l3copy(*l3); + GridKit::BusPartitionInterface* busInterface1 = new GridKit::BusPartitionInterface(bus1, l1copy, 14); + GridKit::BusPartitionInterface* busInterface2 = new GridKit::BusPartitionInterface(bus2, l2copy, 15); + GridKit::BusPartitionInterface* busInterface3 = new GridKit::BusPartitionInterface(bus3, l3copy, 16); - GridKit::SubsystemModel* partition1 = new GridKit::SubsystemModel(vec_size_internals); - GridKit::SubsystemModel* partition2 = new GridKit::SubsystemModel(); - - GridKit::MicrogridLine comp3copy(*l2); - GridKit::BusPartitionInterface* busInterface = new GridKit::BusPartitionInterface(comp3copy, dqbus2, dqbus2 + 1, 14); + busInterface1->allocate(); + busInterface2->allocate(); + busInterface3->allocate(); + partition1->addNode(&dg_signal); partition1->addComponent(dg1); - partition1->addComponent(dg2); - partition1->addComponent(l1); - partition1->addComponent(bus1); - partition1->addComponent(bus2); + partition1->addComponent(bus_para_1); + partition1->addNode(&bus1); partition1->addComponent(load1); - partition1->addComponent(busInterface); - - partition2->addComponent(dg3); - partition2->addComponent(dg4); - partition2->addComponent(l2); - partition2->addComponent(bus3); - partition2->addComponent(bus4); - partition2->addComponent(l3); - partition2->addComponent(load2); - - sysmodel->addComponent(new GridKit::DistributedGenerator(*dg1)); - sysmodel->addComponent(new GridKit::DistributedGenerator(*dg2)); - sysmodel->addComponent(new GridKit::DistributedGenerator(*dg3)); - sysmodel->addComponent(new GridKit::DistributedGenerator(*dg4)); - sysmodel->addComponent(new GridKit::MicrogridLine(*l1)); - sysmodel->addComponent(new GridKit::MicrogridLine(*l2)); - sysmodel->addComponent(new GridKit::MicrogridLine(*l3)); - sysmodel->addComponent(new GridKit::MicrogridBusDQ(*bus1)); - sysmodel->addComponent(new GridKit::MicrogridBusDQ(*bus2)); - sysmodel->addComponent(new GridKit::MicrogridBusDQ(*bus3)); - sysmodel->addComponent(new GridKit::MicrogridBusDQ(*bus4)); - sysmodel->addComponent(new GridKit::MicrogridLoad(*load1)); - sysmodel->addComponent(new GridKit::MicrogridLoad(*load2)); - - partition1->allocate(); - partition2->allocate(); - sysmodel->allocate(vec_size_total); + partition1->addComponent(busInterface1); + + partition2->addComponent(dg2); + partition2->addComponent(l1); + partition2->addComponent(busInterface2); + partition2->addComponent(bus_para_2); + partition2->addNode(&bus2); + + partition3->addComponent(dg3); + partition3->addComponent(l2); + partition3->addComponent(busInterface3); + partition3->addComponent(load2); + partition3->addComponent(bus_para_3); + partition3->addNode(&bus3); + + partition4->addComponent(dg4); + partition4->addComponent(l3); + partition4->addComponent(bus_para_4); + partition4->addNode(&bus4); std::vector y; std::vector yp; - for (size_t i = 0; i < vec_size_total; i++) + for (size_t i = 0; i < sysmodel->size(); i++) { y.push_back(static_cast(i + 1)); yp.push_back(static_cast(i + 1)); } - // Distribute externals to partition 1 - for (size_t i = 0; i < partition1->getExternSize(); i++) + for (size_t i = 0; i < sysmodel->size(); i++) { - partition1->getExternalDataY()[i] = y[partition1->getExternalIndices()[i]]; - partition1->getExternalDataYP()[i] = yp[partition1->getExternalIndices()[i]]; + sysmodel->y()[i] = y[i]; + sysmodel->yp()[i] = yp[i]; } - // Distribute externals to partition 2 - for (size_t i = 0; i < partition2->getExternSize(); i++) + sysmodel->evaluateResidual(); + std::vector f_sysmodel = sysmodel->getResidual(); + + partition1->allocate(); + partition2->allocate(); + partition3->allocate(); + partition4->allocate(); + + std::vector*> partitions = {partition1, partition2, partition3, partition4}; + + // Distribute externals to partition 1 + for (auto* partition : partitions) { - partition2->getExternalDataY()[i] = y[partition2->getExternalIndices()[i]]; - partition2->getExternalDataYP()[i] = yp[partition2->getExternalIndices()[i]]; + for (size_t i = 0; i < partition->getExternSize(); i++) + { + partition->getExternalDataY()[i] = y[partition->getExternalIndices()[i]]; + partition->getExternalDataYP()[i] = yp[partition->getExternalIndices()[i]]; + } } - // Distribute internals to partition 1 - for (size_t i = 0; i < partition1->getInternalSize(); i++) + for (auto* partition : partitions) { - partition1->y()[i] = y[partition1->getNodeConnection(i)]; - partition1->yp()[i] = yp[partition1->getNodeConnection(i)]; + for (size_t i = 0; i < partition->getInternalSize(); i++) + { + partition->y()[i] = y[partition->getNodeConnection(i)]; + partition->yp()[i] = yp[partition->getNodeConnection(i)]; + } } - // Distribute internals to partition 2 - for (size_t i = 0; i < partition2->getInternalSize(); i++) + for (auto* partition : partitions) { - partition2->y()[i] = y[partition2->getNodeConnection(i)]; - partition2->yp()[i] = yp[partition2->getNodeConnection(i)]; + partition->evaluateResidual(); } - // Evaluate Residuals for each partition - partition1->evaluateResidual(); - partition2->evaluateResidual(); - auto printTitle = [](std::string msg) -> void { std::cout << "\n--------------- " << msg << " -------------" << std::endl; @@ -355,57 +261,42 @@ int main() }; // Print Residuals from partition 1 - printTitle("Partition 1"); - for (size_t i = 0; i < partition1->getInternalSize(); i++) - { - auto com_index = partition1->getNodeConnection(static_cast(i)); - printf("%-12.5g ---------- %7zu\n", partition1->getResidual()[i], com_index); - } - - // Print Residuals from partition 2 - printTitle("Partition 2"); - for (size_t i = 0; i < partition2->getInternalSize(); i++) - { - auto com_index = partition2->getNodeConnection(static_cast(i)); - printf("%-12.5g ---------- %7zu\n", partition2->getResidual()[i], com_index); - } - - for (size_t i = 0; i < vec_size_total; i++) + int counter = 1; + for (auto* partition : partitions) { - sysmodel->y()[i] = y[i]; - sysmodel->yp()[i] = yp[i]; + printTitle("Partition " + std::to_string(counter++)); + for (size_t i = 0; i < partition->getInternalSize(); i++) + { + auto com_index = partition->getNodeConnection(static_cast(i)); + printf("%-12.5g ---------- %7zu\n", partition->getResidual()[i], com_index); + } } - sysmodel->evaluateResidual(); - printTitle("Reference Solution"); - for (size_t i = 0; i < vec_size_total; i++) + for (size_t i = 0; i < sysmodel->size(); i++) { printf("%-12.5g ---------- %7zu\n", sysmodel->getResidual()[i], i); } - std::vector f(vec_size_total, 0.0); - std::vector error(vec_size_total, 1.0); + std::vector f(sysmodel->size(), 0.0); + std::vector error(sysmodel->size(), 1.0); // Get internal residuals from partition 1 - for (size_t i = 0; i < partition1->getInternalSize(); i++) - { - f[partition1->getNodeConnection(i)] = partition1->getResidual()[i]; - } - - // Get internal residuals from partition 2 - for (size_t i = 0; i < partition2->getInternalSize(); i++) + for (auto* partition : partitions) { - f[partition2->getNodeConnection(i)] = partition2->getResidual()[i]; + for (size_t i = 0; i < partition->getInternalSize(); i++) + { + f[partition->getNodeConnection(i)] = partition->getResidual()[i]; + } } - for (size_t i = 0; i < vec_size_total; i++) + for (size_t i = 0; i < sysmodel->size(); i++) { - error[i] = sysmodel->getResidual()[i] - f[i]; + error[i] = f_sysmodel[i] - f[i]; } double max_error = 0; - for (size_t i = 0; i < vec_size_total; i++) + for (size_t i = 0; i < sysmodel->size(); i++) { if (max_error < std::abs(error[i])) { @@ -415,8 +306,6 @@ int main() std::cout << "\nMax Error of Reference and Partition Evaluation: " << max_error << std::endl; - delete partition1; - delete partition2; delete sysmodel; return 0; From 5ef63d35e1dedd0913b7ea0646a3fe85c62f63b8 Mon Sep 17 00:00:00 2001 From: abdourahmanbarry Date: Wed, 24 Jun 2026 20:09:34 +0000 Subject: [PATCH 13/52] first version of subsystem --- .../Model/PowerElectronics/SubsystemModel.hpp | 264 ++++++++++++------ 1 file changed, 177 insertions(+), 87 deletions(-) diff --git a/GridKit/Model/PowerElectronics/SubsystemModel.hpp b/GridKit/Model/PowerElectronics/SubsystemModel.hpp index 4db68eddd..f2bfeb2dd 100644 --- a/GridKit/Model/PowerElectronics/SubsystemModel.hpp +++ b/GridKit/Model/PowerElectronics/SubsystemModel.hpp @@ -3,17 +3,16 @@ #pragma once #include +#include #include #include #include +#include #include #include -#include #include -#include "GridKit/Model/PowerElectronics/MicrogridBusDQ/MicrogridBusDQ.hpp" - namespace GridKit { template @@ -23,20 +22,23 @@ namespace GridKit using MatrixT = typename CircuitComponent::MatrixT; using CsrMatrixT = typename CircuitComponent::CsrMatrixT; using component_type = CircuitComponent; - using node_type = CircuitNode; + using node_type = PowerElectronics::NodeBase; using CircuitComponent::size_; - using CircuitComponent::n_extern_; using CircuitComponent::n_intern_; + using CircuitComponent::n_extern_; using CircuitComponent::nnz_; using CircuitComponent::time_; using CircuitComponent::alpha_; using CircuitComponent::y_; + using CircuitComponent::y_int_; using CircuitComponent::yp_; + using CircuitComponent::yp_int_; using CircuitComponent::f_; - using CircuitComponent::jac_; + using CircuitComponent::f_int_; using CircuitComponent::rel_tol_; using CircuitComponent::abs_tol_; + using CircuitComponent::extern_indices_; public: /** @@ -44,7 +46,8 @@ namespace GridKit * * @post System model parameters set as default */ - SubsystemModel(IdxT refframe_index = neg1_) + + SubsystemModel(bool root = true) { // Set system model parameters as default rel_tol_ = 1e-4; @@ -52,7 +55,7 @@ namespace GridKit this->max_steps_ = 2000; // By default don't use the jacobian use_jac_ = false; - refframe_index_ = refframe_index; + root_ = root; } virtual ~SubsystemModel() @@ -82,89 +85,59 @@ namespace GridKit int allocate() override { - size_t counter = 0; - - // First pass: Map global component indices to local subsystem indices. - for (const auto& component : components_) - { - component->allocate(); - - for (IdxT i = component->getExternSize(); i < component->size(); i++) - { - - IdxT index = component->getNodeConnection(i); - - if (index != neg1_) - { - internal_map_[index] = counter; - counter++; - } - } - // TODO : To be removed once MicrogridBusDQ owns its internals - auto* comp = dynamic_cast*>(component); + this->createGlobalToInternalMap(); - if (comp != nullptr) - { - IdxT index1 = component->getNodeConnection(0); - IdxT index2 = component->getNodeConnection(1); - - internal_map_[index1] = counter; - counter++; - internal_map_[index2] = counter; - counter++; - } - } - - // If this subsystem contains the reference generator, add the rotor motor as an internal - if (refframe_index_ != neg1_) - { - internal_map_[refframe_index_] = counter; - counter++; - } - - // Second pass: Identify variables required by this partition but owned by another partition. - for (const auto& component : components_) - { - for (IdxT i = 0; i < component->getExternSize(); i++) - { - IdxT index = component->getNodeConnection(i); - - if (internal_map_.count(index) < 1 && external_map_.count(index) < 1 && index != neg1_) - { - external_map_[index] = counter; - counter++; - } - } - } + this->mapGlobalToLocal(); n_intern_ = internal_map_.size(); n_extern_ = external_map_.size(); size_ = n_intern_ + n_extern_; - this->mapGlobalToLocal(); + CircuitComponent::allocate(); - // Allocate subsystem vectors. y_.resize(n_intern_); yp_.resize(n_intern_); f_.resize(n_intern_); - // Allocate vectors associated with external coupling variables. + // Allocate subsystem vectors. y_ext_.resize(n_extern_); yp_ext_.resize(n_extern_); f_ext_.resize(n_extern_); external_indices_.resize(n_extern_); // Store the mapping from local subsystem indices back to their global system indices - for (const auto& [global_idx, local_idx] : internal_map_) + for (const auto [global_idx, local_idx] : internal_map_) { this->setExternalConnectionNodes(local_idx, global_idx); } // Store the global indices of all external coupling variables. - for (const auto& [global_idx, local_idx] : external_map_) + for (const auto [global_idx, local_idx] : external_map_) { external_indices_[local_idx - n_intern_] = global_idx; + extern_indices_.insert(local_idx); + this->setExternalConnectionNodes(local_idx, global_idx); + } + + { + size_t component_internal_idx = 0; + for (component_type* comp : components_) + { + + comp->setInternalPointer(&y_[component_internal_idx]); + comp->setInternalDerivativePointer(&yp_[component_internal_idx]); + comp->setInternalResidualPointer(&f_[component_internal_idx]); + + const auto& external_indices = comp->getExternIndices(); + for (size_t i = 0; i < comp->size(); i++) + { + if (!external_indices.contains(i)) + { + component_internal_idx++; + } + } + } } return 0; @@ -196,13 +169,13 @@ namespace GridKit */ int distributeVectors() { - for (const auto& component : components_) + for (component_type* component : components_) { - IdxT size = component->size(); - std::vector& y = component->y(); - std::vector& yp = component->yp(); + std::vector& y = component->y(); + std::vector& yp = component->yp(); + const std::set& externals = component->getExternIndices(); - for (IdxT j = 0; j < size; ++j) + for (size_t j : externals) { if (component->getNodeConnection(j) == neg1_) { @@ -234,9 +207,9 @@ namespace GridKit * * @return int 0 if successful, positive if there's a recoverable error, negative if unrecoverable */ - int evaluateResidual() final + int evaluateInternalResidual() final { - for (IdxT i = 0; i < this->f_.size(); i++) + for (IdxT i = 0; i < this->getInternalSize(); i++) { f_[i] = 0.0; } @@ -244,14 +217,24 @@ namespace GridKit this->distributeVectors(); // Update system residual vector - for (const auto& component : components_) + + // Evaluate component internal residuals - this is embarassingly parallel + for (component_type* component : components_) + { + if (int err_code = component->evaluateInternalResidual()) + return err_code; + } + + for (component_type* component : components_) { - // TODO:check return type - component->evaluateResidual(); - IdxT size = component->size(); - const std::vector& residual = component->getResidual(); - for (IdxT j = 0; j < size; ++j) + if (int err_code = component->evaluateExternalResidual()) + return err_code; + + const std::vector& residual = component->getResidual(); + const std::set& externals = component->getExternIndices(); + + for (size_t j : externals) { //@todo should do a different grounding check if (component->getNodeConnection(j) != neg1_ && component->getNodeConnection(j) < this->getInternalSize()) @@ -264,6 +247,14 @@ namespace GridKit return 0; } + /** + * @todo implement this for nested systems + */ + int evaluateExternalResidual() final + { + return 0; + } + /** * @brief Creates the system Jacobian representing \alpha dF/dy' + dF/dy * @@ -349,7 +340,7 @@ namespace GridKit */ void printJacobianMatrixMarket(std::string filename, std::string title) { - jac_.printMatrixMarket(filename, title); + // jac_.printMatrixMarket(filename, title); } CsrMatrixT* getCsrJacobian() const override @@ -374,7 +365,7 @@ namespace GridKit int mapGlobalToLocal() { - for (const auto& component : components_) + for (auto* component : components_) { for (IdxT i = 0; i < component->size(); i++) @@ -396,6 +387,28 @@ namespace GridKit } } + for (auto* node : nodes_) + { + + for (IdxT i = 0; i < node->size(); i++) + { + IdxT index = node->getNodeConnection(i); + + if (index == neg1_) + { + continue; + } + else if (internal_map_.count(index) > 0) + { + node->setExternalConnectionNodes(i, internal_map_.at(index)); + } + else + { + node->setExternalConnectionNodes(i, external_map_.at(index)); + } + } + } + return 0; } @@ -404,6 +417,73 @@ namespace GridKit return 0; } + void createGlobalToInternalMap() + { + + std::vector global_indices; + + // First pass: Map global component indices to local subsystem indices. + for (component_type* comp : components_) + { + for (IdxT i = 0; i < comp->size(); i++) + { + IdxT index = comp->getNodeConnection(i); + + auto extern_indices = comp->getExternIndices(); + + if (index != neg1_ && !extern_indices.contains(i)) + { + global_indices.push_back(index); + } + } + } + + for (node_type* node : nodes_) + { + + for (IdxT i = 0; i < node->size(); i++) + { + IdxT index = node->getNodeConnection(i); + + if (index != neg1_) + { + global_indices.push_back(index); + } + } + } + + std::sort(global_indices.begin(), global_indices.end()); + + for (IdxT i = 0; i < global_indices.size(); i++) + { + internal_map_[global_indices[i]] = i; + } + + size_t counter = global_indices.size(); + for (component_type* comp : components_) + { + auto extern_indices = comp->getExternIndices(); + for (IdxT j = 0; j < comp->size(); j++) + { + if (!extern_indices.contains(j)) + { + continue; + } + IdxT index = comp->getNodeConnection(j); + + if (internal_map_.count(index) < 1 && external_map_.count(index) < 1 && index != neg1_) + { + external_map_[index] = counter++; + } + } + } + } + + std::vector& getExternalIndices() + { + return external_indices_; + } + std::vector& getExternalDataY() { return y_ext_; @@ -414,22 +494,32 @@ namespace GridKit return yp_ext_; } - std::vector& getExternalIndices() + void addNode(node_type* node) { - return external_indices_; + nodes_.push_back(node); + } + + void toString() + { + for (auto* comp : components_) + { + std::cout << ", " << std::to_string(comp->getIDcomponent()); + } + std::cout << std::endl; } private: static constexpr IdxT neg1_ = INVALID_INDEX; std::vector components_; + std::vector nodes_; std::unordered_map internal_map_; std::unordered_map external_map_; std::vector external_indices_; - std::vector y_ext_; - std::vector yp_ext_; - std::vector f_ext_; - IdxT refframe_index_{neg1_}; + bool root_; + std::vector y_ext_; + std::vector yp_ext_; + std::vector f_ext_; IdxT* map_to_csr_{nullptr}; CsrMatrixT* csr_jac_{nullptr}; From c60ba3358f3ab5ff15c8e07a10823d1fde1c5475 Mon Sep 17 00:00:00 2001 From: abdourahmanbarry Date: Wed, 24 Jun 2026 22:09:58 +0000 Subject: [PATCH 14/52] Added partitioned scale microgrid example --- .../BusPartitionInterface.cpp | 89 +++-- .../BusPartitionInterface.hpp | 30 +- .../PartitionInterface/CMakeLists.txt | 2 - .../PartitionInterface/PartitionInterface.hpp | 38 --- .../Model/PowerElectronics/SubsystemModel.hpp | 31 +- .../PowerElectronics/Partition/CMakeLists.txt | 25 +- .../Partition/PartitionMicrogrid.cpp | 56 ++-- .../Partition/PartitionScaleMicrogrid.cpp | 311 +++++++++++++----- 8 files changed, 361 insertions(+), 221 deletions(-) diff --git a/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.cpp b/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.cpp index 2e04e558d..0c4de184c 100644 --- a/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.cpp +++ b/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.cpp @@ -2,9 +2,9 @@ #include "BusPartitionInterface.hpp" #include +#include #include #include -#include namespace GridKit { @@ -26,21 +26,19 @@ namespace GridKit * @param id Unique component identifier. */ template - BusPartitionInterface::BusPartitionInterface(CircuitComponent& component, IdxT bus_i, IdxT bus_j, IdxT id) - : component_(component) + BusPartitionInterface::BusPartitionInterface(node_type& bus, component_type& component, IdxT id) + : component_(component), + bus_(bus) { - size_ = component.size(); + size_ = component_.size(); n_intern_ = 0; - n_extern_ = static_cast(component.size()); + n_extern_ = static_cast(component_.size()); idc_ = id; for (IdxT i = 0; i < size_; i++) { extern_indices_.insert(i); } - - bus_i_ = bus_i; - bus_j_ = bus_j; } template @@ -54,28 +52,51 @@ namespace GridKit template int BusPartitionInterface::allocate() { + + CircuitComponent::allocate(); + y_.resize(static_cast(size_)); yp_.resize(static_cast(size_)); f_.resize(static_cast(size_)); std::fill(f_.begin(), f_.end(), 0); + bool port_i_set = false; + bool port_j_set = false; - component_.allocate(); - - for (size_t i = 0; i < static_cast(component_.size()); i++) + for (size_t i = 0; i < static_cast(size_); i++) { - if (bus_i_ == component_.getNodeConnection(static_cast(i))) + if (bus_.getNodeConnection(0) == component_.getNodeConnection(static_cast(i))) { bus_port_i_ = i; + port_i_set = true; } - else if (bus_j_ == component_.getNodeConnection(static_cast(i))) + else if (bus_.getNodeConnection(1) == component_.getNodeConnection(static_cast(i))) { bus_port_j_ = i; + port_j_set = true; } IdxT global_idx = component_.getNodeConnection(static_cast(i)); this->setExternalConnectionNodes(static_cast(i), global_idx); } + if (!port_i_set || !port_j_set) + { + std::cerr << "ERROR: Invalid partition interface detected. " + << "Bus(ID=" << bus_.busID() + << "), Component(ID=" << component_.getIDcomponent() + << "). Please verify connection-node mappings and internal/external index assignments." + << std::endl; + assert(false); + } + + y_ptr = new ScalarT[component_.getInternalSize()]; + yp_ptr = new ScalarT[component_.getInternalSize()]; + f_ptr = new ScalarT[component_.getInternalSize()]; + + component_.setInternalPointer(y_ptr); + component_.setInternalDerivativePointer(yp_ptr); + component_.setInternalResidualPointer(f_ptr); + return 0; } @@ -101,18 +122,46 @@ namespace GridKit * @brief Eval Micro Load */ template - int BusPartitionInterface::evaluateResidual() + int BusPartitionInterface::evaluateInternalResidual() { - std::copy(y_.begin(), y_.end(), component_.y().begin()); - std::copy(yp_.begin(), yp_.end(), component_.yp().begin()); + return 0; + } - component_.evaluateResidual(); + /** + * @brief Eval Micro Load + */ + template + int BusPartitionInterface::evaluateExternalResidual() + { + + size_t counter_int = 0; + size_t counter_ext = 0; + auto extern_indices = component_.getExternIndices(); + + for (size_t i = 0; i < static_cast(component_.size()); i++) + { + if (extern_indices.contains(static_cast(i))) + { + component_.y()[counter_ext] = y_[i]; + component_.yp()[counter_ext] = yp_[i]; + counter_ext++; + } + else + { + y_ptr[counter_int] = y_[i]; + yp_ptr[counter_int] = yp_[i]; + counter_int++; + } + } - auto& f = component_.getResidual(); + component_.evaluateExternalResidual(); + auto f = component_.getResidual(); + + // TODO: This assumes that external variables are ordered after all internal + // variables in the local indexing. To make this more robust, we need to get rid of this assumption. f_[bus_port_i_] = f[bus_port_i_]; f_[bus_port_j_] = f[bus_port_j_]; - return 0; } @@ -126,8 +175,6 @@ namespace GridKit template int BusPartitionInterface::evaluateJacobian() { - jac_.zeroMatrix(); - return 0; } diff --git a/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.hpp b/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.hpp index 1b1d9e4bd..2c08400b5 100644 --- a/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.hpp +++ b/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.hpp @@ -5,6 +5,7 @@ #include #include +#include #include "GridKit/Model/PowerElectronics/PartitionInterface/PartitionInterface.hpp" @@ -17,46 +18,45 @@ namespace GridKit template class BusPartitionInterface : public PartitionInterface { - using RealT = typename CircuitComponent::RealT; - using MatrixT = typename CircuitComponent::MatrixT; - using PartitionInterface::external_data_y_; - using PartitionInterface::external_data_yp_; - using PartitionInterface::interface_partition_externals_; - using PartitionInterface::bus_port_i_; - using PartitionInterface::bus_port_j_; - using PartitionInterface::bus_i_; - using PartitionInterface::bus_j_; + using component_type = CircuitComponent; + using node_type = typename PowerElectronics::NodeBase; using CircuitComponent::size_; using CircuitComponent::nnz_; using CircuitComponent::time_; using CircuitComponent::alpha_; using CircuitComponent::y_; + using CircuitComponent::y_int_; using CircuitComponent::yp_; + using CircuitComponent::yp_int_; using CircuitComponent::tag_; using CircuitComponent::f_; + using CircuitComponent::f_int_; using CircuitComponent::g_; using CircuitComponent::yB_; using CircuitComponent::ypB_; using CircuitComponent::fB_; using CircuitComponent::gB_; - using CircuitComponent::jac_; using CircuitComponent::param_; using CircuitComponent::idc_; + using PartitionInterface::bus_port_i_; + using PartitionInterface::bus_port_j_; + using CircuitComponent::extern_indices_; using CircuitComponent::n_extern_; using CircuitComponent::n_intern_; public: - BusPartitionInterface(CircuitComponent& component, IdxT bus_i, IdxT bus_j, IdxT id); + BusPartitionInterface(node_type& bus, component_type& component, IdxT id); virtual ~BusPartitionInterface(); int allocate(); int initialize(); int tagDifferentiable(); - int evaluateResidual(); + int evaluateInternalResidual() final; + int evaluateExternalResidual() final; int evaluateJacobian(); int evaluateIntegrand(); @@ -66,6 +66,10 @@ namespace GridKit int evaluateAdjointIntegrand(); private: - CircuitComponent& component_; + component_type& component_; + node_type& bus_; + ScalarT* y_ptr; + ScalarT* yp_ptr; + ScalarT* f_ptr; }; } // namespace GridKit diff --git a/GridKit/Model/PowerElectronics/PartitionInterface/CMakeLists.txt b/GridKit/Model/PowerElectronics/PartitionInterface/CMakeLists.txt index 28fb804b0..938b2f6f1 100644 --- a/GridKit/Model/PowerElectronics/PartitionInterface/CMakeLists.txt +++ b/GridKit/Model/PowerElectronics/PartitionInterface/CMakeLists.txt @@ -1,8 +1,6 @@ gridkit_add_library(power_elec_partition_interfaces SOURCES BusPartitionInterface.cpp - ComponentPartitionInterface.cpp HEADERS BusPartitionInterface.hpp - ComponentPartitionInterface.hpp PartitionInterface.hpp) diff --git a/GridKit/Model/PowerElectronics/PartitionInterface/PartitionInterface.hpp b/GridKit/Model/PowerElectronics/PartitionInterface/PartitionInterface.hpp index 85599404c..1e5a3c52d 100644 --- a/GridKit/Model/PowerElectronics/PartitionInterface/PartitionInterface.hpp +++ b/GridKit/Model/PowerElectronics/PartitionInterface/PartitionInterface.hpp @@ -2,9 +2,6 @@ #pragma once -#include -#include - #include #include #include @@ -36,44 +33,9 @@ namespace GridKit ~PartitionInterface() = default; - int setExternalDataY(const std::vector& data) - { - std::copy(data.begin(), data.end(), external_data_y_.begin()); - - return 0; - } - - int setExternalDataYP(const std::vector& data) - { - std::copy(data.begin(), data.end(), external_data_yp_.begin()); - - return 0; - } - - std::vector& getExternalDataY() - { - return external_data_y_; - } - - std::vector& getExternalDataYP() - { - return external_data_yp_; - } - - std::vector& getPartitionExternalIndices() - { - return interface_partition_externals_; - } - protected: - std::vector external_data_y_; - std::vector external_data_yp_; - std::vector interface_partition_externals_; - size_t bus_port_i_; size_t bus_port_j_; - IdxT bus_i_; - IdxT bus_j_; }; } // namespace GridKit diff --git a/GridKit/Model/PowerElectronics/SubsystemModel.hpp b/GridKit/Model/PowerElectronics/SubsystemModel.hpp index f2bfeb2dd..0ec483cfe 100644 --- a/GridKit/Model/PowerElectronics/SubsystemModel.hpp +++ b/GridKit/Model/PowerElectronics/SubsystemModel.hpp @@ -60,12 +60,6 @@ namespace GridKit virtual ~SubsystemModel() { - for (auto comp : this->components_) - { - delete comp; - } - delete csr_jac_; - delete[] map_to_csr_; } bool hasJacobian() final @@ -92,7 +86,7 @@ namespace GridKit n_intern_ = internal_map_.size(); n_extern_ = external_map_.size(); - size_ = n_intern_ + n_extern_; + size_ = n_intern_; CircuitComponent::allocate(); @@ -116,8 +110,6 @@ namespace GridKit for (const auto [global_idx, local_idx] : external_map_) { external_indices_[local_idx - n_intern_] = global_idx; - extern_indices_.insert(local_idx); - this->setExternalConnectionNodes(local_idx, global_idx); } { @@ -420,8 +412,7 @@ namespace GridKit void createGlobalToInternalMap() { - std::vector global_indices; - + size_t component_internal_idx = 0; // First pass: Map global component indices to local subsystem indices. for (component_type* comp : components_) { @@ -433,7 +424,7 @@ namespace GridKit if (index != neg1_ && !extern_indices.contains(i)) { - global_indices.push_back(index); + internal_map_[index] = component_internal_idx++; } } } @@ -447,19 +438,11 @@ namespace GridKit if (index != neg1_) { - global_indices.push_back(index); + internal_map_[index] = component_internal_idx++; } } } - std::sort(global_indices.begin(), global_indices.end()); - - for (IdxT i = 0; i < global_indices.size(); i++) - { - internal_map_[global_indices[i]] = i; - } - - size_t counter = global_indices.size(); for (component_type* comp : components_) { auto extern_indices = comp->getExternIndices(); @@ -473,7 +456,7 @@ namespace GridKit if (internal_map_.count(index) < 1 && external_map_.count(index) < 1 && index != neg1_) { - external_map_[index] = counter++; + external_map_[index] = component_internal_idx++; } } } @@ -484,12 +467,12 @@ namespace GridKit return external_indices_; } - std::vector& getExternalDataY() + std::vector& getExternalDataY() { return y_ext_; } - std::vector& getExternalDataYP() + std::vector& getExternalDataYP() { return yp_ext_; } diff --git a/examples/PowerElectronics/Partition/CMakeLists.txt b/examples/PowerElectronics/Partition/CMakeLists.txt index 2bc268e4f..00270b746 100644 --- a/examples/PowerElectronics/Partition/CMakeLists.txt +++ b/examples/PowerElectronics/Partition/CMakeLists.txt @@ -1,12 +1,25 @@ + +find_package(OpenMP REQUIRED) + add_executable(partition Partition.cpp) target_link_libraries(partition GridKit::solvers_dyn GridKit::power_elec_partition_interfaces) add_executable(partitionMicrogrid PartitionMicrogrid.cpp) -target_link_libraries(partitionMicrogrid GridKit::power_elec_disgen - GridKit::power_elec_microline - GridKit::power_elec_microload - GridKit::solvers_dyn - GridKit::power_elec_microbusdq - GridKit::power_elec_partition_interfaces) \ No newline at end of file +target_link_libraries(partitionMicrogrid GridKit::power_elec_disgen + GridKit::power_elec_microline + GridKit::power_elec_microload + GridKit::solvers_dyn + GridKit::power_elec_microbusdq + GridKit::power_elec_partition_interfaces) + + +add_executable(PartitionScaleMicrogrid PartitionScaleMicrogrid.cpp) +target_link_libraries(PartitionScaleMicrogrid GridKit::power_elec_disgen + GridKit::power_elec_microline + GridKit::power_elec_microload + GridKit::solvers_dyn + GridKit::power_elec_microbusdq + GridKit::power_elec_partition_interfaces + OpenMP::OpenMP_CXX) \ No newline at end of file diff --git a/examples/PowerElectronics/Partition/PartitionMicrogrid.cpp b/examples/PowerElectronics/Partition/PartitionMicrogrid.cpp index f9e00c37b..ff3d23a5d 100644 --- a/examples/PowerElectronics/Partition/PartitionMicrogrid.cpp +++ b/examples/PowerElectronics/Partition/PartitionMicrogrid.cpp @@ -1,6 +1,9 @@ #include #include #include +#include +#include +#include #define _USE_MATH_DEFINES #include @@ -26,6 +29,7 @@ int main() double rel_tol = 1.0e-8; size_t max_step_number = 3000; bool use_jac = true; + bool debug_output = true; // Create model auto* sysmodel = new GridKit::PowerElectronicsModel(rel_tol, abs_tol, use_jac, max_step_number); @@ -164,46 +168,35 @@ int main() sysmodel->allocate(); - GridKit::SubsystemModel* partition1 = new GridKit::SubsystemModel(false); - GridKit::SubsystemModel* partition2 = new GridKit::SubsystemModel(false); - GridKit::SubsystemModel* partition3 = new GridKit::SubsystemModel(false); - GridKit::SubsystemModel* partition4 = new GridKit::SubsystemModel(false); + GridKit::SubsystemModel* partition1 = new GridKit::SubsystemModel(); + GridKit::SubsystemModel* partition2 = new GridKit::SubsystemModel(); - GridKit::MicrogridLine l1copy(*l1); GridKit::MicrogridLine l2copy(*l2); - GridKit::MicrogridLine l3copy(*l3); - GridKit::BusPartitionInterface* busInterface1 = new GridKit::BusPartitionInterface(bus1, l1copy, 14); - GridKit::BusPartitionInterface* busInterface2 = new GridKit::BusPartitionInterface(bus2, l2copy, 15); - GridKit::BusPartitionInterface* busInterface3 = new GridKit::BusPartitionInterface(bus3, l3copy, 16); + GridKit::BusPartitionInterface* busInterface1 = new GridKit::BusPartitionInterface(bus2, l2copy, 14); busInterface1->allocate(); - busInterface2->allocate(); - busInterface3->allocate(); partition1->addNode(&dg_signal); partition1->addComponent(dg1); - partition1->addComponent(bus_para_1); - partition1->addNode(&bus1); + partition1->addComponent(dg2); + partition1->addComponent(l1); partition1->addComponent(load1); partition1->addComponent(busInterface1); + partition1->addComponent(bus_para_1); + partition1->addComponent(bus_para_2); + partition1->addNode(&bus1); + partition1->addNode(&bus2); - partition2->addComponent(dg2); - partition2->addComponent(l1); - partition2->addComponent(busInterface2); - partition2->addComponent(bus_para_2); - partition2->addNode(&bus2); - - partition3->addComponent(dg3); - partition3->addComponent(l2); - partition3->addComponent(busInterface3); - partition3->addComponent(load2); - partition3->addComponent(bus_para_3); - partition3->addNode(&bus3); + partition2->addComponent(dg3); + partition2->addComponent(dg4); + partition2->addComponent(l2); + partition2->addComponent(l3); - partition4->addComponent(dg4); - partition4->addComponent(l3); - partition4->addComponent(bus_para_4); - partition4->addNode(&bus4); + partition2->addComponent(load2); + partition2->addComponent(bus_para_4); + partition2->addComponent(bus_para_3); + partition2->addNode(&bus3); + partition2->addNode(&bus4); std::vector y; std::vector yp; @@ -225,10 +218,8 @@ int main() partition1->allocate(); partition2->allocate(); - partition3->allocate(); - partition4->allocate(); - std::vector*> partitions = {partition1, partition2, partition3, partition4}; + std::vector*> partitions = {partition1, partition2}; // Distribute externals to partition 1 for (auto* partition : partitions) @@ -293,6 +284,7 @@ int main() for (size_t i = 0; i < sysmodel->size(); i++) { error[i] = f_sysmodel[i] - f[i]; + std::cout << error[i] << std::endl; } double max_error = 0; diff --git a/examples/PowerElectronics/Partition/PartitionScaleMicrogrid.cpp b/examples/PowerElectronics/Partition/PartitionScaleMicrogrid.cpp index 178ad0a47..57f2448bd 100644 --- a/examples/PowerElectronics/Partition/PartitionScaleMicrogrid.cpp +++ b/examples/PowerElectronics/Partition/PartitionScaleMicrogrid.cpp @@ -1,6 +1,10 @@ +#include + +#include #include #include #include +#include #include #include #include @@ -20,6 +24,26 @@ #include #include +/****************************************************************************** + * Partitioned Residual Evaluation + * + * Construct subsystem models by partitioning the original microgrid into + * independent groups of components. Each subsystem is allocated and receives + * the appropriate subset of the global state vectors (y and yp), consisting + * of: + * + * - External variables (coupling variables from neighboring partitions) + * - Internal variables (states owned by the partition) + * + * After distributing the state information, each partition independently + * evaluates its residual. The local residuals are then scattered back into + * the global residual vector to reconstruct the monolithic residual. + * + * Finally, the reconstructed residual is compared against the reference + * monolithic evaluation to verify the correctness of the partitioning + * implementation. + ******************************************************************************/ + using index_type = size_t; using real_type = double; @@ -34,7 +58,7 @@ int printMicrogridSystems(index_type N_size); */ int main(int argc, char const* argv[]) { - // Default value + // Number of IBRs index_type N_size = 2; // Parse command line arguments if provided @@ -71,14 +95,15 @@ int printMicrogridSystems(index_type N_size) bool use_jac = true; - real_type rel_tol = 1e-5; - real_type abs_tol = 1e-5; + real_type rel_tol = 1e-5; + real_type abs_tol = 1e-5; + index_type max_steps = 2000; // Create circuit model - auto* sys_model = new PowerElectronicsModel(rel_tol, - abs_tol, - use_jac, - 2000); + auto* sys_model_control = new PowerElectronicsModel(rel_tol, + abs_tol, + use_jac, + max_steps); // Ensure minimum size requirement if (N_size < 1) @@ -164,26 +189,29 @@ int printMicrogridSystems(index_type N_size) Lload_list[0] = Lload1; } - using SignalNode = GridKit::PowerElectronics::SignalNode; - using Bus = GridKit::PowerElectronics::MicrogridBus; - using BusDQ = MicrogridBusDQ; - using Generator = DistributedGenerator; - using Line = MicrogridLine; - using Load = MicrogridLoad; - - std::vector buses(num_ibrs, nullptr); - std::vector busesDQ(num_ibrs, nullptr); - std::vector generators(num_ibrs, nullptr); - std::vector lines(num_ibrs, nullptr); - std::vector loads(num_ibrs, nullptr); + using SignalNode = GridKit::PowerElectronics::SignalNode; + using Bus = GridKit::PowerElectronics::MicrogridBus; + using BusDQ = MicrogridBusDQ; + using Generator = DistributedGenerator; + using Line = MicrogridLine; + using Load = MicrogridLoad; + using PartitionInterface = BusPartitionInterface; + + std::vector buses(num_ibrs, nullptr); + std::vector busesDQ(num_ibrs, nullptr); + std::vector generators(num_ibrs, nullptr); + std::vector lines(num_ibrs, nullptr); + std::vector loads(num_ibrs, nullptr); + std::vector partitionInterface; + std::vector linesCopies; SignalNode dg_signal; - sys_model->addNode(&dg_signal); + sys_model_control->addNode(&dg_signal); for (size_t i = 0; i < 2 * N_size; i++) { buses[i] = new Bus(); - sys_model->addNode(buses[i]); + sys_model_control->addNode(buses[i]); } // Create the reference DG @@ -192,7 +220,7 @@ int printMicrogridSystems(index_type N_size) true, &dg_signal, buses[0]); - sys_model->addComponent(dg_ref); + sys_model_control->addComponent(dg_ref); generators[0] = dg_ref; @@ -209,7 +237,7 @@ int printMicrogridSystems(index_type N_size) buses[i]); generators[i] = dg; - sys_model->addComponent(dg); + sys_model_control->addComponent(dg); } // Load all the Line components @@ -223,7 +251,7 @@ int printMicrogridSystems(index_type N_size) buses[i], buses[i + 1]); lines[i + 1] = line_model; - sys_model->addComponent(line_model); + sys_model_control->addComponent(line_model); } // Load all the Load components @@ -235,7 +263,7 @@ int printMicrogridSystems(index_type N_size) &dg_signal, buses[2 * i]); loads[2 * i] = load_model; - sys_model->addComponent(load_model); + sys_model_control->addComponent(load_model); } // Add all the microgrid Virtual DQ Buses @@ -244,84 +272,96 @@ int printMicrogridSystems(index_type N_size) auto* virDQbus_model = new MicrogridBusDQ(model_id++, RN, buses[i]); busesDQ[i] = virDQbus_model; - sys_model->addComponent(virDQbus_model); + sys_model_control->addComponent(virDQbus_model); } - sys_model->allocate(); + // sys_model_control->addNode(&dg_signal); + // for (index_type i = 0; i < num_ibrs; ++i) + // { + // sys_model_control->addComponent(generators[i]); + // sys_model_control->addComponent(busesDQ[i]); - std::vector y; - std::vector yp; + // if (loads[i] != nullptr) + // { + // sys_model_control->addComponent(loads[i]); + // } - for (size_t i = 0; i < sys_model->size(); i++) + // if (lines[i] != nullptr) + // { + // sys_model_control->addComponent(lines[i]); + // } + // sys_model_control->addNode(buses[i]); + // } + + sys_model_control->allocate(); + + std::vector y; + std::vector yp; + + for (size_t i = 0; i < sys_model_control->size(); i++) { - y.push_back(static_cast(i + 1)); - yp.push_back(static_cast(i + 1)); + y.push_back(static_cast(i + 1 + ((277 * i) % 100))); + yp.push_back(static_cast(i + 1 + ((288 * i) % 100))); } - for (size_t i = 0; i < sys_model->size(); i++) + auto start = std::chrono::high_resolution_clock::now(); + for (size_t i = 0; i < sys_model_control->size(); i++) { - sys_model->y()[i] = y[i]; - sys_model->yp()[i] = yp[i]; + sys_model_control->y()[i] = y[i]; + sys_model_control->yp()[i] = yp[i]; } - sys_model->evaluateResidual(); - std::vector f_sysmodel = sys_model->getResidual(); + sys_model_control->evaluateResidual(); + + auto end = std::chrono::high_resolution_clock::now(); + auto elapsed = std::chrono::duration(end - start); + std::cout << "Monolithic Evaluation Time: " << elapsed.count() << " s\n"; + + auto& f_sysmodel_control = sys_model_control->getResidual(); std::vector*> subsystems(num_partitions); - // Partition the system - index_type q = (num_ibrs) / num_partitions; - index_type r = (num_ibrs) % num_partitions; - index_type index = 0; - for (index_type j = 0; j < num_partitions; j++) - { - auto* partition = new SubsystemModel(false); + assert(num_ibrs % num_partitions == 0); + index_type partition_size = num_ibrs / num_partitions; + index_type index = 0; - // add Reference rotor to the first partition + for (index_type j = 0; j < num_partitions; ++j) + { + auto* partition = new SubsystemModel(); if (j == 0) { partition->addNode(&dg_signal); } - std::cout << "Partition " << j << std::endl; - - index_type part_size = q + (j < r ? 1 : 0); - index_type end = std::min(index + part_size, num_ibrs); - - // Add all components belonging to this partition - for (; index < end; ++index) + // Add components to belong to the same + for (index_type i = 0; i < partition_size; ++i) { partition->addComponent(generators[index]); - std::cout << "Comp Gen" << index << std::endl; partition->addComponent(busesDQ[index]); - std::cout << "Comp BDQ" << index << std::endl; if (loads[index] != nullptr) { partition->addComponent(loads[index]); - std::cout << "Comp Load" << index << std::endl; } if (lines[index] != nullptr) { partition->addComponent(lines[index]); - std::cout << "Comp Line" << index << std::endl; } - std::cout << "Comp Bus " << index << std::endl; - partition->addNode(buses[index]); + + index++; } - // Add partition interface at a partition point + // Add the partition interface to the left partition if (index < num_ibrs) { - auto* linecopy = new GridKit::MicrogridLine(*lines[index]); - - auto* busInterface = new GridKit::BusPartitionInterface(*buses[index - 1], *linecopy, model_id++); - busInterface->allocate(); + auto* linecopy = new GridKit::MicrogridLine(*lines[index]); + auto* busInterface = new GridKit::BusPartitionInterface(*buses[index - 1], *linecopy, model_id++); - std::cout << "Interface " << "(" << index - 1 << ", " << index << ")" << std::endl; + partitionInterface.push_back(busInterface); + linesCopies.push_back(linecopy); partition->addComponent(busInterface); } @@ -329,56 +369,67 @@ int printMicrogridSystems(index_type N_size) subsystems[j] = partition; } + std::vector f(sys_model_control->size(), 1.0); + std::vector error(sys_model_control->size(), 1.0); + + for (auto* partinterface : partitionInterface) + { + partinterface->allocate(); + } + for (auto* partition : subsystems) { - partition->toString(); partition->allocate(); } - // Distribute externals to partition 1 + start = std::chrono::high_resolution_clock::now(); + + auto sys_max_threads = omp_get_max_threads(); +// Distribute externals to partition 1 +#pragma omp parallel for schedule(guided) num_threads(sys_max_threads) for (auto* partition : subsystems) { + // Distribute external variables for (size_t i = 0; i < partition->getExternSize(); i++) { partition->getExternalDataY()[i] = y[partition->getExternalIndices()[i]]; partition->getExternalDataYP()[i] = yp[partition->getExternalIndices()[i]]; } - } - for (auto* partition : subsystems) - { + // Distribute internal variables for (size_t i = 0; i < partition->getInternalSize(); i++) { partition->y()[i] = y[partition->getNodeConnection(i)]; partition->yp()[i] = yp[partition->getNodeConnection(i)]; } - } - for (auto* partition : subsystems) - { + // Evaluate residual of this partition partition->evaluateResidual(); - } - - std::vector f(sys_model->size(), 0.0); - std::vector error(sys_model->size(), 1.0); - // Get internal residuals from partition 1 - for (auto* partition : subsystems) - { + // Reconstruct the monolithic residual from the partition residuals for (size_t i = 0; i < partition->getInternalSize(); i++) { f[partition->getNodeConnection(i)] = partition->getResidual()[i]; } } - for (size_t i = 0; i < sys_model->size(); i++) + end = std::chrono::high_resolution_clock::now(); + elapsed = std::chrono::duration(end - start); + std::cout << "Partition Evaluation Time: " << elapsed.count() << " s\n"; + + for (size_t i = 0; i < sys_model_control->size(); i++) { - error[i] = f_sysmodel[i] - f[i]; - std::cout << i << " " << error[i] << " ---------- " << i << std::endl; + error[i] = abs(f[i] - f_sysmodel_control[i]) / (f_sysmodel_control[i] + 1); + + std::cout << std::format("{:<8d} {:>15.15e} ---------- {:>15.15e} {:>15.5e}\n", + i, + error[i], + f[i], + f_sysmodel_control[i]); } - double max_error = 0; - for (size_t i = 0; i < sys_model->size(); i++) + real_type max_error = 0; + for (size_t i = 0; i < sys_model_control->size(); i++) { if (max_error < std::abs(error[i])) { @@ -386,7 +437,97 @@ int printMicrogridSystems(index_type N_size) } } - std::cout << "\nMax Error of Reference and Partition Evaluation: " << max_error << std::endl; + std::cout << "\nMax Rel. Error between Reference and Partition Evaluation: " << max_error + << std::endl + << std::endl; + + // real_type max_abs_error = 0.0; + // real_type max_rel_error = 0.0; + // size_t max_idx = 0; + + // for (size_t i = 0; i < sys_model_control->size(); ++i) + // { + // real_type abs_err = std::abs(f[i] - f_sysmodel_control[i]); + + // real_type scale = std::max({real_type(1.0), + // std::abs(f[i]), + // std::abs(f_sysmodel_control[i])}); + + // real_type rel_err = abs_err / scale; + + // if (rel_err > max_rel_error) + // { + // max_rel_error = rel_err; + // max_abs_error = abs_err; + // max_idx = i; + // } + // } + + // std::cout << "Max index : " << max_idx << '\n'; + // std::cout << "Partition f : " << std::format("{:.17e}", f[max_idx]) << '\n'; + // std::cout << "Monolithic f : " << std::format("{:.17e}", f_sysmodel_control[max_idx]) << '\n'; + // std::cout << "Abs error : " << std::format("{:.17e}", max_abs_error) << '\n'; + // std::cout << "Rel error : " << std::format("{:.17e}", max_rel_error) << '\n'; + + for (auto* linescpy : linesCopies) + { + delete linescpy; + } + + for (auto* partition : subsystems) + { + delete partition; + } + delete sys_model_control; return 0; } + +// Partition the system +// index_type q = (num_ibrs) / num_partitions; +// index_type r = (num_ibrs) % num_partitions; +// index_type index = 0; +// for (index_type j = 0; j < num_partitions; j++) +// { +// auto* partition = new SubsystemModel(false); + +// // add Reference rotor to the first partition +// if (j == 0) +// { +// partition->addNode(&dg_signal); +// } + +// index_type part_size = q + (j < r ? 1 : 0); +// index_type end = std::min(index + part_size, num_ibrs); + +// // Add all components belonging to this partition +// for (; index < end; ++index) +// { +// partition->addComponent(generators[index]); +// partition->addComponent(busesDQ[index]); + +// if (loads[index] != nullptr) +// { +// partition->addComponent(loads[index]); +// } + +// if (lines[index] != nullptr) +// { +// partition->addComponent(lines[index]); +// } + +// partition->addNode(buses[index]); +// } + +// // Add partition interface at a partition point +// if (index < num_ibrs) +// { +// auto* linecopy = new GridKit::MicrogridLine(*lines[index]); + +// auto* busInterface = new GridKit::BusPartitionInterface(*buses[index - 1], *linecopy, model_id++); +// busInterface->allocate(); +// partition->addComponent(busInterface); +// } + +// subsystems[j] = partition; +// } From 89ee325c9903a7ccd4752dc997d394b4c634cdd3 Mon Sep 17 00:00:00 2001 From: abdourahmanbarry Date: Fri, 26 Jun 2026 23:55:17 +0000 Subject: [PATCH 15/52] Rmoved obsolete interfaces and clean up partitioning code --- .../ComponentPartitionInterface.cpp | 174 -------------- .../ComponentPartitionInterface.hpp | 74 ------ .../Model/PowerElectronics/SubsystemModel.hpp | 2 - .../Partition/PartitionScaleMicrogrid.cpp | 223 +++++++++--------- 4 files changed, 111 insertions(+), 362 deletions(-) delete mode 100644 GridKit/Model/PowerElectronics/PartitionInterface/ComponentPartitionInterface.cpp delete mode 100644 GridKit/Model/PowerElectronics/PartitionInterface/ComponentPartitionInterface.hpp diff --git a/GridKit/Model/PowerElectronics/PartitionInterface/ComponentPartitionInterface.cpp b/GridKit/Model/PowerElectronics/PartitionInterface/ComponentPartitionInterface.cpp deleted file mode 100644 index c5a06ae66..000000000 --- a/GridKit/Model/PowerElectronics/PartitionInterface/ComponentPartitionInterface.cpp +++ /dev/null @@ -1,174 +0,0 @@ - - -#include "ComponentPartitionInterface.hpp" - -#include -#include -#include -#include - -namespace GridKit -{ - - /*! - * @brief Constructor for the ComponentPartitionInterface - * - * - * - * - */ - - template - ComponentPartitionInterface::ComponentPartitionInterface(CircuitComponent* component, IdxT bus_i, IdxT bus_j, IdxT id) - : component_(component) - { - size_ = component_->size(); - n_intern_ = component_->getInternalSize(); - n_extern_ = component_->getExternSize(); - extern_indices_ = component_->getExternIndices(); - idc_ = id; - - bus_i_ = bus_i; - bus_j_ = bus_j; - } - - template - ComponentPartitionInterface::~ComponentPartitionInterface() - { - } - - /*! - * @brief allocate method. - */ - template - int ComponentPartitionInterface::allocate() - { - - size_t bus_size = 2; - - y_.resize(static_cast(size_)); - yp_.resize(static_cast(size_)); - f_.resize(static_cast(size_)); - interface_partition_externals_.resize(bus_size); - external_data_y_.resize(bus_size, 0); - external_data_yp_.resize(bus_size, 0); - - component_->allocate(); - - for (size_t i = 0; i < static_cast(size_); i++) - { - auto index = component_->getNodeConnection(static_cast(i)); - if (bus_i_ == index) - { - bus_port_i_ = i; - } - else if (bus_j_ == index) - { - bus_port_j_ = i; - } - else - { - this->setExternalConnectionNodes(static_cast(i), index); - } - } - - this->setExternalConnectionNodes(static_cast(bus_port_i_), static_cast(-1)); - this->setExternalConnectionNodes(static_cast(bus_port_j_), static_cast(-1)); - - interface_partition_externals_[0] = bus_i_; - interface_partition_externals_[1] = bus_j_; - - return 0; - } - - /** - * Initialization - */ - template - int ComponentPartitionInterface::initialize() - { - return 0; - } - - /* - * - */ - template - int ComponentPartitionInterface::tagDifferentiable() - { - return 0; - } - - /** - * @brief - */ - template - int ComponentPartitionInterface::evaluateResidual() - { - - auto& y = component_->y(); - auto& yp = component_->yp(); - - std::copy(y_.begin(), y_.end(), y.begin()); - std::copy(yp_.begin(), yp_.end(), yp.begin()); - - y[bus_port_i_] = external_data_y_[0]; - y[bus_port_j_] = external_data_y_[1]; - - yp[bus_port_i_] = external_data_yp_[0]; - yp[bus_port_j_] = external_data_yp_[1]; - - component_->evaluateResidual(); - auto& f = component_->getResidual(); - - std::copy(f.begin(), f.end(), f_.begin()); - - return 0; - } - - /** - * @brief Generate Jacobian for Micro Load - * - * @tparam ScalarT - * @tparam IdxT - * @return int - */ - template - int ComponentPartitionInterface::evaluateJacobian() - { - jac_.zeroMatrix(); - - return 0; - } - - template - int ComponentPartitionInterface::evaluateIntegrand() - { - return 0; - } - - template - int ComponentPartitionInterface::initializeAdjoint() - { - return 0; - } - - template - int ComponentPartitionInterface::evaluateAdjointResidual() - { - return 0; - } - - template - int ComponentPartitionInterface::evaluateAdjointIntegrand() - { - return 0; - } - - // Available template instantiations - template class ComponentPartitionInterface; - template class ComponentPartitionInterface; - template class ComponentPartitionInterface; - template class ComponentPartitionInterface; - -} // namespace GridKit diff --git a/GridKit/Model/PowerElectronics/PartitionInterface/ComponentPartitionInterface.hpp b/GridKit/Model/PowerElectronics/PartitionInterface/ComponentPartitionInterface.hpp deleted file mode 100644 index a46231f6d..000000000 --- a/GridKit/Model/PowerElectronics/PartitionInterface/ComponentPartitionInterface.hpp +++ /dev/null @@ -1,74 +0,0 @@ -#pragma once - -#include - -#include - -#include "GridKit/Model/PowerElectronics/PartitionInterface/PartitionInterface.hpp" - -namespace GridKit -{ - template - class BaseBus; -} - -namespace GridKit -{ - /*! - * @brief Declaration of a passive ComponentPartitionInterface class. - * - */ - template - class ComponentPartitionInterface : public PartitionInterface - { - using RealT = typename CircuitComponent::RealT; - using MatrixT = typename CircuitComponent::MatrixT; - - using PartitionInterface::external_data_y_; - using PartitionInterface::external_data_yp_; - using PartitionInterface::interface_partition_externals_; - using PartitionInterface::bus_port_i_; - using PartitionInterface::bus_port_j_; - using PartitionInterface::bus_i_; - using PartitionInterface::bus_j_; - - using CircuitComponent::size_; - using CircuitComponent::nnz_; - using CircuitComponent::time_; - using CircuitComponent::alpha_; - using CircuitComponent::y_; - using CircuitComponent::yp_; - using CircuitComponent::tag_; - using CircuitComponent::f_; - using CircuitComponent::g_; - using CircuitComponent::yB_; - using CircuitComponent::ypB_; - using CircuitComponent::fB_; - using CircuitComponent::gB_; - using CircuitComponent::jac_; - using CircuitComponent::param_; - using CircuitComponent::idc_; - using CircuitComponent::extern_indices_; - using CircuitComponent::n_extern_; - using CircuitComponent::n_intern_; - - public: - ComponentPartitionInterface(CircuitComponent* component, IdxT bus_i, IdxT bus_j, IdxT id); - virtual ~ComponentPartitionInterface(); - - int allocate(); - int initialize(); - int tagDifferentiable(); - int evaluateResidual(); - int evaluateJacobian(); - int evaluateIntegrand(); - - int initializeAdjoint(); - int evaluateAdjointResidual(); - // int evaluateAdjointJacobian(); - int evaluateAdjointIntegrand(); - - private: - CircuitComponent* component_; - }; -} // namespace GridKit diff --git a/GridKit/Model/PowerElectronics/SubsystemModel.hpp b/GridKit/Model/PowerElectronics/SubsystemModel.hpp index 0ec483cfe..c8aceb2cf 100644 --- a/GridKit/Model/PowerElectronics/SubsystemModel.hpp +++ b/GridKit/Model/PowerElectronics/SubsystemModel.hpp @@ -235,7 +235,6 @@ namespace GridKit } } } - return 0; } @@ -332,7 +331,6 @@ namespace GridKit */ void printJacobianMatrixMarket(std::string filename, std::string title) { - // jac_.printMatrixMarket(filename, title); } CsrMatrixT* getCsrJacobian() const override diff --git a/examples/PowerElectronics/Partition/PartitionScaleMicrogrid.cpp b/examples/PowerElectronics/Partition/PartitionScaleMicrogrid.cpp index 57f2448bd..f0c0cdf8d 100644 --- a/examples/PowerElectronics/Partition/PartitionScaleMicrogrid.cpp +++ b/examples/PowerElectronics/Partition/PartitionScaleMicrogrid.cpp @@ -1,5 +1,6 @@ #include +#include #include #include #include @@ -47,7 +48,16 @@ using index_type = size_t; using real_type = double; -int printMicrogridSystems(index_type N_size); +struct RunResult +{ + index_type num_partitions; + real_type partition_time; // seconds + real_type monolithic_time; // seconds + real_type speedup; // monolithic / partition + real_type max_error; +}; + +RunResult printMicrogridSystems(index_type N_size, index_type num_partitions); /** * @brief Run Scale Microgrid for a specific N given by the user. @@ -58,24 +68,33 @@ int printMicrogridSystems(index_type N_size); */ int main(int argc, char const* argv[]) { - // Number of IBRs - index_type N_size = 2; + index_type N_size = 5000; + + std::cout << std::format("{:<16}{:>16}{:>18}{:>12}{:>14}\n", + "num_partitions", + "partition_time", + "monolithic_time", + "speedup", + "error"); + + std::cout << std::string(76, '-') << "\n"; - // Parse command line arguments if provided - if (argc > 1) + for (index_type p : {10, 100, 1000, 2000}) { - try - { - N_size = static_cast(std::stoi(argv[1])); - } - catch (const std::exception& e) - { - std::cerr << "Error parsing grid size argument: " << e.what() << std::endl; - std::cerr << "Using default grid size = " << N_size << std::endl; - } + if ((2 * N_size) % p != 0) // assert(num_ibrs % num_partitions == 0) + continue; + + RunResult r = printMicrogridSystems(N_size, p); + + std::cout << std::format("{:<16d}{:>14.3f} s{:>16.3f} s{:>11.2f}x{:>14.3e}\n", + r.num_partitions, + r.partition_time, + r.monolithic_time, + r.speedup, + r.max_error); } - return printMicrogridSystems(N_size); + return 0; } /** @@ -84,12 +103,11 @@ int main(int argc, char const* argv[]) * @param[in] N_size - The number of DG line load cobinations to generate for scale * @return int returns 0 if successful, >0 otherwise */ -int printMicrogridSystems(index_type N_size) +RunResult printMicrogridSystems(index_type N_size, index_type num_partitions) { using namespace GridKit; - const size_t num_ibrs = 2 * N_size; - const size_t num_partitions = 4; + const index_type num_ibrs = 2 * N_size; assert(num_partitions <= num_ibrs); @@ -106,11 +124,7 @@ int printMicrogridSystems(index_type N_size) max_steps); // Ensure minimum size requirement - if (N_size < 1) - { - std::cout << "N_size must be at least 1.\n"; - return 1; - } + assert(N_size >= 1); // Modeled after the problem in the paper // Every Bus has the same virtual resistance. This is due to numerical stability as mentioned in the paper. @@ -206,12 +220,12 @@ int printMicrogridSystems(index_type N_size) std::vector linesCopies; SignalNode dg_signal; - sys_model_control->addNode(&dg_signal); + // sys_model_control->addNode(&dg_signal); for (size_t i = 0; i < 2 * N_size; i++) { buses[i] = new Bus(); - sys_model_control->addNode(buses[i]); + // sys_model_control->addNode(buses[i]); } // Create the reference DG @@ -220,7 +234,7 @@ int printMicrogridSystems(index_type N_size) true, &dg_signal, buses[0]); - sys_model_control->addComponent(dg_ref); + // sys_model_control->addComponent(dg_ref); generators[0] = dg_ref; @@ -237,7 +251,7 @@ int printMicrogridSystems(index_type N_size) buses[i]); generators[i] = dg; - sys_model_control->addComponent(dg); + // sys_model_control->addComponent(dg); } // Load all the Line components @@ -251,7 +265,7 @@ int printMicrogridSystems(index_type N_size) buses[i], buses[i + 1]); lines[i + 1] = line_model; - sys_model_control->addComponent(line_model); + // sys_model_control->addComponent(line_model); } // Load all the Load components @@ -263,7 +277,7 @@ int printMicrogridSystems(index_type N_size) &dg_signal, buses[2 * i]); loads[2 * i] = load_model; - sys_model_control->addComponent(load_model); + // sys_model_control->addComponent(load_model); } // Add all the microgrid Virtual DQ Buses @@ -272,26 +286,26 @@ int printMicrogridSystems(index_type N_size) auto* virDQbus_model = new MicrogridBusDQ(model_id++, RN, buses[i]); busesDQ[i] = virDQbus_model; - sys_model_control->addComponent(virDQbus_model); + // sys_model_control->addComponent(virDQbus_model); } - // sys_model_control->addNode(&dg_signal); - // for (index_type i = 0; i < num_ibrs; ++i) - // { - // sys_model_control->addComponent(generators[i]); - // sys_model_control->addComponent(busesDQ[i]); - - // if (loads[i] != nullptr) - // { - // sys_model_control->addComponent(loads[i]); - // } - - // if (lines[i] != nullptr) - // { - // sys_model_control->addComponent(lines[i]); - // } - // sys_model_control->addNode(buses[i]); - // } + sys_model_control->addNode(&dg_signal); + for (index_type i = 0; i < num_ibrs; ++i) + { + sys_model_control->addComponent(generators[i]); + sys_model_control->addComponent(busesDQ[i]); + + if (loads[i] != nullptr) + { + sys_model_control->addComponent(loads[i]); + } + + if (lines[i] != nullptr) + { + sys_model_control->addComponent(lines[i]); + } + sys_model_control->addNode(buses[i]); + } sys_model_control->allocate(); @@ -300,11 +314,12 @@ int printMicrogridSystems(index_type N_size) for (size_t i = 0; i < sys_model_control->size(); i++) { - y.push_back(static_cast(i + 1 + ((277 * i) % 100))); - yp.push_back(static_cast(i + 1 + ((288 * i) % 100))); + y.push_back(static_cast(i + 1)); + yp.push_back(static_cast(i + 1)); } - auto start = std::chrono::high_resolution_clock::now(); + auto start_time = std::chrono::high_resolution_clock::now(); + for (size_t i = 0; i < sys_model_control->size(); i++) { sys_model_control->y()[i] = y[i]; @@ -313,12 +328,20 @@ int printMicrogridSystems(index_type N_size) sys_model_control->evaluateResidual(); - auto end = std::chrono::high_resolution_clock::now(); - auto elapsed = std::chrono::duration(end - start); - std::cout << "Monolithic Evaluation Time: " << elapsed.count() << " s\n"; + auto end_time = std::chrono::high_resolution_clock::now(); + auto monolithic_time = std::chrono::duration(end_time - start_time); auto& f_sysmodel_control = sys_model_control->getResidual(); + //--------------------------------------------------------------- + // Partition the monolithic network into independent subsystem + // models. Each subsystem owns a contiguous block of generators, + // buses, lines, and loads. Whenever a partition boundary is + // encountered, a BusPartitionInterface is inserted to expose the + // neighboring partition's boundary variables while maintaining + // the same electrical connectivity as the original network. + //--------------------------------------------------------------- + std::vector*> subsystems(num_partitions); assert(num_ibrs % num_partitions == 0); @@ -360,6 +383,7 @@ int printMicrogridSystems(index_type N_size) auto* linecopy = new GridKit::MicrogridLine(*lines[index]); auto* busInterface = new GridKit::BusPartitionInterface(*buses[index - 1], *linecopy, model_id++); + busInterface->allocate(); partitionInterface.push_back(busInterface); linesCopies.push_back(linecopy); @@ -369,20 +393,25 @@ int printMicrogridSystems(index_type N_size) subsystems[j] = partition; } + //--------------------------------------------------------------- + // Allocate each subsystem and evaluate the partitioned residual. + // + // The global state vectors (y and yp) are scattered into the + // corresponding internal and external vectors of every subsystem. + // After evaluating the residuals independently, the local + // residuals are gathered back into the global residual vector for + // comparison with the monolithic evaluation. + //--------------------------------------------------------------- + std::vector f(sys_model_control->size(), 1.0); std::vector error(sys_model_control->size(), 1.0); - for (auto* partinterface : partitionInterface) - { - partinterface->allocate(); - } - for (auto* partition : subsystems) { partition->allocate(); } - start = std::chrono::high_resolution_clock::now(); + start_time = std::chrono::high_resolution_clock::now(); auto sys_max_threads = omp_get_max_threads(); // Distribute externals to partition 1 @@ -406,81 +435,51 @@ int printMicrogridSystems(index_type N_size) // Evaluate residual of this partition partition->evaluateResidual(); - // Reconstruct the monolithic residual from the partition residuals + // Reconstructs the monolithic residual from the partition residuals for (size_t i = 0; i < partition->getInternalSize(); i++) { f[partition->getNodeConnection(i)] = partition->getResidual()[i]; } } - end = std::chrono::high_resolution_clock::now(); - elapsed = std::chrono::duration(end - start); - std::cout << "Partition Evaluation Time: " << elapsed.count() << " s\n"; + end_time = std::chrono::high_resolution_clock::now(); + auto partition_time = std::chrono::duration(end_time - start_time); - for (size_t i = 0; i < sys_model_control->size(); i++) - { - error[i] = abs(f[i] - f_sysmodel_control[i]) / (f_sysmodel_control[i] + 1); - - std::cout << std::format("{:<8d} {:>15.15e} ---------- {:>15.15e} {:>15.5e}\n", - i, - error[i], - f[i], - f_sysmodel_control[i]); - } + //--------------------------------------------------------------- + // Compare the reconstructed partition residual against the + // reference monolithic residual. The per-entry error is computed + // and the maximum error is reported to verify that the partitioned + // formulation reproduces the monolithic model within numerical + // roundoff. + //--------------------------------------------------------------- real_type max_error = 0; for (size_t i = 0; i < sys_model_control->size(); i++) { - if (max_error < std::abs(error[i])) + error[i] = abs(f[i] - f_sysmodel_control[i]) / (f_sysmodel_control[i] + 1); + + if (max_error < error[i]) { - max_error = std::abs(error[i]); + max_error = error[i]; } } - std::cout << "\nMax Rel. Error between Reference and Partition Evaluation: " << max_error - << std::endl - << std::endl; - - // real_type max_abs_error = 0.0; - // real_type max_rel_error = 0.0; - // size_t max_idx = 0; - - // for (size_t i = 0; i < sys_model_control->size(); ++i) - // { - // real_type abs_err = std::abs(f[i] - f_sysmodel_control[i]); - - // real_type scale = std::max({real_type(1.0), - // std::abs(f[i]), - // std::abs(f_sysmodel_control[i])}); - - // real_type rel_err = abs_err / scale; - - // if (rel_err > max_rel_error) - // { - // max_rel_error = rel_err; - // max_abs_error = abs_err; - // max_idx = i; - // } - // } - - // std::cout << "Max index : " << max_idx << '\n'; - // std::cout << "Partition f : " << std::format("{:.17e}", f[max_idx]) << '\n'; - // std::cout << "Monolithic f : " << std::format("{:.17e}", f_sysmodel_control[max_idx]) << '\n'; - // std::cout << "Abs error : " << std::format("{:.17e}", max_abs_error) << '\n'; - // std::cout << "Rel error : " << std::format("{:.17e}", max_rel_error) << '\n'; + RunResult result; + result.num_partitions = num_partitions; + result.partition_time = partition_time.count(); + result.monolithic_time = monolithic_time.count(); + result.speedup = monolithic_time.count() / partition_time.count(); + result.max_error = max_error; for (auto* linescpy : linesCopies) - { delete linescpy; - } - for (auto* partition : subsystems) - { delete partition; - } + for (auto* part_interface : partitionInterface) + delete part_interface; delete sys_model_control; - return 0; + return result; } // Partition the system From 1e99fc3dfb063ae20df469c1c91a5a68d72d39c1 Mon Sep 17 00:00:00 2001 From: abdourahmanbarry Date: Sat, 27 Jun 2026 00:28:53 +0000 Subject: [PATCH 16/52] Partition Scale Microgrid configured with 10000IBRs --- .../Partition/PartitionScaleMicrogrid.cpp | 90 ++++--------------- 1 file changed, 19 insertions(+), 71 deletions(-) diff --git a/examples/PowerElectronics/Partition/PartitionScaleMicrogrid.cpp b/examples/PowerElectronics/Partition/PartitionScaleMicrogrid.cpp index f0c0cdf8d..c55447fad 100644 --- a/examples/PowerElectronics/Partition/PartitionScaleMicrogrid.cpp +++ b/examples/PowerElectronics/Partition/PartitionScaleMicrogrid.cpp @@ -4,10 +4,7 @@ #include #include #include -#include #include -#include -#include #include #include #include @@ -66,7 +63,7 @@ RunResult printMicrogridSystems(index_type N_size, index_type num_partitions); * @param[in] argv can contain the N value (argv[1]) * @return int */ -int main(int argc, char const* argv[]) +int main() { index_type N_size = 5000; @@ -79,14 +76,13 @@ int main(int argc, char const* argv[]) std::cout << std::string(76, '-') << "\n"; - for (index_type p : {10, 100, 1000, 2000}) + for (index_type p : {10, 48, 100, 500, 1000, 2000, 5000}) { - if ((2 * N_size) % p != 0) // assert(num_ibrs % num_partitions == 0) - continue; + assert(p <= 2 * N_size); RunResult r = printMicrogridSystems(N_size, p); - std::cout << std::format("{:<16d}{:>14.3f} s{:>16.3f} s{:>11.2f}x{:>14.3e}\n", + std::cout << std::format("{:<16d}{:>14.4f} s{:>16.4f} s{:>11.2f}x{:>14.3e}\n", r.num_partitions, r.partition_time, r.monolithic_time, @@ -344,20 +340,25 @@ RunResult printMicrogridSystems(index_type N_size, index_type num_partitions) std::vector*> subsystems(num_partitions); - assert(num_ibrs % num_partitions == 0); - index_type partition_size = num_ibrs / num_partitions; - index_type index = 0; - - for (index_type j = 0; j < num_partitions; ++j) + // Partition the system + index_type q = (num_ibrs) / num_partitions; + index_type r = (num_ibrs) % num_partitions; + index_type index = 0; + for (index_type j = 0; j < num_partitions; j++) { - auto* partition = new SubsystemModel(); + auto* partition = new SubsystemModel(false); + + // add Reference rotor to the first partition if (j == 0) { partition->addNode(&dg_signal); } - // Add components to belong to the same - for (index_type i = 0; i < partition_size; ++i) + index_type part_size = q + (j < r ? 1 : 0); + index_type end = std::min(index + part_size, num_ibrs); + + // Add all components belonging to this partition + for (; index < end; ++index) { partition->addComponent(generators[index]); partition->addComponent(busesDQ[index]); @@ -373,10 +374,7 @@ RunResult printMicrogridSystems(index_type N_size, index_type num_partitions) } partition->addNode(buses[index]); - - index++; } - // Add the partition interface to the left partition if (index < num_ibrs) { @@ -413,9 +411,8 @@ RunResult printMicrogridSystems(index_type N_size, index_type num_partitions) start_time = std::chrono::high_resolution_clock::now(); - auto sys_max_threads = omp_get_max_threads(); // Distribute externals to partition 1 -#pragma omp parallel for schedule(guided) num_threads(sys_max_threads) +#pragma omp parallel for schedule(guided) for (auto* partition : subsystems) { // Distribute external variables @@ -480,53 +477,4 @@ RunResult printMicrogridSystems(index_type N_size, index_type num_partitions) delete sys_model_control; return result; -} - -// Partition the system -// index_type q = (num_ibrs) / num_partitions; -// index_type r = (num_ibrs) % num_partitions; -// index_type index = 0; -// for (index_type j = 0; j < num_partitions; j++) -// { -// auto* partition = new SubsystemModel(false); - -// // add Reference rotor to the first partition -// if (j == 0) -// { -// partition->addNode(&dg_signal); -// } - -// index_type part_size = q + (j < r ? 1 : 0); -// index_type end = std::min(index + part_size, num_ibrs); - -// // Add all components belonging to this partition -// for (; index < end; ++index) -// { -// partition->addComponent(generators[index]); -// partition->addComponent(busesDQ[index]); - -// if (loads[index] != nullptr) -// { -// partition->addComponent(loads[index]); -// } - -// if (lines[index] != nullptr) -// { -// partition->addComponent(lines[index]); -// } - -// partition->addNode(buses[index]); -// } - -// // Add partition interface at a partition point -// if (index < num_ibrs) -// { -// auto* linecopy = new GridKit::MicrogridLine(*lines[index]); - -// auto* busInterface = new GridKit::BusPartitionInterface(*buses[index - 1], *linecopy, model_id++); -// busInterface->allocate(); -// partition->addComponent(busInterface); -// } - -// subsystems[j] = partition; -// } +} \ No newline at end of file From 5437b8960bd6feefc0b620a160106e055107b6af Mon Sep 17 00:00:00 2001 From: abdourahmanbarry Date: Mon, 6 Jul 2026 19:49:41 -0400 Subject: [PATCH 17/52] Added Jacobian contributions for bus interfaces --- .../BusPartitionInterface.cpp | 89 ++++++++++++++----- .../BusPartitionInterface.hpp | 9 +- .../PowerElectronics/Partition/CMakeLists.txt | 8 +- 3 files changed, 79 insertions(+), 27 deletions(-) diff --git a/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.cpp b/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.cpp index 0c4de184c..ebab170a8 100644 --- a/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.cpp +++ b/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.cpp @@ -39,6 +39,28 @@ namespace GridKit { extern_indices_.insert(i); } + + + const IdxT* cooRow = component_->jacobianCooRows(); + const IdxT* cooCol = component_->jacobianCooCols(); + + const IdxT bus_i = bus.getNodeConnection(0); + const IdxT bus_j = bus.getNodeConnection(1); + + for (IdxT k = 0; k < component_->nnz(); ++k) + { + const IdxT row_node = component.getNodeConnection(cooRow[k]); + const IdxT col_node = component.getNodeConnection(cooCol[k]); + + const bool row_is_bus = (row_node == bus_i || row_node == bus_j); + const bool col_is_bus = (col_node == bus_i || col_node == bus_j); + + if (row_is_bus && col_is_bus) + { + ++nnz_; + jac_map_.push_back(i); + } + } } template @@ -89,13 +111,16 @@ namespace GridKit assert(false); } - y_ptr = new ScalarT[component_.getInternalSize()]; - yp_ptr = new ScalarT[component_.getInternalSize()]; - f_ptr = new ScalarT[component_.getInternalSize()]; + + const auto n = component_.getInternalSize(); - component_.setInternalPointer(y_ptr); - component_.setInternalDerivativePointer(yp_ptr); - component_.setInternalResidualPointer(f_ptr); + y_ptr = std::make_unique(n); + yp_ptr = std::make_unique(n); + f_ptr = std::make_unique(n); + + component_.setInternalPointer(y_ptr.get()); + component_.setInternalDerivativePointer(yp_ptr.get()); + component_.setInternalResidualPointer(f_ptr.get()); return 0; } @@ -134,24 +159,26 @@ namespace GridKit int BusPartitionInterface::evaluateExternalResidual() { - size_t counter_int = 0; - size_t counter_ext = 0; - auto extern_indices = component_.getExternIndices(); + size_t internal = 0; + size_t external = 0; + + const auto& extern_indices = component_.getExternIndices(); - for (size_t i = 0; i < static_cast(component_.size()); i++) + for (IdxT i = 0; i < component_.size(); ++i) { - if (extern_indices.contains(static_cast(i))) - { - component_.y()[counter_ext] = y_[i]; - component_.yp()[counter_ext] = yp_[i]; - counter_ext++; - } - else + const bool is_external = extern_indices.contains(i); + + if (is_external) { - y_ptr[counter_int] = y_[i]; - yp_ptr[counter_int] = yp_[i]; - counter_int++; + component_.y()[external] = y_[i]; + component_.yp()[external] = yp_[i]; + ++external; + continue; } + + y_ptr[internal] = y_[i]; + yp_ptr[internal] = yp_[i]; + ++internal; } component_.evaluateExternalResidual(); @@ -175,6 +202,28 @@ namespace GridKit template int BusPartitionInterface::evaluateJacobian() { + this->zeroJacMatrix(); + + component_->evaluateJacobian(); + + const IdxT* cooRow = component_->jacobianCooRows(); + const IdxT* cooCol = component_->jacobianCooCols(); + const RealT* cooVals = component_->jacobianCooValues(); + + std::vector r = {}; + std::vector c = {}; + std::vector v = {}; + + + for(const auto& index: jac_map_) + { + r.push_back(cooRow[index]); + c.push_back(cooCol[index]); + v.push_back(cooVals[index]); + } + + this->setJacValues(r, c, v); + return 0; } diff --git a/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.hpp b/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.hpp index 2c08400b5..3f9bc1a64 100644 --- a/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.hpp +++ b/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.hpp @@ -68,8 +68,11 @@ namespace GridKit private: component_type& component_; node_type& bus_; - ScalarT* y_ptr; - ScalarT* yp_ptr; - ScalarT* f_ptr; + + std::unique_ptr y_ptr; + std::unique_ptr yp_ptr; + std::unique_ptr f_ptr; + + std::vector jac_map_; }; } // namespace GridKit diff --git a/examples/PowerElectronics/Partition/CMakeLists.txt b/examples/PowerElectronics/Partition/CMakeLists.txt index 00270b746..6876c4754 100644 --- a/examples/PowerElectronics/Partition/CMakeLists.txt +++ b/examples/PowerElectronics/Partition/CMakeLists.txt @@ -1,9 +1,9 @@ find_package(OpenMP REQUIRED) -add_executable(partition Partition.cpp) -target_link_libraries(partition GridKit::solvers_dyn - GridKit::power_elec_partition_interfaces) +#add_executable(partition Partition.cpp) +#target_link_libraries(partition GridKit::solvers_dyn +# GridKit::power_elec_partition_interfaces) add_executable(partitionMicrogrid PartitionMicrogrid.cpp) @@ -22,4 +22,4 @@ target_link_libraries(PartitionScaleMicrogrid GridKit::power_elec_disgen GridKit::solvers_dyn GridKit::power_elec_microbusdq GridKit::power_elec_partition_interfaces - OpenMP::OpenMP_CXX) \ No newline at end of file + OpenMP::OpenMP_CXX) From 5506d2b298c4542a5ddba05d29ccacce0af91f22 Mon Sep 17 00:00:00 2001 From: abdourahmanbarry Date: Sat, 27 Jun 2026 04:23:37 +0000 Subject: [PATCH 18/52] Benchmark Setup for Parallel Function Evaluation --- .../Partition/PartitionScaleMicrogrid.cpp | 49 ++++++++++--------- 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/examples/PowerElectronics/Partition/PartitionScaleMicrogrid.cpp b/examples/PowerElectronics/Partition/PartitionScaleMicrogrid.cpp index c55447fad..6d1a37666 100644 --- a/examples/PowerElectronics/Partition/PartitionScaleMicrogrid.cpp +++ b/examples/PowerElectronics/Partition/PartitionScaleMicrogrid.cpp @@ -4,10 +4,11 @@ #include #include #include +<<<<<<< HEAD #include + ======= +>>>>>>> 5723ffb9 (Benchmark Setup for Parallel Function Evaluation) #include -#include -#include #include #include @@ -22,28 +23,28 @@ #include #include -/****************************************************************************** - * Partitioned Residual Evaluation - * - * Construct subsystem models by partitioning the original microgrid into - * independent groups of components. Each subsystem is allocated and receives - * the appropriate subset of the global state vectors (y and yp), consisting - * of: - * - * - External variables (coupling variables from neighboring partitions) - * - Internal variables (states owned by the partition) - * - * After distributing the state information, each partition independently - * evaluates its residual. The local residuals are then scattered back into - * the global residual vector to reconstruct the monolithic residual. - * - * Finally, the reconstructed residual is compared against the reference - * monolithic evaluation to verify the correctness of the partitioning - * implementation. - ******************************************************************************/ - -using index_type = size_t; -using real_type = double; + /****************************************************************************** + * Partitioned Residual Evaluation + * + * Construct subsystem models by partitioning the original microgrid into + * independent groups of components. Each subsystem is allocated and receives + * the appropriate subset of the global state vectors (y and yp), consisting + * of: + * + * - External variables (coupling variables from neighboring partitions) + * - Internal variables (states owned by the partition) + * + * After distributing the state information, each partition independently + * evaluates its residual. The local residuals are then scattered back into + * the global residual vector to reconstruct the monolithic residual. + * + * Finally, the reconstructed residual is compared against the reference + * monolithic evaluation to verify the correctness of the partitioning + * implementation. + ******************************************************************************/ + + using index_type = size_t; +using real_type = double; struct RunResult { From 288acabee9f0dde9618d778eb0a72d9cbe60db66 Mon Sep 17 00:00:00 2001 From: abdourahmanbarry Date: Tue, 7 Jul 2026 02:31:38 +0000 Subject: [PATCH 19/52] Minor updates --- .../BusPartitionInterface.cpp | 24 +++++----- .../BusPartitionInterface.hpp | 1 + .../Partition/PartitionScaleMicrogrid.cpp | 48 +++++++++---------- 3 files changed, 35 insertions(+), 38 deletions(-) diff --git a/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.cpp b/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.cpp index ebab170a8..0ede5de02 100644 --- a/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.cpp +++ b/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.cpp @@ -41,16 +41,16 @@ namespace GridKit } - const IdxT* cooRow = component_->jacobianCooRows(); - const IdxT* cooCol = component_->jacobianCooCols(); + const IdxT* cooRow = component_.jacobianCooRows(); + const IdxT* cooCol = component_.jacobianCooCols(); const IdxT bus_i = bus.getNodeConnection(0); const IdxT bus_j = bus.getNodeConnection(1); - for (IdxT k = 0; k < component_->nnz(); ++k) + for (IdxT k = 0; k < component_.nnz(); ++k) { - const IdxT row_node = component.getNodeConnection(cooRow[k]); - const IdxT col_node = component.getNodeConnection(cooCol[k]); + const IdxT row_node = component_.getNodeConnection(cooRow[k]); + const IdxT col_node = component_.getNodeConnection(cooCol[k]); const bool row_is_bus = (row_node == bus_i || row_node == bus_j); const bool col_is_bus = (col_node == bus_i || col_node == bus_j); @@ -58,7 +58,7 @@ namespace GridKit if (row_is_bus && col_is_bus) { ++nnz_; - jac_map_.push_back(i); + jac_map_.push_back(k); } } } @@ -204,11 +204,11 @@ namespace GridKit { this->zeroJacMatrix(); - component_->evaluateJacobian(); + component_.evaluateJacobian(); - const IdxT* cooRow = component_->jacobianCooRows(); - const IdxT* cooCol = component_->jacobianCooCols(); - const RealT* cooVals = component_->jacobianCooValues(); + const IdxT* cooRows = component_.jacobianCooRows(); + const IdxT* cooCols = component_.jacobianCooCols(); + const RealT* cooVals = component_.jacobianCooValues(); std::vector r = {}; std::vector c = {}; @@ -217,8 +217,8 @@ namespace GridKit for(const auto& index: jac_map_) { - r.push_back(cooRow[index]); - c.push_back(cooCol[index]); + r.push_back(cooRows[index]); + c.push_back(cooCols[index]); v.push_back(cooVals[index]); } diff --git a/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.hpp b/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.hpp index 3f9bc1a64..1bda78a89 100644 --- a/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.hpp +++ b/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.hpp @@ -21,6 +21,7 @@ namespace GridKit using component_type = CircuitComponent; using node_type = typename PowerElectronics::NodeBase; + using RealT = typename CircuitComponent::RealT; using CircuitComponent::size_; using CircuitComponent::nnz_; diff --git a/examples/PowerElectronics/Partition/PartitionScaleMicrogrid.cpp b/examples/PowerElectronics/Partition/PartitionScaleMicrogrid.cpp index 6d1a37666..4e59848c8 100644 --- a/examples/PowerElectronics/Partition/PartitionScaleMicrogrid.cpp +++ b/examples/PowerElectronics/Partition/PartitionScaleMicrogrid.cpp @@ -4,10 +4,6 @@ #include #include #include -<<<<<<< HEAD -#include - ======= ->>>>>>> 5723ffb9 (Benchmark Setup for Parallel Function Evaluation) #include #include @@ -23,28 +19,28 @@ #include #include - /****************************************************************************** - * Partitioned Residual Evaluation - * - * Construct subsystem models by partitioning the original microgrid into - * independent groups of components. Each subsystem is allocated and receives - * the appropriate subset of the global state vectors (y and yp), consisting - * of: - * - * - External variables (coupling variables from neighboring partitions) - * - Internal variables (states owned by the partition) - * - * After distributing the state information, each partition independently - * evaluates its residual. The local residuals are then scattered back into - * the global residual vector to reconstruct the monolithic residual. - * - * Finally, the reconstructed residual is compared against the reference - * monolithic evaluation to verify the correctness of the partitioning - * implementation. - ******************************************************************************/ - - using index_type = size_t; -using real_type = double; +/****************************************************************************** + * Partitioned Residual Evaluation + * + * Construct subsystem models by partitioning the original microgrid into + * independent groups of components. Each subsystem is allocated and receives + * the appropriate subset of the global state vectors (y and yp), consisting + * of: + * + * - External variables (coupling variables from neighboring partitions) + * - Internal variables (states owned by the partition) + * + * After distributing the state information, each partition independently + * evaluates its residual. The local residuals are then scattered back into + * the global residual vector to reconstruct the monolithic residual. + * + * Finally, the reconstructed residual is compared against the reference + * monolithic evaluation to verify the correctness of the partitioning + * implementation. + ******************************************************************************/ + +using index_type = size_t; +using real_type = double; struct RunResult { From 1e6a67e782d13c11a9174a9776f87682cbe6f6a2 Mon Sep 17 00:00:00 2001 From: abdourahmanbarry Date: Mon, 6 Jul 2026 23:16:43 -0400 Subject: [PATCH 20/52] Added subsystem Jacobian --- .../Model/PowerElectronics/SubsystemModel.hpp | 166 ++++++++++++++---- 1 file changed, 133 insertions(+), 33 deletions(-) diff --git a/GridKit/Model/PowerElectronics/SubsystemModel.hpp b/GridKit/Model/PowerElectronics/SubsystemModel.hpp index c8aceb2cf..ea6ee31fb 100644 --- a/GridKit/Model/PowerElectronics/SubsystemModel.hpp +++ b/GridKit/Model/PowerElectronics/SubsystemModel.hpp @@ -112,7 +112,7 @@ namespace GridKit external_indices_[local_idx - n_intern_] = global_idx; } - { + size_t component_internal_idx = 0; for (component_type* comp : components_) { @@ -121,16 +121,96 @@ namespace GridKit comp->setInternalDerivativePointer(&yp_[component_internal_idx]); comp->setInternalResidualPointer(&f_[component_internal_idx]); - const auto& external_indices = comp->getExternIndices(); - for (size_t i = 0; i < comp->size(); i++) - { - if (!external_indices.contains(i)) - { - component_internal_idx++; - } - } + component_internal_idx += comp->getInternalSize(); } - } + + + // // Evaluate component Jacobians to get sparsity + // distributeVectors(); + // for (component_type* component : components_) + // { + // component->evaluateJacobian(); + // } + + // // Count the number of non-zeros + // IdxT nnz_dup = 0; + // for (const component_type* component : components_) + // { + // const IdxT* r = component->jacobianCooRows(); + // const IdxT* c = component->jacobianCooCols(); + // IdxT nnz = component->nnz(); + + // for (IdxT i = 0; i < nnz; ++i) + // { + // if(component->getNodeConnection(r[i])>= this->getInternalSize() && component->getNodeConnection(c[i])>= this->getInternalSize()) + // { + // continue; + // } + // if (component->getNodeConnection(r[i]) != neg1_ && component->getNodeConnection(c[i]) != neg1_) + // { + // ++nnz_dup; + // } + // } + // } + + // // Allocate COO triplet arrays (we own these until we hand off to CsrMatrix) + // IdxT* rows_dup = new IdxT[nnz_dup]; + // IdxT* cols_dup = new IdxT[nnz_dup]; + // RealT* vals_dup = new RealT[nnz_dup]; + + // IdxT counter = 0; + // for (const auto& component : components_) + // { + // const IdxT* r = component->jacobianCooRows(); + // const IdxT* c = component->jacobianCooCols(); + // const RealT* v = component->jacobianCooValues(); + // IdxT nnz = component->nnz(); + + // for (IdxT i = 0; i < nnz; ++i) + // { + // if(component->getNodeConnection(r[i])>= this->getInternalSize() && component->getNodeConnection(c[i])>= this->getInternalSize()) + // { + // continue; + // } + + // if (component->getNodeConnection(r[i]) != neg1_ && component->getNodeConnection(c[i]) != neg1_) + // { + // rows_dup[counter] = component->getNodeConnection(r[i]); + // cols_dup[counter] = component->getNodeConnection(c[i]); + // vals_dup[counter] = v[i]; + // counter++; + // } + // } + // } + + // // Build the system COO Jacobian + // LinearAlgebra::CooMatrix jac(size_, size_, nnz_dup, &rows_dup, &cols_dup, &vals_dup); + + // // Populate CSR data with sort and deduplicate + // IdxT* row_ptrs = jac.getCsrRowData(); + + // // Deduplicated nnz + // nnz_ = jac.getNnz(); + + // // Allocate cols/vals with deduplicated nnz + // IdxT* cols = new IdxT[nnz_]; + // RealT* vals = new RealT[nnz_]; + + // std::copy(jac.getColData(), jac.getColData() + nnz_, cols); + // std::copy(jac.getValues(), jac.getValues() + nnz_, vals); + + // // Create the CSR Jacobian + // csr_jac_ = new CsrMatrixT(size_, size_, nnz_, &row_ptrs, &cols, &vals); + + // const IdxT* map_to_sorted = jac.getMapToSorted(); + // const IdxT* map_to_dedup = jac.getMapToDeduplicated(); + + // // Build a mappping from original COO index to CSR index + // map_to_csr_ = new IdxT[nnz_dup]; + // for (IdxT i = 0; i < nnz_dup; ++i) + // { + // map_to_csr_[map_to_sorted[i]] = map_to_dedup[i]; + // } return 0; } @@ -254,6 +334,45 @@ namespace GridKit */ int evaluateJacobian() final { + + distributeVectors(); + + // Zero out values + RealT* vals = csr_jac_->getValues(); + for (IdxT i = 0; i < csr_jac_->getNnz(); ++i) + { + vals[i] = 0.0; + } + + // Update CSR values from component Jacobians + IdxT counter = 0; + for (const auto& component : components_) + { + component->evaluateJacobian(); + + const IdxT* r = component->jacobianCooRows(); + const IdxT* c = component->jacobianCooCols(); + const RealT* v = component->jacobianCooValues(); + IdxT nnz = component->nnz(); + + for (IdxT i = 0; i < nnz; ++i) + { + + if(component->getNodeConnection(r[i]) >= this->getInternalSize() && component->getNodeConnection(c[i]) >= this->getInternalSize()) + { + continue; + } + + if (component->getNodeConnection(r[i]) != neg1_ && component->getNodeConnection(c[i]) != neg1_) + { + vals[map_to_csr_[counter]] += v[i]; + ++counter; + } + } + } + + jac_call_count_++; + return 0; } @@ -343,14 +462,9 @@ namespace GridKit components_.push_back(component); } - std::unordered_map& getInternalMap() - { - return internal_map_; - } - - std::unordered_map& getExternalMap() + void addNode(node_type* node) { - return external_map_; + nodes_.push_back(node); } int mapGlobalToLocal() @@ -402,11 +516,6 @@ namespace GridKit return 0; } - int mapLocalToGlobal() - { - return 0; - } - void createGlobalToInternalMap() { @@ -475,18 +584,9 @@ namespace GridKit return yp_ext_; } - void addNode(node_type* node) - { - nodes_.push_back(node); - } - - void toString() + std::vector& getExternalDataF() { - for (auto* comp : components_) - { - std::cout << ", " << std::to_string(comp->getIDcomponent()); - } - std::cout << std::endl; + return f_ext_; } private: From 67eb9bcb95f39c08f097093b6ed5a0d7e633e3f6 Mon Sep 17 00:00:00 2001 From: abdourahmanbarry Date: Mon, 13 Jul 2026 17:20:30 +0000 Subject: [PATCH 21/52] Updated Hires test problem --- .../PowerElectronics/Partition/CMakeLists.txt | 6 +- .../PowerElectronics/Partition/HiresBus.hpp | 33 ++++++---- .../Partition/HiresComponent1.hpp | 47 +++++++++++---- .../Partition/HiresComponent3.hpp | 60 +++++++++++++++---- 4 files changed, 109 insertions(+), 37 deletions(-) diff --git a/examples/PowerElectronics/Partition/CMakeLists.txt b/examples/PowerElectronics/Partition/CMakeLists.txt index 6876c4754..cdb522167 100644 --- a/examples/PowerElectronics/Partition/CMakeLists.txt +++ b/examples/PowerElectronics/Partition/CMakeLists.txt @@ -1,9 +1,9 @@ find_package(OpenMP REQUIRED) -#add_executable(partition Partition.cpp) -#target_link_libraries(partition GridKit::solvers_dyn -# GridKit::power_elec_partition_interfaces) +add_executable(partition Partition.cpp) +target_link_libraries(partition GridKit::solvers_dyn + GridKit::power_elec_partition_interfaces) add_executable(partitionMicrogrid PartitionMicrogrid.cpp) diff --git a/examples/PowerElectronics/Partition/HiresBus.hpp b/examples/PowerElectronics/Partition/HiresBus.hpp index 585d25ce0..eeb7f6080 100644 --- a/examples/PowerElectronics/Partition/HiresBus.hpp +++ b/examples/PowerElectronics/Partition/HiresBus.hpp @@ -19,15 +19,17 @@ namespace GridKit using CircuitComponent::time_; using CircuitComponent::alpha_; using CircuitComponent::y_; + using CircuitComponent::y_int_; using CircuitComponent::yp_; + using CircuitComponent::yp_int_; using CircuitComponent::tag_; using CircuitComponent::f_; + using CircuitComponent::f_int_; using CircuitComponent::g_; using CircuitComponent::yB_; using CircuitComponent::ypB_; using CircuitComponent::fB_; using CircuitComponent::gB_; - using CircuitComponent::jac_; using CircuitComponent::param_; using CircuitComponent::idc_; @@ -39,10 +41,11 @@ namespace GridKit HiresBus(IdxT id) { size_ = 2; - n_intern_ = 2; - n_extern_ = 0; - extern_indices_ = {}; + n_intern_ = 0; + n_extern_ = 2; + extern_indices_ = {0, 1}; idc_ = id; + nnz_ = 2; } ~HiresBus() @@ -51,9 +54,7 @@ namespace GridKit int allocate() { - y_.resize(static_cast(size_)); - yp_.resize(static_cast(size_)); - f_.resize(static_cast(size_)); + CircuitComponent::allocate(); return 0; } @@ -68,16 +69,28 @@ namespace GridKit return 0; } - int evaluateResidual() + int evaluateInternalResidual() { - f_[0] = yp_[0] + y_[0]; - f_[1] = yp_[1] + y_[1]; + return 0; + } + int evaluateExternalResidual() + { + f_[0] = -yp_[0] - y_[0]; + f_[1] = -yp_[1] - y_[1]; return 0; } int evaluateJacobian() { + this->zeroJacMatrix(); + + std::vector row = {0, 1}; + std::vector col = {0, 1}; + std::vector val = {-alpha_ - 1.0, -alpha_ - 1.0}; + + this->setJacValues(row, col, val); + return 0; } diff --git a/examples/PowerElectronics/Partition/HiresComponent1.hpp b/examples/PowerElectronics/Partition/HiresComponent1.hpp index 1d6c51fc8..d72a7a068 100644 --- a/examples/PowerElectronics/Partition/HiresComponent1.hpp +++ b/examples/PowerElectronics/Partition/HiresComponent1.hpp @@ -21,15 +21,17 @@ namespace GridKit using CircuitComponent::time_; using CircuitComponent::alpha_; using CircuitComponent::y_; + using CircuitComponent::y_int_; using CircuitComponent::yp_; + using CircuitComponent::yp_int_; using CircuitComponent::tag_; using CircuitComponent::f_; + using CircuitComponent::f_int_; using CircuitComponent::g_; using CircuitComponent::yB_; using CircuitComponent::ypB_; using CircuitComponent::fB_; using CircuitComponent::gB_; - using CircuitComponent::jac_; using CircuitComponent::param_; using CircuitComponent::idc_; @@ -45,6 +47,7 @@ namespace GridKit n_extern_ = 2; extern_indices_ = {0, 1}; idc_ = id; + nnz_ = 12; } ~HiresComponent1() @@ -53,9 +56,7 @@ namespace GridKit int allocate() { - y_.resize(static_cast(size_)); - yp_.resize(static_cast(size_)); - f_.resize(static_cast(size_)); + CircuitComponent::allocate(); return 0; } @@ -70,23 +71,45 @@ namespace GridKit return 0; } - int evaluateResidual() + int evaluateInternalResidual() { - // outputs - f_[0] = -8.32 * y_[3] - 1.71 * y_[4] + 0.1 * y_[0]; - f_[1] = +0.7 * y_[1]; - // Internals - f_[2] = yp_[2] + 1.71 * y_[2] - 0.43 * y_[3] - 8.32 * y_[4] - 0.0007; - f_[3] = yp_[3] - 1.71 * y_[2] + 8.75 * y_[3]; - f_[4] = yp_[4] + 10.03 * y_[4] - 0.43 * y_[0] - 0.035 * y_[1]; + f_int_[0] = -yp_int_[0] - 1.71 * y_int_[0] + 0.43 * y_int_[1] + 8.32 * y_int_[2] + 0.0007; + f_int_[1] = -yp_int_[1] + 1.71 * y_int_[0] - 8.75 * y_int_[1]; + f_int_[2] = -yp_int_[2] - 10.03 * y_int_[2] + 0.43 * y_[0] + 0.035 * y_[1]; + + return 0; + } + + int evaluateExternalResidual() + { + // outputs + f_[0] = 8.32 * y_int_[1] + 1.71 * y_int_[2] - 0.1 * y_[0]; + f_[1] = -0.7 * y_[1]; return 0; } int evaluateJacobian() { + + this->zeroJacMatrix(); + + // Internal Jacobian Entries + std::vector row = {2, 2, 2, 3, 3, 4, 4, 4}; + std::vector col = {2, 3, 4, 2, 3, 4, 0, 1}; + std::vector val = {-1.71 - alpha_, 0.43, 8.32, 1.71, -8.75 - alpha_, -10.03 - alpha_, 0.43, 0.035}; + + this->setJacValues(row, col, val); + + // External Jacobian Entries + row = {0, 0, 0, 1}; + col = {3, 4, 0, 1}; + val = {8.32, 1.71, -0.1, -0.7}; + + this->setJacValues(row, col, val); + return 0; } diff --git a/examples/PowerElectronics/Partition/HiresComponent3.hpp b/examples/PowerElectronics/Partition/HiresComponent3.hpp index d0b71f5c1..81314895d 100644 --- a/examples/PowerElectronics/Partition/HiresComponent3.hpp +++ b/examples/PowerElectronics/Partition/HiresComponent3.hpp @@ -21,15 +21,17 @@ namespace GridKit using CircuitComponent::time_; using CircuitComponent::alpha_; using CircuitComponent::y_; + using CircuitComponent::y_int_; using CircuitComponent::yp_; + using CircuitComponent::yp_int_; using CircuitComponent::tag_; using CircuitComponent::f_; + using CircuitComponent::f_int_; using CircuitComponent::g_; using CircuitComponent::yB_; using CircuitComponent::ypB_; using CircuitComponent::fB_; using CircuitComponent::gB_; - using CircuitComponent::jac_; using CircuitComponent::param_; using CircuitComponent::idc_; @@ -45,6 +47,7 @@ namespace GridKit n_extern_ = 2; extern_indices_ = {0, 1}; idc_ = id; + nnz_ = 15; } ~HiresComponent3() @@ -53,9 +56,7 @@ namespace GridKit int allocate() { - y_.resize(static_cast(size_)); - yp_.resize(static_cast(size_)); - f_.resize(static_cast(size_)); + CircuitComponent::allocate(); return 0; } @@ -70,22 +71,57 @@ namespace GridKit return 0; } - int evaluateResidual() + int evaluateInternalResidual() { - // Outputs - f_[0] = 0.02 * y_[0]; - f_[1] = 0.045 * y_[1] - 0.43 * y_[2] - 0.43 * y_[3]; - // Internals - f_[2] = yp_[2] + 280 * y_[2] * y_[4] - 0.69 * y_[0] - 1.71 * y_[1] + 0.43 * y_[2] - 0.69 * y_[3]; - f_[3] = yp_[3] - 280 * y_[2] * y_[4] + 1.81 * y_[3]; - f_[4] = yp_[4] + 280 * y_[2] * y_[4] - 1.81 * y_[3]; + f_int_[0] = -yp_int_[0] - 280 * y_int_[0] * y_int_[2] + 0.69 * y_[0] + 1.71 * y_[1] - 0.43 * y_int_[0] + 0.69 * y_int_[1]; + f_int_[1] = -yp_int_[1] + 280 * y_int_[0] * y_int_[2] - 1.81 * y_int_[1]; + f_int_[2] = -yp_int_[2] - 280 * y_int_[0] * y_int_[2] + 1.81 * y_int_[1]; + + return 0; + } + + int evaluateExternalResidual() + { + // Externals + f_[0] = -0.02 * y_[0]; + f_[1] = -0.045 * y_[1] + 0.43 * y_int_[0] + 0.43 * y_int_[1]; return 0; } int evaluateJacobian() { + this->zeroJacMatrix(); + + // Internal Jacobian Entries [row 1] + std::vector row = {2, 2, 2, 2, 2}; + std::vector col = {2, 3, 4, 0, 1}; + std::vector val = {-280 * y_int_[2] - 0.43 - alpha_, 0.69, -280 * y_int_[0], 0.69, 1.71}; + + this->setJacValues(row, col, val); + + // Internal Jacobian Entries [row 2] + row = {3, 3, 3}; + col = {2, 3, 4}; + val = {280 * y_int_[2], -1.81 - alpha_, 280 * y_int_[0]}; + + this->setJacValues(row, col, val); + + // Internal Jacobian Entries [row 3] + row = {4, 4, 4}; + col = {2, 3, 4}; + val = {-280 * y_int_[2], 1.81, -280 * y_int_[0] - alpha_}; + + this->setJacValues(row, col, val); + + // External Jacobian Entries + row = {0, 1, 1, 1}; + col = {0, 2, 3, 1}; + val = {-0.02, 0.43, 0.43, -0.045}; + + this->setJacValues(row, col, val); + return 0; } From 67435a533a9fe2d4d5b8b97b9b61a60601dfd9f3 Mon Sep 17 00:00:00 2001 From: abdourahmanbarry Date: Mon, 13 Jul 2026 17:21:19 +0000 Subject: [PATCH 22/52] Add subsystem Jacobian testing utilities and update partition test problems --- GridKit/Model/PartitionEvaluator.hpp | 2 +- GridKit/Model/PowerElectronics/CMakeLists.txt | 1 - .../BusPartitionInterface.cpp | 15 +- .../PartitionInterface/CMakeLists.txt | 10 +- .../Model/PowerElectronics/SubsystemModel.hpp | 339 +++++++++++------- .../Solver/Dynamic/MultiStageCosimulation.cpp | 2 +- .../PowerElectronics/Partition/CMakeLists.txt | 37 +- .../PowerElectronics/Partition/HiresBus.hpp | 2 +- .../PowerElectronics/Partition/Partition.cpp | 153 +++++++- .../Partition/PartitionMicrogrid.cpp | 72 ++-- .../Partition/PartitionScaleMicrogrid.cpp | 67 +++- .../Partition/jac_test_helper.hpp | 250 +++++++++++++ 12 files changed, 717 insertions(+), 233 deletions(-) create mode 100644 examples/PowerElectronics/Partition/jac_test_helper.hpp diff --git a/GridKit/Model/PartitionEvaluator.hpp b/GridKit/Model/PartitionEvaluator.hpp index 96d906ea3..1ce80b513 100644 --- a/GridKit/Model/PartitionEvaluator.hpp +++ b/GridKit/Model/PartitionEvaluator.hpp @@ -279,4 +279,4 @@ namespace GridKit }; } // namespace Model -} // namespace GridKit \ No newline at end of file +} // namespace GridKit diff --git a/GridKit/Model/PowerElectronics/CMakeLists.txt b/GridKit/Model/PowerElectronics/CMakeLists.txt index a45284ee7..368f89515 100644 --- a/GridKit/Model/PowerElectronics/CMakeLists.txt +++ b/GridKit/Model/PowerElectronics/CMakeLists.txt @@ -32,4 +32,3 @@ install( ExternalConnection.hpp SubsystemModel.hpp DESTINATION include/GridKit/Model/PowerElectronics) - diff --git a/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.cpp b/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.cpp index 0ede5de02..85d8d43fb 100644 --- a/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.cpp +++ b/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.cpp @@ -40,7 +40,6 @@ namespace GridKit extern_indices_.insert(i); } - const IdxT* cooRow = component_.jacobianCooRows(); const IdxT* cooCol = component_.jacobianCooCols(); @@ -111,7 +110,6 @@ namespace GridKit assert(false); } - const auto n = component_.getInternalSize(); y_ptr = std::make_unique(n); @@ -206,16 +204,15 @@ namespace GridKit component_.evaluateJacobian(); - const IdxT* cooRows = component_.jacobianCooRows(); - const IdxT* cooCols = component_.jacobianCooCols(); - const RealT* cooVals = component_.jacobianCooValues(); + const IdxT* cooRows = component_.jacobianCooRows(); + const IdxT* cooCols = component_.jacobianCooCols(); + const RealT* cooVals = component_.jacobianCooValues(); - std::vector r = {}; - std::vector c = {}; + std::vector r = {}; + std::vector c = {}; std::vector v = {}; - - for(const auto& index: jac_map_) + for (const auto& index : jac_map_) { r.push_back(cooRows[index]); c.push_back(cooCols[index]); diff --git a/GridKit/Model/PowerElectronics/PartitionInterface/CMakeLists.txt b/GridKit/Model/PowerElectronics/PartitionInterface/CMakeLists.txt index 938b2f6f1..b339bfa2f 100644 --- a/GridKit/Model/PowerElectronics/PartitionInterface/CMakeLists.txt +++ b/GridKit/Model/PowerElectronics/PartitionInterface/CMakeLists.txt @@ -1,6 +1,4 @@ -gridkit_add_library(power_elec_partition_interfaces - SOURCES - BusPartitionInterface.cpp - HEADERS - BusPartitionInterface.hpp - PartitionInterface.hpp) +gridkit_add_library( + power_elec_partition_interfaces + SOURCES BusPartitionInterface.cpp + HEADERS BusPartitionInterface.hpp PartitionInterface.hpp) diff --git a/GridKit/Model/PowerElectronics/SubsystemModel.hpp b/GridKit/Model/PowerElectronics/SubsystemModel.hpp index ea6ee31fb..1fdb79193 100644 --- a/GridKit/Model/PowerElectronics/SubsystemModel.hpp +++ b/GridKit/Model/PowerElectronics/SubsystemModel.hpp @@ -3,6 +3,8 @@ #pragma once #include +#include +#include #include #include #include @@ -15,6 +17,7 @@ namespace GridKit { + template class SubsystemModel : public CircuitComponent { @@ -23,6 +26,8 @@ namespace GridKit using CsrMatrixT = typename CircuitComponent::CsrMatrixT; using component_type = CircuitComponent; using node_type = PowerElectronics::NodeBase; + using ForcingData = std::pair, std::vector>; + using TimeFunction = std::function; using CircuitComponent::size_; using CircuitComponent::n_intern_; @@ -47,7 +52,7 @@ namespace GridKit * @post System model parameters set as default */ - SubsystemModel(bool root = true) + SubsystemModel() { // Set system model parameters as default rel_tol_ = 1e-4; @@ -55,7 +60,6 @@ namespace GridKit this->max_steps_ = 2000; // By default don't use the jacobian use_jac_ = false; - root_ = root; } virtual ~SubsystemModel() @@ -93,6 +97,7 @@ namespace GridKit y_.resize(n_intern_); yp_.resize(n_intern_); f_.resize(n_intern_); + neumaier_compensation.resize(n_intern_); // Allocate subsystem vectors. y_ext_.resize(n_extern_); @@ -112,105 +117,117 @@ namespace GridKit external_indices_[local_idx - n_intern_] = global_idx; } - - size_t component_internal_idx = 0; - for (component_type* comp : components_) + size_t component_internal_idx = 0; + for (component_type* comp : components_) + { + + comp->setInternalPointer(&y_[component_internal_idx]); + comp->setInternalDerivativePointer(&yp_[component_internal_idx]); + comp->setInternalResidualPointer(&f_[component_internal_idx]); + + component_internal_idx += comp->getInternalSize(); + } + + // Evaluate component Jacobians to get sparsity + distributeVectors(); + for (component_type* component : components_) + { + component->evaluateJacobian(); + } + + auto isValidEntry = [this](IdxT row, IdxT col) + { + if (row == neg1_ || col == neg1_) { + return false; + } + + const bool row_is_internal = row < this->getInternalSize(); + const bool col_is_internal = col < this->getInternalSize(); - comp->setInternalPointer(&y_[component_internal_idx]); - comp->setInternalDerivativePointer(&yp_[component_internal_idx]); - comp->setInternalResidualPointer(&f_[component_internal_idx]); + return (row_is_internal && col_is_internal); + }; + + IdxT nnz_dup = 0; + + for (const component_type* component : components_) + { + const IdxT* r = component->jacobianCooRows(); + const IdxT* c = component->jacobianCooCols(); + const IdxT nnz = component->nnz(); - component_internal_idx += comp->getInternalSize(); + for (IdxT i = 0; i < nnz; ++i) + { + const IdxT row = component->getNodeConnection(r[i]); + const IdxT col = component->getNodeConnection(c[i]); + + if (isValidEntry(row, col)) + { + ++nnz_dup; + } } - - - // // Evaluate component Jacobians to get sparsity - // distributeVectors(); - // for (component_type* component : components_) - // { - // component->evaluateJacobian(); - // } - - // // Count the number of non-zeros - // IdxT nnz_dup = 0; - // for (const component_type* component : components_) - // { - // const IdxT* r = component->jacobianCooRows(); - // const IdxT* c = component->jacobianCooCols(); - // IdxT nnz = component->nnz(); - - // for (IdxT i = 0; i < nnz; ++i) - // { - // if(component->getNodeConnection(r[i])>= this->getInternalSize() && component->getNodeConnection(c[i])>= this->getInternalSize()) - // { - // continue; - // } - // if (component->getNodeConnection(r[i]) != neg1_ && component->getNodeConnection(c[i]) != neg1_) - // { - // ++nnz_dup; - // } - // } - // } - - // // Allocate COO triplet arrays (we own these until we hand off to CsrMatrix) - // IdxT* rows_dup = new IdxT[nnz_dup]; - // IdxT* cols_dup = new IdxT[nnz_dup]; - // RealT* vals_dup = new RealT[nnz_dup]; - - // IdxT counter = 0; - // for (const auto& component : components_) - // { - // const IdxT* r = component->jacobianCooRows(); - // const IdxT* c = component->jacobianCooCols(); - // const RealT* v = component->jacobianCooValues(); - // IdxT nnz = component->nnz(); - - // for (IdxT i = 0; i < nnz; ++i) - // { - // if(component->getNodeConnection(r[i])>= this->getInternalSize() && component->getNodeConnection(c[i])>= this->getInternalSize()) - // { - // continue; - // } - - // if (component->getNodeConnection(r[i]) != neg1_ && component->getNodeConnection(c[i]) != neg1_) - // { - // rows_dup[counter] = component->getNodeConnection(r[i]); - // cols_dup[counter] = component->getNodeConnection(c[i]); - // vals_dup[counter] = v[i]; - // counter++; - // } - // } - // } - - // // Build the system COO Jacobian - // LinearAlgebra::CooMatrix jac(size_, size_, nnz_dup, &rows_dup, &cols_dup, &vals_dup); - - // // Populate CSR data with sort and deduplicate - // IdxT* row_ptrs = jac.getCsrRowData(); - - // // Deduplicated nnz - // nnz_ = jac.getNnz(); - - // // Allocate cols/vals with deduplicated nnz - // IdxT* cols = new IdxT[nnz_]; - // RealT* vals = new RealT[nnz_]; - - // std::copy(jac.getColData(), jac.getColData() + nnz_, cols); - // std::copy(jac.getValues(), jac.getValues() + nnz_, vals); - - // // Create the CSR Jacobian - // csr_jac_ = new CsrMatrixT(size_, size_, nnz_, &row_ptrs, &cols, &vals); - - // const IdxT* map_to_sorted = jac.getMapToSorted(); - // const IdxT* map_to_dedup = jac.getMapToDeduplicated(); - - // // Build a mappping from original COO index to CSR index - // map_to_csr_ = new IdxT[nnz_dup]; - // for (IdxT i = 0; i < nnz_dup; ++i) - // { - // map_to_csr_[map_to_sorted[i]] = map_to_dedup[i]; - // } + } + + // Allocate COO triplet arrays (we own these until we hand off to CsrMatrix) + IdxT* rows_dup = new IdxT[nnz_dup]; + IdxT* cols_dup = new IdxT[nnz_dup]; + RealT* vals_dup = new RealT[nnz_dup]; + + IdxT counter = 0; + + for (const component_type* component : components_) + { + const IdxT* r = component->jacobianCooRows(); + const IdxT* c = component->jacobianCooCols(); + const RealT* v = component->jacobianCooValues(); + const IdxT nnz = component->nnz(); + + for (IdxT i = 0; i < nnz; ++i) + { + const IdxT row = component->getNodeConnection(r[i]); + const IdxT col = component->getNodeConnection(c[i]); + + if (!isValidEntry(row, col)) + { + continue; + } + + rows_dup[counter] = row; + cols_dup[counter] = col; + vals_dup[counter] = v[i]; + + ++counter; + } + } + + // Build the system COO Jacobian + LinearAlgebra::CooMatrix jac(size_, size_, nnz_dup, &rows_dup, &cols_dup, &vals_dup); + + // Populate CSR data with sort and deduplicate + IdxT* row_ptrs = jac.getCsrRowData(); + + // Deduplicated nnz + nnz_ = jac.getNnz(); + + // Allocate cols/vals with deduplicated nnz + IdxT* cols = new IdxT[nnz_]; + RealT* vals = new RealT[nnz_]; + + std::copy(jac.getColData(), jac.getColData() + nnz_, cols); + std::copy(jac.getValues(), jac.getValues() + nnz_, vals); + + // Create the CSR Jacobian + csr_jac_ = new CsrMatrixT(size_, size_, nnz_, &row_ptrs, &cols, &vals); + + const IdxT* map_to_sorted = jac.getMapToSorted(); + const IdxT* map_to_dedup = jac.getMapToDeduplicated(); + + // Build a mappping from original COO index to CSR index + map_to_csr_ = new IdxT[nnz_dup]; + for (IdxT i = 0; i < nnz_dup; ++i) + { + map_to_csr_[map_to_sorted[i]] = map_to_dedup[i]; + } return 0; } @@ -241,6 +258,21 @@ namespace GridKit */ int distributeVectors() { + + if (forcing_function_) + { + const auto forcing = (*forcing_function_)(time_); + + const auto& y_forcing = forcing.first; + const auto& yp_forcing = forcing.second; + + assert(y_forcing.size() == y_ext_.size()); + assert(yp_forcing.size() == yp_ext_.size()); + + std::copy(y_forcing.begin(), y_forcing.end(), y_ext_.begin()); + std::copy(yp_forcing.begin(), yp_forcing.end(), yp_ext_.begin()); + } + for (component_type* component : components_) { std::vector& y = component->y(); @@ -249,20 +281,22 @@ namespace GridKit for (size_t j : externals) { - if (component->getNodeConnection(j) == neg1_) + IdxT local_idx = component->getNodeConnection(j); + + if (local_idx == neg1_) { y[j] = 0.0; yp[j] = 0.0; } - else if (component->getNodeConnection(j) < this->getInternalSize()) + else if (local_idx < this->getInternalSize()) { - y[j] = y_[component->getNodeConnection(j)]; - yp[j] = yp_[component->getNodeConnection(j)]; + y[j] = y_[local_idx]; + yp[j] = yp_[local_idx]; } else { - y[j] = y_ext_[component->getNodeConnection(j) - this->getInternalSize()]; - yp[j] = yp_ext_[component->getNodeConnection(j) - this->getInternalSize()]; + y[j] = y_ext_[local_idx - this->getInternalSize()]; + yp[j] = yp_ext_[local_idx - this->getInternalSize()]; } } } @@ -284,6 +318,9 @@ namespace GridKit for (IdxT i = 0; i < this->getInternalSize(); i++) { f_[i] = 0.0; + + if (use_neumaier_sum) + neumaier_compensation[i] = 0.0; } this->distributeVectors(); @@ -311,10 +348,19 @@ namespace GridKit //@todo should do a different grounding check if (component->getNodeConnection(j) != neg1_ && component->getNodeConnection(j) < this->getInternalSize()) { - f_[component->getNodeConnection(j)] += residual[j]; + if (!use_neumaier_sum) + { + f_[component->getNodeConnection(j)] += residual[j]; + } + else + { + getNeumaierCompensation(f_[component->getNodeConnection(j)], residual[j], component->getNodeConnection(j)); + } } } } + if (use_neumaier_sum) + addNeumaierCompensation(f_); return 0; } @@ -358,16 +404,18 @@ namespace GridKit for (IdxT i = 0; i < nnz; ++i) { - if(component->getNodeConnection(r[i]) >= this->getInternalSize() && component->getNodeConnection(c[i]) >= this->getInternalSize()) + const IdxT row = component->getNodeConnection(r[i]); + const IdxT col = component->getNodeConnection(c[i]); + + const bool is_internal_entry = row != neg1_ && col != neg1_ && row < n_intern_ && col < n_intern_; + + if (!is_internal_entry) { continue; } - if (component->getNodeConnection(r[i]) != neg1_ && component->getNodeConnection(c[i]) != neg1_) - { - vals[map_to_csr_[counter]] += v[i]; - ++counter; - } + vals[map_to_csr_[counter]] += v[i]; + ++counter; } } @@ -431,27 +479,6 @@ namespace GridKit alpha_ = a; } - /** - * @brief print the system residual in COO format - * - * @param[in] filename - * @param[in] title - */ - void printResidualMatrixMarket(std::string filename, std::string title) - { - writeVectorToMatrixMarket(f_, filename, title); - } - - /** - * @brief print the system Jacobian in COO format - * - * @param[in] filename - * @param[in] title - */ - void printJacobianMatrixMarket(std::string filename, std::string title) - { - } - CsrMatrixT* getCsrJacobian() const override { return csr_jac_; @@ -589,6 +616,43 @@ namespace GridKit return f_ext_; } + void setTimeFunction(TimeFunction function) + { + forcing_function_ = std::move(function); + } + + void getNeumaierCompensation(ScalarT& sum, const ScalarT& x, const IdxT& index) + { + ScalarT t = sum + x; + if (std::abs(sum) >= std::abs(x)) + { + neumaier_compensation[index] += (sum - t) + x; + } + else + { + neumaier_compensation[index] += (x - t) + sum; + } + sum = t; + } + + void addNeumaierCompensation(std::vector& f) + { + for (IdxT i = 0; i < this->f_.size(); i++) + { + f[i] += neumaier_compensation[i]; + } + } + + std::unordered_map& getInternalMap() + { + return internal_map_; + } + + std::unordered_map& getExternalMap() + { + return external_map_; + } + private: static constexpr IdxT neg1_ = INVALID_INDEX; @@ -597,10 +661,15 @@ namespace GridKit std::unordered_map internal_map_; std::unordered_map external_map_; std::vector external_indices_; - bool root_; - std::vector y_ext_; - std::vector yp_ext_; - std::vector f_ext_; + + bool use_neumaier_sum{false}; + + std::vector neumaier_compensation; + std::vector y_ext_; + std::vector yp_ext_; + std::vector f_ext_; + + std::optional forcing_function_; IdxT* map_to_csr_{nullptr}; CsrMatrixT* csr_jac_{nullptr}; diff --git a/GridKit/Solver/Dynamic/MultiStageCosimulation.cpp b/GridKit/Solver/Dynamic/MultiStageCosimulation.cpp index 9ce53ab3c..7b1803bca 100644 --- a/GridKit/Solver/Dynamic/MultiStageCosimulation.cpp +++ b/GridKit/Solver/Dynamic/MultiStageCosimulation.cpp @@ -189,4 +189,4 @@ namespace Integrator return 0; } -} // namespace Integrator \ No newline at end of file +} // namespace Integrator diff --git a/examples/PowerElectronics/Partition/CMakeLists.txt b/examples/PowerElectronics/Partition/CMakeLists.txt index cdb522167..6f2f633d6 100644 --- a/examples/PowerElectronics/Partition/CMakeLists.txt +++ b/examples/PowerElectronics/Partition/CMakeLists.txt @@ -1,25 +1,26 @@ - find_package(OpenMP REQUIRED) add_executable(partition Partition.cpp) -target_link_libraries(partition GridKit::solvers_dyn - GridKit::power_elec_partition_interfaces) - +target_link_libraries( + partition GridKit::solvers_dyn GridKit::power_elec_partition_interfaces) add_executable(partitionMicrogrid PartitionMicrogrid.cpp) -target_link_libraries(partitionMicrogrid GridKit::power_elec_disgen - GridKit::power_elec_microline - GridKit::power_elec_microload - GridKit::solvers_dyn - GridKit::power_elec_microbusdq - GridKit::power_elec_partition_interfaces) - +target_link_libraries( + partitionMicrogrid + GridKit::power_elec_disgen + GridKit::power_elec_microline + GridKit::power_elec_microload + GridKit::solvers_dyn + GridKit::power_elec_microbusdq + GridKit::power_elec_partition_interfaces) add_executable(PartitionScaleMicrogrid PartitionScaleMicrogrid.cpp) -target_link_libraries(PartitionScaleMicrogrid GridKit::power_elec_disgen - GridKit::power_elec_microline - GridKit::power_elec_microload - GridKit::solvers_dyn - GridKit::power_elec_microbusdq - GridKit::power_elec_partition_interfaces - OpenMP::OpenMP_CXX) +target_link_libraries( + PartitionScaleMicrogrid + GridKit::power_elec_disgen + GridKit::power_elec_microline + GridKit::power_elec_microload + GridKit::solvers_dyn + GridKit::power_elec_microbusdq + GridKit::power_elec_partition_interfaces + OpenMP::OpenMP_CXX) diff --git a/examples/PowerElectronics/Partition/HiresBus.hpp b/examples/PowerElectronics/Partition/HiresBus.hpp index eeb7f6080..ae834aaba 100644 --- a/examples/PowerElectronics/Partition/HiresBus.hpp +++ b/examples/PowerElectronics/Partition/HiresBus.hpp @@ -114,4 +114,4 @@ namespace GridKit return 0; } }; -} // namespace GridKit \ No newline at end of file +} // namespace GridKit diff --git a/examples/PowerElectronics/Partition/Partition.cpp b/examples/PowerElectronics/Partition/Partition.cpp index 319e4dc00..acd56914f 100644 --- a/examples/PowerElectronics/Partition/Partition.cpp +++ b/examples/PowerElectronics/Partition/Partition.cpp @@ -1,11 +1,15 @@ #include +#include "GridKit/LinearAlgebra/DenseMatrix/DenseMatrix.hpp" + #define _USE_MATH_DEFINES #include #include +#include #include #include +#include #include #include @@ -15,9 +19,21 @@ std::vector hires(const std::vector& y, const std::vector& yp); +template +int jac_hires(const std::vector& y, [[maybe_unused]] const std::vector& yp, GridKit::LinearAlgebra::DenseMatrix& jac, RealT alpha); + +template +void addCsrToDense( + GridKit::LinearAlgebra::CsrMatrix* csr, + GridKit::LinearAlgebra::DenseMatrix& dense, + GridKit::SubsystemModel* partition); + int main(int /* argc */, char const** /* argv */) { + using Bus = GridKit::PowerElectronics::MicrogridBus; + Bus bus; + GridKit::SubsystemModel* partition1 = new GridKit::SubsystemModel(); GridKit::SubsystemModel* partition2 = new GridKit::SubsystemModel(); @@ -25,6 +41,15 @@ int main(int /* argc */, char const** /* argv */) GridKit::HiresBus* bus1 = new GridKit::HiresBus(2); GridKit::HiresComponent3* comp3 = new GridKit::HiresComponent3(3); + bus.allocate(); + + bus.setExternalConnectionNodes(0, 3); + bus.setExternalConnectionNodes(1, 4); + + comp1->allocate(); + comp3->allocate(); + bus1->allocate(); + comp1->setExternalConnectionNodes(0, 3); comp1->setExternalConnectionNodes(1, 4); comp1->setExternalConnectionNodes(2, 0); @@ -40,15 +65,19 @@ int main(int /* argc */, char const** /* argv */) comp3->setExternalConnectionNodes(3, 6); comp3->setExternalConnectionNodes(4, 7); - GridKit::HiresComponent3 comp3copy(*comp3); - GridKit::BusPartitionInterface* busInterface = new GridKit::BusPartitionInterface(comp3copy, 3, 4, 4); + GridKit::HiresComponent3 + comp3copy(*comp3); + GridKit::BusPartitionInterface* busInterface = new GridKit::BusPartitionInterface(bus, comp3copy, 4); + busInterface->allocate(); partition1->addComponent(comp1); partition1->addComponent(bus1); partition1->addComponent(busInterface); + partition1->addNode(&bus); partition2->addComponent(comp3); + std::cout << "Msg" << std::endl; partition1->allocate(); partition2->allocate(); @@ -117,6 +146,42 @@ int main(int /* argc */, char const** /* argv */) printf("%-10.5g ---------- %10zu\n", ref[i], i); } + partition1->updateTime(0, 1.0); + partition2->updateTime(0, 1.0); + + partition1->evaluateJacobian(); + partition2->evaluateJacobian(); + + GridKit::LinearAlgebra::DenseMatrix jac1(8, 8); + GridKit::LinearAlgebra::DenseMatrix jac2(8, 8); + + GridKit::LinearAlgebra::DenseMatrix jac_ref(8, 8); + + addCsrToDense(partition1->getCsrJacobian(), jac1, partition1); + addCsrToDense(partition2->getCsrJacobian(), jac2, partition2); + jac_hires(y, yp, jac_ref, 1.0); + + auto print_dense = [&](GridKit::LinearAlgebra::DenseMatrix& jc) -> void + { + for (size_t i = 0; i < 8; ++i) + { + for (size_t j = 0; j < 8; ++j) + { + std::cout << std::setw(10) << jc.getValue(i, j) << ' '; + } + std::cout << '\n'; + } + std::cout << '\n'; + }; + + print_dense(jac1); + print_dense(jac2); + print_dense(jac_ref); + + delete comp1; + delete bus1; + delete busInterface; + delete comp3; delete partition1; delete partition2; } @@ -125,14 +190,82 @@ std::vector hires(const std::vector& y, const std::vector f(8); - f[0] = yp[0] + 1.71 * y[0] - 0.43 * y[1] - 8.32 * y[2] - 0.0007; - f[1] = yp[1] - 1.71 * y[0] + 8.75 * y[1]; - f[2] = yp[2] + 10.03 * y[2] - 0.43 * y[3] - 0.035 * y[4]; - f[3] = yp[3] - 8.32 * y[1] - 1.71 * y[2] + 1.12 * y[3]; - f[4] = yp[4] + 1.745 * y[4] - 0.43 * y[5] - 0.43 * y[6]; - f[5] = yp[5] + 280 * y[5] * y[7] - 0.69 * y[3] - 1.71 * y[4] + 0.43 * y[5] - 0.69 * y[6]; - f[6] = yp[6] - 280 * y[5] * y[7] + 1.81 * y[6]; - f[7] = yp[7] + 280 * y[5] * y[7] - 1.81 * y[6]; + f[0] = -yp[0] - 1.71 * y[0] + 0.43 * y[1] + 8.32 * y[2] + 0.0007; + f[1] = -yp[1] + 1.71 * y[0] - 8.75 * y[1]; + f[2] = -yp[2] - 10.03 * y[2] + 0.43 * y[3] + 0.035 * y[4]; + f[3] = -yp[3] + 8.32 * y[1] + 1.71 * y[2] - 1.12 * y[3]; + f[4] = -yp[4] - 1.745 * y[4] + 0.43 * y[5] + 0.43 * y[6]; + f[5] = -yp[5] - 280 * y[5] * y[7] + 0.69 * y[3] + 1.71 * y[4] - 0.43 * y[5] + 0.69 * y[6]; + f[6] = -yp[6] + 280 * y[5] * y[7] - 1.81 * y[6]; + f[7] = -yp[7] - 280 * y[5] * y[7] + 1.81 * y[6]; return f; } + +template +int jac_hires(const std::vector& y, [[maybe_unused]] const std::vector& yp, GridKit::LinearAlgebra::DenseMatrix& jac, RealT alpha) +{ + + jac.setValue(0, 0, -alpha - 1.71); + jac.setValue(0, 1, 0.43); + jac.setValue(0, 2, 8.32); + + jac.setValue(1, 0, 1.71); + jac.setValue(1, 1, -alpha - 8.75); + + jac.setValue(2, 2, -alpha - 10.03); + jac.setValue(2, 3, 0.43); + jac.setValue(2, 4, 0.035); + + jac.setValue(3, 1, 8.32); + jac.setValue(3, 2, 1.71); + jac.setValue(3, 3, -alpha - 1.12); + + jac.setValue(4, 4, -alpha - 1.745); + jac.setValue(4, 5, 0.43); + jac.setValue(4, 6, 0.43); + + jac.setValue(5, 3, 0.69); + jac.setValue(5, 4, 1.71); + jac.setValue(5, 5, -alpha - 280.0 * y[7] - 0.43); + jac.setValue(5, 6, 0.69); + jac.setValue(5, 7, -280.0 * y[5]); + + jac.setValue(6, 5, 280.0 * y[7]); + jac.setValue(6, 6, -alpha - 1.81); + jac.setValue(6, 7, 280.0 * y[5]); + + jac.setValue(7, 5, -280.0 * y[7]); + jac.setValue(7, 6, 1.81); + jac.setValue(7, 7, -alpha - 280.0 * y[5]); + + return 0; +} + +template +void addCsrToDense( + GridKit::LinearAlgebra::CsrMatrix* csr, + GridKit::LinearAlgebra::DenseMatrix& dense, + GridKit::SubsystemModel* partition) +{ + auto* row_ptr = csr->getRowData(GridKit::LinearAlgebra::memory::HOST); + auto* col_ind = csr->getColData(GridKit::LinearAlgebra::memory::HOST); + auto* values = csr->getValues(GridKit::LinearAlgebra::memory::HOST); + + assert(row_ptr != nullptr); + assert(col_ind != nullptr); + assert(values != nullptr); + + for (IdxT row = 0; row < csr->getNumRows(); ++row) + { + for (IdxT k = row_ptr[row]; k < row_ptr[row + 1]; ++k) + { + const IdxT col = col_ind[k]; + + auto r = partition->getNodeConnection(row); + auto c = partition->getNodeConnection(col); + + dense.setValue(r, c, dense.getValue(r, c) + values[k]); + } + } +} diff --git a/examples/PowerElectronics/Partition/PartitionMicrogrid.cpp b/examples/PowerElectronics/Partition/PartitionMicrogrid.cpp index ff3d23a5d..460b8ba67 100644 --- a/examples/PowerElectronics/Partition/PartitionMicrogrid.cpp +++ b/examples/PowerElectronics/Partition/PartitionMicrogrid.cpp @@ -1,8 +1,8 @@ -#include + #include #include #include -#include +#include #include #define _USE_MATH_DEFINES @@ -21,6 +21,8 @@ #include #include +#include "jac_test_helper.hpp" + int main() { /// @todo Needs to be modified. Some components are small relative to others thus @@ -29,7 +31,6 @@ int main() double rel_tol = 1.0e-8; size_t max_step_number = 3000; bool use_jac = true; - bool debug_output = true; // Create model auto* sysmodel = new GridKit::PowerElectronicsModel(rel_tol, abs_tol, use_jac, max_step_number); @@ -191,7 +192,6 @@ int main() partition2->addComponent(dg4); partition2->addComponent(l2); partition2->addComponent(l3); - partition2->addComponent(load2); partition2->addComponent(bus_para_4); partition2->addComponent(bus_para_3); @@ -213,60 +213,51 @@ int main() sysmodel->yp()[i] = yp[i]; } + sysmodel->updateTime(2, 5); sysmodel->evaluateResidual(); - std::vector f_sysmodel = sysmodel->getResidual(); + sysmodel->evaluateJacobian(); - partition1->allocate(); - partition2->allocate(); + auto full_jac = sysmodel->getCsrJacobian(); + + std::vector f_sysmodel = sysmodel->getResidual(); std::vector*> partitions = {partition1, partition2}; // Distribute externals to partition 1 for (auto* partition : partitions) { + partition->allocate(); + + partition->updateTime(2, 5); + for (size_t i = 0; i < partition->getExternSize(); i++) { partition->getExternalDataY()[i] = y[partition->getExternalIndices()[i]]; partition->getExternalDataYP()[i] = yp[partition->getExternalIndices()[i]]; } - } - for (auto* partition : partitions) - { for (size_t i = 0; i < partition->getInternalSize(); i++) { partition->y()[i] = y[partition->getNodeConnection(i)]; partition->yp()[i] = yp[partition->getNodeConnection(i)]; } - } - for (auto* partition : partitions) - { partition->evaluateResidual(); + partition->evaluateJacobian(); } - auto printTitle = [](std::string msg) -> void - { - std::cout << "\n--------------- " << msg << " -------------" << std::endl; - printf("%-12s ---------- %12s\n", "Res Values", "Comp. Index"); - }; + auto partition1_jac = partition1->getCsrJacobian(); + auto partition2_jac = partition2->getCsrJacobian(); - // Print Residuals from partition 1 - int counter = 1; - for (auto* partition : partitions) - { - printTitle("Partition " + std::to_string(counter++)); - for (size_t i = 0; i < partition->getInternalSize(); i++) - { - auto com_index = partition->getNodeConnection(static_cast(i)); - printf("%-12.5g ---------- %7zu\n", partition->getResidual()[i], com_index); - } - } + bool jac_matched_1 = GridKit::Testing::verifySubsystemJacobian(*full_jac, *partition1_jac, *partition1); + bool jac_matched_2 = GridKit::Testing::verifySubsystemJacobian(*full_jac, *partition2_jac, *partition2); - printTitle("Reference Solution"); - for (size_t i = 0; i < sysmodel->size(); i++) + std::cout << "Jacobian Matched: " << jac_matched_1 * jac_matched_2 << std::endl; + + if (!jac_matched_1 || !jac_matched_2) { - printf("%-12.5g ---------- %7zu\n", sysmodel->getResidual()[i], i); + std::cout << "ERROR: At least one subsystem Jacobian is incorrect!" << std::endl; + return 1; } std::vector f(sysmodel->size(), 0.0); @@ -281,24 +272,27 @@ int main() } } - for (size_t i = 0; i < sysmodel->size(); i++) - { - error[i] = f_sysmodel[i] - f[i]; - std::cout << error[i] << std::endl; - } - double max_error = 0; for (size_t i = 0; i < sysmodel->size(); i++) { + error[i] = (f_sysmodel[i] - f[i]) / f_sysmodel[i]; if (max_error < std::abs(error[i])) { max_error = std::abs(error[i]); } } + if (max_error > std::numeric_limits::epsilon()) + { + std::cout << "ERROR: Max Error too high!" << std::endl; + return 1; + } + std::cout << "\nMax Error of Reference and Partition Evaluation: " << max_error << std::endl; delete sysmodel; + delete partition1; + delete partition2; return 0; -} \ No newline at end of file +} diff --git a/examples/PowerElectronics/Partition/PartitionScaleMicrogrid.cpp b/examples/PowerElectronics/Partition/PartitionScaleMicrogrid.cpp index 4e59848c8..a27f0092a 100644 --- a/examples/PowerElectronics/Partition/PartitionScaleMicrogrid.cpp +++ b/examples/PowerElectronics/Partition/PartitionScaleMicrogrid.cpp @@ -4,7 +4,9 @@ #include #include #include +#include #include +#include #include #include @@ -19,6 +21,8 @@ #include #include +#include "jac_test_helper.hpp" + /****************************************************************************** * Partitioned Residual Evaluation * @@ -44,11 +48,13 @@ using real_type = double; struct RunResult { - index_type num_partitions; - real_type partition_time; // seconds - real_type monolithic_time; // seconds - real_type speedup; // monolithic / partition - real_type max_error; + bool success = true; + index_type num_partitions; + real_type partition_time; // seconds + real_type monolithic_time; // seconds + real_type speedup; // monolithic / partition + real_type max_error; + std::string subsys_jac; }; RunResult printMicrogridSystems(index_type N_size, index_type num_partitions); @@ -64,14 +70,15 @@ int main() { index_type N_size = 5000; - std::cout << std::format("{:<16}{:>16}{:>18}{:>12}{:>14}\n", + std::cout << std::format("{:<16}{:>16}{:>18}{:>12}{:>14}{:>16}\n", "num_partitions", "partition_time", "monolithic_time", "speedup", - "error"); + "error", + "Jacobians"); - std::cout << std::string(76, '-') << "\n"; + std::cout << std::string(93, '-') << "\n"; for (index_type p : {10, 48, 100, 500, 1000, 2000, 5000}) { @@ -79,12 +86,18 @@ int main() RunResult r = printMicrogridSystems(N_size, p); - std::cout << std::format("{:<16d}{:>14.4f} s{:>16.4f} s{:>11.2f}x{:>14.3e}\n", + if (!r.success) + { + return 1; + } + + std::cout << std::format("{:<16d}{:>14.4f} s{:>16.4f} s{:>11.2f}x{:>14.3e} {:>16s}\n", r.num_partitions, r.partition_time, r.monolithic_time, r.speedup, - r.max_error); + r.max_error, + r.subsys_jac); } return 0; @@ -319,12 +332,16 @@ RunResult printMicrogridSystems(index_type N_size, index_type num_partitions) sys_model_control->yp()[i] = yp[i]; } + sys_model_control->updateTime(2, 5); sys_model_control->evaluateResidual(); auto end_time = std::chrono::high_resolution_clock::now(); auto monolithic_time = std::chrono::duration(end_time - start_time); + sys_model_control->evaluateJacobian(); + auto& f_sysmodel_control = sys_model_control->getResidual(); + auto* full_jac = sys_model_control->getCsrJacobian(); //--------------------------------------------------------------- // Partition the monolithic network into independent subsystem @@ -343,7 +360,7 @@ RunResult printMicrogridSystems(index_type N_size, index_type num_partitions) index_type index = 0; for (index_type j = 0; j < num_partitions; j++) { - auto* partition = new SubsystemModel(false); + auto* partition = new SubsystemModel(); // add Reference rotor to the first partition if (j == 0) @@ -404,6 +421,7 @@ RunResult printMicrogridSystems(index_type N_size, index_type num_partitions) for (auto* partition : subsystems) { partition->allocate(); + partition->updateTime(2, 5); } start_time = std::chrono::high_resolution_clock::now(); @@ -447,6 +465,18 @@ RunResult printMicrogridSystems(index_type N_size, index_type num_partitions) // roundoff. //--------------------------------------------------------------- + bool matched = true; + + for (auto* partition : subsystems) + { + partition->evaluateJacobian(); + + matched &= GridKit::Testing::verifySubsystemJacobian( + *full_jac, + *partition->getCsrJacobian(), + *partition); + } + real_type max_error = 0; for (size_t i = 0; i < sys_model_control->size(); i++) { @@ -464,6 +494,19 @@ RunResult printMicrogridSystems(index_type N_size, index_type num_partitions) result.monolithic_time = monolithic_time.count(); result.speedup = monolithic_time.count() / partition_time.count(); result.max_error = max_error; + result.subsys_jac = matched ? "Correct" : "Wrong"; + + if (!matched) + { + std::cout << "ERROR: At least one subsystem Jacobian is incorrect!" << std::endl; + result.success = false; + } + + if (max_error > std::numeric_limits::epsilon()) + { + std::cout << "ERROR: Max Error too high!" << std::endl; + result.success = false; + } for (auto* linescpy : linesCopies) delete linescpy; @@ -474,4 +517,4 @@ RunResult printMicrogridSystems(index_type N_size, index_type num_partitions) delete sys_model_control; return result; -} \ No newline at end of file +} diff --git a/examples/PowerElectronics/Partition/jac_test_helper.hpp b/examples/PowerElectronics/Partition/jac_test_helper.hpp new file mode 100644 index 000000000..10daf9631 --- /dev/null +++ b/examples/PowerElectronics/Partition/jac_test_helper.hpp @@ -0,0 +1,250 @@ +#pragma once + +#include +#include +#include +#include + +#include +#include + +namespace GridKit +{ + namespace Testing + { + + template + bool verifySubsystemJacobian( + GridKit::LinearAlgebra::CsrMatrix& full_jac, + GridKit::LinearAlgebra::CsrMatrix& sub_jac, + GridKit::SubsystemModel& subsystem, + RealT tolerance = static_cast(1e-12)) + { + constexpr auto host = GridKit::LinearAlgebra::memory::HOST; + + // Full-system CSR data. + const IdxT* full_rows = full_jac.getRowData(host); + const IdxT* full_cols = full_jac.getColData(host); + const RealT* full_vals = full_jac.getValues(host); + + // Subsystem CSR data. + const IdxT* sub_rows = sub_jac.getRowData(host); + const IdxT* sub_cols = sub_jac.getColData(host); + const RealT* sub_vals = sub_jac.getValues(host); + + const IdxT num_sub_rows = sub_jac.getNumRows(); + const IdxT num_sub_cols = sub_jac.getNumColumns(); + + /* + * The subsystem internal map stores: + * + * global index -> local index + */ + const auto& global_to_local = subsystem.getInternalMap(); + + if (global_to_local.size() != num_sub_rows) + { + std::cout << "Internal map size mismatch: map has " + << global_to_local.size() + << " entries, but subsystem Jacobian has " + << num_sub_rows + << " rows\n"; + + return false; + } + + if (num_sub_rows != num_sub_cols) + { + std::cout << "Subsystem Jacobian is not square: " + << num_sub_rows + << " rows and " + << num_sub_cols + << " columns\n"; + + return false; + } + + /* + * Build the reverse mapping: + * + * local index -> global index + * + * This is needed because the subsystem Jacobian stores local row + * and column indices. + */ + std::vector local_to_global(num_sub_rows); + std::vector local_index_found(num_sub_rows, false); + + for (const auto& [global_index, local_index] : global_to_local) + { + + if (global_index >= full_jac.getNumRows()) + { + std::cout << "Invalid global index " + << global_index + << " in subsystem internal map\n"; + + return false; + } + + if (local_index >= num_sub_rows) + { + std::cout << "Invalid local index " + << local_index + << " mapped from global index " + << global_index << '\n'; + + return false; + } + + if (local_index_found[local_index]) + { + std::cout << "Duplicate local index " + << local_index + << " in subsystem internal map\n"; + + return false; + } + + local_to_global[local_index] = global_index; + local_index_found[local_index] = true; + } + + // Verify that every local index has a corresponding global index. + for (IdxT local_index = 0; local_index < num_sub_rows; ++local_index) + { + if (!local_index_found[local_index]) + { + std::cout << "No global index maps to local index " + << local_index << '\n'; + + return false; + } + } + + bool matches = true; + + /* + * Compare each subsystem row against the corresponding full-system row. + */ + for (IdxT local_row = 0; local_row < num_sub_rows; ++local_row) + { + const IdxT global_row = local_to_global[local_row]; + + const IdxT sub_begin = sub_rows[local_row]; + const IdxT sub_end = sub_rows[local_row + 1]; + const IdxT full_begin = full_rows[global_row]; + const IdxT full_end = full_rows[global_row + 1]; + + /* + * Store this subsystem row using global column indices. + * + * The subsystem CSR row is sorted by local column index. After + * converting local columns to global columns, the global columns + * may no longer be sorted. Therefore, we use a lookup map instead + * of comparing both CSR rows sequentially. + */ + std::unordered_map sub_row_entries; + + for (IdxT sub_index = sub_begin; sub_index < sub_end; ++sub_index) + { + const IdxT local_column = sub_cols[sub_index]; + + if (local_column >= num_sub_cols) + { + std::cout << "Invalid subsystem column index " + << local_column + << " in local row " + << local_row << '\n'; + + matches = false; + continue; + } + + const IdxT global_column = local_to_global[local_column]; + + // Check for duplicate columns in this subsystem row. + if (sub_row_entries.find(global_column) != sub_row_entries.end()) + { + std::cout << "Duplicate subsystem entry at (" + << global_row << ", " + << global_column << ")\n"; + + matches = false; + continue; + } + + sub_row_entries[global_column] = sub_vals[sub_index]; + } + + /* + * Compare every full-system entry whose column belongs to this + * subsystem. + */ + for (IdxT full_index = full_begin; + full_index < full_end; + ++full_index) + { + const IdxT global_column = full_cols[full_index]; + + // Ignore columns owned by another subsystem. + if (global_to_local.find(global_column) == global_to_local.end()) + { + continue; + } + + auto sub_entry = sub_row_entries.find(global_column); + + if (sub_entry == sub_row_entries.end()) + { + std::cout << "Entry exists only in full Jacobian at (" + << global_row << ", " + << global_column << ")\n"; + + matches = false; + continue; + } + + const RealT full_value = full_vals[full_index]; + const RealT sub_value = sub_entry->second; + const RealT difference = std::abs(full_value - sub_value); + + if (difference > tolerance) + { + std::cout << "Jacobian value mismatch at (" + << global_row << ", " + << global_column << "): " + << "full = " << full_value + << ", subsystem = " << sub_value + << ", difference = " << difference << '\n'; + + matches = false; + } + + /* + * Remove the matched subsystem entry. + * + * After the full row has been checked, any remaining entries + * exist only in the subsystem Jacobian. + */ + sub_row_entries.erase(sub_entry); + } + + // Report subsystem entries that were not found in the full Jacobian. + for (const auto& entry : sub_row_entries) + { + const IdxT global_column = entry.first; + + std::cout << "Entry exists only in subsystem Jacobian at (" + << global_row << ", " + << global_column << ")\n"; + + matches = false; + } + } + + return matches; + } + + } // namespace Testing +} // namespace GridKit From c1e3bd432f09f1ea71a2a5238f2bbbf0adda61b1 Mon Sep 17 00:00:00 2001 From: abdourahmanbarry Date: Sun, 19 Jul 2026 08:26:03 +0000 Subject: [PATCH 23/52] Update partitioning code to work with recent changes in develop --- .../PowerElectronics/CircuitComponent.hpp | 126 ++++- .../BusPartitionInterface.cpp | 55 +- .../BusPartitionInterface.hpp | 8 +- .../PartitionInterface/PartitionInterface.hpp | 4 - .../Model/PowerElectronics/SubsystemModel.hpp | 156 +++--- .../PowerElectronics/Partition/Partition.cpp | 501 +++++++++--------- .../Partition/PartitionMicrogrid.cpp | 42 +- .../Partition/PartitionScaleMicrogrid.cpp | 44 +- .../Partition/jac_test_helper.hpp | 10 +- 9 files changed, 573 insertions(+), 373 deletions(-) diff --git a/GridKit/Model/PowerElectronics/CircuitComponent.hpp b/GridKit/Model/PowerElectronics/CircuitComponent.hpp index becbc4208..72bbda514 100644 --- a/GridKit/Model/PowerElectronics/CircuitComponent.hpp +++ b/GridKit/Model/PowerElectronics/CircuitComponent.hpp @@ -27,6 +27,130 @@ namespace GridKit CircuitComponent() = default; + CircuitComponent(const CircuitComponent& other) + : n_extern_(other.n_extern_), + n_intern_(other.n_intern_), + extern_indices_(other.extern_indices_), + size_(other.size_), + nnz_(other.nnz_), + size_quad_(other.size_quad_), + size_opt_(other.size_opt_), + current_jac_size_(other.current_jac_size_), + + // These pointers refer to storage supplied by a parent system. + // The copied component must be connected to its own storage later. + y_int_(nullptr), + yp_int_(nullptr), + f_int_(nullptr), + + tag_(other.tag_), + time_(other.time_), + alpha_(other.alpha_), + max_steps_(other.max_steps_), + idc_(other.idc_), + allocated_(other.allocated_) + { + /* + * VectorT disables its normal copy constructor and copy-assignment + * operator. Use its provided copyFromExternal() operation to perform + * an independent copy of the vector data. + */ + auto copyVector = [](VectorT& destination, const VectorT& source) + { + const IdxT source_size = source.getSize(); + + if (source_size == 0) + { + return; + } + + destination.resize(source_size); + + const int error_code = destination.copyFromExternal(source); + assert(error_code == 0); + }; + + /* + * Deep-copy the local-to-global connection mapping. + */ + if (other.connection_nodes_) + { + connection_nodes_ = + std::make_unique(static_cast(size_)); + + for (IdxT i = 0; i < size_; ++i) + { + connection_nodes_[static_cast(i)] = + other.connection_nodes_[static_cast(i)]; + } + } + + /* + * Deep-copy the COO Jacobian row indices. + */ + if (other.jacobian_coo_rows_) + { + jacobian_coo_rows_ = + std::make_unique(static_cast(nnz_)); + + for (IdxT i = 0; i < nnz_; ++i) + { + jacobian_coo_rows_[static_cast(i)] = + other.jacobian_coo_rows_[static_cast(i)]; + } + } + + /* + * Deep-copy the COO Jacobian column indices. + */ + if (other.jacobian_coo_cols_) + { + jacobian_coo_cols_ = + std::make_unique(static_cast(nnz_)); + + for (IdxT i = 0; i < nnz_; ++i) + { + jacobian_coo_cols_[static_cast(i)] = + other.jacobian_coo_cols_[static_cast(i)]; + } + } + + /* + * Deep-copy the COO Jacobian values. + */ + if (other.jacobian_coo_values_) + { + jacobian_coo_values_ = + std::make_unique(static_cast(nnz_)); + + for (IdxT i = 0; i < nnz_; ++i) + { + jacobian_coo_values_[static_cast(i)] = + other.jacobian_coo_values_[static_cast(i)]; + } + } + + // State, state derivative, residual, and absolute tolerance. + copyVector(y_, other.y_); + copyVector(yp_, other.yp_); + copyVector(f_, other.f_); + copyVector(abs_tol_, other.abs_tol_); + + // Quadrature vector. + copyVector(g_, other.g_); + + // Adjoint vectors. + copyVector(yB_, other.yB_); + copyVector(ypB_, other.ypB_); + copyVector(fB_, other.fB_); + copyVector(gB_, other.gB_); + + // Parameter vectors. + copyVector(param_, other.param_); + copyVector(param_up_, other.param_up_); + copyVector(param_lo_, other.param_lo_); + } + /** * @note Cannot be marked final, since it is overriden to recurse in the system model. */ @@ -51,7 +175,7 @@ namespace GridKit return this->n_intern_; } - std::set getExternIndices() + std::set getExternIndices() { return this->extern_indices_; } diff --git a/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.cpp b/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.cpp index 85d8d43fb..070c190f6 100644 --- a/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.cpp +++ b/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.cpp @@ -76,11 +76,8 @@ namespace GridKit CircuitComponent::allocate(); - y_.resize(static_cast(size_)); - yp_.resize(static_cast(size_)); - f_.resize(static_cast(size_)); + std::fill_n(f_.getData(), size_, 0); - std::fill(f_.begin(), f_.end(), 0); bool port_i_set = false; bool port_j_set = false; @@ -123,6 +120,13 @@ namespace GridKit return 0; } + template + int BusPartitionInterface::setAbsoluteTolerance(RealT rel_tol) + { + abs_tol_.setToConst(static_cast(rel_tol)); + return 0; + } + /** * Initialization of the grid model */ @@ -150,43 +154,48 @@ namespace GridKit return 0; } - /** - * @brief Eval Micro Load - */ template int BusPartitionInterface::evaluateExternalResidual() { - size_t internal = 0; size_t external = 0; + ScalarT* y = y_.getData(); + ScalarT* yp = yp_.getData(); + ScalarT* f = f_.getData(); + + ScalarT* component_y = component_.y().getData(); + ScalarT* component_yp = component_.yp().getData(); + const auto& extern_indices = component_.getExternIndices(); - for (IdxT i = 0; i < component_.size(); ++i) + for (size_t i = 0; i < static_cast(component_.size()); ++i) { - const bool is_external = extern_indices.contains(i); - - if (is_external) + if (extern_indices.contains(i)) { - component_.y()[external] = y_[i]; - component_.yp()[external] = yp_[i]; + component_y[external] = y[i]; + component_yp[external] = yp[i]; ++external; - continue; } - - y_ptr[internal] = y_[i]; - yp_ptr[internal] = yp_[i]; - ++internal; + else + { + y_ptr[internal] = y[i]; + yp_ptr[internal] = yp[i]; + ++internal; + } } - component_.evaluateExternalResidual(); + component_.evaluateResidual(); - auto f = component_.getResidual(); + const auto* residual = component_.getResidual().getData(); // TODO: This assumes that external variables are ordered after all internal // variables in the local indexing. To make this more robust, we need to get rid of this assumption. - f_[bus_port_i_] = f[bus_port_i_]; - f_[bus_port_j_] = f[bus_port_j_]; + f[bus_port_i_] = residual[bus_port_i_]; + f[bus_port_j_] = residual[bus_port_j_]; + + f_.setDataUpdated(); + return 0; } diff --git a/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.hpp b/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.hpp index 1bda78a89..7244fad4d 100644 --- a/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.hpp +++ b/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.hpp @@ -32,6 +32,7 @@ namespace GridKit using CircuitComponent::yp_; using CircuitComponent::yp_int_; using CircuitComponent::tag_; + using CircuitComponent::abs_tol_; using CircuitComponent::f_; using CircuitComponent::f_int_; using CircuitComponent::g_; @@ -42,13 +43,13 @@ namespace GridKit using CircuitComponent::param_; using CircuitComponent::idc_; - using PartitionInterface::bus_port_i_; - using PartitionInterface::bus_port_j_; - using CircuitComponent::extern_indices_; using CircuitComponent::n_extern_; using CircuitComponent::n_intern_; + using PartitionInterface::bus_port_i_; + using PartitionInterface::bus_port_j_; + public: BusPartitionInterface(node_type& bus, component_type& component, IdxT id); virtual ~BusPartitionInterface(); @@ -56,6 +57,7 @@ namespace GridKit int allocate(); int initialize(); int tagDifferentiable(); + int setAbsoluteTolerance(RealT); int evaluateInternalResidual() final; int evaluateExternalResidual() final; int evaluateJacobian(); diff --git a/GridKit/Model/PowerElectronics/PartitionInterface/PartitionInterface.hpp b/GridKit/Model/PowerElectronics/PartitionInterface/PartitionInterface.hpp index 1e5a3c52d..f639b0448 100644 --- a/GridKit/Model/PowerElectronics/PartitionInterface/PartitionInterface.hpp +++ b/GridKit/Model/PowerElectronics/PartitionInterface/PartitionInterface.hpp @@ -25,10 +25,6 @@ namespace GridKit class PartitionInterface : public CircuitComponent { public: - using RealT = typename Model::Evaluator::RealT; - using MatrixT = typename Model::Evaluator::MatrixT; - using CsrMatrixT = typename Model::Evaluator::CsrMatrixT; - PartitionInterface() = default; ~PartitionInterface() = default; diff --git a/GridKit/Model/PowerElectronics/SubsystemModel.hpp b/GridKit/Model/PowerElectronics/SubsystemModel.hpp index 1fdb79193..cf793725e 100644 --- a/GridKit/Model/PowerElectronics/SubsystemModel.hpp +++ b/GridKit/Model/PowerElectronics/SubsystemModel.hpp @@ -5,7 +5,6 @@ #include #include #include -#include #include #include @@ -22,7 +21,6 @@ namespace GridKit class SubsystemModel : public CircuitComponent { using RealT = typename CircuitComponent::RealT; - using MatrixT = typename CircuitComponent::MatrixT; using CsrMatrixT = typename CircuitComponent::CsrMatrixT; using component_type = CircuitComponent; using node_type = PowerElectronics::NodeBase; @@ -41,9 +39,10 @@ namespace GridKit using CircuitComponent::yp_int_; using CircuitComponent::f_; using CircuitComponent::f_int_; - using CircuitComponent::rel_tol_; + using CircuitComponent::tag_; using CircuitComponent::abs_tol_; - using CircuitComponent::extern_indices_; + using CircuitComponent::allocated_; + using CircuitComponent::allocateVectors; public: /** @@ -55,11 +54,7 @@ namespace GridKit SubsystemModel() { // Set system model parameters as default - rel_tol_ = 1e-4; - abs_tol_ = 1e-4; - this->max_steps_ = 2000; - // By default don't use the jacobian - use_jac_ = false; + use_jac_ = false; } virtual ~SubsystemModel() @@ -94,15 +89,20 @@ namespace GridKit CircuitComponent::allocate(); - y_.resize(n_intern_); - yp_.resize(n_intern_); - f_.resize(n_intern_); - neumaier_compensation.resize(n_intern_); + // Allocation always rebuilds the system Jacobian and its COO-to-CSR map. + delete csr_jac_; + csr_jac_ = nullptr; + + delete[] map_to_csr_; + map_to_csr_ = nullptr; + + tag_.resize(size_); // Allocate subsystem vectors. y_ext_.resize(n_extern_); yp_ext_.resize(n_extern_); f_ext_.resize(n_extern_); + external_indices_.resize(n_extern_); // Store the mapping from local subsystem indices back to their global system indices @@ -117,13 +117,16 @@ namespace GridKit external_indices_[local_idx - n_intern_] = global_idx; } - size_t component_internal_idx = 0; + size_t component_internal_idx = 0; + const auto* y = y_.getData(); + const auto* yp = yp_.getData(); + auto* f = f_.getData(); for (component_type* comp : components_) { - comp->setInternalPointer(&y_[component_internal_idx]); - comp->setInternalDerivativePointer(&yp_[component_internal_idx]); - comp->setInternalResidualPointer(&f_[component_internal_idx]); + comp->setInternalPointer(&y[component_internal_idx]); + comp->setInternalDerivativePointer(&yp[component_internal_idx]); + comp->setInternalResidualPointer(&f[component_internal_idx]); component_internal_idx += comp->getInternalSize(); } @@ -229,6 +232,7 @@ namespace GridKit map_to_csr_[map_to_sorted[i]] = map_to_dedup[i]; } + allocated_ = true; return 0; } @@ -244,6 +248,8 @@ namespace GridKit { component->initialize(); } + y_.setDataUpdated(); + yp_.setDataUpdated(); this->distributeVectors(); return 0; @@ -273,11 +279,14 @@ namespace GridKit std::copy(yp_forcing.begin(), yp_forcing.end(), yp_ext_.begin()); } + const auto* y_system = y_.getData(); + const auto* yp_system = yp_.getData(); + for (component_type* component : components_) { - std::vector& y = component->y(); - std::vector& yp = component->yp(); - const std::set& externals = component->getExternIndices(); + auto* y = component->y().getData(); + auto* yp = component->yp().getData(); + const auto& externals = component->getExternIndices(); for (size_t j : externals) { @@ -290,8 +299,8 @@ namespace GridKit } else if (local_idx < this->getInternalSize()) { - y[j] = y_[local_idx]; - yp[j] = yp_[local_idx]; + y[j] = y_system[local_idx]; + yp[j] = yp_system[local_idx]; } else { @@ -299,6 +308,8 @@ namespace GridKit yp[j] = yp_ext_[local_idx - this->getInternalSize()]; } } + component->y().setDataUpdated(); + component->yp().setDataUpdated(); } return 0; } @@ -315,12 +326,16 @@ namespace GridKit */ int evaluateInternalResidual() final { + auto* f = f_.getData(); + for (IdxT i = 0; i < this->getInternalSize(); i++) { - f_[i] = 0.0; + f[i] = 0.0; - if (use_neumaier_sum) - neumaier_compensation[i] = 0.0; + // if (use_neumaier_sum) + // { + // neumaier_compensation[i] = 0.0; + // } } this->distributeVectors(); @@ -340,27 +355,33 @@ namespace GridKit if (int err_code = component->evaluateExternalResidual()) return err_code; - const std::vector& residual = component->getResidual(); - const std::set& externals = component->getExternIndices(); + const auto* residual = component->getResidual().getData(); + const auto& externals = component->getExternIndices(); for (size_t j : externals) { //@todo should do a different grounding check if (component->getNodeConnection(j) != neg1_ && component->getNodeConnection(j) < this->getInternalSize()) { - if (!use_neumaier_sum) - { - f_[component->getNodeConnection(j)] += residual[j]; - } - else - { - getNeumaierCompensation(f_[component->getNodeConnection(j)], residual[j], component->getNodeConnection(j)); - } + // if (!use_neumaier_sum) + // { + f[component->getNodeConnection(j)] += residual[j]; + // } + // else + // { + // getNeumaierCompensation(f_[component->getNodeConnection(j)], residual[j], component->getNodeConnection(j)); + // } } } } - if (use_neumaier_sum) - addNeumaierCompensation(f_); + + // if (use_neumaier_sum) + // { + // addNeumaierCompensation(f_); + // } + + f_.setDataUpdated(); + return 0; } @@ -420,7 +441,6 @@ namespace GridKit } jac_call_count_++; - return 0; } @@ -463,6 +483,24 @@ namespace GridKit return 0; } + /** + * @brief Compute the absolute tolerance for each variable in the model + * + * @param rel_tol The relative tolerance which can be used to pick the + * absolute tolerance. + * @tparam ScalarT Scalar data type + * @tparam IdxT Index data type + * @return int 0 if successful, non-zero otherwise. + * + * This represents a "noise" level close to zero for which pure relative + * error cannot be used. + */ + int setAbsoluteTolerance(RealT rel_tol) final + { + abs_tol_.setToConst(static_cast(rel_tol)); + return 0; + } + /** * @brief Distribute time and time scaling for each component * @@ -621,27 +659,27 @@ namespace GridKit forcing_function_ = std::move(function); } - void getNeumaierCompensation(ScalarT& sum, const ScalarT& x, const IdxT& index) - { - ScalarT t = sum + x; - if (std::abs(sum) >= std::abs(x)) - { - neumaier_compensation[index] += (sum - t) + x; - } - else - { - neumaier_compensation[index] += (x - t) + sum; - } - sum = t; - } - - void addNeumaierCompensation(std::vector& f) - { - for (IdxT i = 0; i < this->f_.size(); i++) - { - f[i] += neumaier_compensation[i]; - } - } + // void getNeumaierCompensation(ScalarT& sum, const ScalarT& x, const IdxT& index) + // { + // ScalarT t = sum + x; + // if (std::abs(sum) >= std::abs(x)) + // { + // neumaier_compensation[index] += (sum - t) + x; + // } + // else + // { + // neumaier_compensation[index] += (x - t) + sum; + // } + // sum = t; + // } + + // void addNeumaierCompensation(VectorT f) + // { + // for (IdxT i = 0; i < size_; i++) + // { + // f[i] += neumaier_compensation[i]; + // } + // } std::unordered_map& getInternalMap() { diff --git a/examples/PowerElectronics/Partition/Partition.cpp b/examples/PowerElectronics/Partition/Partition.cpp index acd56914f..ab6d981f5 100644 --- a/examples/PowerElectronics/Partition/Partition.cpp +++ b/examples/PowerElectronics/Partition/Partition.cpp @@ -1,271 +1,270 @@ -#include - -#include "GridKit/LinearAlgebra/DenseMatrix/DenseMatrix.hpp" +// #include #define _USE_MATH_DEFINES #include #include -#include -#include -#include +// #include +// #include +// #include #include #include #include -#include "HiresBus.hpp" -#include "HiresComponent1.hpp" -#include "HiresComponent3.hpp" +// #include "HiresBus.hpp" +// #include "HiresComponent1.hpp" +// #include "HiresComponent3.hpp" std::vector hires(const std::vector& y, const std::vector& yp); -template -int jac_hires(const std::vector& y, [[maybe_unused]] const std::vector& yp, GridKit::LinearAlgebra::DenseMatrix& jac, RealT alpha); +// template +// int jac_hires(const std::vector& y, [[maybe_unused]] const std::vector& yp, GridKit::LinearAlgebra::DenseMatrix& jac, RealT alpha); -template -void addCsrToDense( - GridKit::LinearAlgebra::CsrMatrix* csr, - GridKit::LinearAlgebra::DenseMatrix& dense, - GridKit::SubsystemModel* partition); +// template +// void addCsrToDense( +// GridKit::LinearAlgebra::CsrMatrix* csr, +// GridKit::LinearAlgebra::DenseMatrix& dense, +// GridKit::SubsystemModel* partition); int main(int /* argc */, char const** /* argv */) { - using Bus = GridKit::PowerElectronics::MicrogridBus; - Bus bus; - - GridKit::SubsystemModel* partition1 = new GridKit::SubsystemModel(); - GridKit::SubsystemModel* partition2 = new GridKit::SubsystemModel(); - - GridKit::HiresComponent1* comp1 = new GridKit::HiresComponent1(1); - GridKit::HiresBus* bus1 = new GridKit::HiresBus(2); - GridKit::HiresComponent3* comp3 = new GridKit::HiresComponent3(3); - - bus.allocate(); - - bus.setExternalConnectionNodes(0, 3); - bus.setExternalConnectionNodes(1, 4); - - comp1->allocate(); - comp3->allocate(); - bus1->allocate(); - - comp1->setExternalConnectionNodes(0, 3); - comp1->setExternalConnectionNodes(1, 4); - comp1->setExternalConnectionNodes(2, 0); - comp1->setExternalConnectionNodes(3, 1); - comp1->setExternalConnectionNodes(4, 2); - - bus1->setExternalConnectionNodes(0, 3); - bus1->setExternalConnectionNodes(1, 4); - - comp3->setExternalConnectionNodes(0, 3); - comp3->setExternalConnectionNodes(1, 4); - comp3->setExternalConnectionNodes(2, 5); - comp3->setExternalConnectionNodes(3, 6); - comp3->setExternalConnectionNodes(4, 7); - - GridKit::HiresComponent3 - comp3copy(*comp3); - GridKit::BusPartitionInterface* busInterface = new GridKit::BusPartitionInterface(bus, comp3copy, 4); - - busInterface->allocate(); - partition1->addComponent(comp1); - partition1->addComponent(bus1); - partition1->addComponent(busInterface); - partition1->addNode(&bus); - - partition2->addComponent(comp3); - - std::cout << "Msg" << std::endl; - partition1->allocate(); - partition2->allocate(); - - std::vector y = {1, 2, 3, 4, 5, 6, 7, 8}; - std::vector yp = {1, 2, 3, 4, 5, 6, 7, 8}; - - // Distribute externals to partition 1 - for (size_t i = 0; i < partition1->getExternSize(); i++) - { - partition1->getExternalDataY()[i] = y[partition1->getExternalIndices()[i]]; - partition1->getExternalDataYP()[i] = yp[partition1->getExternalIndices()[i]]; - } - - // Distribute externals to partition 2 - for (size_t i = 0; i < partition2->getExternSize(); i++) - { - partition2->getExternalDataY()[i] = y[partition2->getExternalIndices()[i]]; - partition2->getExternalDataYP()[i] = yp[partition2->getExternalIndices()[i]]; - } - - // Distribute internals to partition 1 - for (size_t i = 0; i < partition1->getInternalSize(); i++) - { - partition1->y()[i] = static_cast(1 + i); - partition1->yp()[i] = static_cast(1 + i); - } - - // Distribute internals to partition 2 - for (size_t i = 0; i < partition2->getInternalSize(); i++) - { - partition2->y()[i] = static_cast(6 + i); - partition2->yp()[i] = static_cast(6 + i); - } - - // Evaluate Residuals for each partition - partition1->evaluateResidual(); - partition2->evaluateResidual(); - - auto printTitle = [](std::string msg) -> void - { - std::cout << "\n------------- " << msg << " -----------" << std::endl; - printf("%-10s ---------- %10s\n", "Res Values", "Comp. Index"); - }; - - // Print Residuals from partition 1 - printTitle("Partition 1"); - for (size_t i = 0; i < partition1->getInternalSize(); i++) - { - auto com_index = partition1->getNodeConnection(static_cast(i)); - printf("%-10.5g ---------- %10zu\n", partition1->getResidual()[i], com_index); - } - - // Print Residuals from partition 2 - printTitle("Partition 2"); - for (size_t i = 0; i < partition2->getInternalSize(); i++) - { - auto com_index = partition2->getNodeConnection(static_cast(i)); - printf("%-10.5g ---------- %10zu\n", partition2->getResidual()[i], com_index); - } - - auto ref = hires(y, yp); - - printTitle("Reference Solution"); - for (size_t i = 0; i < 8; i++) - { - printf("%-10.5g ---------- %10zu\n", ref[i], i); - } - - partition1->updateTime(0, 1.0); - partition2->updateTime(0, 1.0); - - partition1->evaluateJacobian(); - partition2->evaluateJacobian(); - - GridKit::LinearAlgebra::DenseMatrix jac1(8, 8); - GridKit::LinearAlgebra::DenseMatrix jac2(8, 8); - - GridKit::LinearAlgebra::DenseMatrix jac_ref(8, 8); - - addCsrToDense(partition1->getCsrJacobian(), jac1, partition1); - addCsrToDense(partition2->getCsrJacobian(), jac2, partition2); - jac_hires(y, yp, jac_ref, 1.0); - - auto print_dense = [&](GridKit::LinearAlgebra::DenseMatrix& jc) -> void - { - for (size_t i = 0; i < 8; ++i) - { - for (size_t j = 0; j < 8; ++j) - { - std::cout << std::setw(10) << jc.getValue(i, j) << ' '; - } - std::cout << '\n'; - } - std::cout << '\n'; - }; - - print_dense(jac1); - print_dense(jac2); - print_dense(jac_ref); - - delete comp1; - delete bus1; - delete busInterface; - delete comp3; - delete partition1; - delete partition2; -} - -std::vector hires(const std::vector& y, const std::vector& yp) -{ - std::vector f(8); - - f[0] = -yp[0] - 1.71 * y[0] + 0.43 * y[1] + 8.32 * y[2] + 0.0007; - f[1] = -yp[1] + 1.71 * y[0] - 8.75 * y[1]; - f[2] = -yp[2] - 10.03 * y[2] + 0.43 * y[3] + 0.035 * y[4]; - f[3] = -yp[3] + 8.32 * y[1] + 1.71 * y[2] - 1.12 * y[3]; - f[4] = -yp[4] - 1.745 * y[4] + 0.43 * y[5] + 0.43 * y[6]; - f[5] = -yp[5] - 280 * y[5] * y[7] + 0.69 * y[3] + 1.71 * y[4] - 0.43 * y[5] + 0.69 * y[6]; - f[6] = -yp[6] + 280 * y[5] * y[7] - 1.81 * y[6]; - f[7] = -yp[7] - 280 * y[5] * y[7] + 1.81 * y[6]; - - return f; -} - -template -int jac_hires(const std::vector& y, [[maybe_unused]] const std::vector& yp, GridKit::LinearAlgebra::DenseMatrix& jac, RealT alpha) -{ - - jac.setValue(0, 0, -alpha - 1.71); - jac.setValue(0, 1, 0.43); - jac.setValue(0, 2, 8.32); - - jac.setValue(1, 0, 1.71); - jac.setValue(1, 1, -alpha - 8.75); - - jac.setValue(2, 2, -alpha - 10.03); - jac.setValue(2, 3, 0.43); - jac.setValue(2, 4, 0.035); - - jac.setValue(3, 1, 8.32); - jac.setValue(3, 2, 1.71); - jac.setValue(3, 3, -alpha - 1.12); - - jac.setValue(4, 4, -alpha - 1.745); - jac.setValue(4, 5, 0.43); - jac.setValue(4, 6, 0.43); - - jac.setValue(5, 3, 0.69); - jac.setValue(5, 4, 1.71); - jac.setValue(5, 5, -alpha - 280.0 * y[7] - 0.43); - jac.setValue(5, 6, 0.69); - jac.setValue(5, 7, -280.0 * y[5]); - - jac.setValue(6, 5, 280.0 * y[7]); - jac.setValue(6, 6, -alpha - 1.81); - jac.setValue(6, 7, 280.0 * y[5]); - - jac.setValue(7, 5, -280.0 * y[7]); - jac.setValue(7, 6, 1.81); - jac.setValue(7, 7, -alpha - 280.0 * y[5]); - + // using Bus = GridKit::PowerElectronics::MicrogridBus; + // Bus bus; + + // GridKit::SubsystemModel* partition1 = new GridKit::SubsystemModel(); + // GridKit::SubsystemModel* partition2 = new GridKit::SubsystemModel(); + + // GridKit::HiresComponent1* comp1 = new GridKit::HiresComponent1(1); + // GridKit::HiresBus* bus1 = new GridKit::HiresBus(2); + // GridKit::HiresComponent3* comp3 = new GridKit::HiresComponent3(3); + + // bus.allocate(); + + // bus.setExternalConnectionNodes(0, 3); + // bus.setExternalConnectionNodes(1, 4); + + // comp1->allocate(); + // comp3->allocate(); + // bus1->allocate(); + + // comp1->setExternalConnectionNodes(0, 3); + // comp1->setExternalConnectionNodes(1, 4); + // comp1->setExternalConnectionNodes(2, 0); + // comp1->setExternalConnectionNodes(3, 1); + // comp1->setExternalConnectionNodes(4, 2); + + // bus1->setExternalConnectionNodes(0, 3); + // bus1->setExternalConnectionNodes(1, 4); + + // comp3->setExternalConnectionNodes(0, 3); + // comp3->setExternalConnectionNodes(1, 4); + // comp3->setExternalConnectionNodes(2, 5); + // comp3->setExternalConnectionNodes(3, 6); + // comp3->setExternalConnectionNodes(4, 7); + + // GridKit::HiresComponent3 + // comp3copy(*comp3); + // GridKit::BusPartitionInterface* busInterface = new GridKit::BusPartitionInterface(bus, comp3copy, 4); + + // busInterface->allocate(); + // partition1->addComponent(comp1); + // partition1->addComponent(bus1); + // partition1->addComponent(busInterface); + // partition1->addNode(&bus); + + // partition2->addComponent(comp3); + + // std::cout << "Msg" << std::endl; + // partition1->allocate(); + // partition2->allocate(); + + // std::vector y = {1, 2, 3, 4, 5, 6, 7, 8}; + // std::vector yp = {1, 2, 3, 4, 5, 6, 7, 8}; + + // // Distribute externals to partition 1 + // for (size_t i = 0; i < partition1->getExternSize(); i++) + // { + // partition1->getExternalDataY()[i] = y[partition1->getExternalIndices()[i]]; + // partition1->getExternalDataYP()[i] = yp[partition1->getExternalIndices()[i]]; + // } + + // // Distribute externals to partition 2 + // for (size_t i = 0; i < partition2->getExternSize(); i++) + // { + // partition2->getExternalDataY()[i] = y[partition2->getExternalIndices()[i]]; + // partition2->getExternalDataYP()[i] = yp[partition2->getExternalIndices()[i]]; + // } + + // // Distribute internals to partition 1 + // for (size_t i = 0; i < partition1->getInternalSize(); i++) + // { + // partition1->y()[i] = static_cast(1 + i); + // partition1->yp()[i] = static_cast(1 + i); + // } + + // // Distribute internals to partition 2 + // for (size_t i = 0; i < partition2->getInternalSize(); i++) + // { + // partition2->y()[i] = static_cast(6 + i); + // partition2->yp()[i] = static_cast(6 + i); + // } + + // // Evaluate Residuals for each partition + // partition1->evaluateResidual(); + // partition2->evaluateResidual(); + + // auto printTitle = [](std::string msg) -> void + // { + // std::cout << "\n------------- " << msg << " -----------" << std::endl; + // printf("%-10s ---------- %10s\n", "Res Values", "Comp. Index"); + // }; + + // // Print Residuals from partition 1 + // printTitle("Partition 1"); + // for (size_t i = 0; i < partition1->getInternalSize(); i++) + // { + // auto com_index = partition1->getNodeConnection(static_cast(i)); + // printf("%-10.5g ---------- %10zu\n", partition1->getResidual()[i], com_index); + // } + + // // Print Residuals from partition 2 + // printTitle("Partition 2"); + // for (size_t i = 0; i < partition2->getInternalSize(); i++) + // { + // auto com_index = partition2->getNodeConnection(static_cast(i)); + // printf("%-10.5g ---------- %10zu\n", partition2->getResidual()[i], com_index); + // } + + // auto ref = hires(y, yp); + + // printTitle("Reference Solution"); + // for (size_t i = 0; i < 8; i++) + // { + // printf("%-10.5g ---------- %10zu\n", ref[i], i); + // } + + // partition1->updateTime(0, 1.0); + // partition2->updateTime(0, 1.0); + + // partition1->evaluateJacobian(); + // partition2->evaluateJacobian(); + + // GridKit::LinearAlgebra::DenseMatrix jac1(8, 8); + // GridKit::LinearAlgebra::DenseMatrix jac2(8, 8); + + // GridKit::LinearAlgebra::DenseMatrix jac_ref(8, 8); + + // addCsrToDense(partition1->getCsrJacobian(), jac1, partition1); + // addCsrToDense(partition2->getCsrJacobian(), jac2, partition2); + // jac_hires(y, yp, jac_ref, 1.0); + + // auto print_dense = [&](GridKit::LinearAlgebra::DenseMatrix& jc) -> void + // { + // for (size_t i = 0; i < 8; ++i) + // { + // for (size_t j = 0; j < 8; ++j) + // { + // std::cout << std::setw(10) << jc.getValue(i, j) << ' '; + // } + // std::cout << '\n'; + // } + // std::cout << '\n'; + // }; + + // print_dense(jac1); + // print_dense(jac2); + // print_dense(jac_ref); + + // delete comp1; + // delete bus1; + // delete busInterface; + // delete comp3; + // delete partition1; + // delete partition2; return 0; } -template -void addCsrToDense( - GridKit::LinearAlgebra::CsrMatrix* csr, - GridKit::LinearAlgebra::DenseMatrix& dense, - GridKit::SubsystemModel* partition) -{ - auto* row_ptr = csr->getRowData(GridKit::LinearAlgebra::memory::HOST); - auto* col_ind = csr->getColData(GridKit::LinearAlgebra::memory::HOST); - auto* values = csr->getValues(GridKit::LinearAlgebra::memory::HOST); - - assert(row_ptr != nullptr); - assert(col_ind != nullptr); - assert(values != nullptr); - - for (IdxT row = 0; row < csr->getNumRows(); ++row) - { - for (IdxT k = row_ptr[row]; k < row_ptr[row + 1]; ++k) - { - const IdxT col = col_ind[k]; - - auto r = partition->getNodeConnection(row); - auto c = partition->getNodeConnection(col); - - dense.setValue(r, c, dense.getValue(r, c) + values[k]); - } - } -} +// std::vector hires(const std::vector& y, const std::vector& yp) +// { +// std::vector f(8); + +// f[0] = -yp[0] - 1.71 * y[0] + 0.43 * y[1] + 8.32 * y[2] + 0.0007; +// f[1] = -yp[1] + 1.71 * y[0] - 8.75 * y[1]; +// f[2] = -yp[2] - 10.03 * y[2] + 0.43 * y[3] + 0.035 * y[4]; +// f[3] = -yp[3] + 8.32 * y[1] + 1.71 * y[2] - 1.12 * y[3]; +// f[4] = -yp[4] - 1.745 * y[4] + 0.43 * y[5] + 0.43 * y[6]; +// f[5] = -yp[5] - 280 * y[5] * y[7] + 0.69 * y[3] + 1.71 * y[4] - 0.43 * y[5] + 0.69 * y[6]; +// f[6] = -yp[6] + 280 * y[5] * y[7] - 1.81 * y[6]; +// f[7] = -yp[7] - 280 * y[5] * y[7] + 1.81 * y[6]; + +// return f; +// } + +// template +// int jac_hires(const std::vector& y, [[maybe_unused]] const std::vector& yp, GridKit::LinearAlgebra::DenseMatrix& jac, RealT alpha) +// { + +// jac.setValue(0, 0, -alpha - 1.71); +// jac.setValue(0, 1, 0.43); +// jac.setValue(0, 2, 8.32); + +// jac.setValue(1, 0, 1.71); +// jac.setValue(1, 1, -alpha - 8.75); + +// jac.setValue(2, 2, -alpha - 10.03); +// jac.setValue(2, 3, 0.43); +// jac.setValue(2, 4, 0.035); + +// jac.setValue(3, 1, 8.32); +// jac.setValue(3, 2, 1.71); +// jac.setValue(3, 3, -alpha - 1.12); + +// jac.setValue(4, 4, -alpha - 1.745); +// jac.setValue(4, 5, 0.43); +// jac.setValue(4, 6, 0.43); + +// jac.setValue(5, 3, 0.69); +// jac.setValue(5, 4, 1.71); +// jac.setValue(5, 5, -alpha - 280.0 * y[7] - 0.43); +// jac.setValue(5, 6, 0.69); +// jac.setValue(5, 7, -280.0 * y[5]); + +// jac.setValue(6, 5, 280.0 * y[7]); +// jac.setValue(6, 6, -alpha - 1.81); +// jac.setValue(6, 7, 280.0 * y[5]); + +// jac.setValue(7, 5, -280.0 * y[7]); +// jac.setValue(7, 6, 1.81); +// jac.setValue(7, 7, -alpha - 280.0 * y[5]); + +// return 0; +// } + +// template +// void addCsrToDense( +// GridKit::LinearAlgebra::CsrMatrix* csr, +// GridKit::LinearAlgebra::DenseMatrix& dense, +// GridKit::SubsystemModel* partition) +// { +// auto* row_ptr = csr->getRowData(GridKit::LinearAlgebra::memory::HOST); +// auto* col_ind = csr->getColData(GridKit::LinearAlgebra::memory::HOST); +// auto* values = csr->getValues(GridKit::LinearAlgebra::memory::HOST); + +// assert(row_ptr != nullptr); +// assert(col_ind != nullptr); +// assert(values != nullptr); + +// for (IdxT row = 0; row < csr->getNumRows(); ++row) +// { +// for (IdxT k = row_ptr[row]; k < row_ptr[row + 1]; ++k) +// { +// const IdxT col = col_ind[k]; + +// auto r = partition->getNodeConnection(row); +// auto c = partition->getNodeConnection(col); + +// dense.setValue(r, c, dense.getValue(r, c) + values[k]); +// } +// } +// } diff --git a/examples/PowerElectronics/Partition/PartitionMicrogrid.cpp b/examples/PowerElectronics/Partition/PartitionMicrogrid.cpp index 460b8ba67..6dd8563d7 100644 --- a/examples/PowerElectronics/Partition/PartitionMicrogrid.cpp +++ b/examples/PowerElectronics/Partition/PartitionMicrogrid.cpp @@ -27,13 +27,11 @@ int main() { /// @todo Needs to be modified. Some components are small relative to others thus /// there error is high (or could be matlab vector issue) - double abs_tol = 1.0e-8; - double rel_tol = 1.0e-8; - size_t max_step_number = 3000; - bool use_jac = true; + + bool use_jac = true; // Create model - auto* sysmodel = new GridKit::PowerElectronicsModel(rel_tol, abs_tol, use_jac, max_step_number); + auto* sysmodel = new GridKit::PowerElectronicsModel(use_jac); // Modeled after the problem in the paper double RN = 1.0e4; @@ -207,19 +205,25 @@ int main() yp.push_back(static_cast(i + 1)); } + auto* system_y = sysmodel->y().getData(); + auto* system_yp = sysmodel->yp().getData(); + for (size_t i = 0; i < sysmodel->size(); i++) { - sysmodel->y()[i] = y[i]; - sysmodel->yp()[i] = yp[i]; + system_y[i] = y[i]; + system_yp[i] = yp[i]; } + sysmodel->y().setDataUpdated(); + sysmodel->yp().setDataUpdated(); + sysmodel->updateTime(2, 5); sysmodel->evaluateResidual(); sysmodel->evaluateJacobian(); auto full_jac = sysmodel->getCsrJacobian(); - std::vector f_sysmodel = sysmodel->getResidual(); + auto* f_sysmodel = sysmodel->getResidual().getData(); std::vector*> partitions = {partition1, partition2}; @@ -236,12 +240,18 @@ int main() partition->getExternalDataYP()[i] = yp[partition->getExternalIndices()[i]]; } + auto* partition_y = partition->y().getData(); + auto* partition_yp = partition->yp().getData(); + for (size_t i = 0; i < partition->getInternalSize(); i++) { - partition->y()[i] = y[partition->getNodeConnection(i)]; - partition->yp()[i] = yp[partition->getNodeConnection(i)]; + partition_y[i] = y[partition->getNodeConnection(i)]; + partition_yp[i] = yp[partition->getNodeConnection(i)]; } + partition->y().setDataUpdated(); + partition->yp().setDataUpdated(); + partition->evaluateResidual(); partition->evaluateJacobian(); } @@ -266,30 +276,36 @@ int main() // Get internal residuals from partition 1 for (auto* partition : partitions) { + const auto* residual = partition->getResidual().getData(); + for (size_t i = 0; i < partition->getInternalSize(); i++) { - f[partition->getNodeConnection(i)] = partition->getResidual()[i]; + + f[partition->getNodeConnection(i)] = residual[i]; } + std::cout << std::endl; } double max_error = 0; for (size_t i = 0; i < sysmodel->size(); i++) { error[i] = (f_sysmodel[i] - f[i]) / f_sysmodel[i]; + + std::cout << i << " " << f[i] << " " << f_sysmodel[i] << " " << error[i] << std::endl; if (max_error < std::abs(error[i])) { max_error = std::abs(error[i]); } } + std::cout << "\nMax Error of Reference and Partition Evaluation: " << max_error << std::endl; + if (max_error > std::numeric_limits::epsilon()) { std::cout << "ERROR: Max Error too high!" << std::endl; return 1; } - std::cout << "\nMax Error of Reference and Partition Evaluation: " << max_error << std::endl; - delete sysmodel; delete partition1; delete partition2; diff --git a/examples/PowerElectronics/Partition/PartitionScaleMicrogrid.cpp b/examples/PowerElectronics/Partition/PartitionScaleMicrogrid.cpp index a27f0092a..2a6da84d0 100644 --- a/examples/PowerElectronics/Partition/PartitionScaleMicrogrid.cpp +++ b/examples/PowerElectronics/Partition/PartitionScaleMicrogrid.cpp @@ -119,15 +119,8 @@ RunResult printMicrogridSystems(index_type N_size, index_type num_partitions) bool use_jac = true; - real_type rel_tol = 1e-5; - real_type abs_tol = 1e-5; - index_type max_steps = 2000; - // Create circuit model - auto* sys_model_control = new PowerElectronicsModel(rel_tol, - abs_tol, - use_jac, - max_steps); + auto* sys_model_control = new PowerElectronicsModel(use_jac); // Ensure minimum size requirement assert(N_size >= 1); @@ -176,9 +169,13 @@ RunResult printMicrogridSystems(index_type N_size, index_type num_partitions) // First two generators use parameters 1 if (DGParams_list.size() >= 1) + { DGParams_list[0] = DG_parms1; + } if (DGParams_list.size() >= 2) + { DGParams_list[1] = DG_parms1; + } // line vector params // Every odd line has the same parameters and every even line has the same parameters @@ -326,12 +323,18 @@ RunResult printMicrogridSystems(index_type N_size, index_type num_partitions) auto start_time = std::chrono::high_resolution_clock::now(); + auto* sys_model_y = sys_model_control->y().getData(); + auto* sys_model_yp = sys_model_control->yp().getData(); + for (size_t i = 0; i < sys_model_control->size(); i++) { - sys_model_control->y()[i] = y[i]; - sys_model_control->yp()[i] = yp[i]; + sys_model_y[i] = y[i]; + sys_model_yp[i] = yp[i]; } + sys_model_control->y().setDataUpdated(); + sys_model_control->yp().setDataUpdated(); + sys_model_control->updateTime(2, 5); sys_model_control->evaluateResidual(); @@ -340,7 +343,7 @@ RunResult printMicrogridSystems(index_type N_size, index_type num_partitions) sys_model_control->evaluateJacobian(); - auto& f_sysmodel_control = sys_model_control->getResidual(); + auto* f_sysmodel_control = sys_model_control->getResidual().getData(); auto* full_jac = sys_model_control->getCsrJacobian(); //--------------------------------------------------------------- @@ -437,20 +440,27 @@ RunResult printMicrogridSystems(index_type N_size, index_type num_partitions) partition->getExternalDataYP()[i] = yp[partition->getExternalIndices()[i]]; } + auto* partition_y = partition->y().getData(); + auto* partition_yp = partition->yp().getData(); + // Distribute internal variables for (size_t i = 0; i < partition->getInternalSize(); i++) { - partition->y()[i] = y[partition->getNodeConnection(i)]; - partition->yp()[i] = yp[partition->getNodeConnection(i)]; + partition_y[i] = y[partition->getNodeConnection(i)]; + partition_yp[i] = yp[partition->getNodeConnection(i)]; } + partition->y().setDataUpdated(); + partition->yp().setDataUpdated(); + // Evaluate residual of this partition partition->evaluateResidual(); + auto* residual = partition->getResidual().getData(); // Reconstructs the monolithic residual from the partition residuals for (size_t i = 0; i < partition->getInternalSize(); i++) { - f[partition->getNodeConnection(i)] = partition->getResidual()[i]; + f[partition->getNodeConnection(i)] = residual[i]; } } @@ -509,11 +519,17 @@ RunResult printMicrogridSystems(index_type N_size, index_type num_partitions) } for (auto* linescpy : linesCopies) + { delete linescpy; + } for (auto* partition : subsystems) + { delete partition; + } for (auto* part_interface : partitionInterface) + { delete part_interface; + } delete sys_model_control; return result; diff --git a/examples/PowerElectronics/Partition/jac_test_helper.hpp b/examples/PowerElectronics/Partition/jac_test_helper.hpp index 10daf9631..8851bc2a3 100644 --- a/examples/PowerElectronics/Partition/jac_test_helper.hpp +++ b/examples/PowerElectronics/Partition/jac_test_helper.hpp @@ -6,6 +6,7 @@ #include #include +#include #include namespace GridKit @@ -20,12 +21,11 @@ namespace GridKit GridKit::SubsystemModel& subsystem, RealT tolerance = static_cast(1e-12)) { - constexpr auto host = GridKit::LinearAlgebra::memory::HOST; - + constexpr auto host = memory::HOST; // Full-system CSR data. - const IdxT* full_rows = full_jac.getRowData(host); - const IdxT* full_cols = full_jac.getColData(host); - const RealT* full_vals = full_jac.getValues(host); + const IdxT* full_rows = full_jac.getRowData(host); + const IdxT* full_cols = full_jac.getColData(host); + const RealT* full_vals = full_jac.getValues(host); // Subsystem CSR data. const IdxT* sub_rows = sub_jac.getRowData(host); From 74ac54a830b25eb091f2f5ff864d8097c81371ea Mon Sep 17 00:00:00 2001 From: abdourahmanbarry Date: Mon, 20 Jul 2026 05:06:22 +0000 Subject: [PATCH 24/52] Updated copy constructor in circuit component --- GridKit/Model/PowerElectronics/CircuitComponent.hpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/GridKit/Model/PowerElectronics/CircuitComponent.hpp b/GridKit/Model/PowerElectronics/CircuitComponent.hpp index 72bbda514..f9a8e6ab5 100644 --- a/GridKit/Model/PowerElectronics/CircuitComponent.hpp +++ b/GridKit/Model/PowerElectronics/CircuitComponent.hpp @@ -65,9 +65,7 @@ namespace GridKit } destination.resize(source_size); - - const int error_code = destination.copyFromExternal(source); - assert(error_code == 0); + destination.copyFromExternal(source); }; /* From 0bd364ac0dacb610df4f08001d5eb62ceb88e842 Mon Sep 17 00:00:00 2001 From: abdourahmanbarry Date: Mon, 20 Jul 2026 05:07:06 +0000 Subject: [PATCH 25/52] Removed Neumaier Compensation --- .../Model/PowerElectronics/SubsystemModel.hpp | 42 ------------------- 1 file changed, 42 deletions(-) diff --git a/GridKit/Model/PowerElectronics/SubsystemModel.hpp b/GridKit/Model/PowerElectronics/SubsystemModel.hpp index cf793725e..51df8a508 100644 --- a/GridKit/Model/PowerElectronics/SubsystemModel.hpp +++ b/GridKit/Model/PowerElectronics/SubsystemModel.hpp @@ -331,11 +331,6 @@ namespace GridKit for (IdxT i = 0; i < this->getInternalSize(); i++) { f[i] = 0.0; - - // if (use_neumaier_sum) - // { - // neumaier_compensation[i] = 0.0; - // } } this->distributeVectors(); @@ -363,23 +358,11 @@ namespace GridKit //@todo should do a different grounding check if (component->getNodeConnection(j) != neg1_ && component->getNodeConnection(j) < this->getInternalSize()) { - // if (!use_neumaier_sum) - // { f[component->getNodeConnection(j)] += residual[j]; - // } - // else - // { - // getNeumaierCompensation(f_[component->getNodeConnection(j)], residual[j], component->getNodeConnection(j)); - // } } } } - // if (use_neumaier_sum) - // { - // addNeumaierCompensation(f_); - // } - f_.setDataUpdated(); return 0; @@ -659,28 +642,6 @@ namespace GridKit forcing_function_ = std::move(function); } - // void getNeumaierCompensation(ScalarT& sum, const ScalarT& x, const IdxT& index) - // { - // ScalarT t = sum + x; - // if (std::abs(sum) >= std::abs(x)) - // { - // neumaier_compensation[index] += (sum - t) + x; - // } - // else - // { - // neumaier_compensation[index] += (x - t) + sum; - // } - // sum = t; - // } - - // void addNeumaierCompensation(VectorT f) - // { - // for (IdxT i = 0; i < size_; i++) - // { - // f[i] += neumaier_compensation[i]; - // } - // } - std::unordered_map& getInternalMap() { return internal_map_; @@ -700,9 +661,6 @@ namespace GridKit std::unordered_map external_map_; std::vector external_indices_; - bool use_neumaier_sum{false}; - - std::vector neumaier_compensation; std::vector y_ext_; std::vector yp_ext_; std::vector f_ext_; From 3b62428be1bbe6d4949a1797655ff3d7b61e9813 Mon Sep 17 00:00:00 2001 From: abdourahmanbarry Date: Mon, 20 Jul 2026 05:07:45 +0000 Subject: [PATCH 26/52] Updated Hires test problem --- .../PowerElectronics/Partition/HiresBus.hpp | 44 ++- .../Partition/HiresComponent1.hpp | 44 ++- .../Partition/HiresComponent3.hpp | 47 ++- .../PowerElectronics/Partition/Partition.cpp | 360 ++++++++---------- 4 files changed, 265 insertions(+), 230 deletions(-) diff --git a/examples/PowerElectronics/Partition/HiresBus.hpp b/examples/PowerElectronics/Partition/HiresBus.hpp index ae834aaba..273570e30 100644 --- a/examples/PowerElectronics/Partition/HiresBus.hpp +++ b/examples/PowerElectronics/Partition/HiresBus.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include namespace GridKit { @@ -11,8 +12,9 @@ namespace GridKit template class HiresBus : public CircuitComponent { - using RealT = typename CircuitComponent::RealT; - using MatrixT = typename CircuitComponent::MatrixT; + + using RealT = typename CircuitComponent::RealT; + using NodeT = typename PowerElectronics::NodeBase; using CircuitComponent::size_; using CircuitComponent::nnz_; @@ -23,6 +25,7 @@ namespace GridKit using CircuitComponent::yp_; using CircuitComponent::yp_int_; using CircuitComponent::tag_; + using CircuitComponent::abs_tol_; using CircuitComponent::f_; using CircuitComponent::f_int_; using CircuitComponent::g_; @@ -38,7 +41,7 @@ namespace GridKit using CircuitComponent::n_intern_; public: - HiresBus(IdxT id) + HiresBus(NodeT* bus, IdxT id) : node_ref_(bus) { size_ = 2; n_intern_ = 0; @@ -56,6 +59,9 @@ namespace GridKit { CircuitComponent::allocate(); + this->setExternalConnectionNodes(0, node_ref_->getNodeConnection(0)); + this->setExternalConnectionNodes(1, node_ref_->getNodeConnection(1)); + return 0; } @@ -76,8 +82,15 @@ namespace GridKit int evaluateExternalResidual() { - f_[0] = -yp_[0] - y_[0]; - f_[1] = -yp_[1] - y_[1]; + auto* y = y_.getData(); + auto* yp = yp_.getData(); + auto* f = f_.getData(); + + f[0] = -yp[0] - y[0]; + f[1] = -yp[1] - y[1]; + + f_.setDataUpdated(); + return 0; } @@ -113,5 +126,26 @@ namespace GridKit { return 0; } + + /** + * @brief Compute the absolute tolerance for each variable in the model + * + * @param rel_tol The relative tolerance which can be used to pick the + * absolute tolerance. + * @tparam ScalarT Scalar data type + * @tparam IdxT Index data type + * @return int 0 if successful, non-zero otherwise. + * + * This represents a "noise" level close to zero for which pure relative + * error cannot be used. + */ + int setAbsoluteTolerance(RealT rel_tol) + { + abs_tol_.setToConst(static_cast(rel_tol)); + return 0; + } + + private: + NodeT* node_ref_; }; } // namespace GridKit diff --git a/examples/PowerElectronics/Partition/HiresComponent1.hpp b/examples/PowerElectronics/Partition/HiresComponent1.hpp index d72a7a068..d8931739f 100644 --- a/examples/PowerElectronics/Partition/HiresComponent1.hpp +++ b/examples/PowerElectronics/Partition/HiresComponent1.hpp @@ -3,6 +3,7 @@ #pragma once #include +#include namespace GridKit { @@ -13,8 +14,8 @@ namespace GridKit template class HiresComponent1 : public CircuitComponent { - using RealT = typename CircuitComponent::RealT; - using MatrixT = typename CircuitComponent::MatrixT; + using RealT = typename CircuitComponent::RealT; + using NodeT = typename PowerElectronics::NodeBase; using CircuitComponent::size_; using CircuitComponent::nnz_; @@ -25,6 +26,7 @@ namespace GridKit using CircuitComponent::yp_; using CircuitComponent::yp_int_; using CircuitComponent::tag_; + using CircuitComponent::abs_tol_; using CircuitComponent::f_; using CircuitComponent::f_int_; using CircuitComponent::g_; @@ -40,7 +42,7 @@ namespace GridKit using CircuitComponent::n_intern_; public: - HiresComponent1(IdxT id) + HiresComponent1(NodeT* bus, IdxT id) : node_ref_(bus) { size_ = 5; n_intern_ = 3; @@ -58,6 +60,9 @@ namespace GridKit { CircuitComponent::allocate(); + this->setExternalConnectionNodes(0, node_ref_->getNodeConnection(0)); + this->setExternalConnectionNodes(1, node_ref_->getNodeConnection(1)); + return 0; } @@ -73,20 +78,26 @@ namespace GridKit int evaluateInternalResidual() { + auto* y = y_.getData(); // Internals f_int_[0] = -yp_int_[0] - 1.71 * y_int_[0] + 0.43 * y_int_[1] + 8.32 * y_int_[2] + 0.0007; f_int_[1] = -yp_int_[1] + 1.71 * y_int_[0] - 8.75 * y_int_[1]; - f_int_[2] = -yp_int_[2] - 10.03 * y_int_[2] + 0.43 * y_[0] + 0.035 * y_[1]; + f_int_[2] = -yp_int_[2] - 10.03 * y_int_[2] + 0.43 * y[0] + 0.035 * y[1]; return 0; } int evaluateExternalResidual() { + auto* y = y_.getData(); + auto* f = f_.getData(); + // outputs - f_[0] = 8.32 * y_int_[1] + 1.71 * y_int_[2] - 0.1 * y_[0]; - f_[1] = -0.7 * y_[1]; + f[0] = 8.32 * y_int_[1] + 1.71 * y_int_[2] - 0.1 * y[0]; + f[1] = -0.7 * y[1]; + + f_.setDataUpdated(); return 0; } @@ -132,5 +143,26 @@ namespace GridKit { return 0; } + + /** + * @brief Compute the absolute tolerance for each variable in the model + * + * @param rel_tol The relative tolerance which can be used to pick the + * absolute tolerance. + * @tparam ScalarT Scalar data type + * @tparam IdxT Index data type + * @return int 0 if successful, non-zero otherwise. + * + * This represents a "noise" level close to zero for which pure relative + * error cannot be used. + */ + int setAbsoluteTolerance(RealT rel_tol) + { + abs_tol_.setToConst(static_cast(rel_tol)); + return 0; + } + + private: + NodeT* node_ref_; }; } // namespace GridKit diff --git a/examples/PowerElectronics/Partition/HiresComponent3.hpp b/examples/PowerElectronics/Partition/HiresComponent3.hpp index 81314895d..92380bb8a 100644 --- a/examples/PowerElectronics/Partition/HiresComponent3.hpp +++ b/examples/PowerElectronics/Partition/HiresComponent3.hpp @@ -1,8 +1,7 @@ - - #pragma once #include +#include namespace GridKit { @@ -13,8 +12,8 @@ namespace GridKit template class HiresComponent3 : public CircuitComponent { - using RealT = typename CircuitComponent::RealT; - using MatrixT = typename CircuitComponent::MatrixT; + using RealT = typename CircuitComponent::RealT; + using NodeT = typename PowerElectronics::NodeBase; using CircuitComponent::size_; using CircuitComponent::nnz_; @@ -25,6 +24,7 @@ namespace GridKit using CircuitComponent::yp_; using CircuitComponent::yp_int_; using CircuitComponent::tag_; + using CircuitComponent::abs_tol_; using CircuitComponent::f_; using CircuitComponent::f_int_; using CircuitComponent::g_; @@ -40,7 +40,7 @@ namespace GridKit using CircuitComponent::n_intern_; public: - HiresComponent3(IdxT id) + HiresComponent3(NodeT* bus, IdxT id) : node_ref_(bus) { size_ = 5; n_intern_ = 3; @@ -58,6 +58,9 @@ namespace GridKit { CircuitComponent::allocate(); + this->setExternalConnectionNodes(0, node_ref_->getNodeConnection(0)); + this->setExternalConnectionNodes(1, node_ref_->getNodeConnection(1)); + return 0; } @@ -73,8 +76,10 @@ namespace GridKit int evaluateInternalResidual() { + auto* y = y_.getData(); + // Internals - f_int_[0] = -yp_int_[0] - 280 * y_int_[0] * y_int_[2] + 0.69 * y_[0] + 1.71 * y_[1] - 0.43 * y_int_[0] + 0.69 * y_int_[1]; + f_int_[0] = -yp_int_[0] - 280 * y_int_[0] * y_int_[2] + 0.69 * y[0] + 1.71 * y[1] - 0.43 * y_int_[0] + 0.69 * y_int_[1]; f_int_[1] = -yp_int_[1] + 280 * y_int_[0] * y_int_[2] - 1.81 * y_int_[1]; f_int_[2] = -yp_int_[2] - 280 * y_int_[0] * y_int_[2] + 1.81 * y_int_[1]; @@ -83,9 +88,14 @@ namespace GridKit int evaluateExternalResidual() { + auto* y = y_.getData(); + auto* f = f_.getData(); + // Externals - f_[0] = -0.02 * y_[0]; - f_[1] = -0.045 * y_[1] + 0.43 * y_int_[0] + 0.43 * y_int_[1]; + f[0] = -0.02 * y[0]; + f[1] = -0.045 * y[1] + 0.43 * y_int_[0] + 0.43 * y_int_[1]; + + f_.setDataUpdated(); return 0; } @@ -144,5 +154,26 @@ namespace GridKit { return 0; } + + /** + * @brief Compute the absolute tolerance for each variable in the model + * + * @param rel_tol The relative tolerance which can be used to pick the + * absolute tolerance. + * @tparam ScalarT Scalar data type + * @tparam IdxT Index data type + * @return int 0 if successful, non-zero otherwise. + * + * This represents a "noise" level close to zero for which pure relative + * error cannot be used. + */ + int setAbsoluteTolerance(RealT rel_tol) + { + abs_tol_.setToConst(static_cast(rel_tol)); + return 0; + } + + private: + NodeT* node_ref_; }; } // namespace GridKit diff --git a/examples/PowerElectronics/Partition/Partition.cpp b/examples/PowerElectronics/Partition/Partition.cpp index ab6d981f5..93f14cbc8 100644 --- a/examples/PowerElectronics/Partition/Partition.cpp +++ b/examples/PowerElectronics/Partition/Partition.cpp @@ -3,203 +3,169 @@ #define _USE_MATH_DEFINES #include #include -// #include -// #include -// #include +#include +#include +#include #include #include #include -// #include "HiresBus.hpp" -// #include "HiresComponent1.hpp" -// #include "HiresComponent3.hpp" +#include "HiresBus.hpp" +#include "HiresComponent1.hpp" +#include "HiresComponent3.hpp" std::vector hires(const std::vector& y, const std::vector& yp); -// template -// int jac_hires(const std::vector& y, [[maybe_unused]] const std::vector& yp, GridKit::LinearAlgebra::DenseMatrix& jac, RealT alpha); - -// template -// void addCsrToDense( -// GridKit::LinearAlgebra::CsrMatrix* csr, -// GridKit::LinearAlgebra::DenseMatrix& dense, -// GridKit::SubsystemModel* partition); - int main(int /* argc */, char const** /* argv */) { - // using Bus = GridKit::PowerElectronics::MicrogridBus; - // Bus bus; - - // GridKit::SubsystemModel* partition1 = new GridKit::SubsystemModel(); - // GridKit::SubsystemModel* partition2 = new GridKit::SubsystemModel(); - - // GridKit::HiresComponent1* comp1 = new GridKit::HiresComponent1(1); - // GridKit::HiresBus* bus1 = new GridKit::HiresBus(2); - // GridKit::HiresComponent3* comp3 = new GridKit::HiresComponent3(3); - - // bus.allocate(); - - // bus.setExternalConnectionNodes(0, 3); - // bus.setExternalConnectionNodes(1, 4); - - // comp1->allocate(); - // comp3->allocate(); - // bus1->allocate(); - - // comp1->setExternalConnectionNodes(0, 3); - // comp1->setExternalConnectionNodes(1, 4); - // comp1->setExternalConnectionNodes(2, 0); - // comp1->setExternalConnectionNodes(3, 1); - // comp1->setExternalConnectionNodes(4, 2); - - // bus1->setExternalConnectionNodes(0, 3); - // bus1->setExternalConnectionNodes(1, 4); - - // comp3->setExternalConnectionNodes(0, 3); - // comp3->setExternalConnectionNodes(1, 4); - // comp3->setExternalConnectionNodes(2, 5); - // comp3->setExternalConnectionNodes(3, 6); - // comp3->setExternalConnectionNodes(4, 7); - - // GridKit::HiresComponent3 - // comp3copy(*comp3); - // GridKit::BusPartitionInterface* busInterface = new GridKit::BusPartitionInterface(bus, comp3copy, 4); - - // busInterface->allocate(); - // partition1->addComponent(comp1); - // partition1->addComponent(bus1); - // partition1->addComponent(busInterface); - // partition1->addNode(&bus); - - // partition2->addComponent(comp3); - - // std::cout << "Msg" << std::endl; - // partition1->allocate(); - // partition2->allocate(); - - // std::vector y = {1, 2, 3, 4, 5, 6, 7, 8}; - // std::vector yp = {1, 2, 3, 4, 5, 6, 7, 8}; - - // // Distribute externals to partition 1 - // for (size_t i = 0; i < partition1->getExternSize(); i++) - // { - // partition1->getExternalDataY()[i] = y[partition1->getExternalIndices()[i]]; - // partition1->getExternalDataYP()[i] = yp[partition1->getExternalIndices()[i]]; - // } - - // // Distribute externals to partition 2 - // for (size_t i = 0; i < partition2->getExternSize(); i++) - // { - // partition2->getExternalDataY()[i] = y[partition2->getExternalIndices()[i]]; - // partition2->getExternalDataYP()[i] = yp[partition2->getExternalIndices()[i]]; - // } - - // // Distribute internals to partition 1 - // for (size_t i = 0; i < partition1->getInternalSize(); i++) - // { - // partition1->y()[i] = static_cast(1 + i); - // partition1->yp()[i] = static_cast(1 + i); - // } - - // // Distribute internals to partition 2 - // for (size_t i = 0; i < partition2->getInternalSize(); i++) - // { - // partition2->y()[i] = static_cast(6 + i); - // partition2->yp()[i] = static_cast(6 + i); - // } - - // // Evaluate Residuals for each partition - // partition1->evaluateResidual(); - // partition2->evaluateResidual(); - - // auto printTitle = [](std::string msg) -> void - // { - // std::cout << "\n------------- " << msg << " -----------" << std::endl; - // printf("%-10s ---------- %10s\n", "Res Values", "Comp. Index"); - // }; - - // // Print Residuals from partition 1 - // printTitle("Partition 1"); - // for (size_t i = 0; i < partition1->getInternalSize(); i++) - // { - // auto com_index = partition1->getNodeConnection(static_cast(i)); - // printf("%-10.5g ---------- %10zu\n", partition1->getResidual()[i], com_index); - // } - - // // Print Residuals from partition 2 - // printTitle("Partition 2"); - // for (size_t i = 0; i < partition2->getInternalSize(); i++) - // { - // auto com_index = partition2->getNodeConnection(static_cast(i)); - // printf("%-10.5g ---------- %10zu\n", partition2->getResidual()[i], com_index); - // } - - // auto ref = hires(y, yp); - - // printTitle("Reference Solution"); - // for (size_t i = 0; i < 8; i++) - // { - // printf("%-10.5g ---------- %10zu\n", ref[i], i); - // } - - // partition1->updateTime(0, 1.0); - // partition2->updateTime(0, 1.0); - - // partition1->evaluateJacobian(); - // partition2->evaluateJacobian(); - - // GridKit::LinearAlgebra::DenseMatrix jac1(8, 8); - // GridKit::LinearAlgebra::DenseMatrix jac2(8, 8); - - // GridKit::LinearAlgebra::DenseMatrix jac_ref(8, 8); - - // addCsrToDense(partition1->getCsrJacobian(), jac1, partition1); - // addCsrToDense(partition2->getCsrJacobian(), jac2, partition2); - // jac_hires(y, yp, jac_ref, 1.0); - - // auto print_dense = [&](GridKit::LinearAlgebra::DenseMatrix& jc) -> void - // { - // for (size_t i = 0; i < 8; ++i) - // { - // for (size_t j = 0; j < 8; ++j) - // { - // std::cout << std::setw(10) << jc.getValue(i, j) << ' '; - // } - // std::cout << '\n'; - // } - // std::cout << '\n'; - // }; - - // print_dense(jac1); - // print_dense(jac2); - // print_dense(jac_ref); - - // delete comp1; - // delete bus1; - // delete busInterface; - // delete comp3; - // delete partition1; - // delete partition2; + using Bus = GridKit::PowerElectronics::MicrogridBus; + Bus bus; + + GridKit::SubsystemModel* partition1 = new GridKit::SubsystemModel(); + GridKit::SubsystemModel* partition2 = new GridKit::SubsystemModel(); + + GridKit::HiresComponent1* comp1 = new GridKit::HiresComponent1(&bus, 1); + GridKit::HiresBus* bus1 = new GridKit::HiresBus(&bus, 2); + GridKit::HiresComponent3* comp3 = new GridKit::HiresComponent3(&bus, 3); + + bus.allocate(); + + bus.setExternalConnectionNodes(0, 3); + bus.setExternalConnectionNodes(1, 4); + + comp1->allocate(); + comp3->allocate(); + bus1->allocate(); + + comp1->setExternalConnectionNodes(2, 0); + comp1->setExternalConnectionNodes(3, 1); + comp1->setExternalConnectionNodes(4, 2); + + bus1->setExternalConnectionNodes(0, 3); + bus1->setExternalConnectionNodes(1, 4); + + comp3->setExternalConnectionNodes(2, 5); + comp3->setExternalConnectionNodes(3, 6); + comp3->setExternalConnectionNodes(4, 7); + + GridKit::HiresComponent3 comp3copy(*comp3); + GridKit::BusPartitionInterface* busInterface = new GridKit::BusPartitionInterface(bus, comp3copy, 4); + + busInterface->allocate(); + partition1->addComponent(comp1); + partition1->addComponent(bus1); + partition1->addComponent(busInterface); + partition1->addNode(&bus); + + partition2->addComponent(comp3); + + auto subsystems = {partition1, partition2}; + + std::vector y = {1, 2, 3, 4, 5, 6, 7, 8}; + std::vector yp = {1, 2, 3, 4, 5, 6, 7, 8}; + + // Distribute externals to partition 1 + + for (auto* partition : subsystems) + { + partition->allocate(); + partition->updateTime(2, 5); + + for (size_t i = 0; i < partition->getExternSize(); i++) + { + partition->getExternalDataY()[i] = y[partition->getExternalIndices()[i]]; + partition->getExternalDataYP()[i] = yp[partition->getExternalIndices()[i]]; + } + + auto* partition_y = partition->y().getData(); + auto* partition_yp = partition->yp().getData(); + + for (size_t i = 0; i < partition->getInternalSize(); i++) + { + partition_y[i] = y[partition->getNodeConnection(i)]; + partition_yp[i] = yp[partition->getNodeConnection(i)]; + } + + partition->y().setDataUpdated(); + partition->yp().setDataUpdated(); + + partition->evaluateResidual(); + partition->evaluateJacobian(); + } + + auto printHeader = [](const std::string& title) + { + std::cout << "\n------------- " << title << " -----------\n"; + + std::cout << std::left << std::setw(10) << "Res Values" + << " ---------- " + << std::right << std::setw(10) << "Comp. Index" + << '\n'; + }; + + auto printRow = [](double residual, size_t index) + { + std::cout << std::left << std::setw(10) + << std::defaultfloat << std::setprecision(5) + << residual + << " ---------- " + << std::right << std::setw(10) + << index + << '\n'; + }; + + auto printPartitionResiduals = [&](auto* partition, const std::string& title) + { + printHeader(title); + + auto* residual = partition->getResidual().getData(); + + for (size_t i = 0; i < partition->getInternalSize(); ++i) + { + const auto comp_index = partition->getNodeConnection(i); + printRow(residual[i], comp_index); + } + }; + + printPartitionResiduals(partition1, "Partition 1"); + printPartitionResiduals(partition2, "Partition 2"); + + auto ref = hires(y, yp); + + printHeader("Reference Output"); + for (size_t i = 0; i < ref.size(); ++i) + { + printRow(ref[i], i); + } + + delete comp1; + delete bus1; + delete busInterface; + delete comp3; + + delete partition1; + delete partition2; + return 0; } -// std::vector hires(const std::vector& y, const std::vector& yp) -// { -// std::vector f(8); - -// f[0] = -yp[0] - 1.71 * y[0] + 0.43 * y[1] + 8.32 * y[2] + 0.0007; -// f[1] = -yp[1] + 1.71 * y[0] - 8.75 * y[1]; -// f[2] = -yp[2] - 10.03 * y[2] + 0.43 * y[3] + 0.035 * y[4]; -// f[3] = -yp[3] + 8.32 * y[1] + 1.71 * y[2] - 1.12 * y[3]; -// f[4] = -yp[4] - 1.745 * y[4] + 0.43 * y[5] + 0.43 * y[6]; -// f[5] = -yp[5] - 280 * y[5] * y[7] + 0.69 * y[3] + 1.71 * y[4] - 0.43 * y[5] + 0.69 * y[6]; -// f[6] = -yp[6] + 280 * y[5] * y[7] - 1.81 * y[6]; -// f[7] = -yp[7] - 280 * y[5] * y[7] + 1.81 * y[6]; - -// return f; -// } +std::vector hires(const std::vector& y, const std::vector& yp) +{ + std::vector f(8); + + f[0] = -yp[0] - 1.71 * y[0] + 0.43 * y[1] + 8.32 * y[2] + 0.0007; + f[1] = -yp[1] + 1.71 * y[0] - 8.75 * y[1]; + f[2] = -yp[2] - 10.03 * y[2] + 0.43 * y[3] + 0.035 * y[4]; + f[3] = -yp[3] + 8.32 * y[1] + 1.71 * y[2] - 1.12 * y[3]; + f[4] = -yp[4] - 1.745 * y[4] + 0.43 * y[5] + 0.43 * y[6]; + f[5] = -yp[5] - 280 * y[5] * y[7] + 0.69 * y[3] + 1.71 * y[4] - 0.43 * y[5] + 0.69 * y[6]; + f[6] = -yp[6] + 280 * y[5] * y[7] - 1.81 * y[6]; + f[7] = -yp[7] - 280 * y[5] * y[7] + 1.81 * y[6]; + + return f; +} // template // int jac_hires(const std::vector& y, [[maybe_unused]] const std::vector& yp, GridKit::LinearAlgebra::DenseMatrix& jac, RealT alpha) @@ -240,31 +206,3 @@ int main(int /* argc */, char const** /* argv */) // return 0; // } - -// template -// void addCsrToDense( -// GridKit::LinearAlgebra::CsrMatrix* csr, -// GridKit::LinearAlgebra::DenseMatrix& dense, -// GridKit::SubsystemModel* partition) -// { -// auto* row_ptr = csr->getRowData(GridKit::LinearAlgebra::memory::HOST); -// auto* col_ind = csr->getColData(GridKit::LinearAlgebra::memory::HOST); -// auto* values = csr->getValues(GridKit::LinearAlgebra::memory::HOST); - -// assert(row_ptr != nullptr); -// assert(col_ind != nullptr); -// assert(values != nullptr); - -// for (IdxT row = 0; row < csr->getNumRows(); ++row) -// { -// for (IdxT k = row_ptr[row]; k < row_ptr[row + 1]; ++k) -// { -// const IdxT col = col_ind[k]; - -// auto r = partition->getNodeConnection(row); -// auto c = partition->getNodeConnection(col); - -// dense.setValue(r, c, dense.getValue(r, c) + values[k]); -// } -// } -// } From 261ea10c5626c97aa24b6681cb43f6cee653b528 Mon Sep 17 00:00:00 2001 From: abdourahmanbarry Date: Mon, 20 Jul 2026 05:08:41 +0000 Subject: [PATCH 27/52] Removed print statements --- examples/PowerElectronics/Partition/PartitionMicrogrid.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/examples/PowerElectronics/Partition/PartitionMicrogrid.cpp b/examples/PowerElectronics/Partition/PartitionMicrogrid.cpp index 6dd8563d7..d72c50f4e 100644 --- a/examples/PowerElectronics/Partition/PartitionMicrogrid.cpp +++ b/examples/PowerElectronics/Partition/PartitionMicrogrid.cpp @@ -283,7 +283,6 @@ int main() f[partition->getNodeConnection(i)] = residual[i]; } - std::cout << std::endl; } double max_error = 0; @@ -291,7 +290,6 @@ int main() { error[i] = (f_sysmodel[i] - f[i]) / f_sysmodel[i]; - std::cout << i << " " << f[i] << " " << f_sysmodel[i] << " " << error[i] << std::endl; if (max_error < std::abs(error[i])) { max_error = std::abs(error[i]); From a8bf8bbc0d52618713ac90bf1b136bce879ebcf0 Mon Sep 17 00:00:00 2001 From: abdourahmanbarry Date: Mon, 20 Jul 2026 05:09:28 +0000 Subject: [PATCH 28/52] Apply pre-commit fixes --- examples/PowerElectronics/Partition/HiresBus.hpp | 3 ++- examples/PowerElectronics/Partition/HiresComponent1.hpp | 3 ++- examples/PowerElectronics/Partition/HiresComponent3.hpp | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/examples/PowerElectronics/Partition/HiresBus.hpp b/examples/PowerElectronics/Partition/HiresBus.hpp index 273570e30..d37e396d3 100644 --- a/examples/PowerElectronics/Partition/HiresBus.hpp +++ b/examples/PowerElectronics/Partition/HiresBus.hpp @@ -41,7 +41,8 @@ namespace GridKit using CircuitComponent::n_intern_; public: - HiresBus(NodeT* bus, IdxT id) : node_ref_(bus) + HiresBus(NodeT* bus, IdxT id) + : node_ref_(bus) { size_ = 2; n_intern_ = 0; diff --git a/examples/PowerElectronics/Partition/HiresComponent1.hpp b/examples/PowerElectronics/Partition/HiresComponent1.hpp index d8931739f..503bf963d 100644 --- a/examples/PowerElectronics/Partition/HiresComponent1.hpp +++ b/examples/PowerElectronics/Partition/HiresComponent1.hpp @@ -42,7 +42,8 @@ namespace GridKit using CircuitComponent::n_intern_; public: - HiresComponent1(NodeT* bus, IdxT id) : node_ref_(bus) + HiresComponent1(NodeT* bus, IdxT id) + : node_ref_(bus) { size_ = 5; n_intern_ = 3; diff --git a/examples/PowerElectronics/Partition/HiresComponent3.hpp b/examples/PowerElectronics/Partition/HiresComponent3.hpp index 92380bb8a..5e8ddf4cd 100644 --- a/examples/PowerElectronics/Partition/HiresComponent3.hpp +++ b/examples/PowerElectronics/Partition/HiresComponent3.hpp @@ -40,7 +40,8 @@ namespace GridKit using CircuitComponent::n_intern_; public: - HiresComponent3(NodeT* bus, IdxT id) : node_ref_(bus) + HiresComponent3(NodeT* bus, IdxT id) + : node_ref_(bus) { size_ = 5; n_intern_ = 3; From 1ba215c301236594840cd48138ea0e1f2f2c9ddc Mon Sep 17 00:00:00 2001 From: abdourahmanbarry Date: Thu, 23 Jul 2026 05:14:19 +0000 Subject: [PATCH 29/52] Make SubsystemModel inherit from PowerElectronicsModel for code reuse --- .../Model/PowerElectronics/SubsystemModel.hpp | 306 +++--------------- .../SystemModelPowerElectronics.hpp | 36 +-- 2 files changed, 53 insertions(+), 289 deletions(-) diff --git a/GridKit/Model/PowerElectronics/SubsystemModel.hpp b/GridKit/Model/PowerElectronics/SubsystemModel.hpp index 51df8a508..35bb0eab9 100644 --- a/GridKit/Model/PowerElectronics/SubsystemModel.hpp +++ b/GridKit/Model/PowerElectronics/SubsystemModel.hpp @@ -12,14 +12,17 @@ #include #include #include +#include #include namespace GridKit { template - class SubsystemModel : public CircuitComponent + class SubsystemModel : public PowerElectronicsModel { + + using SystemModel = PowerElectronicsModel; using RealT = typename CircuitComponent::RealT; using CsrMatrixT = typename CircuitComponent::CsrMatrixT; using component_type = CircuitComponent; @@ -27,22 +30,30 @@ namespace GridKit using ForcingData = std::pair, std::vector>; using TimeFunction = std::function; - using CircuitComponent::size_; - using CircuitComponent::n_intern_; - using CircuitComponent::n_extern_; - using CircuitComponent::nnz_; - using CircuitComponent::time_; - using CircuitComponent::alpha_; - using CircuitComponent::y_; - using CircuitComponent::y_int_; - using CircuitComponent::yp_; - using CircuitComponent::yp_int_; - using CircuitComponent::f_; - using CircuitComponent::f_int_; - using CircuitComponent::tag_; - using CircuitComponent::abs_tol_; - using CircuitComponent::allocated_; - using CircuitComponent::allocateVectors; + using SystemModel::abs_tol_; + using SystemModel::allocated_; + using SystemModel::allocateVectors; + using SystemModel::alpha_; + using SystemModel::f_; + using SystemModel::f_int_; + using SystemModel::n_extern_; + using SystemModel::n_intern_; + using SystemModel::nnz_; + using SystemModel::size_; + using SystemModel::tag_; + using SystemModel::time_; + using SystemModel::y_; + using SystemModel::y_int_; + using SystemModel::yp_; + using SystemModel::yp_int_; + + using SystemModel::components_; + using SystemModel::csr_jac_; + using SystemModel::jac_call_count_; + using SystemModel::map_to_csr_; + using SystemModel::neg1_; + using SystemModel::nodes_; + using SystemModel::use_jac_; public: /** @@ -51,29 +62,16 @@ namespace GridKit * @post System model parameters set as default */ - SubsystemModel() + SubsystemModel(bool use_jac = false) : SystemModel(use_jac) { - // Set system model parameters as default - use_jac_ = false; } - virtual ~SubsystemModel() + ~SubsystemModel() override { - } - - bool hasJacobian() final - { - if (!this->use_jac_) - return false; - - for (const auto& component : components_) - { - if (!component->hasJacobian()) - { - return false; - } - } - return true; + // SubsystemModel does not own components_/nodes_ — they belong to (and are + // deleted by) the parent system model. + components_.clear(); + nodes_.clear(); } int allocate() override @@ -236,25 +234,6 @@ namespace GridKit return 0; } - /** - * @brief Set intial y and y' of each component - * - * @return int 0 if successful, positive if there's a recoverable error, negative if unrecoverable - */ - int initialize() final - { - // Initialize components - for (const auto& component : components_) - { - component->initialize(); - } - y_.setDataUpdated(); - yp_.setDataUpdated(); - this->distributeVectors(); - - return 0; - } - /** * @brief Distribute y and y' to each component based of node connection graph * @@ -314,207 +293,6 @@ namespace GridKit return 0; } - int tagDifferentiable() final - { - return 0; - } - - /** - * @brief Evaluate Residuals at each component then collect them - * - * @return int 0 if successful, positive if there's a recoverable error, negative if unrecoverable - */ - int evaluateInternalResidual() final - { - auto* f = f_.getData(); - - for (IdxT i = 0; i < this->getInternalSize(); i++) - { - f[i] = 0.0; - } - - this->distributeVectors(); - - // Update system residual vector - - // Evaluate component internal residuals - this is embarassingly parallel - for (component_type* component : components_) - { - if (int err_code = component->evaluateInternalResidual()) - return err_code; - } - - for (component_type* component : components_) - { - - if (int err_code = component->evaluateExternalResidual()) - return err_code; - - const auto* residual = component->getResidual().getData(); - const auto& externals = component->getExternIndices(); - - for (size_t j : externals) - { - //@todo should do a different grounding check - if (component->getNodeConnection(j) != neg1_ && component->getNodeConnection(j) < this->getInternalSize()) - { - f[component->getNodeConnection(j)] += residual[j]; - } - } - } - - f_.setDataUpdated(); - - return 0; - } - - /** - * @todo implement this for nested systems - */ - int evaluateExternalResidual() final - { - return 0; - } - - /** - * @brief Creates the system Jacobian representing \alpha dF/dy' + dF/dy - * - * - * @return int 0 if successful, positive if there's a recoverable error, negative if unrecoverable - */ - int evaluateJacobian() final - { - - distributeVectors(); - - // Zero out values - RealT* vals = csr_jac_->getValues(); - for (IdxT i = 0; i < csr_jac_->getNnz(); ++i) - { - vals[i] = 0.0; - } - - // Update CSR values from component Jacobians - IdxT counter = 0; - for (const auto& component : components_) - { - component->evaluateJacobian(); - - const IdxT* r = component->jacobianCooRows(); - const IdxT* c = component->jacobianCooCols(); - const RealT* v = component->jacobianCooValues(); - IdxT nnz = component->nnz(); - - for (IdxT i = 0; i < nnz; ++i) - { - - const IdxT row = component->getNodeConnection(r[i]); - const IdxT col = component->getNodeConnection(c[i]); - - const bool is_internal_entry = row != neg1_ && col != neg1_ && row < n_intern_ && col < n_intern_; - - if (!is_internal_entry) - { - continue; - } - - vals[map_to_csr_[counter]] += v[i]; - ++counter; - } - } - - jac_call_count_++; - return 0; - } - - /** - * @brief Evaluate integrands for the system quadratures. - */ - int evaluateIntegrand() final - { - return 0; - } - - /** - * @brief Initialize system adjoint. - * - * Updates variables and optimization parameters, then initializes - * adjoints locally and copies them to the system adjoint vector. - */ - int initializeAdjoint() final - { - return 0; - } - - /** - * @brief Compute adjoint residual for the system model. - * - * - */ - int evaluateAdjointResidual() final - { - return 0; - } - - /** - * @brief Evaluate adjoint integrand for the system model. - * - * - */ - int evaluateAdjointIntegrand() final - { - return 0; - } - - /** - * @brief Compute the absolute tolerance for each variable in the model - * - * @param rel_tol The relative tolerance which can be used to pick the - * absolute tolerance. - * @tparam ScalarT Scalar data type - * @tparam IdxT Index data type - * @return int 0 if successful, non-zero otherwise. - * - * This represents a "noise" level close to zero for which pure relative - * error cannot be used. - */ - int setAbsoluteTolerance(RealT rel_tol) final - { - abs_tol_.setToConst(static_cast(rel_tol)); - return 0; - } - - /** - * @brief Distribute time and time scaling for each component - * - * @param t - * @param a - */ - void updateTime(RealT t, RealT a) final - { - for (const auto& component : components_) - { - component->updateTime(t, a); - } - time_ = t; - alpha_ = a; - } - - CsrMatrixT* getCsrJacobian() const override - { - return csr_jac_; - } - - void addComponent(component_type* component) - { - components_.push_back(component); - } - - void addNode(node_type* node) - { - nodes_.push_back(node); - } - int mapGlobalToLocal() { for (auto* component : components_) @@ -642,21 +420,17 @@ namespace GridKit forcing_function_ = std::move(function); } - std::unordered_map& getInternalMap() + const std::unordered_map& getInternalMap() const { return internal_map_; } - std::unordered_map& getExternalMap() + const std::unordered_map& getExternalMap() const { return external_map_; } private: - static constexpr IdxT neg1_ = INVALID_INDEX; - - std::vector components_; - std::vector nodes_; std::unordered_map internal_map_; std::unordered_map external_map_; std::vector external_indices_; @@ -667,12 +441,6 @@ namespace GridKit std::optional forcing_function_; - IdxT* map_to_csr_{nullptr}; - CsrMatrixT* csr_jac_{nullptr}; - - int jac_call_count_{0}; - bool use_jac_; - }; // class SubsystemModel -} // namespace GridKit +} // namespace GridKit \ No newline at end of file diff --git a/GridKit/Model/PowerElectronics/SystemModelPowerElectronics.hpp b/GridKit/Model/PowerElectronics/SystemModelPowerElectronics.hpp index 174e28568..7e72f2549 100644 --- a/GridKit/Model/PowerElectronics/SystemModelPowerElectronics.hpp +++ b/GridKit/Model/PowerElectronics/SystemModelPowerElectronics.hpp @@ -41,17 +41,6 @@ namespace GridKit using Base::yp_int_; public: - /** - * @brief Default constructor for the system model - * - * @post System model parameters set as default - */ - PowerElectronicsModel() - { - // By default don't use the jacobian - use_jac_ = false; - } - /** * @brief Constructor for the system model * @@ -117,7 +106,7 @@ namespace GridKit * * @return int 0 if successful, positive if there's a recoverable error, negative if unrecoverable */ - int allocate() final + int allocate() override { size_t component_internal_size = 0; for (component_type* comp : components_) @@ -282,7 +271,7 @@ namespace GridKit * * @return int 0 if successful, positive if there's a recoverable error, negative if unrecoverable */ - int initialize() final + int initialize() { // Initialize components for (const auto& component : components_) @@ -293,7 +282,7 @@ namespace GridKit return Base::initialize(); } - int tagDifferentiable() final + int tagDifferentiable() { return 0; } @@ -321,7 +310,7 @@ namespace GridKit * * @return int 0 if successful, positive if there's a recoverable error, negative if unrecoverable */ - int evaluateInternalResidual() final + int evaluateInternalResidual() override { for (IdxT i = 0; i < size_; i++) { @@ -362,7 +351,7 @@ namespace GridKit * * @return int 0 if successful, positive if there's a recoverable error, negative if unrecoverable */ - int evaluateJacobian() final + int evaluateJacobian() override { // Zero out values RealT* vals = csr_jac_->getValues(); @@ -384,11 +373,18 @@ namespace GridKit for (IdxT i = 0; i < nnz; ++i) { - if (component->getNodeConnection(r[i]) != neg1_ && component->getNodeConnection(c[i]) != neg1_) + const IdxT row = component->getNodeConnection(r[i]); + const IdxT col = component->getNodeConnection(c[i]); + + const bool is_internal_entry = row != neg1_ && col != neg1_ && row < n_intern_ && col < n_intern_; + + if (!is_internal_entry) { - vals[map_to_csr_[counter]] += v[i]; - ++counter; + continue; } + + vals[map_to_csr_[counter]] += v[i]; + ++counter; } } @@ -468,7 +464,7 @@ namespace GridKit allocated_ = false; } - private: + protected: static constexpr IdxT neg1_ = INVALID_INDEX; std::vector components_; From eb528deadc61a022ad557d802ccfafb128a46825 Mon Sep 17 00:00:00 2001 From: abdourahmanbarry Date: Thu, 23 Jul 2026 05:17:43 +0000 Subject: [PATCH 30/52] Apply pre-commit fixes --- GridKit/Model/PowerElectronics/SubsystemModel.hpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/GridKit/Model/PowerElectronics/SubsystemModel.hpp b/GridKit/Model/PowerElectronics/SubsystemModel.hpp index 35bb0eab9..8e7d920ad 100644 --- a/GridKit/Model/PowerElectronics/SubsystemModel.hpp +++ b/GridKit/Model/PowerElectronics/SubsystemModel.hpp @@ -62,7 +62,8 @@ namespace GridKit * @post System model parameters set as default */ - SubsystemModel(bool use_jac = false) : SystemModel(use_jac) + SubsystemModel(bool use_jac = false) + : SystemModel(use_jac) { } @@ -443,4 +444,4 @@ namespace GridKit }; // class SubsystemModel -} // namespace GridKit \ No newline at end of file +} // namespace GridKit From 6328a8a89bd624541c5640de8dc76378c17d86c3 Mon Sep 17 00:00:00 2001 From: abdourahmanbarry Date: Fri, 24 Jul 2026 04:55:47 +0000 Subject: [PATCH 31/52] Updated SubsystemModel and Partition interfaces --- .../BusPartitionInterface.cpp | 15 +- .../BusPartitionInterface.hpp | 2 - .../Model/PowerElectronics/SubsystemModel.hpp | 312 +++++++++++++++--- 3 files changed, 271 insertions(+), 58 deletions(-) diff --git a/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.cpp b/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.cpp index 070c190f6..578493931 100644 --- a/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.cpp +++ b/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.cpp @@ -46,6 +46,7 @@ namespace GridKit const IdxT bus_i = bus.getNodeConnection(0); const IdxT bus_j = bus.getNodeConnection(1); + nnz_ = 0; for (IdxT k = 0; k < component_.nnz(); ++k) { const IdxT row_node = component_.getNodeConnection(cooRow[k]); @@ -104,7 +105,7 @@ namespace GridKit << "), Component(ID=" << component_.getIDcomponent() << "). Please verify connection-node mappings and internal/external index assignments." << std::endl; - assert(false); + return 1; } const auto n = component_.getInternalSize(); @@ -146,7 +147,7 @@ namespace GridKit } /** - * @brief Eval Micro Load + * @brief Eval Internal Residual */ template int BusPartitionInterface::evaluateInternalResidual() @@ -171,7 +172,7 @@ namespace GridKit for (size_t i = 0; i < static_cast(component_.size()); ++i) { - if (extern_indices.contains(i)) + if (extern_indices.contains(static_cast(i))) { component_y[external] = y[i]; component_yp[external] = yp[i]; @@ -185,12 +186,16 @@ namespace GridKit } } + component_.y().setDataUpdated(); + component_.y().setDataUpdated(); + component_.evaluateResidual(); const auto* residual = component_.getResidual().getData(); // TODO: This assumes that external variables are ordered after all internal - // variables in the local indexing. To make this more robust, we need to get rid of this assumption. + // variables in the indexing. To make this more robust, we need to get rid of this assumption + // (although true for all components that we have in GridKit). f[bus_port_i_] = residual[bus_port_i_]; f[bus_port_j_] = residual[bus_port_j_]; @@ -200,7 +205,7 @@ namespace GridKit } /** - * @brief Generate Jacobian for Micro Load + * @brief Generate Jacobian for * * @tparam ScalarT * @tparam IdxT diff --git a/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.hpp b/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.hpp index 7244fad4d..93c764b8f 100644 --- a/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.hpp +++ b/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.hpp @@ -2,8 +2,6 @@ #pragma once -#include - #include #include diff --git a/GridKit/Model/PowerElectronics/SubsystemModel.hpp b/GridKit/Model/PowerElectronics/SubsystemModel.hpp index 8e7d920ad..ff6a25d51 100644 --- a/GridKit/Model/PowerElectronics/SubsystemModel.hpp +++ b/GridKit/Model/PowerElectronics/SubsystemModel.hpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -21,14 +22,16 @@ namespace GridKit template class SubsystemModel : public PowerElectronicsModel { + public: + using ForcingData = std::tuple, std::vector>; + using TimeFunction = std::function; + protected: using SystemModel = PowerElectronicsModel; using RealT = typename CircuitComponent::RealT; using CsrMatrixT = typename CircuitComponent::CsrMatrixT; using component_type = CircuitComponent; using node_type = PowerElectronics::NodeBase; - using ForcingData = std::pair, std::vector>; - using TimeFunction = std::function; using SystemModel::abs_tol_; using SystemModel::allocated_; @@ -78,9 +81,7 @@ namespace GridKit int allocate() override { - this->createGlobalToInternalMap(); - - this->mapGlobalToLocal(); + hold(); n_intern_ = internal_map_.size(); n_extern_ = external_map_.size(); @@ -102,7 +103,7 @@ namespace GridKit yp_ext_.resize(n_extern_); f_ext_.resize(n_extern_); - external_indices_.resize(n_extern_); + external_data_indices_.resize(n_extern_); // Store the mapping from local subsystem indices back to their global system indices for (const auto [global_idx, local_idx] : internal_map_) @@ -113,7 +114,7 @@ namespace GridKit // Store the global indices of all external coupling variables. for (const auto [global_idx, local_idx] : external_map_) { - external_indices_[local_idx - n_intern_] = global_idx; + external_data_indices_[local_idx - n_intern_] = global_idx; } size_t component_internal_idx = 0; @@ -242,15 +243,12 @@ namespace GridKit * * @return int 0 if successful, positive if there's a recoverable error, negative if unrecoverable */ - int distributeVectors() + int distributeVectors() override { if (forcing_function_) { - const auto forcing = (*forcing_function_)(time_); - - const auto& y_forcing = forcing.first; - const auto& yp_forcing = forcing.second; + const auto [y_forcing, yp_forcing] = (*forcing_function_)(time_); assert(y_forcing.size() == y_ext_.size()); assert(yp_forcing.size() == yp_ext_.size()); @@ -294,8 +292,232 @@ namespace GridKit return 0; } + /** + * @brief Add a component to the subsystem. + * + * Rejected while connections are in the local-indexed state, since the + * component's stored connection indices would otherwise be interpreted + * inconsistently with the rest of the subsystem. Call release() first. + * + * @param[in] component Component to add. + */ + void addComponent(component_type* component) + { + if (is_comp_in_local_state) + { + std::cerr << "SubsystemModel::addComponent: cannot add component while " + "in local-indexed state. Call release() first.\n"; + return; + } + + SystemModel::addComponent(component); + } + + /** + * @brief Add a node to the subsystem. + * + * Rejected while connections are in the local-indexed state, since the + * node's stored connection indices would otherwise be interpreted + * inconsistently with the rest of the subsystem. Call release() first. + * + * @param[in] node Node to add. + */ + void addNode(node_type* node) + { + if (is_comp_in_local_state) + { + std::cerr << "SubsystemModel::addNode: cannot add node while in " + "local-indexed state. Call release() first.\n"; + return; + } + + SystemModel::addNode(node); + } + + /** + * @brief Acquire the subsystem's global-to-local index mappings. + * + * Builds the internal/external index maps via buildIndexMappings(), + * then converts component/node connections from global to local + * indices via mapGlobalToLocal(). + * + * Call this after modifying subsystem topology (adding or removing + * components/nodes) and before allocate(), to (re)establish local + * indexing. Pairs with release(), which undoes this. + * + * @return int 0 if successful, positive if there's a recoverable error, negative if unrecoverable + */ + int hold() + { + buildIndexMappings(); + + if (int err_code = mapGlobalToLocal()) + { + return err_code; + } + + return 0; + } + + /** + * @brief Release the subsystem's global-to-local index mappings. + * + * If components/nodes currently hold local indices, they are first + * converted back to their original global indices via + * mapLocalToGlobal(). The internal/external index maps built by + * buildIndexMappings() are then cleared, and the model is marked as + * not allocated. + * + * + * Call this before modifying subsystem topology (adding or removing + * components/nodes), so stale local indices aren't left dangling. + * + * @return int 0 if successful, positive if there's a recoverable error, negative if unrecoverable + */ + int release() + { + if (int err_code = mapLocalToGlobal()) + { + return err_code; + } + + internal_map_.clear(); + external_map_.clear(); + + allocated_ = false; + + return 0; + } + + const std::vector& getExternalDataIndices() + { + return external_data_indices_; + } + + std::vector& getExternalDataY() + { + return y_ext_; + } + + std::vector& getExternalDataYP() + { + return yp_ext_; + } + + std::vector& getExternalDataF() + { + return f_ext_; + } + + void setTimeFunction(TimeFunction function) + { + forcing_function_ = std::move(function); + } + + const std::unordered_map& getInternalMap() const + { + return internal_map_; + } + + const std::unordered_map& getExternalMap() const + { + return external_map_; + } + + private: + /** + * @brief Replace local subsystem connection indices with their original global indices + * for every component and node. + * + * Inverse of mapGlobalToLocal(). Internal connections (index < internal + * size) are mapped back through this subsystem's own node-connection + * table; external connections are mapped back through + * @c external_data_indices_. Entries equal to @c neg1_ represent no + * connection and are left unchanged. + * + * @return Always returns 0. + */ + int mapLocalToGlobal() + { + if (!is_comp_in_local_state) + { + return 0; + } + + for (auto* component : components_) + { + + for (IdxT i = 0; i < component->size(); i++) + { + IdxT index = component->getNodeConnection(i); + + if (index == neg1_) + { + continue; + } + else if (index < this->getInternalSize()) + { + component->setExternalConnectionNodes(i, this->getNodeConnection(index)); + } + else + { + component->setExternalConnectionNodes(i, external_data_indices_[index - this->getInternalSize()]); + } + } + } + + for (auto* node : nodes_) + { + + for (IdxT i = 0; i < node->size(); i++) + { + IdxT index = node->getNodeConnection(i); + + if (index == neg1_) + { + continue; + } + else if (index < this->getInternalSize()) + { + node->setExternalConnectionNodes(i, this->getNodeConnection(index)); + } + else + { + node->setExternalConnectionNodes(i, external_data_indices_[index - this->getInternalSize()]); + } + } + } + + is_comp_in_local_state = false; + + return 0; + } + + /** + * @brief Replace global connection indices with local subsystem indices for every component + * and node. + * + * After the internal and external maps have been constructed, each component + * and node still stores the original global connection indices. This function + * traverses every connection and replaces each global index with its + * corresponding local subsystem index. + * + * Internal connections are mapped using @c internal_map_, while connections + * that reference variables outside the subsystem are mapped using + * @c external_map_. Entries equal to @c neg1_ represent no connection and + * are left unchanged. + * + * @return Always returns 0. + */ + int mapGlobalToLocal() { + + if (is_comp_in_local_state) + { + return 0; + } + for (auto* component : components_) { @@ -340,12 +562,34 @@ namespace GridKit } } + is_comp_in_local_state = true; + return 0; } - void createGlobalToInternalMap() + /** + * @brief Construct the mappings from global variable indices to local + * subsystem indices. + * + * The subsystem stores its variables in a contiguous local ordering. This + * function assigns each global variable index a corresponding local index and + * separates them into internal and external variables. + * + * Internal variables are those owned by this subsystem, while external + * variables are owned by neighboring subsystems but are required for residual + * and Jacobian evaluation. + */ + void buildIndexMappings() { + if (is_comp_in_local_state) + { + return; + } + + internal_map_.clear(); + external_map_.clear(); + size_t component_internal_idx = 0; // First pass: Map global component indices to local subsystem indices. for (component_type* comp : components_) @@ -396,45 +640,9 @@ namespace GridKit } } - std::vector& getExternalIndices() - { - return external_indices_; - } - - std::vector& getExternalDataY() - { - return y_ext_; - } - - std::vector& getExternalDataYP() - { - return yp_ext_; - } - - std::vector& getExternalDataF() - { - return f_ext_; - } - - void setTimeFunction(TimeFunction function) - { - forcing_function_ = std::move(function); - } - - const std::unordered_map& getInternalMap() const - { - return internal_map_; - } - - const std::unordered_map& getExternalMap() const - { - return external_map_; - } - - private: std::unordered_map internal_map_; std::unordered_map external_map_; - std::vector external_indices_; + std::vector external_data_indices_; std::vector y_ext_; std::vector yp_ext_; @@ -442,6 +650,8 @@ namespace GridKit std::optional forcing_function_; + bool is_comp_in_local_state{false}; + }; // class SubsystemModel -} // namespace GridKit +} // namespace GridKit \ No newline at end of file From 96ac403715796512b7cdde3c8f9e4245d83c51ee Mon Sep 17 00:00:00 2001 From: abdourahmanbarry Date: Fri, 24 Jul 2026 05:05:37 +0000 Subject: [PATCH 32/52] Updated examples to reflect new changes in subsystem model and partition interfaces --- .../PowerElectronics/Partition/CMakeLists.txt | 76 ++++++++++++++----- .../PowerElectronics/Partition/Partition.cpp | 4 +- .../Partition/PartitionMicrogrid.cpp | 31 ++++++-- .../Partition/PartitionScaleMicrogrid.cpp | 4 +- 4 files changed, 86 insertions(+), 29 deletions(-) diff --git a/examples/PowerElectronics/Partition/CMakeLists.txt b/examples/PowerElectronics/Partition/CMakeLists.txt index 6f2f633d6..03d45b6f5 100644 --- a/examples/PowerElectronics/Partition/CMakeLists.txt +++ b/examples/PowerElectronics/Partition/CMakeLists.txt @@ -1,26 +1,64 @@ -find_package(OpenMP REQUIRED) +find_package(OpenMP) add_executable(partition Partition.cpp) + target_link_libraries( - partition GridKit::solvers_dyn GridKit::power_elec_partition_interfaces) + partition + PRIVATE + GridKit::solvers_dyn + GridKit::power_elec_partition_interfaces +) add_executable(partitionMicrogrid PartitionMicrogrid.cpp) + target_link_libraries( partitionMicrogrid - GridKit::power_elec_disgen - GridKit::power_elec_microline - GridKit::power_elec_microload - GridKit::solvers_dyn - GridKit::power_elec_microbusdq - GridKit::power_elec_partition_interfaces) - -add_executable(PartitionScaleMicrogrid PartitionScaleMicrogrid.cpp) -target_link_libraries( - PartitionScaleMicrogrid - GridKit::power_elec_disgen - GridKit::power_elec_microline - GridKit::power_elec_microload - GridKit::solvers_dyn - GridKit::power_elec_microbusdq - GridKit::power_elec_partition_interfaces - OpenMP::OpenMP_CXX) + PRIVATE + GridKit::power_elec_disgen + GridKit::power_elec_microline + GridKit::power_elec_microload + GridKit::solvers_dyn + GridKit::power_elec_microbusdq + GridKit::power_elec_partition_interfaces +) + +add_test( + NAME PartitionMicrogrid + COMMAND partitionMicrogrid +) + +install( + TARGETS + partition + partitionMicrogrid + RUNTIME DESTINATION bin +) + +if(OpenMP_CXX_FOUND) + add_executable( + PartitionScaleMicrogrid + PartitionScaleMicrogrid.cpp + ) + + target_link_libraries( + PartitionScaleMicrogrid + PRIVATE + GridKit::power_elec_disgen + GridKit::power_elec_microline + GridKit::power_elec_microload + GridKit::solvers_dyn + GridKit::power_elec_microbusdq + GridKit::power_elec_partition_interfaces + OpenMP::OpenMP_CXX + ) + + add_test( + NAME PartitionScaleMicrogrid + COMMAND PartitionScaleMicrogrid + ) + + install( + TARGETS PartitionScaleMicrogrid + RUNTIME DESTINATION bin + ) +endif() \ No newline at end of file diff --git a/examples/PowerElectronics/Partition/Partition.cpp b/examples/PowerElectronics/Partition/Partition.cpp index 93f14cbc8..05feee692 100644 --- a/examples/PowerElectronics/Partition/Partition.cpp +++ b/examples/PowerElectronics/Partition/Partition.cpp @@ -75,8 +75,8 @@ int main(int /* argc */, char const** /* argv */) for (size_t i = 0; i < partition->getExternSize(); i++) { - partition->getExternalDataY()[i] = y[partition->getExternalIndices()[i]]; - partition->getExternalDataYP()[i] = yp[partition->getExternalIndices()[i]]; + partition->getExternalDataY()[i] = y[partition->getExternalDataIndices()[i]]; + partition->getExternalDataYP()[i] = yp[partition->getExternalDataIndices()[i]]; } auto* partition_y = partition->y().getData(); diff --git a/examples/PowerElectronics/Partition/PartitionMicrogrid.cpp b/examples/PowerElectronics/Partition/PartitionMicrogrid.cpp index d72c50f4e..ba9e7df64 100644 --- a/examples/PowerElectronics/Partition/PartitionMicrogrid.cpp +++ b/examples/PowerElectronics/Partition/PartitionMicrogrid.cpp @@ -90,6 +90,8 @@ int main() double Lload2 = 1.0 / (2.0 * M_PI * 50.0); using SignalNode = GridKit::PowerElectronics::SignalNode; + using Subsystem = GridKit::SubsystemModel; + SignalNode dg_signal; sysmodel->addNode(&dg_signal); @@ -167,8 +169,8 @@ int main() sysmodel->allocate(); - GridKit::SubsystemModel* partition1 = new GridKit::SubsystemModel(); - GridKit::SubsystemModel* partition2 = new GridKit::SubsystemModel(); + Subsystem* partition1 = new Subsystem(); + Subsystem* partition2 = new Subsystem(); GridKit::MicrogridLine l2copy(*l2); GridKit::BusPartitionInterface* busInterface1 = new GridKit::BusPartitionInterface(bus2, l2copy, 14); @@ -232,13 +234,30 @@ int main() { partition->allocate(); + // test hold and release methods + partition->release(); + partition->hold(); + partition->updateTime(2, 5); - for (size_t i = 0; i < partition->getExternSize(); i++) + // Test setTimeFunction function + auto forcing = [&]([[maybe_unused]] double t) -> Subsystem::ForcingData { - partition->getExternalDataY()[i] = y[partition->getExternalIndices()[i]]; - partition->getExternalDataYP()[i] = yp[partition->getExternalIndices()[i]]; - } + auto ext_size = partition->getExternSize(); + + std::vector y_ext(ext_size); + std::vector yp_ext(ext_size); + + for (size_t i = 0; i < partition->getExternSize(); i++) + { + y_ext[i] = y[partition->getExternalDataIndices()[i]]; + yp_ext[i] = yp[partition->getExternalDataIndices()[i]]; + } + + return {y_ext, yp_ext}; + }; + + partition->setTimeFunction(forcing); auto* partition_y = partition->y().getData(); auto* partition_yp = partition->yp().getData(); diff --git a/examples/PowerElectronics/Partition/PartitionScaleMicrogrid.cpp b/examples/PowerElectronics/Partition/PartitionScaleMicrogrid.cpp index 2a6da84d0..13b10a4ad 100644 --- a/examples/PowerElectronics/Partition/PartitionScaleMicrogrid.cpp +++ b/examples/PowerElectronics/Partition/PartitionScaleMicrogrid.cpp @@ -436,8 +436,8 @@ RunResult printMicrogridSystems(index_type N_size, index_type num_partitions) // Distribute external variables for (size_t i = 0; i < partition->getExternSize(); i++) { - partition->getExternalDataY()[i] = y[partition->getExternalIndices()[i]]; - partition->getExternalDataYP()[i] = yp[partition->getExternalIndices()[i]]; + partition->getExternalDataY()[i] = y[partition->getExternalDataIndices()[i]]; + partition->getExternalDataYP()[i] = yp[partition->getExternalDataIndices()[i]]; } auto* partition_y = partition->y().getData(); From 4ce744b5b8ba2bc41a3d8e5d10a7d71cef6f7848 Mon Sep 17 00:00:00 2001 From: abdourahmanbarry Date: Fri, 24 Jul 2026 05:15:31 +0000 Subject: [PATCH 33/52] Apply pre-commit fixes --- .../Model/PowerElectronics/SubsystemModel.hpp | 2 +- .../PowerElectronics/Partition/CMakeLists.txt | 68 ++++++------------- 2 files changed, 23 insertions(+), 47 deletions(-) diff --git a/GridKit/Model/PowerElectronics/SubsystemModel.hpp b/GridKit/Model/PowerElectronics/SubsystemModel.hpp index ff6a25d51..0c0cbcab5 100644 --- a/GridKit/Model/PowerElectronics/SubsystemModel.hpp +++ b/GridKit/Model/PowerElectronics/SubsystemModel.hpp @@ -654,4 +654,4 @@ namespace GridKit }; // class SubsystemModel -} // namespace GridKit \ No newline at end of file +} // namespace GridKit diff --git a/examples/PowerElectronics/Partition/CMakeLists.txt b/examples/PowerElectronics/Partition/CMakeLists.txt index 03d45b6f5..e428f5908 100644 --- a/examples/PowerElectronics/Partition/CMakeLists.txt +++ b/examples/PowerElectronics/Partition/CMakeLists.txt @@ -4,61 +4,37 @@ add_executable(partition Partition.cpp) target_link_libraries( partition - PRIVATE - GridKit::solvers_dyn - GridKit::power_elec_partition_interfaces -) + PRIVATE GridKit::solvers_dyn GridKit::power_elec_partition_interfaces) add_executable(partitionMicrogrid PartitionMicrogrid.cpp) target_link_libraries( partitionMicrogrid - PRIVATE - GridKit::power_elec_disgen - GridKit::power_elec_microline - GridKit::power_elec_microload - GridKit::solvers_dyn - GridKit::power_elec_microbusdq - GridKit::power_elec_partition_interfaces -) + PRIVATE GridKit::power_elec_disgen + GridKit::power_elec_microline + GridKit::power_elec_microload + GridKit::solvers_dyn + GridKit::power_elec_microbusdq + GridKit::power_elec_partition_interfaces) -add_test( - NAME PartitionMicrogrid - COMMAND partitionMicrogrid -) +add_test(NAME PartitionMicrogrid COMMAND partitionMicrogrid) -install( - TARGETS - partition - partitionMicrogrid - RUNTIME DESTINATION bin -) +install(TARGETS partition partitionMicrogrid RUNTIME DESTINATION bin) if(OpenMP_CXX_FOUND) - add_executable( - PartitionScaleMicrogrid - PartitionScaleMicrogrid.cpp - ) + add_executable(PartitionScaleMicrogrid PartitionScaleMicrogrid.cpp) target_link_libraries( PartitionScaleMicrogrid - PRIVATE - GridKit::power_elec_disgen - GridKit::power_elec_microline - GridKit::power_elec_microload - GridKit::solvers_dyn - GridKit::power_elec_microbusdq - GridKit::power_elec_partition_interfaces - OpenMP::OpenMP_CXX - ) - - add_test( - NAME PartitionScaleMicrogrid - COMMAND PartitionScaleMicrogrid - ) - - install( - TARGETS PartitionScaleMicrogrid - RUNTIME DESTINATION bin - ) -endif() \ No newline at end of file + PRIVATE GridKit::power_elec_disgen + GridKit::power_elec_microline + GridKit::power_elec_microload + GridKit::solvers_dyn + GridKit::power_elec_microbusdq + GridKit::power_elec_partition_interfaces + OpenMP::OpenMP_CXX) + + add_test(NAME PartitionScaleMicrogrid COMMAND PartitionScaleMicrogrid) + + install(TARGETS PartitionScaleMicrogrid RUNTIME DESTINATION bin) +endif() From 1887bf0317c417199a39503fdc980db911edd825 Mon Sep 17 00:00:00 2001 From: abdourahmanbarry Date: Fri, 24 Jul 2026 20:02:23 +0000 Subject: [PATCH 34/52] Addressed leaks --- examples/PowerElectronics/Partition/PartitionMicrogrid.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/PowerElectronics/Partition/PartitionMicrogrid.cpp b/examples/PowerElectronics/Partition/PartitionMicrogrid.cpp index ba9e7df64..c9a984950 100644 --- a/examples/PowerElectronics/Partition/PartitionMicrogrid.cpp +++ b/examples/PowerElectronics/Partition/PartitionMicrogrid.cpp @@ -326,6 +326,7 @@ int main() delete sysmodel; delete partition1; delete partition2; + delete busInterface1; return 0; } From 19a0a6f8d7158a15eea010c8b040cbf47fef14c6 Mon Sep 17 00:00:00 2001 From: abdourahmanbarry Date: Mon, 3 Aug 2026 05:13:25 +0000 Subject: [PATCH 35/52] Update partitioning code to use the no-copy implementation from develop --- .../PowerElectronics/CircuitComponent.hpp | 131 ++++-- GridKit/Model/PowerElectronics/NodeBase.hpp | 17 + .../BusPartitionInterface.cpp | 161 +++---- .../BusPartitionInterface.hpp | 12 +- .../Model/PowerElectronics/SubsystemModel.hpp | 394 ++++++++++-------- .../SystemModelPowerElectronics.hpp | 3 +- .../PowerElectronics/Partition/HiresBus.hpp | 18 +- .../Partition/HiresComponent1.hpp | 21 +- .../Partition/HiresComponent3.hpp | 20 +- .../PowerElectronics/Partition/Partition.cpp | 39 +- .../Partition/PartitionMicrogrid.cpp | 10 +- .../Partition/PartitionScaleMicrogrid.cpp | 38 +- 12 files changed, 507 insertions(+), 357 deletions(-) diff --git a/GridKit/Model/PowerElectronics/CircuitComponent.hpp b/GridKit/Model/PowerElectronics/CircuitComponent.hpp index f9a8e6ab5..3dacdb055 100644 --- a/GridKit/Model/PowerElectronics/CircuitComponent.hpp +++ b/GridKit/Model/PowerElectronics/CircuitComponent.hpp @@ -73,13 +73,11 @@ namespace GridKit */ if (other.connection_nodes_) { - connection_nodes_ = - std::make_unique(static_cast(size_)); + connection_nodes_ = std::make_unique(static_cast(size_)); for (IdxT i = 0; i < size_; ++i) { - connection_nodes_[static_cast(i)] = - other.connection_nodes_[static_cast(i)]; + connection_nodes_[static_cast(i)] = other.connection_nodes_[static_cast(i)]; } } @@ -88,13 +86,11 @@ namespace GridKit */ if (other.jacobian_coo_rows_) { - jacobian_coo_rows_ = - std::make_unique(static_cast(nnz_)); + jacobian_coo_rows_ = std::make_unique(static_cast(nnz_)); for (IdxT i = 0; i < nnz_; ++i) { - jacobian_coo_rows_[static_cast(i)] = - other.jacobian_coo_rows_[static_cast(i)]; + jacobian_coo_rows_[static_cast(i)] = other.jacobian_coo_rows_[static_cast(i)]; } } @@ -103,13 +99,11 @@ namespace GridKit */ if (other.jacobian_coo_cols_) { - jacobian_coo_cols_ = - std::make_unique(static_cast(nnz_)); + jacobian_coo_cols_ = std::make_unique(static_cast(nnz_)); for (IdxT i = 0; i < nnz_; ++i) { - jacobian_coo_cols_[static_cast(i)] = - other.jacobian_coo_cols_[static_cast(i)]; + jacobian_coo_cols_[static_cast(i)] = other.jacobian_coo_cols_[static_cast(i)]; } } @@ -118,13 +112,25 @@ namespace GridKit */ if (other.jacobian_coo_values_) { - jacobian_coo_values_ = - std::make_unique(static_cast(nnz_)); + jacobian_coo_values_ = std::make_unique(static_cast(nnz_)); for (IdxT i = 0; i < nnz_; ++i) { - jacobian_coo_values_[static_cast(i)] = - other.jacobian_coo_values_[static_cast(i)]; + jacobian_coo_values_[static_cast(i)] = other.jacobian_coo_values_[static_cast(i)]; + } + } + + if (size_ > 0) + { + y_ext_ = std::make_unique(static_cast(size_)); + yp_ext_ = std::make_unique(static_cast(size_)); + f_ext_ = std::make_unique(static_cast(size_)); + + for (IdxT i = 0; i < size_; ++i) + { + y_ext_[i] = nullptr; + yp_ext_[i] = nullptr; + f_ext_[i] = nullptr; } } @@ -133,17 +139,11 @@ namespace GridKit copyVector(yp_, other.yp_); copyVector(f_, other.f_); copyVector(abs_tol_, other.abs_tol_); - - // Quadrature vector. copyVector(g_, other.g_); - - // Adjoint vectors. copyVector(yB_, other.yB_); copyVector(ypB_, other.ypB_); copyVector(fB_, other.fB_); copyVector(gB_, other.gB_); - - // Parameter vectors. copyVector(param_, other.param_); copyVector(param_up_, other.param_up_); copyVector(param_lo_, other.param_lo_); @@ -191,7 +191,7 @@ namespace GridKit int setInternalConnectionNodes(size_t local_index, IdxT global_index) { assert(!extern_indices_.contains(static_cast(local_index))); - connection_nodes_[local_index] = global_index; + setConnectionNodes(local_index, global_index); return 0; } @@ -217,6 +217,23 @@ namespace GridKit return 0; } + /** + * @brief Update the connection index for a variable. + * + * Changes only the connection index without modifying the variable's + * internal/external classification or its associated data pointers. + * + * @param local_index Index of the local variable. + * @param connection_index New connection index for the variable. + * + * @return int 0 if successful. + */ + int setConnectionNodes(size_t local_index, IdxT connection_index) + { + connection_nodes_[local_index] = connection_index; + return 0; + } + /** * @brief Given the location of value in the local vector map to global index * @@ -254,10 +271,15 @@ namespace GridKit jacobian_coo_cols_ = std::make_unique(static_cast(nnz_)); jacobian_coo_values_ = std::make_unique(static_cast(nnz_)); - y_ext_ = std::make_unique(static_cast(size_)); - yp_ext_ = std::make_unique(static_cast(size_)); - f_ext_ = std::make_unique(static_cast(size_)); - connection_nodes_ = std::make_unique(static_cast(size_)); + y_ext_ = std::make_unique(static_cast(size_)); + yp_ext_ = std::make_unique(static_cast(size_)); + f_ext_ = std::make_unique(static_cast(size_)); + + // Make the existing allocation code idempotent: + if (!connection_nodes_) + { + connection_nodes_ = std::make_unique(static_cast(size_)); + } if (!allocated_) { @@ -347,6 +369,51 @@ namespace GridKit f_int_ = internal_res; } + /** + * @brief Clear all internal and external data pointers. + * + * This disconnects the component from the data storage currently associated + * with its internal and external variables. The external pointer arrays + * themselves remain allocated so that the component can be connected to + * new data later. + * + * @return int 0 if successful. + */ + int clearPointers() + { + // Clear internal data pointers. + y_int_ = nullptr; + yp_int_ = nullptr; + f_int_ = nullptr; + + // Clear external data pointers, but keep the pointer arrays allocated. + if (y_ext_) + { + for (IdxT i = 0; i < size_; ++i) + { + y_ext_[i] = nullptr; + } + } + + if (yp_ext_) + { + for (IdxT i = 0; i < size_; ++i) + { + yp_ext_[i] = nullptr; + } + } + + if (f_ext_) + { + for (IdxT i = 0; i < size_; ++i) + { + f_ext_[i] = nullptr; + } + } + + return 0; + } + protected: /** * @brief Reset the Jacobian so it can be constructed. Helper method for \ref setJacValues(). @@ -561,6 +628,16 @@ namespace GridKit return idc_; } + /** + * @brief Check whether the component has already been allocated. + * + * @return true if allocate() has previously completed, false otherwise. + */ + bool isAllocated() const + { + return allocated_; + } + protected: /** * @brief Allocate state and residual storage owned by this component. diff --git a/GridKit/Model/PowerElectronics/NodeBase.hpp b/GridKit/Model/PowerElectronics/NodeBase.hpp index 3c7f7c80b..2e04acff8 100644 --- a/GridKit/Model/PowerElectronics/NodeBase.hpp +++ b/GridKit/Model/PowerElectronics/NodeBase.hpp @@ -136,6 +136,23 @@ namespace GridKit }; } + /** + * @brief Update the connection index for a variable. + * + * Changes only the connection index without modifying the variable's + * internal/external classification or its associated data pointers. + * + * @param local_index Index of the local variable. + * @param connection_index New connection index for the variable. + * + * @return int 0 if successful. + */ + int setConnectionNodes(size_t local_index, IdxT connection_index) + { + connection_nodes_[local_index] = connection_index; + return 0; + } + int allocate() override { size_t size = static_cast(n_intern_ + n_extern_); diff --git a/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.cpp b/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.cpp index 578493931..7e26e05da 100644 --- a/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.cpp +++ b/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.cpp @@ -1,11 +1,12 @@ #include "BusPartitionInterface.hpp" -#include #include #include #include +#include "GridKit/Model/PowerElectronics/ExternalConnection.hpp" + namespace GridKit { @@ -40,17 +41,53 @@ namespace GridKit extern_indices_.insert(i); } + // The subsystem needs access to the interface connection indices before + // the interface is fully allocated. + connection_nodes_ = std::make_unique(static_cast(size_)); + + const IdxT bus_i = bus_.getNodeConnection(0).idx_; + const IdxT bus_j = bus_.getNodeConnection(1).idx_; + + bool port_i_set = false; + bool port_j_set = false; + + // Copy the wrapped component topology and identify the two bus ports. + for (size_t i = 0; i < static_cast(size_); ++i) + { + const IdxT connection_index = component_.getNodeConnection(i); + + this->setConnectionNodes(i, connection_index); + + if (connection_index == bus_i) + { + bus_port_i_ = i; + port_i_set = true; + } + else if (connection_index == bus_j) + { + bus_port_j_ = i; + port_j_set = true; + } + } + + if (!port_i_set || !port_j_set) + { + std::cerr << "ERROR: Invalid partition interface detected. " + << "Bus(ID=" << bus_.busID() + << "), Component(ID=" << component_.getIDcomponent() + << "). Please verify connection-node mappings and " + "internal/external index assignments." + << std::endl; + } + const IdxT* cooRow = component_.jacobianCooRows(); const IdxT* cooCol = component_.jacobianCooCols(); - const IdxT bus_i = bus.getNodeConnection(0); - const IdxT bus_j = bus.getNodeConnection(1); - nnz_ = 0; for (IdxT k = 0; k < component_.nnz(); ++k) { - const IdxT row_node = component_.getNodeConnection(cooRow[k]); - const IdxT col_node = component_.getNodeConnection(cooCol[k]); + const IdxT row_node = component_.getNodeConnection(static_cast(cooRow[k])); + const IdxT col_node = component_.getNodeConnection(static_cast(cooCol[k])); const bool row_is_bus = (row_node == bus_i || row_node == bus_j); const bool col_is_bus = (col_node == bus_i || col_node == bus_j); @@ -77,37 +114,6 @@ namespace GridKit CircuitComponent::allocate(); - std::fill_n(f_.getData(), size_, 0); - - bool port_i_set = false; - bool port_j_set = false; - - for (size_t i = 0; i < static_cast(size_); i++) - { - if (bus_.getNodeConnection(0) == component_.getNodeConnection(static_cast(i))) - { - bus_port_i_ = i; - port_i_set = true; - } - else if (bus_.getNodeConnection(1) == component_.getNodeConnection(static_cast(i))) - { - bus_port_j_ = i; - port_j_set = true; - } - IdxT global_idx = component_.getNodeConnection(static_cast(i)); - this->setExternalConnectionNodes(static_cast(i), global_idx); - } - - if (!port_i_set || !port_j_set) - { - std::cerr << "ERROR: Invalid partition interface detected. " - << "Bus(ID=" << bus_.busID() - << "), Component(ID=" << component_.getIDcomponent() - << "). Please verify connection-node mappings and internal/external index assignments." - << std::endl; - return 1; - } - const auto n = component_.getInternalSize(); y_ptr = std::make_unique(n); @@ -158,48 +164,17 @@ namespace GridKit template int BusPartitionInterface::evaluateExternalResidual() { - size_t internal = 0; - size_t external = 0; - - ScalarT* y = y_.getData(); - ScalarT* yp = yp_.getData(); - ScalarT* f = f_.getData(); - - ScalarT* component_y = component_.y().getData(); - ScalarT* component_yp = component_.yp().getData(); + std::vector component_residual(static_cast(component_.size())); - const auto& extern_indices = component_.getExternIndices(); + updateComponentPointers(component_residual.data()); - for (size_t i = 0; i < static_cast(component_.size()); ++i) + if (int err_code = component_.evaluateResidual()) { - if (extern_indices.contains(static_cast(i))) - { - component_y[external] = y[i]; - component_yp[external] = yp[i]; - ++external; - } - else - { - y_ptr[internal] = y[i]; - yp_ptr[internal] = yp[i]; - ++internal; - } + return err_code; } - component_.y().setDataUpdated(); - component_.y().setDataUpdated(); - - component_.evaluateResidual(); - - const auto* residual = component_.getResidual().getData(); - - // TODO: This assumes that external variables are ordered after all internal - // variables in the indexing. To make this more robust, we need to get rid of this assumption - // (although true for all components that we have in GridKit). - f[bus_port_i_] = residual[bus_port_i_]; - f[bus_port_j_] = residual[bus_port_j_]; - - f_.setDataUpdated(); + *f_ext_[bus_port_i_] += component_residual[bus_port_i_]; + *f_ext_[bus_port_j_] += component_residual[bus_port_j_]; return 0; } @@ -214,8 +189,13 @@ namespace GridKit template int BusPartitionInterface::evaluateJacobian() { + this->zeroJacMatrix(); + // The Jacobian only requires the component state pointers. Residual + // contributions are redirected to dummy storage. + updateComponentPointers(nullptr); + component_.evaluateJacobian(); const IdxT* cooRows = component_.jacobianCooRows(); @@ -238,6 +218,37 @@ namespace GridKit return 0; } + template + int BusPartitionInterface::updateComponentPointers(ScalarT* residual) + { + size_t internal_index = 0; + + const auto& external_indices = component_.getExternIndices(); + + for (size_t i = 0; i < static_cast(component_.size()); ++i) + { + if (external_indices.contains(static_cast(i))) + { + ExternalConnection connection{ + .y_ = y_ext_[i], + .yp_ = yp_ext_[i], + .f_ = residual ? &residual[i] : &dummy_residual_, + .idx_ = component_.getNodeConnection(i)}; + + component_.setExternalConnectionNodes(i, connection); + } + else + { + y_ptr[internal_index] = *y_ext_[i]; + yp_ptr[internal_index] = *yp_ext_[i]; + + ++internal_index; + } + } + + return 0; + } + template int BusPartitionInterface::evaluateIntegrand() { @@ -268,4 +279,4 @@ namespace GridKit template class BusPartitionInterface; template class BusPartitionInterface; -} // namespace GridKit +} // namespace GridKit \ No newline at end of file diff --git a/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.hpp b/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.hpp index 93c764b8f..8c679a9f0 100644 --- a/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.hpp +++ b/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.hpp @@ -25,13 +25,13 @@ namespace GridKit using CircuitComponent::nnz_; using CircuitComponent::time_; using CircuitComponent::alpha_; - using CircuitComponent::y_; + using CircuitComponent::y_ext_; using CircuitComponent::y_int_; - using CircuitComponent::yp_; + using CircuitComponent::yp_ext_; using CircuitComponent::yp_int_; using CircuitComponent::tag_; using CircuitComponent::abs_tol_; - using CircuitComponent::f_; + using CircuitComponent::f_ext_; using CircuitComponent::f_int_; using CircuitComponent::g_; using CircuitComponent::yB_; @@ -44,6 +44,7 @@ namespace GridKit using CircuitComponent::extern_indices_; using CircuitComponent::n_extern_; using CircuitComponent::n_intern_; + using CircuitComponent::connection_nodes_; using PartitionInterface::bus_port_i_; using PartitionInterface::bus_port_j_; @@ -65,6 +66,7 @@ namespace GridKit int evaluateAdjointResidual(); // int evaluateAdjointJacobian(); int evaluateAdjointIntegrand(); + int updateComponentPointers(ScalarT* residual); private: component_type& component_; @@ -74,6 +76,8 @@ namespace GridKit std::unique_ptr yp_ptr; std::unique_ptr f_ptr; + ScalarT dummy_residual_{}; + std::vector jac_map_; }; -} // namespace GridKit +} // namespace GridKit \ No newline at end of file diff --git a/GridKit/Model/PowerElectronics/SubsystemModel.hpp b/GridKit/Model/PowerElectronics/SubsystemModel.hpp index 0c0cbcab5..59746ad71 100644 --- a/GridKit/Model/PowerElectronics/SubsystemModel.hpp +++ b/GridKit/Model/PowerElectronics/SubsystemModel.hpp @@ -37,7 +37,6 @@ namespace GridKit using SystemModel::allocated_; using SystemModel::allocateVectors; using SystemModel::alpha_; - using SystemModel::f_; using SystemModel::f_int_; using SystemModel::n_extern_; using SystemModel::n_intern_; @@ -45,9 +44,7 @@ namespace GridKit using SystemModel::size_; using SystemModel::tag_; using SystemModel::time_; - using SystemModel::y_; using SystemModel::y_int_; - using SystemModel::yp_; using SystemModel::yp_int_; using SystemModel::components_; @@ -87,6 +84,11 @@ namespace GridKit n_extern_ = external_map_.size(); size_ = n_intern_; + if (!allocated_) + { + allocateVectors(static_cast(size_), true); + } + CircuitComponent::allocate(); // Allocation always rebuilds the system Jacobian and its COO-to-CSR map. @@ -99,16 +101,16 @@ namespace GridKit tag_.resize(size_); // Allocate subsystem vectors. - y_ext_.resize(n_extern_); - yp_ext_.resize(n_extern_); - f_ext_.resize(n_extern_); + y_ext_data_.resize(n_extern_); + yp_ext_data_.resize(n_extern_); + f_ext_data_.resize(n_extern_); external_data_indices_.resize(n_extern_); // Store the mapping from local subsystem indices back to their global system indices for (const auto [global_idx, local_idx] : internal_map_) { - this->setExternalConnectionNodes(local_idx, global_idx); + this->setConnectionNodes(local_idx, global_idx); } // Store the global indices of all external coupling variables. @@ -117,22 +119,80 @@ namespace GridKit external_data_indices_[local_idx - n_intern_] = global_idx; } - size_t component_internal_idx = 0; - const auto* y = y_.getData(); - const auto* yp = yp_.getData(); - auto* f = f_.getData(); - for (component_type* comp : components_) + tag_.resize(size_); + + { // Start node internal indexing after all component internals for proper KLU ordering + size_t node_internal_idx; + for (node_type* node : nodes_) + { + for (size_t i = 0; i < node->getInternalSize(); i++) + { + node_internal_idx = node->getNodeConnection(i).idx_; + + ExternalConnection node_connection{ + .y_ = y_int_ + node_internal_idx, + .yp_ = yp_int_ + node_internal_idx, + .f_ = f_int_ + node_internal_idx, + .idx_ = static_cast(node_internal_idx)}; + + node->setExternalConnectionNodes(i, node_connection); + node_internal_idx++; + } + } + } + { + // The offset for each component's internal variables in the system vector. + // They start at 0, and are stacked on top of each other. + size_t component_internal_idx = 0; + for (component_type* comp : components_) + { + if (!comp->isAllocated()) + { + comp->allocate(); + } + // Update component internal pointers to their correct offsets + comp->setInternalPointer(&y_int_[component_internal_idx]); + comp->setInternalDerivativePointer(&yp_int_[component_internal_idx]); + comp->setInternalResidualPointer(&f_int_[component_internal_idx]); - comp->setInternalPointer(&y[component_internal_idx]); - comp->setInternalDerivativePointer(&yp[component_internal_idx]); - comp->setInternalResidualPointer(&f[component_internal_idx]); + component_internal_idx += comp->getInternalSize(); - component_internal_idx += comp->getInternalSize(); + const auto& external_indices = comp->getExternIndices(); + + for (IdxT local_index : external_indices) + { + const IdxT connection_index = comp->getNodeConnection(local_index); + + // External to the component but internal to the subsystem. + if (connection_index < n_intern_) + { + ExternalConnection connection{ + .y_ = y_int_ + connection_index, + .yp_ = yp_int_ + connection_index, + .f_ = f_int_ + connection_index, + .idx_ = connection_index}; + + comp->setExternalConnectionNodes(local_index, connection); + + continue; + } + + // External to both the component and subsystem. + const IdxT external_offset = connection_index - static_cast(n_intern_); + + ExternalConnection connection{ + .y_ = &y_ext_data_[external_offset], + .yp_ = &yp_ext_data_[external_offset], + .f_ = &f_ext_data_[external_offset], + .idx_ = connection_index}; + + comp->setExternalConnectionNodes(local_index, connection); + } + } } // Evaluate component Jacobians to get sparsity - distributeVectors(); for (component_type* component : components_) { component->evaluateJacobian(); @@ -243,52 +303,51 @@ namespace GridKit * * @return int 0 if successful, positive if there's a recoverable error, negative if unrecoverable */ - int distributeVectors() override + int distributeExternalVectors() { if (forcing_function_) { const auto [y_forcing, yp_forcing] = (*forcing_function_)(time_); - assert(y_forcing.size() == y_ext_.size()); - assert(yp_forcing.size() == yp_ext_.size()); + assert(y_forcing.size() == y_ext_data_.size()); + assert(yp_forcing.size() == yp_ext_data_.size()); - std::copy(y_forcing.begin(), y_forcing.end(), y_ext_.begin()); - std::copy(yp_forcing.begin(), yp_forcing.end(), yp_ext_.begin()); + std::copy(y_forcing.begin(), y_forcing.end(), y_ext_data_.begin()); + std::copy(yp_forcing.begin(), yp_forcing.end(), yp_ext_data_.begin()); } - const auto* y_system = y_.getData(); - const auto* yp_system = yp_.getData(); + return 0; + } - for (component_type* component : components_) - { - auto* y = component->y().getData(); - auto* yp = component->yp().getData(); - const auto& externals = component->getExternIndices(); + /** + * @brief Evaluate Residuals at each component then collect them + * + * @return int 0 if successful, positive if there's a recoverable error, negative if unrecoverable + */ + int evaluateInternalResidual() override + { + distributeExternalVectors(); - for (size_t j : externals) - { - IdxT local_idx = component->getNodeConnection(j); + SystemModel::evaluateInternalResidual(); + + return 0; + } + + /** + * @brief Creates the system Jacobian representing \f$\alpha dF/dy' + dF/dy\f$ + * + * Updates the CSR Jacobian values using the per-component mappings + * computed during allocate(). + * + * @return int 0 if successful, positive if there's a recoverable error, negative if unrecoverable + */ + int evaluateJacobian() override + { + distributeExternalVectors(); + + SystemModel::evaluateJacobian(); - if (local_idx == neg1_) - { - y[j] = 0.0; - yp[j] = 0.0; - } - else if (local_idx < this->getInternalSize()) - { - y[j] = y_system[local_idx]; - yp[j] = yp_system[local_idx]; - } - else - { - y[j] = y_ext_[local_idx - this->getInternalSize()]; - yp[j] = yp_ext_[local_idx - this->getInternalSize()]; - } - } - component->y().setDataUpdated(); - component->yp().setDataUpdated(); - } return 0; } @@ -301,7 +360,9 @@ namespace GridKit * * @param[in] component Component to add. */ - void addComponent(component_type* component) + void + addComponent(component_type* component) + { if (is_comp_in_local_state) { @@ -359,35 +420,35 @@ namespace GridKit return 0; } - /** - * @brief Release the subsystem's global-to-local index mappings. - * - * If components/nodes currently hold local indices, they are first - * converted back to their original global indices via - * mapLocalToGlobal(). The internal/external index maps built by - * buildIndexMappings() are then cleared, and the model is marked as - * not allocated. - * - * - * Call this before modifying subsystem topology (adding or removing - * components/nodes), so stale local indices aren't left dangling. - * - * @return int 0 if successful, positive if there's a recoverable error, negative if unrecoverable - */ - int release() - { - if (int err_code = mapLocalToGlobal()) - { - return err_code; - } - - internal_map_.clear(); - external_map_.clear(); - - allocated_ = false; - - return 0; - } + // /** + // * @brief Release the subsystem's global-to-local index mappings. + // * + // * If components/nodes currently hold local indices, they are first + // * converted back to their original global indices via + // * mapLocalToGlobal(). The internal/external index maps built by + // * buildIndexMappings() are then cleared, and the model is marked as + // * not allocated. + // * + // * + // * Call this before modifying subsystem topology (adding or removing + // * components/nodes), so stale local indices aren't left dangling. + // * + // * @return int 0 if successful, positive if there's a recoverable error, negative if unrecoverable + // */ + // int release() + // { + // if (int err_code = mapLocalToGlobal()) + // { + // return err_code; + // } + + // internal_map_.clear(); + // external_map_.clear(); + + // allocated_ = false; + + // return 0; + // } const std::vector& getExternalDataIndices() { @@ -396,17 +457,17 @@ namespace GridKit std::vector& getExternalDataY() { - return y_ext_; + return y_ext_data_; } std::vector& getExternalDataYP() { - return yp_ext_; + return yp_ext_data_; } std::vector& getExternalDataF() { - return f_ext_; + return f_ext_data_; } void setTimeFunction(TimeFunction function) @@ -425,73 +486,66 @@ namespace GridKit } private: - /** - * @brief Replace local subsystem connection indices with their original global indices - * for every component and node. - * - * Inverse of mapGlobalToLocal(). Internal connections (index < internal - * size) are mapped back through this subsystem's own node-connection - * table; external connections are mapped back through - * @c external_data_indices_. Entries equal to @c neg1_ represent no - * connection and are left unchanged. - * - * @return Always returns 0. - */ - int mapLocalToGlobal() - { - if (!is_comp_in_local_state) - { - return 0; - } - - for (auto* component : components_) - { - - for (IdxT i = 0; i < component->size(); i++) - { - IdxT index = component->getNodeConnection(i); - - if (index == neg1_) - { - continue; - } - else if (index < this->getInternalSize()) - { - component->setExternalConnectionNodes(i, this->getNodeConnection(index)); - } - else - { - component->setExternalConnectionNodes(i, external_data_indices_[index - this->getInternalSize()]); - } - } - } - - for (auto* node : nodes_) - { - - for (IdxT i = 0; i < node->size(); i++) - { - IdxT index = node->getNodeConnection(i); - - if (index == neg1_) - { - continue; - } - else if (index < this->getInternalSize()) - { - node->setExternalConnectionNodes(i, this->getNodeConnection(index)); - } - else - { - node->setExternalConnectionNodes(i, external_data_indices_[index - this->getInternalSize()]); - } - } - } - - is_comp_in_local_state = false; - - return 0; - } + // /** + // * @brief Replace local subsystem connection indices with their original global indices + // * for every component and node. + // * + // * Inverse of mapGlobalToLocal(). Internal connections (index < internal + // * size) are mapped back through this subsystem's own node-connection + // * table; external connections are mapped back through + // * @c external_data_indices_. Entries equal to @c neg1_ represent no + // * connection and are left unchanged. + // * + // * @return Always returns 0. + // */ + // int mapLocalToGlobal() + // { + // if (!is_comp_in_local_state) + // { + // return 0; + // } + + // for (auto* component : components_) + // { + + // for (IdxT i = 0; i < component->size(); i++) + // { + // IdxT index = component->getNodeConnection(i); + + // if (index == neg1_) + // { + // continue; + // } + // else if (index < this->getInternalSize()) + // { + // component->setExternalConnectionNodes(i, this->getNodeConnection(index)); + // } + // else + // { + // component->setExternalConnectionNodes(i, external_data_indices_[index - this->getInternalSize()]); + // } + // } + // } + + // for (auto* node : nodes_) + // { + + // for (IdxT i = 0; i < node->size(); i++) + // { + // IdxT index = node->getNodeConnection(i); + + // if (index == neg1_) + // { + // continue; + // } + // node->setExternalConnectionNodes(i, this->getNodeConnection(index)); + // } + // } + + // is_comp_in_local_state = false; + + // return 0; + // } /** * @brief Replace global connection indices with local subsystem indices for every component @@ -512,7 +566,6 @@ namespace GridKit int mapGlobalToLocal() { - if (is_comp_in_local_state) { return 0; @@ -520,45 +573,38 @@ namespace GridKit for (auto* component : components_) { - - for (IdxT i = 0; i < component->size(); i++) + for (IdxT i = 0; i < component->size(); ++i) { - IdxT index = component->getNodeConnection(i); + const IdxT index = component->getNodeConnection(i); if (index == neg1_) { continue; } - else if (internal_map_.count(index) > 0) + + if (internal_map_.contains(index)) { - component->setExternalConnectionNodes(i, internal_map_.at(index)); + component->setConnectionNodes(i, internal_map_.at(index)); } else { - component->setExternalConnectionNodes(i, external_map_.at(index)); + component->setConnectionNodes(i, external_map_.at(index)); } } } for (auto* node : nodes_) { - - for (IdxT i = 0; i < node->size(); i++) + for (IdxT i = 0; i < node->size(); ++i) { - IdxT index = node->getNodeConnection(i); + const IdxT index = node->getNodeConnection(i).idx_; if (index == neg1_) { continue; } - else if (internal_map_.count(index) > 0) - { - node->setExternalConnectionNodes(i, internal_map_.at(index)); - } - else - { - node->setExternalConnectionNodes(i, external_map_.at(index)); - } + + node->setConnectionNodes(i, internal_map_.at(index)); } } @@ -612,7 +658,7 @@ namespace GridKit for (IdxT i = 0; i < node->size(); i++) { - IdxT index = node->getNodeConnection(i); + IdxT index = node->getNodeConnection(i).idx_; if (index != neg1_) { @@ -644,9 +690,9 @@ namespace GridKit std::unordered_map external_map_; std::vector external_data_indices_; - std::vector y_ext_; - std::vector yp_ext_; - std::vector f_ext_; + std::vector y_ext_data_; + std::vector yp_ext_data_; + std::vector f_ext_data_; std::optional forcing_function_; @@ -654,4 +700,4 @@ namespace GridKit }; // class SubsystemModel -} // namespace GridKit +} // namespace GridKit \ No newline at end of file diff --git a/GridKit/Model/PowerElectronics/SystemModelPowerElectronics.hpp b/GridKit/Model/PowerElectronics/SystemModelPowerElectronics.hpp index 7e72f2549..76f3f4c5d 100644 --- a/GridKit/Model/PowerElectronics/SystemModelPowerElectronics.hpp +++ b/GridKit/Model/PowerElectronics/SystemModelPowerElectronics.hpp @@ -23,6 +23,7 @@ namespace GridKit using component_type = CircuitComponent; using node_type = PowerElectronics::NodeBase; + protected: using Base::abs_tol_; using Base::allocated_; using Base::allocateVectors; @@ -478,4 +479,4 @@ namespace GridKit }; // class PowerElectronicsModel -} // namespace GridKit +} // namespace GridKit \ No newline at end of file diff --git a/examples/PowerElectronics/Partition/HiresBus.hpp b/examples/PowerElectronics/Partition/HiresBus.hpp index d37e396d3..220d20521 100644 --- a/examples/PowerElectronics/Partition/HiresBus.hpp +++ b/examples/PowerElectronics/Partition/HiresBus.hpp @@ -20,13 +20,13 @@ namespace GridKit using CircuitComponent::nnz_; using CircuitComponent::time_; using CircuitComponent::alpha_; - using CircuitComponent::y_; + using CircuitComponent::y_ext_; using CircuitComponent::y_int_; - using CircuitComponent::yp_; + using CircuitComponent::yp_ext_; using CircuitComponent::yp_int_; using CircuitComponent::tag_; using CircuitComponent::abs_tol_; - using CircuitComponent::f_; + using CircuitComponent::f_ext_; using CircuitComponent::f_int_; using CircuitComponent::g_; using CircuitComponent::yB_; @@ -83,14 +83,10 @@ namespace GridKit int evaluateExternalResidual() { - auto* y = y_.getData(); - auto* yp = yp_.getData(); - auto* f = f_.getData(); + *f_ext_[0] += -*yp_ext_[0] - *y_ext_[0]; + *f_ext_[1] += -*yp_ext_[1] - *y_ext_[1]; - f[0] = -yp[0] - y[0]; - f[1] = -yp[1] - y[1]; - - f_.setDataUpdated(); + this->getResidual().setDataUpdated(); return 0; } @@ -149,4 +145,4 @@ namespace GridKit private: NodeT* node_ref_; }; -} // namespace GridKit +} // namespace GridKit \ No newline at end of file diff --git a/examples/PowerElectronics/Partition/HiresComponent1.hpp b/examples/PowerElectronics/Partition/HiresComponent1.hpp index 503bf963d..77226b3f5 100644 --- a/examples/PowerElectronics/Partition/HiresComponent1.hpp +++ b/examples/PowerElectronics/Partition/HiresComponent1.hpp @@ -21,13 +21,13 @@ namespace GridKit using CircuitComponent::nnz_; using CircuitComponent::time_; using CircuitComponent::alpha_; - using CircuitComponent::y_; + using CircuitComponent::y_ext_; using CircuitComponent::y_int_; - using CircuitComponent::yp_; + using CircuitComponent::yp_ext_; using CircuitComponent::yp_int_; using CircuitComponent::tag_; using CircuitComponent::abs_tol_; - using CircuitComponent::f_; + using CircuitComponent::f_ext_; using CircuitComponent::f_int_; using CircuitComponent::g_; using CircuitComponent::yB_; @@ -79,26 +79,19 @@ namespace GridKit int evaluateInternalResidual() { - auto* y = y_.getData(); - // Internals f_int_[0] = -yp_int_[0] - 1.71 * y_int_[0] + 0.43 * y_int_[1] + 8.32 * y_int_[2] + 0.0007; f_int_[1] = -yp_int_[1] + 1.71 * y_int_[0] - 8.75 * y_int_[1]; - f_int_[2] = -yp_int_[2] - 10.03 * y_int_[2] + 0.43 * y[0] + 0.035 * y[1]; + f_int_[2] = -yp_int_[2] - 10.03 * y_int_[2] + 0.43 * *y_ext_[0] + 0.035 * *y_ext_[1]; return 0; } int evaluateExternalResidual() { - auto* y = y_.getData(); - auto* f = f_.getData(); - // outputs - f[0] = 8.32 * y_int_[1] + 1.71 * y_int_[2] - 0.1 * y[0]; - f[1] = -0.7 * y[1]; - - f_.setDataUpdated(); + *f_ext_[0] += 8.32 * y_int_[1] + 1.71 * y_int_[2] - 0.1 * *y_ext_[0]; + *f_ext_[1] += -0.7 * *y_ext_[1]; return 0; } @@ -166,4 +159,4 @@ namespace GridKit private: NodeT* node_ref_; }; -} // namespace GridKit +} // namespace GridKit \ No newline at end of file diff --git a/examples/PowerElectronics/Partition/HiresComponent3.hpp b/examples/PowerElectronics/Partition/HiresComponent3.hpp index 5e8ddf4cd..6f0761777 100644 --- a/examples/PowerElectronics/Partition/HiresComponent3.hpp +++ b/examples/PowerElectronics/Partition/HiresComponent3.hpp @@ -19,13 +19,13 @@ namespace GridKit using CircuitComponent::nnz_; using CircuitComponent::time_; using CircuitComponent::alpha_; - using CircuitComponent::y_; + using CircuitComponent::y_ext_; using CircuitComponent::y_int_; - using CircuitComponent::yp_; + using CircuitComponent::yp_ext_; using CircuitComponent::yp_int_; using CircuitComponent::tag_; using CircuitComponent::abs_tol_; - using CircuitComponent::f_; + using CircuitComponent::f_ext_; using CircuitComponent::f_int_; using CircuitComponent::g_; using CircuitComponent::yB_; @@ -77,10 +77,9 @@ namespace GridKit int evaluateInternalResidual() { - auto* y = y_.getData(); // Internals - f_int_[0] = -yp_int_[0] - 280 * y_int_[0] * y_int_[2] + 0.69 * y[0] + 1.71 * y[1] - 0.43 * y_int_[0] + 0.69 * y_int_[1]; + f_int_[0] = -yp_int_[0] - 280 * y_int_[0] * y_int_[2] + 0.69 * *y_ext_[0] + 1.71 * *y_ext_[1] - 0.43 * y_int_[0] + 0.69 * y_int_[1]; f_int_[1] = -yp_int_[1] + 280 * y_int_[0] * y_int_[2] - 1.81 * y_int_[1]; f_int_[2] = -yp_int_[2] - 280 * y_int_[0] * y_int_[2] + 1.81 * y_int_[1]; @@ -89,14 +88,9 @@ namespace GridKit int evaluateExternalResidual() { - auto* y = y_.getData(); - auto* f = f_.getData(); - // Externals - f[0] = -0.02 * y[0]; - f[1] = -0.045 * y[1] + 0.43 * y_int_[0] + 0.43 * y_int_[1]; - - f_.setDataUpdated(); + *f_ext_[0] = -0.02 * *y_ext_[0]; + *f_ext_[1] = -0.045 * *y_ext_[1] + 0.43 * y_int_[0] + 0.43 * y_int_[1]; return 0; } @@ -177,4 +171,4 @@ namespace GridKit private: NodeT* node_ref_; }; -} // namespace GridKit +} // namespace GridKit \ No newline at end of file diff --git a/examples/PowerElectronics/Partition/Partition.cpp b/examples/PowerElectronics/Partition/Partition.cpp index 05feee692..18abba3dc 100644 --- a/examples/PowerElectronics/Partition/Partition.cpp +++ b/examples/PowerElectronics/Partition/Partition.cpp @@ -1,5 +1,8 @@ // #include +#include + +#include "GridKit/ScalarTraits.hpp" #define _USE_MATH_DEFINES #include #include @@ -8,6 +11,7 @@ #include #include +#include #include #include @@ -32,28 +36,36 @@ int main(int /* argc */, char const** /* argv */) bus.allocate(); - bus.setExternalConnectionNodes(0, 3); - bus.setExternalConnectionNodes(1, 4); + GridKit::ExternalConnection connection_node1{ + .y_ = nullptr, + .yp_ = nullptr, + .f_ = nullptr, + .idx_ = 3}; + + GridKit::ExternalConnection connection_node2{ + .y_ = nullptr, + .yp_ = nullptr, + .f_ = nullptr, + .idx_ = 4}; + + bus.setExternalConnectionNodes(0, connection_node1); + bus.setExternalConnectionNodes(1, connection_node2); comp1->allocate(); comp3->allocate(); bus1->allocate(); - comp1->setExternalConnectionNodes(2, 0); - comp1->setExternalConnectionNodes(3, 1); - comp1->setExternalConnectionNodes(4, 2); - - bus1->setExternalConnectionNodes(0, 3); - bus1->setExternalConnectionNodes(1, 4); + comp1->setInternalConnectionNodes(2, 0); + comp1->setInternalConnectionNodes(3, 1); + comp1->setInternalConnectionNodes(4, 2); - comp3->setExternalConnectionNodes(2, 5); - comp3->setExternalConnectionNodes(3, 6); - comp3->setExternalConnectionNodes(4, 7); + comp3->setInternalConnectionNodes(2, 5); + comp3->setInternalConnectionNodes(3, 6); + comp3->setInternalConnectionNodes(4, 7); GridKit::HiresComponent3 comp3copy(*comp3); GridKit::BusPartitionInterface* busInterface = new GridKit::BusPartitionInterface(bus, comp3copy, 4); - busInterface->allocate(); partition1->addComponent(comp1); partition1->addComponent(bus1); partition1->addComponent(busInterface); @@ -71,6 +83,7 @@ int main(int /* argc */, char const** /* argv */) for (auto* partition : subsystems) { partition->allocate(); + partition->initialize(); partition->updateTime(2, 5); for (size_t i = 0; i < partition->getExternSize(); i++) @@ -205,4 +218,4 @@ std::vector hires(const std::vector& y, const std::vector l2copy(*l2); GridKit::BusPartitionInterface* busInterface1 = new GridKit::BusPartitionInterface(bus2, l2copy, 14); - busInterface1->allocate(); - + // busInterface1->allocate(); partition1->addNode(&dg_signal); partition1->addComponent(dg1); partition1->addComponent(dg2); @@ -233,10 +232,11 @@ int main() for (auto* partition : partitions) { partition->allocate(); + partition->initialize(); // test hold and release methods - partition->release(); - partition->hold(); + // partition->release(); + // partition->hold(); partition->updateTime(2, 5); @@ -329,4 +329,4 @@ int main() delete busInterface1; return 0; -} +} \ No newline at end of file diff --git a/examples/PowerElectronics/Partition/PartitionScaleMicrogrid.cpp b/examples/PowerElectronics/Partition/PartitionScaleMicrogrid.cpp index 13b10a4ad..8f7b5b397 100644 --- a/examples/PowerElectronics/Partition/PartitionScaleMicrogrid.cpp +++ b/examples/PowerElectronics/Partition/PartitionScaleMicrogrid.cpp @@ -68,7 +68,7 @@ RunResult printMicrogridSystems(index_type N_size, index_type num_partitions); */ int main() { - index_type N_size = 5000; + index_type N_size = 5; std::cout << std::format("{:<16}{:>16}{:>18}{:>12}{:>14}{:>16}\n", "num_partitions", @@ -80,7 +80,7 @@ int main() std::cout << std::string(93, '-') << "\n"; - for (index_type p : {10, 48, 100, 500, 1000, 2000, 5000}) + for (index_type p : {2, 3, 4, 5}) { assert(p <= 2 * N_size); @@ -214,7 +214,7 @@ RunResult printMicrogridSystems(index_type N_size, index_type num_partitions) using Load = MicrogridLoad; using PartitionInterface = BusPartitionInterface; - std::vector buses(num_ibrs, nullptr); + std::vector buses(num_ibrs); std::vector busesDQ(num_ibrs, nullptr); std::vector generators(num_ibrs, nullptr); std::vector lines(num_ibrs, nullptr); @@ -225,18 +225,18 @@ RunResult printMicrogridSystems(index_type N_size, index_type num_partitions) SignalNode dg_signal; // sys_model_control->addNode(&dg_signal); - for (size_t i = 0; i < 2 * N_size; i++) - { - buses[i] = new Bus(); - // sys_model_control->addNode(buses[i]); - } + // for (size_t i = 0; i < 2 * N_size; i++) + // { + // buses[i] = new Bus(); + // sys_model_control->addNode(buses[i]); + // } // Create the reference DG auto* dg_ref = new DistributedGenerator(0, DGParams_list[0], true, &dg_signal, - buses[0]); + &buses[0]); // sys_model_control->addComponent(dg_ref); generators[0] = dg_ref; @@ -251,7 +251,7 @@ RunResult printMicrogridSystems(index_type N_size, index_type num_partitions) DGParams_list[i], false, &dg_signal, - buses[i]); + &buses[i]); generators[i] = dg; // sys_model_control->addComponent(dg); @@ -265,8 +265,8 @@ RunResult printMicrogridSystems(index_type N_size, index_type num_partitions) rline_list[i], Lline_list[i], &dg_signal, - buses[i], - buses[i + 1]); + &buses[i], + &buses[i + 1]); lines[i + 1] = line_model; // sys_model_control->addComponent(line_model); } @@ -278,7 +278,7 @@ RunResult printMicrogridSystems(index_type N_size, index_type num_partitions) rload_list[i], Lload_list[i], &dg_signal, - buses[2 * i]); + &buses[2 * i]); loads[2 * i] = load_model; // sys_model_control->addComponent(load_model); } @@ -286,7 +286,7 @@ RunResult printMicrogridSystems(index_type N_size, index_type num_partitions) // Add all the microgrid Virtual DQ Buses for (index_type i = 0; i < 2 * N_size; i++) { - auto* virDQbus_model = new MicrogridBusDQ(model_id++, RN, buses[i]); + auto* virDQbus_model = new MicrogridBusDQ(model_id++, RN, &buses[i]); busesDQ[i] = virDQbus_model; // sys_model_control->addComponent(virDQbus_model); @@ -307,7 +307,7 @@ RunResult printMicrogridSystems(index_type N_size, index_type num_partitions) { sys_model_control->addComponent(lines[i]); } - sys_model_control->addNode(buses[i]); + sys_model_control->addNode(&buses[i]); } sys_model_control->allocate(); @@ -390,15 +390,13 @@ RunResult printMicrogridSystems(index_type N_size, index_type num_partitions) partition->addComponent(lines[index]); } - partition->addNode(buses[index]); + partition->addNode(&buses[index]); } // Add the partition interface to the left partition if (index < num_ibrs) { auto* linecopy = new GridKit::MicrogridLine(*lines[index]); - auto* busInterface = new GridKit::BusPartitionInterface(*buses[index - 1], *linecopy, model_id++); - - busInterface->allocate(); + auto* busInterface = new GridKit::BusPartitionInterface(buses[index - 1], *linecopy, model_id++); partitionInterface.push_back(busInterface); linesCopies.push_back(linecopy); @@ -533,4 +531,4 @@ RunResult printMicrogridSystems(index_type N_size, index_type num_partitions) delete sys_model_control; return result; -} +} \ No newline at end of file From 7fa22d6677867ce88545f5f871d1b208fb65c4e6 Mon Sep 17 00:00:00 2001 From: abdourahmanbarry Date: Mon, 3 Aug 2026 05:16:38 +0000 Subject: [PATCH 36/52] Apply pre-commit fixes --- .../PartitionInterface/BusPartitionInterface.cpp | 2 +- .../PartitionInterface/BusPartitionInterface.hpp | 2 +- GridKit/Model/PowerElectronics/SubsystemModel.hpp | 2 +- GridKit/Model/PowerElectronics/SystemModelPowerElectronics.hpp | 2 +- examples/PowerElectronics/Partition/HiresBus.hpp | 2 +- examples/PowerElectronics/Partition/HiresComponent1.hpp | 2 +- examples/PowerElectronics/Partition/HiresComponent3.hpp | 2 +- examples/PowerElectronics/Partition/Partition.cpp | 2 +- examples/PowerElectronics/Partition/PartitionMicrogrid.cpp | 2 +- examples/PowerElectronics/Partition/PartitionScaleMicrogrid.cpp | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.cpp b/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.cpp index 7e26e05da..99d173f8b 100644 --- a/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.cpp +++ b/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.cpp @@ -279,4 +279,4 @@ namespace GridKit template class BusPartitionInterface; template class BusPartitionInterface; -} // namespace GridKit \ No newline at end of file +} // namespace GridKit diff --git a/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.hpp b/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.hpp index 8c679a9f0..077449b52 100644 --- a/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.hpp +++ b/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.hpp @@ -80,4 +80,4 @@ namespace GridKit std::vector jac_map_; }; -} // namespace GridKit \ No newline at end of file +} // namespace GridKit diff --git a/GridKit/Model/PowerElectronics/SubsystemModel.hpp b/GridKit/Model/PowerElectronics/SubsystemModel.hpp index 59746ad71..d21382bb6 100644 --- a/GridKit/Model/PowerElectronics/SubsystemModel.hpp +++ b/GridKit/Model/PowerElectronics/SubsystemModel.hpp @@ -700,4 +700,4 @@ namespace GridKit }; // class SubsystemModel -} // namespace GridKit \ No newline at end of file +} // namespace GridKit diff --git a/GridKit/Model/PowerElectronics/SystemModelPowerElectronics.hpp b/GridKit/Model/PowerElectronics/SystemModelPowerElectronics.hpp index 76f3f4c5d..2e4768ee7 100644 --- a/GridKit/Model/PowerElectronics/SystemModelPowerElectronics.hpp +++ b/GridKit/Model/PowerElectronics/SystemModelPowerElectronics.hpp @@ -479,4 +479,4 @@ namespace GridKit }; // class PowerElectronicsModel -} // namespace GridKit \ No newline at end of file +} // namespace GridKit diff --git a/examples/PowerElectronics/Partition/HiresBus.hpp b/examples/PowerElectronics/Partition/HiresBus.hpp index 220d20521..e3a57c0ca 100644 --- a/examples/PowerElectronics/Partition/HiresBus.hpp +++ b/examples/PowerElectronics/Partition/HiresBus.hpp @@ -145,4 +145,4 @@ namespace GridKit private: NodeT* node_ref_; }; -} // namespace GridKit \ No newline at end of file +} // namespace GridKit diff --git a/examples/PowerElectronics/Partition/HiresComponent1.hpp b/examples/PowerElectronics/Partition/HiresComponent1.hpp index 77226b3f5..da2707cdb 100644 --- a/examples/PowerElectronics/Partition/HiresComponent1.hpp +++ b/examples/PowerElectronics/Partition/HiresComponent1.hpp @@ -159,4 +159,4 @@ namespace GridKit private: NodeT* node_ref_; }; -} // namespace GridKit \ No newline at end of file +} // namespace GridKit diff --git a/examples/PowerElectronics/Partition/HiresComponent3.hpp b/examples/PowerElectronics/Partition/HiresComponent3.hpp index 6f0761777..af1146ae9 100644 --- a/examples/PowerElectronics/Partition/HiresComponent3.hpp +++ b/examples/PowerElectronics/Partition/HiresComponent3.hpp @@ -171,4 +171,4 @@ namespace GridKit private: NodeT* node_ref_; }; -} // namespace GridKit \ No newline at end of file +} // namespace GridKit diff --git a/examples/PowerElectronics/Partition/Partition.cpp b/examples/PowerElectronics/Partition/Partition.cpp index 18abba3dc..ffa5018ba 100644 --- a/examples/PowerElectronics/Partition/Partition.cpp +++ b/examples/PowerElectronics/Partition/Partition.cpp @@ -218,4 +218,4 @@ std::vector hires(const std::vector& y, const std::vector Date: Thu, 6 Aug 2026 04:18:56 +0000 Subject: [PATCH 37/52] Improve subsystem partitioning and bus interface handling - Improved subsystem residual and Jacobian validation against the full-system model. - Updated BusPartitionInterface to support arbitrary ordering of internal and external component variables. - Added ownership and cleanup of copied components used by partition interfaces. - Improved comments and documentation for partitioning, Jacobian verification, and interface behavior. --- .../BusPartitionInterface.cpp | 137 ++++--- .../BusPartitionInterface.hpp | 19 +- .../PartitionInterface/PartitionInterface.hpp | 11 +- .../Model/PowerElectronics/SubsystemModel.hpp | 336 ++++++++++-------- .../SystemModelPowerElectronics.hpp | 5 +- .../PowerElectronics/Partition/Partition.cpp | 9 +- .../Partition/PartitionMicrogrid.cpp | 115 +++++- .../Partition/PartitionScaleMicrogrid.cpp | 65 +--- .../Partition/jac_test_helper.hpp | 82 +++-- 9 files changed, 461 insertions(+), 318 deletions(-) diff --git a/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.cpp b/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.cpp index 99d173f8b..392f221b4 100644 --- a/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.cpp +++ b/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.cpp @@ -5,37 +5,45 @@ #include #include -#include "GridKit/Model/PowerElectronics/ExternalConnection.hpp" +#include namespace GridKit { - /*! - * @brief Construct a bus-level partition interface. + /** + * @brief Construct a bus partition interface for a circuit component. + * + * During partitioning, the interface receives a bus and a component that + * participate in the partition boundary. The interface is added directly + * to the partition containing the bus and uses the component model to + * compute the residual and Jacobian contributions to that bus. * - * This interface exposes only the two bus variables associated with a - * component that has been separated from the rest of the system during - * partitioning. Internally, it maintains a copy of the original component - * and acts as a proxy between the co-simulation algorithm and the component - * model. + * The component passed to this constructor must be a copy of the original + * component, not the original component itself. This allows the interface + * to modify and evaluate the copied component without affecting the component + * stored in the neighboring partition. * - * The bus variables become the internal variables of this interface, - * while all remaining component variables are treated as external data - * supplied by neighboring partitions. + * For example: + * @code + * CompType* comp_copy = new CompType(*comp); + * auto* interface = new BusPartitionInterface(bus, comp_copy, id); + * @endcode * - * @param component Circuit component associated with this interface. - * @param id Unique component identifier. + * @param bus Bus belonging to the partition where the interface is added. + * @param component Copy of the circuit component across the partition boundary. + * @param id Unique interface identifier. */ template - BusPartitionInterface::BusPartitionInterface(node_type& bus, component_type& component, IdxT id) + BusPartitionInterface::BusPartitionInterface(node_type* bus, component_type* component, IdxT id) : component_(component), bus_(bus) { - size_ = component_.size(); + size_ = component_->size(); n_intern_ = 0; - n_extern_ = static_cast(component_.size()); + n_extern_ = static_cast(component_->size()); idc_ = id; + // All variables of the bus interface are external to the interface. for (IdxT i = 0; i < size_; i++) { extern_indices_.insert(i); @@ -45,54 +53,75 @@ namespace GridKit // the interface is fully allocated. connection_nodes_ = std::make_unique(static_cast(size_)); - const IdxT bus_i = bus_.getNodeConnection(0).idx_; - const IdxT bus_j = bus_.getNodeConnection(1).idx_; + // Global indices of the two variables associated with the bus. + const IdxT bus_i = bus_->getNodeConnection(0).idx_; + const IdxT bus_j = bus_->getNodeConnection(1).idx_; + + is_external_.resize(static_cast(size_), false); bool port_i_set = false; bool port_j_set = false; - // Copy the wrapped component topology and identify the two bus ports. + const auto& external_indices = component_->getExternIndices(); + IdxT external_index = 0; + + // Copy the wrapped component topology and identify the two bus ports and + // their corresponding output ports in the wrapped component. for (size_t i = 0; i < static_cast(size_); ++i) { - const IdxT connection_index = component_.getNodeConnection(i); + const IdxT connection_index = component_->getNodeConnection(i); this->setConnectionNodes(i, connection_index); + // Skip indices that are not external to the wrapped component. + if (!external_indices.contains(static_cast(i))) + { + continue; + } + + // Identify the bus ports and their positions in the component's external output. if (connection_index == bus_i) { - bus_port_i_ = i; - port_i_set = true; + bus_port_i_ = i; + bus_port_out_i_ = static_cast(external_index); + port_i_set = true; } else if (connection_index == bus_j) { - bus_port_j_ = i; - port_j_set = true; + bus_port_j_ = i; + bus_port_out_j_ = static_cast(external_index); + port_j_set = true; } + + ++external_index; + is_external_[static_cast(i)] = true; } + // Report an error if either bus connection is missing from the component. if (!port_i_set || !port_j_set) { std::cerr << "ERROR: Invalid partition interface detected. " - << "Bus(ID=" << bus_.busID() - << "), Component(ID=" << component_.getIDcomponent() + << "Bus(ID=" << bus_->busID() + << "), Component(ID=" << component_->getIDcomponent() << "). Please verify connection-node mappings and " "internal/external index assignments." << std::endl; } - const IdxT* cooRow = component_.jacobianCooRows(); - const IdxT* cooCol = component_.jacobianCooCols(); + // Record the Jacobian entries whose residual row belongs to either bus + // variable. jac_map_ stores their positions in the wrapped component's + // COO Jacobian so that the same entries can be extracted during each Jacobian evaluation. + + const IdxT* cooRow = component_->jacobianCooRows(); nnz_ = 0; - for (IdxT k = 0; k < component_.nnz(); ++k) + for (IdxT k = 0; k < component_->nnz(); ++k) { - const IdxT row_node = component_.getNodeConnection(static_cast(cooRow[k])); - const IdxT col_node = component_.getNodeConnection(static_cast(cooCol[k])); + const IdxT row_node = component_->getNodeConnection(static_cast(cooRow[k])); const bool row_is_bus = (row_node == bus_i || row_node == bus_j); - const bool col_is_bus = (col_node == bus_i || col_node == bus_j); - if (row_is_bus && col_is_bus) + if (row_is_bus) { ++nnz_; jac_map_.push_back(k); @@ -103,6 +132,7 @@ namespace GridKit template BusPartitionInterface::~BusPartitionInterface() { + delete component_; } /*! @@ -114,15 +144,15 @@ namespace GridKit CircuitComponent::allocate(); - const auto n = component_.getInternalSize(); + const auto n = component_->getInternalSize(); y_ptr = std::make_unique(n); yp_ptr = std::make_unique(n); f_ptr = std::make_unique(n); - component_.setInternalPointer(y_ptr.get()); - component_.setInternalDerivativePointer(yp_ptr.get()); - component_.setInternalResidualPointer(f_ptr.get()); + component_->setInternalPointer(y_ptr.get()); + component_->setInternalDerivativePointer(yp_ptr.get()); + component_->setInternalResidualPointer(f_ptr.get()); return 0; } @@ -164,17 +194,17 @@ namespace GridKit template int BusPartitionInterface::evaluateExternalResidual() { - std::vector component_residual(static_cast(component_.size())); + std::vector component_ext_residual(static_cast(component_->getExternSize())); - updateComponentPointers(component_residual.data()); + updateComponentPointers(component_ext_residual.data()); - if (int err_code = component_.evaluateResidual()) + if (int err_code = component_->evaluateResidual()) { return err_code; } - *f_ext_[bus_port_i_] += component_residual[bus_port_i_]; - *f_ext_[bus_port_j_] += component_residual[bus_port_j_]; + *f_ext_[bus_port_i_] += component_ext_residual[bus_port_out_i_]; + *f_ext_[bus_port_j_] += component_ext_residual[bus_port_out_j_]; return 0; } @@ -196,11 +226,11 @@ namespace GridKit // contributions are redirected to dummy storage. updateComponentPointers(nullptr); - component_.evaluateJacobian(); + component_->evaluateJacobian(); - const IdxT* cooRows = component_.jacobianCooRows(); - const IdxT* cooCols = component_.jacobianCooCols(); - const RealT* cooVals = component_.jacobianCooValues(); + const IdxT* cooRows = component_->jacobianCooRows(); + const IdxT* cooCols = component_->jacobianCooCols(); + const RealT* cooVals = component_->jacobianCooValues(); std::vector r = {}; std::vector c = {}; @@ -222,20 +252,23 @@ namespace GridKit int BusPartitionInterface::updateComponentPointers(ScalarT* residual) { size_t internal_index = 0; + size_t external_index = 0; - const auto& external_indices = component_.getExternIndices(); + const auto& external_indices = component_->getExternIndices(); - for (size_t i = 0; i < static_cast(component_.size()); ++i) + for (size_t i = 0; i < static_cast(component_->size()); ++i) { - if (external_indices.contains(static_cast(i))) + if (is_external_[i]) { ExternalConnection connection{ .y_ = y_ext_[i], .yp_ = yp_ext_[i], - .f_ = residual ? &residual[i] : &dummy_residual_, - .idx_ = component_.getNodeConnection(i)}; + .f_ = residual ? &residual[external_index] : &dummy_residual_, + .idx_ = component_->getNodeConnection(i)}; + + component_->setExternalConnectionNodes(i, connection); - component_.setExternalConnectionNodes(i, connection); + ++external_index; } else { diff --git a/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.hpp b/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.hpp index 077449b52..006d8c9f3 100644 --- a/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.hpp +++ b/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.hpp @@ -49,8 +49,11 @@ namespace GridKit using PartitionInterface::bus_port_i_; using PartitionInterface::bus_port_j_; + using PartitionInterface::bus_port_out_i_; + using PartitionInterface::bus_port_out_j_; + public: - BusPartitionInterface(node_type& bus, component_type& component, IdxT id); + BusPartitionInterface(node_type* bus, component_type* component, IdxT id); virtual ~BusPartitionInterface(); int allocate(); @@ -66,18 +69,26 @@ namespace GridKit int evaluateAdjointResidual(); // int evaluateAdjointJacobian(); int evaluateAdjointIntegrand(); - int updateComponentPointers(ScalarT* residual); private: - component_type& component_; - node_type& bus_; + int updateComponentPointers(ScalarT* residual); + // Component and bus associated with the partition interface. + component_type* component_; + node_type* bus_; + + // Storage for the wrapped component's internal variables. std::unique_ptr y_ptr; std::unique_ptr yp_ptr; std::unique_ptr f_ptr; + // Dummy storage used when the component residual is not needed. ScalarT dummy_residual_{}; + // Maps interface Jacobian entries to the wrapped component Jacobian. std::vector jac_map_; + + // Identifies which wrapped component variables are external. + std::vector is_external_; }; } // namespace GridKit diff --git a/GridKit/Model/PowerElectronics/PartitionInterface/PartitionInterface.hpp b/GridKit/Model/PowerElectronics/PartitionInterface/PartitionInterface.hpp index f639b0448..2df9be323 100644 --- a/GridKit/Model/PowerElectronics/PartitionInterface/PartitionInterface.hpp +++ b/GridKit/Model/PowerElectronics/PartitionInterface/PartitionInterface.hpp @@ -11,15 +11,6 @@ namespace GridKit /*! * @brief Base class for partition interface components. * - * PartitionInterface provides the infrastructure needed by components - * that exchange information across partition boundaries during - * co-simulation. It stores the values of external variables received - * from neighboring partitions together with the corresponding global - * indices in the original unpartitioned system. - * - * Derived classes are responsible for implementing the specific - * interface behavior associated with buses, circuit components, or - * other partition boundary entities. */ template class PartitionInterface : public CircuitComponent @@ -32,6 +23,8 @@ namespace GridKit protected: size_t bus_port_i_; size_t bus_port_j_; + size_t bus_port_out_i_; + size_t bus_port_out_j_; }; } // namespace GridKit diff --git a/GridKit/Model/PowerElectronics/SubsystemModel.hpp b/GridKit/Model/PowerElectronics/SubsystemModel.hpp index d21382bb6..9d64b76c3 100644 --- a/GridKit/Model/PowerElectronics/SubsystemModel.hpp +++ b/GridKit/Model/PowerElectronics/SubsystemModel.hpp @@ -4,7 +4,9 @@ #include #include +#include #include +#include #include #include #include @@ -37,6 +39,7 @@ namespace GridKit using SystemModel::allocated_; using SystemModel::allocateVectors; using SystemModel::alpha_; + using SystemModel::connection_nodes_; using SystemModel::f_int_; using SystemModel::n_extern_; using SystemModel::n_intern_; @@ -62,13 +65,28 @@ namespace GridKit * @post System model parameters set as default */ - SubsystemModel(bool use_jac = false) + SubsystemModel() + : SystemModel(false) + { + } + + /** + * @brief Default constructor for the system model + * + * @post System model parameters set as default + */ + + SubsystemModel(bool use_jac) : SystemModel(use_jac) { } ~SubsystemModel() override { + for (auto* comp : interfaces_) + { + delete comp; + } // SubsystemModel does not own components_/nodes_ — they belong to (and are // deleted by) the parent system model. components_.clear(); @@ -78,33 +96,34 @@ namespace GridKit int allocate() override { - hold(); + for (auto* comp : interfaces_) + { + comp->allocate(); + } + + if (int err = hold()) + { + return err; + } n_intern_ = internal_map_.size(); n_extern_ = external_map_.size(); size_ = n_intern_; + // Allocate subsystem vectors. + y_ext_data_.resize(n_extern_); + yp_ext_data_.resize(n_extern_); + f_ext_data_.resize(n_extern_); + + connection_nodes_ = std::make_unique(size_); if (!allocated_) { allocateVectors(static_cast(size_), true); + abs_tol_.setToZero(memory::HOST); } - CircuitComponent::allocate(); - - // Allocation always rebuilds the system Jacobian and its COO-to-CSR map. - delete csr_jac_; - csr_jac_ = nullptr; - - delete[] map_to_csr_; - map_to_csr_ = nullptr; - tag_.resize(size_); - // Allocate subsystem vectors. - y_ext_data_.resize(n_extern_); - yp_ext_data_.resize(n_extern_); - f_ext_data_.resize(n_extern_); - external_data_indices_.resize(n_extern_); // Store the mapping from local subsystem indices back to their global system indices @@ -119,8 +138,6 @@ namespace GridKit external_data_indices_[local_idx - n_intern_] = global_idx; } - tag_.resize(size_); - { // Start node internal indexing after all component internals for proper KLU ordering size_t node_internal_idx; for (node_type* node : nodes_) @@ -136,7 +153,6 @@ namespace GridKit .idx_ = static_cast(node_internal_idx)}; node->setExternalConnectionNodes(i, node_connection); - node_internal_idx++; } } } @@ -147,10 +163,6 @@ namespace GridKit size_t component_internal_idx = 0; for (component_type* comp : components_) { - if (!comp->isAllocated()) - { - comp->allocate(); - } // Update component internal pointers to their correct offsets comp->setInternalPointer(&y_int_[component_internal_idx]); comp->setInternalDerivativePointer(&yp_int_[component_internal_idx]); @@ -192,6 +204,13 @@ namespace GridKit } } + // Allocation always rebuilds the system Jacobian and its COO-to-CSR map. + delete csr_jac_; + csr_jac_ = nullptr; + + delete[] map_to_csr_; + map_to_csr_ = nullptr; + // Evaluate component Jacobians to get sparsity for (component_type* component : components_) { @@ -310,8 +329,12 @@ namespace GridKit { const auto [y_forcing, yp_forcing] = (*forcing_function_)(time_); - assert(y_forcing.size() == y_ext_data_.size()); - assert(yp_forcing.size() == yp_ext_data_.size()); + if (y_forcing.size() != y_ext_data_.size() || yp_forcing.size() != yp_ext_data_.size()) + { + throw std::runtime_error( + "SubsystemModel::distributeExternalVectors: forcing function " + "returned vectors with incorrect sizes."); + } std::copy(y_forcing.begin(), y_forcing.end(), y_ext_data_.begin()); std::copy(yp_forcing.begin(), yp_forcing.end(), yp_ext_data_.begin()); @@ -327,11 +350,12 @@ namespace GridKit */ int evaluateInternalResidual() override { - distributeExternalVectors(); - - SystemModel::evaluateInternalResidual(); + if (int err_code = distributeExternalVectors()) + { + return err_code; + } - return 0; + return SystemModel::evaluateInternalResidual(); } /** @@ -344,11 +368,12 @@ namespace GridKit */ int evaluateJacobian() override { - distributeExternalVectors(); - - SystemModel::evaluateJacobian(); + if (int err_code = distributeExternalVectors()) + { + return err_code; + } - return 0; + return SystemModel::evaluateJacobian(); } /** @@ -360,15 +385,13 @@ namespace GridKit * * @param[in] component Component to add. */ - void - addComponent(component_type* component) - + void addComponent(component_type* component) { - if (is_comp_in_local_state) + if (connections_are_local_) { - std::cerr << "SubsystemModel::addComponent: cannot add component while " - "in local-indexed state. Call release() first.\n"; - return; + throw std::logic_error( + "SubsystemModel::addComponent: cannot add component while " + "in local-indexed state. Call release() first."); } SystemModel::addComponent(component); @@ -385,16 +408,30 @@ namespace GridKit */ void addNode(node_type* node) { - if (is_comp_in_local_state) + if (connections_are_local_) { - std::cerr << "SubsystemModel::addNode: cannot add node while in " - "local-indexed state. Call release() first.\n"; - return; + throw std::logic_error( + "SubsystemModel::addNode: cannot add node while in " + "local-indexed state. Call release() first."); } SystemModel::addNode(node); } + /** + * @brief Add a partition interface to the subsystem. + * + * Adds the partition interface to the subsystem's component list and keeps a + * separate reference to it in the interface list. + * + * @param component Pointer to the interface component to add. + */ + void addInterface(component_type* component) + { + addComponent(component); + interfaces_.push_back(component); + } + /** * @brief Acquire the subsystem's global-to-local index mappings. * @@ -403,7 +440,7 @@ namespace GridKit * indices via mapGlobalToLocal(). * * Call this after modifying subsystem topology (adding or removing - * components/nodes) and before allocate(), to (re)establish local + * components/nodes), to (re)establish local * indexing. Pairs with release(), which undoes this. * * @return int 0 if successful, positive if there's a recoverable error, negative if unrecoverable @@ -420,37 +457,37 @@ namespace GridKit return 0; } - // /** - // * @brief Release the subsystem's global-to-local index mappings. - // * - // * If components/nodes currently hold local indices, they are first - // * converted back to their original global indices via - // * mapLocalToGlobal(). The internal/external index maps built by - // * buildIndexMappings() are then cleared, and the model is marked as - // * not allocated. - // * - // * - // * Call this before modifying subsystem topology (adding or removing - // * components/nodes), so stale local indices aren't left dangling. - // * - // * @return int 0 if successful, positive if there's a recoverable error, negative if unrecoverable - // */ - // int release() - // { - // if (int err_code = mapLocalToGlobal()) - // { - // return err_code; - // } - - // internal_map_.clear(); - // external_map_.clear(); - - // allocated_ = false; - - // return 0; - // } - - const std::vector& getExternalDataIndices() + /** + * @brief Release the subsystem's global-to-local index mappings. + * + * If components/nodes currently hold local indices, they are first + * converted back to their original global indices via + * mapLocalToGlobal(). The internal/external index maps built by + * buildIndexMappings() are then cleared, and the model is marked as + * not allocated. + * + * + * Call this before modifying subsystem topology (adding or removing + * components/nodes), so stale local indices aren't left dangling. + * + * @return int 0 if successful, positive if there's a recoverable error, negative if unrecoverable + */ + int release() + { + if (int err_code = mapLocalToGlobal()) + { + return err_code; + } + + internal_map_.clear(); + external_map_.clear(); + + allocated_ = false; + + return 0; + } + + const std::vector& getExternalDataIndices() const { return external_data_indices_; } @@ -486,66 +523,66 @@ namespace GridKit } private: - // /** - // * @brief Replace local subsystem connection indices with their original global indices - // * for every component and node. - // * - // * Inverse of mapGlobalToLocal(). Internal connections (index < internal - // * size) are mapped back through this subsystem's own node-connection - // * table; external connections are mapped back through - // * @c external_data_indices_. Entries equal to @c neg1_ represent no - // * connection and are left unchanged. - // * - // * @return Always returns 0. - // */ - // int mapLocalToGlobal() - // { - // if (!is_comp_in_local_state) - // { - // return 0; - // } - - // for (auto* component : components_) - // { - - // for (IdxT i = 0; i < component->size(); i++) - // { - // IdxT index = component->getNodeConnection(i); - - // if (index == neg1_) - // { - // continue; - // } - // else if (index < this->getInternalSize()) - // { - // component->setExternalConnectionNodes(i, this->getNodeConnection(index)); - // } - // else - // { - // component->setExternalConnectionNodes(i, external_data_indices_[index - this->getInternalSize()]); - // } - // } - // } - - // for (auto* node : nodes_) - // { - - // for (IdxT i = 0; i < node->size(); i++) - // { - // IdxT index = node->getNodeConnection(i); - - // if (index == neg1_) - // { - // continue; - // } - // node->setExternalConnectionNodes(i, this->getNodeConnection(index)); - // } - // } - - // is_comp_in_local_state = false; - - // return 0; - // } + /** + * @brief Replace local subsystem connection indices with their original global indices + * for every component and node. + * + * Inverse of mapGlobalToLocal(). Internal connections (index < internal + * size) are mapped back through this subsystem's own node-connection + * table; external connections are mapped back through + * @c external_data_indices_. Entries equal to @c neg1_ represent no + * connection and are left unchanged. + * + * @return Always returns 0. + */ + int mapLocalToGlobal() + { + if (!connections_are_local_) + { + return 0; + } + + for (component_type* component : components_) + { + + for (IdxT i = 0; i < component->size(); i++) + { + const IdxT index = component->getNodeConnection(i); + + if (index == neg1_) + { + continue; + } + else if (index < this->getInternalSize()) + { + component->setConnectionNodes(i, this->getNodeConnection(index)); + } + else + { + component->setConnectionNodes(i, external_data_indices_[index - this->getInternalSize()]); + } + } + } + + for (node_type* node : nodes_) + { + + for (IdxT i = 0; i < node->size(); i++) + { + const IdxT index = node->getNodeConnection(i).idx_; + + if (index == neg1_) + { + continue; + } + node->setConnectionNodes(i, this->getNodeConnection(index)); + } + } + + connections_are_local_ = false; + + return 0; + } /** * @brief Replace global connection indices with local subsystem indices for every component @@ -563,15 +600,14 @@ namespace GridKit * * @return Always returns 0. */ - int mapGlobalToLocal() { - if (is_comp_in_local_state) + if (connections_are_local_) { return 0; } - for (auto* component : components_) + for (component_type* component : components_) { for (IdxT i = 0; i < component->size(); ++i) { @@ -593,7 +629,7 @@ namespace GridKit } } - for (auto* node : nodes_) + for (node_type* node : nodes_) { for (IdxT i = 0; i < node->size(); ++i) { @@ -608,7 +644,7 @@ namespace GridKit } } - is_comp_in_local_state = true; + connections_are_local_ = true; return 0; } @@ -628,7 +664,7 @@ namespace GridKit void buildIndexMappings() { - if (is_comp_in_local_state) + if (connections_are_local_) { return; } @@ -640,11 +676,11 @@ namespace GridKit // First pass: Map global component indices to local subsystem indices. for (component_type* comp : components_) { + const auto& extern_indices = comp->getExternIndices(); + for (IdxT i = 0; i < comp->size(); i++) { - IdxT index = comp->getNodeConnection(i); - - auto extern_indices = comp->getExternIndices(); + const IdxT index = comp->getNodeConnection(i); if (index != neg1_ && !extern_indices.contains(i)) { @@ -658,7 +694,7 @@ namespace GridKit for (IdxT i = 0; i < node->size(); i++) { - IdxT index = node->getNodeConnection(i).idx_; + const IdxT index = node->getNodeConnection(i).idx_; if (index != neg1_) { @@ -676,7 +712,7 @@ namespace GridKit { continue; } - IdxT index = comp->getNodeConnection(j); + const IdxT index = comp->getNodeConnection(j); if (internal_map_.count(index) < 1 && external_map_.count(index) < 1 && index != neg1_) { @@ -696,7 +732,11 @@ namespace GridKit std::optional forcing_function_; - bool is_comp_in_local_state{false}; + // Partition interfaces are owned by SubsystemModel. + // components_ contains non-owning pointers, including these interfaces. + std::vector interfaces_; + + bool connections_are_local_{false}; }; // class SubsystemModel diff --git a/GridKit/Model/PowerElectronics/SystemModelPowerElectronics.hpp b/GridKit/Model/PowerElectronics/SystemModelPowerElectronics.hpp index 2e4768ee7..3f1aee396 100644 --- a/GridKit/Model/PowerElectronics/SystemModelPowerElectronics.hpp +++ b/GridKit/Model/PowerElectronics/SystemModelPowerElectronics.hpp @@ -28,6 +28,7 @@ namespace GridKit using Base::allocated_; using Base::allocateVectors; using Base::alpha_; + using Base::connection_nodes_; using Base::f_ext_; using Base::f_int_; using Base::n_extern_; @@ -272,7 +273,7 @@ namespace GridKit * * @return int 0 if successful, positive if there's a recoverable error, negative if unrecoverable */ - int initialize() + int initialize() final { // Initialize components for (const auto& component : components_) @@ -283,7 +284,7 @@ namespace GridKit return Base::initialize(); } - int tagDifferentiable() + int tagDifferentiable() override { return 0; } diff --git a/examples/PowerElectronics/Partition/Partition.cpp b/examples/PowerElectronics/Partition/Partition.cpp index ffa5018ba..8849d6a63 100644 --- a/examples/PowerElectronics/Partition/Partition.cpp +++ b/examples/PowerElectronics/Partition/Partition.cpp @@ -1,8 +1,8 @@ // #include #include +#include -#include "GridKit/ScalarTraits.hpp" #define _USE_MATH_DEFINES #include #include @@ -63,12 +63,12 @@ int main(int /* argc */, char const** /* argv */) comp3->setInternalConnectionNodes(3, 6); comp3->setInternalConnectionNodes(4, 7); - GridKit::HiresComponent3 comp3copy(*comp3); - GridKit::BusPartitionInterface* busInterface = new GridKit::BusPartitionInterface(bus, comp3copy, 4); + GridKit::HiresComponent3* comp3copy = new GridKit::HiresComponent3(*comp3); + GridKit::BusPartitionInterface* busInterface = new GridKit::BusPartitionInterface(&bus, comp3copy, 4); partition1->addComponent(comp1); partition1->addComponent(bus1); - partition1->addComponent(busInterface); + partition1->addInterface(busInterface); partition1->addNode(&bus); partition2->addComponent(comp3); @@ -155,7 +155,6 @@ int main(int /* argc */, char const** /* argv */) delete comp1; delete bus1; - delete busInterface; delete comp3; delete partition1; diff --git a/examples/PowerElectronics/Partition/PartitionMicrogrid.cpp b/examples/PowerElectronics/Partition/PartitionMicrogrid.cpp index 6e8a945e4..cf4b201f1 100644 --- a/examples/PowerElectronics/Partition/PartitionMicrogrid.cpp +++ b/examples/PowerElectronics/Partition/PartitionMicrogrid.cpp @@ -23,10 +23,25 @@ #include "jac_test_helper.hpp" +/** + * Partitioned Residual Evaluation + * + * Partition the microgrid into subsystems and evaluate each subsystem + * independently using the appropriate internal and external state values. + * + * The subsystem residuals are gathered into a global residual vector and + * compared with the monolithic residual to verify that the partitioned + * evaluation reproduces the full-system result. + */ + +using Component = GridKit::CircuitComponent; +using Node = GridKit::PowerElectronics::NodeBase; + +std::vector getComponentConnections(const std::vector& components); +std::vector getNodeConnections(const std::vector& nodes); + int main() { - /// @todo Needs to be modified. Some components are small relative to others thus - /// there error is high (or could be matlab vector issue) bool use_jac = true; @@ -172,21 +187,36 @@ int main() Subsystem* partition1 = new Subsystem(); Subsystem* partition2 = new Subsystem(); - GridKit::MicrogridLine l2copy(*l2); - GridKit::BusPartitionInterface* busInterface1 = new GridKit::BusPartitionInterface(bus2, l2copy, 14); + GridKit::MicrogridLine* l2copy = new GridKit::MicrogridLine(*l2); + GridKit::BusPartitionInterface* busInterface1 = new GridKit::BusPartitionInterface(&bus2, l2copy, 14); + + std::vector partition1_components = {dg1, dg2, l1, load1, busInterface1, bus_para_1, bus_para_2}; + std::vector partition2_components = {dg3, dg4, l2, l3, load2, bus_para_4, bus_para_3}; + std::vector partition1_nodes = {&dg_signal, &bus1, &bus2}; + std::vector partition2_nodes = {&bus3, &bus4}; - // busInterface1->allocate(); + std::vector component_global_indices_1 = getComponentConnections(partition1_components); + std::vector component_global_indices_2 = getComponentConnections(partition2_components); + std::vector node_global_indices_1 = getNodeConnections(partition1_nodes); + std::vector node_global_indices_2 = getNodeConnections(partition2_nodes); + + // --------------------------------------------------------------------------- + // Populate partition 1 + // -------------------------------------------------------------------------- partition1->addNode(&dg_signal); partition1->addComponent(dg1); partition1->addComponent(dg2); partition1->addComponent(l1); partition1->addComponent(load1); - partition1->addComponent(busInterface1); + partition1->addInterface(busInterface1); partition1->addComponent(bus_para_1); partition1->addComponent(bus_para_2); partition1->addNode(&bus1); partition1->addNode(&bus2); + // --------------------------------------------------------------------------- + // Populate partition 2 + // --------------------------------------------------------------------------- partition2->addComponent(dg3); partition2->addComponent(dg4); partition2->addComponent(l2); @@ -197,6 +227,10 @@ int main() partition2->addNode(&bus3); partition2->addNode(&bus4); + // --------------------------------------------------------------------------- + // Initialize the full-system state and evaluate residuals and system Jacobian + // --------------------------------------------------------------------------- + std::vector y; std::vector yp; @@ -228,16 +262,16 @@ int main() std::vector*> partitions = {partition1, partition2}; + // --------------------------------------------------------------------------- + // Allocate and evaluate the subsystem partitions + // --------------------------------------------------------------------------- + // Distribute externals to partition 1 for (auto* partition : partitions) { partition->allocate(); partition->initialize(); - // test hold and release methods - // partition->release(); - // partition->hold(); - partition->updateTime(2, 5); // Test setTimeFunction function @@ -275,6 +309,9 @@ int main() partition->evaluateJacobian(); } + // --------------------------------------------------------------------------- + // Verify the subsystem Jacobians + // --------------------------------------------------------------------------- auto partition1_jac = partition1->getCsrJacobian(); auto partition2_jac = partition2->getCsrJacobian(); @@ -289,6 +326,9 @@ int main() return 1; } + // --------------------------------------------------------------------------- + // Gather and verify the subsystem residuals + // --------------------------------------------------------------------------- std::vector f(sysmodel->size(), 0.0); std::vector error(sysmodel->size(), 1.0); @@ -323,10 +363,63 @@ int main() return 1; } + // --------------------------------------------------------------------------- + // Verify subsystem release() from SubsystemModel + // --------------------------------------------------------------------------- + partitions[0]->release(); + partitions[1]->release(); + + if (getComponentConnections(partition1_components) != component_global_indices_1 + || getNodeConnections(partition1_nodes) != node_global_indices_1) + { + std::cout << "ERROR: Subsystem release did not restore " + "the original global connection indices!\n"; + return 1; + } + + if (getComponentConnections(partition2_components) != component_global_indices_2 + || getNodeConnections(partition2_nodes) != node_global_indices_2) + { + std::cout << "ERROR: Subsystem release did not restore " + "the original global connection indices!\n"; + return 1; + } + + // --------------------------------------------------------------------------- + // Clean up + // --------------------------------------------------------------------------- delete sysmodel; delete partition1; delete partition2; - delete busInterface1; return 0; } + +std::vector getComponentConnections(const std::vector& components) +{ + std::vector connections; + + for (const auto* component : components) + { + for (size_t i = 0; i < component->size(); ++i) + { + connections.push_back(component->getNodeConnection(i)); + } + } + + return connections; +} + +std::vector getNodeConnections(const std::vector& nodes) +{ + std::vector connections; + + for (auto* node : nodes) + + for (size_t i = 0; i < node->size(); ++i) + { + connections.push_back(node->getNodeConnection(i).idx_); + } + + return connections; +} \ No newline at end of file diff --git a/examples/PowerElectronics/Partition/PartitionScaleMicrogrid.cpp b/examples/PowerElectronics/Partition/PartitionScaleMicrogrid.cpp index ae0659cc5..150a6eeef 100644 --- a/examples/PowerElectronics/Partition/PartitionScaleMicrogrid.cpp +++ b/examples/PowerElectronics/Partition/PartitionScaleMicrogrid.cpp @@ -23,26 +23,6 @@ #include "jac_test_helper.hpp" -/****************************************************************************** - * Partitioned Residual Evaluation - * - * Construct subsystem models by partitioning the original microgrid into - * independent groups of components. Each subsystem is allocated and receives - * the appropriate subset of the global state vectors (y and yp), consisting - * of: - * - * - External variables (coupling variables from neighboring partitions) - * - Internal variables (states owned by the partition) - * - * After distributing the state information, each partition independently - * evaluates its residual. The local residuals are then scattered back into - * the global residual vector to reconstruct the monolithic residual. - * - * Finally, the reconstructed residual is compared against the reference - * monolithic evaluation to verify the correctness of the partitioning - * implementation. - ******************************************************************************/ - using index_type = size_t; using real_type = double; @@ -80,7 +60,7 @@ int main() std::cout << std::string(93, '-') << "\n"; - for (index_type p : {2, 3, 4, 5}) + for (index_type p : {1, 2, 3, 4, 5}) { assert(p <= 2 * N_size); @@ -206,21 +186,19 @@ RunResult printMicrogridSystems(index_type N_size, index_type num_partitions) Lload_list[0] = Lload1; } - using SignalNode = GridKit::PowerElectronics::SignalNode; - using Bus = GridKit::PowerElectronics::MicrogridBus; - using BusDQ = MicrogridBusDQ; - using Generator = DistributedGenerator; - using Line = MicrogridLine; - using Load = MicrogridLoad; - using PartitionInterface = BusPartitionInterface; - - std::vector buses(num_ibrs); - std::vector busesDQ(num_ibrs, nullptr); - std::vector generators(num_ibrs, nullptr); - std::vector lines(num_ibrs, nullptr); - std::vector loads(num_ibrs, nullptr); - std::vector partitionInterface; - std::vector linesCopies; + using SignalNode = GridKit::PowerElectronics::SignalNode; + using Bus = GridKit::PowerElectronics::MicrogridBus; + using BusDQ = MicrogridBusDQ; + using Generator = DistributedGenerator; + using Line = MicrogridLine; + using Load = MicrogridLoad; + + std::vector buses(num_ibrs); + std::vector busesDQ(num_ibrs, nullptr); + std::vector generators(num_ibrs, nullptr); + std::vector lines(num_ibrs, nullptr); + std::vector loads(num_ibrs, nullptr); + std::vector linesCopies; SignalNode dg_signal; // sys_model_control->addNode(&dg_signal); @@ -396,11 +374,8 @@ RunResult printMicrogridSystems(index_type N_size, index_type num_partitions) if (index < num_ibrs) { auto* linecopy = new GridKit::MicrogridLine(*lines[index]); - auto* busInterface = new GridKit::BusPartitionInterface(buses[index - 1], *linecopy, model_id++); - partitionInterface.push_back(busInterface); - linesCopies.push_back(linecopy); - - partition->addComponent(busInterface); + auto* busInterface = new GridKit::BusPartitionInterface(&buses[index - 1], linecopy, model_id++); + partition->addInterface(busInterface); } subsystems[j] = partition; @@ -516,18 +491,10 @@ RunResult printMicrogridSystems(index_type N_size, index_type num_partitions) result.success = false; } - for (auto* linescpy : linesCopies) - { - delete linescpy; - } for (auto* partition : subsystems) { delete partition; } - for (auto* part_interface : partitionInterface) - { - delete part_interface; - } delete sys_model_control; return result; diff --git a/examples/PowerElectronics/Partition/jac_test_helper.hpp b/examples/PowerElectronics/Partition/jac_test_helper.hpp index 8851bc2a3..34b84790e 100644 --- a/examples/PowerElectronics/Partition/jac_test_helper.hpp +++ b/examples/PowerElectronics/Partition/jac_test_helper.hpp @@ -13,6 +13,27 @@ namespace GridKit { namespace Testing { + /** + * @brief Verify that the subsystem Jacobian is an exact subset of the + * full-system Jacobian to ensure accuracy of subsystem Jacobians. + * + * Each subsystem Jacobian entry should match the corresponding entry in the + * full-system Jacobian after converting local subsystem indices back to global + * indices. The check verifies that: + * + * - every subsystem entry exists in the full-system Jacobian, + * - every full-system entry whose row and column belong to the subsystem + * exists in the subsystem Jacobian, and + * - matching entries agree within the specified tolerance. + * + * @param full_jac Full-system Jacobian. + * @param sub_jac Subsystem Jacobian. + * @param subsystem Subsystem associated with sub_jac. + * @param tolerance Maximum allowed difference between matching entries. + * + * @return true if the subsystem Jacobian is an exact subset of the + * full-system Jacobian, false otherwise. + */ template bool verifySubsystemJacobian( @@ -35,13 +56,10 @@ namespace GridKit const IdxT num_sub_rows = sub_jac.getNumRows(); const IdxT num_sub_cols = sub_jac.getNumColumns(); - /* - * The subsystem internal map stores: - * - * global index -> local index - */ + // The subsystem internal map stores global-to-local indices. const auto& global_to_local = subsystem.getInternalMap(); + // The internal map should contain one entry for every subsystem row. if (global_to_local.size() != num_sub_rows) { std::cout << "Internal map size mismatch: map has " @@ -53,6 +71,7 @@ namespace GridKit return false; } + // The subsystem Jacobian is expected to be square. if (num_sub_rows != num_sub_cols) { std::cout << "Subsystem Jacobian is not square: " @@ -64,20 +83,16 @@ namespace GridKit return false; } - /* - * Build the reverse mapping: - * - * local index -> global index - * - * This is needed because the subsystem Jacobian stores local row - * and column indices. - */ + // Build the reverse map from local subsystem indices to global indices. + // This is needed to compare subsystem rows and columns with the + // corresponding entries in the full-system Jacobian. std::vector local_to_global(num_sub_rows); std::vector local_index_found(num_sub_rows, false); for (const auto& [global_index, local_index] : global_to_local) { + // Make sure the global index is valid for the full-system Jacobian. if (global_index >= full_jac.getNumRows()) { std::cout << "Invalid global index " @@ -87,6 +102,7 @@ namespace GridKit return false; } + // Make sure the local index is valid for the subsystem Jacobian. if (local_index >= num_sub_rows) { std::cout << "Invalid local index " @@ -97,6 +113,7 @@ namespace GridKit return false; } + // Each local index should correspond to only one global index. if (local_index_found[local_index]) { std::cout << "Duplicate local index " @@ -110,7 +127,7 @@ namespace GridKit local_index_found[local_index] = true; } - // Verify that every local index has a corresponding global index. + // Make sure every subsystem local index has a global index. for (IdxT local_index = 0; local_index < num_sub_rows; ++local_index) { if (!local_index_found[local_index]) @@ -124,9 +141,7 @@ namespace GridKit bool matches = true; - /* - * Compare each subsystem row against the corresponding full-system row. - */ + // Compare each subsystem row with the corresponding full-system row. for (IdxT local_row = 0; local_row < num_sub_rows; ++local_row) { const IdxT global_row = local_to_global[local_row]; @@ -136,14 +151,9 @@ namespace GridKit const IdxT full_begin = full_rows[global_row]; const IdxT full_end = full_rows[global_row + 1]; - /* - * Store this subsystem row using global column indices. - * - * The subsystem CSR row is sorted by local column index. After - * converting local columns to global columns, the global columns - * may no longer be sorted. Therefore, we use a lookup map instead - * of comparing both CSR rows sequentially. - */ + // Store the subsystem row using global column indices. The subsystem + // CSR row is sorted by local column index, but this ordering may change + // after converting the columns back to global indices. std::unordered_map sub_row_entries; for (IdxT sub_index = sub_begin; sub_index < sub_end; ++sub_index) @@ -163,7 +173,7 @@ namespace GridKit const IdxT global_column = local_to_global[local_column]; - // Check for duplicate columns in this subsystem row. + // A CSR row should contain only one entry for each column. if (sub_row_entries.find(global_column) != sub_row_entries.end()) { std::cout << "Duplicate subsystem entry at (" @@ -177,17 +187,14 @@ namespace GridKit sub_row_entries[global_column] = sub_vals[sub_index]; } - /* - * Compare every full-system entry whose column belongs to this - * subsystem. - */ + // Compare full-system entries whose columns belong to the subsystem. for (IdxT full_index = full_begin; full_index < full_end; ++full_index) { const IdxT global_column = full_cols[full_index]; - // Ignore columns owned by another subsystem. + // Ignore entries whose column belongs outside the subsystem. if (global_to_local.find(global_column) == global_to_local.end()) { continue; @@ -195,6 +202,8 @@ namespace GridKit auto sub_entry = sub_row_entries.find(global_column); + // The full Jacobian contains an entry that is missing from the + // subsystem Jacobian. if (sub_entry == sub_row_entries.end()) { std::cout << "Entry exists only in full Jacobian at (" @@ -209,6 +218,7 @@ namespace GridKit const RealT sub_value = sub_entry->second; const RealT difference = std::abs(full_value - sub_value); + // Compare the values of matching Jacobian entries. if (difference > tolerance) { std::cout << "Jacobian value mismatch at (" @@ -221,16 +231,12 @@ namespace GridKit matches = false; } - /* - * Remove the matched subsystem entry. - * - * After the full row has been checked, any remaining entries - * exist only in the subsystem Jacobian. - */ + // Remove the matched entry. Any entries left after checking the + // full-system row exist only in the subsystem Jacobian. sub_row_entries.erase(sub_entry); } - // Report subsystem entries that were not found in the full Jacobian. + // Report entries that exist only in the subsystem Jacobian. for (const auto& entry : sub_row_entries) { const IdxT global_column = entry.first; From 2e360d9b45f0ada0378a13ca5d1446f8078d23a0 Mon Sep 17 00:00:00 2001 From: abdourahmanbarry Date: Thu, 6 Aug 2026 04:21:18 +0000 Subject: [PATCH 38/52] Apply pre-commit fixes --- examples/PowerElectronics/Partition/PartitionMicrogrid.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/PowerElectronics/Partition/PartitionMicrogrid.cpp b/examples/PowerElectronics/Partition/PartitionMicrogrid.cpp index cf4b201f1..9782f92f7 100644 --- a/examples/PowerElectronics/Partition/PartitionMicrogrid.cpp +++ b/examples/PowerElectronics/Partition/PartitionMicrogrid.cpp @@ -422,4 +422,4 @@ std::vector getNodeConnections(const std::vector& nodes) } return connections; -} \ No newline at end of file +} From 838af48e08e0177b13f9b89380c503e3bd9e7833 Mon Sep 17 00:00:00 2001 From: abdourahmanbarry Date: Thu, 6 Aug 2026 05:38:42 +0000 Subject: [PATCH 39/52] Update ChangeLog file --- CHANGELOG.md | 1 + examples/PowerElectronics/Partition/Partition.cpp | 1 - .../PowerElectronics/Partition/PartitionScaleMicrogrid.cpp | 7 ++++--- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 59e9c884f..a7238d9ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -80,6 +80,7 @@ - Added `HYGOV` governor model implementation for PhasorDynamics. - Added `REPCA` controller model implementation for PhasorDynamics. - Added `REECB` electrical-control model implementation for PhasorDynamics. +- Added subsystem partitioning support for `PowerElectronics` models, including partition interfaces, and independent residual and Jacobian evaluation. ## v0.1 diff --git a/examples/PowerElectronics/Partition/Partition.cpp b/examples/PowerElectronics/Partition/Partition.cpp index 8849d6a63..60280576f 100644 --- a/examples/PowerElectronics/Partition/Partition.cpp +++ b/examples/PowerElectronics/Partition/Partition.cpp @@ -1,7 +1,6 @@ // #include #include -#include #define _USE_MATH_DEFINES #include diff --git a/examples/PowerElectronics/Partition/PartitionScaleMicrogrid.cpp b/examples/PowerElectronics/Partition/PartitionScaleMicrogrid.cpp index 150a6eeef..0b87fdc3c 100644 --- a/examples/PowerElectronics/Partition/PartitionScaleMicrogrid.cpp +++ b/examples/PowerElectronics/Partition/PartitionScaleMicrogrid.cpp @@ -4,7 +4,7 @@ #include #include #include -#include +#include #include #include @@ -48,7 +48,8 @@ RunResult printMicrogridSystems(index_type N_size, index_type num_partitions); */ int main() { - index_type N_size = 5; + index_type N_size = 5; // Number of IBRs/2 + std::vector num_partitions_list = {1, 2, 3, 4, 5}; std::cout << std::format("{:<16}{:>16}{:>18}{:>12}{:>14}{:>16}\n", "num_partitions", @@ -60,7 +61,7 @@ int main() std::cout << std::string(93, '-') << "\n"; - for (index_type p : {1, 2, 3, 4, 5}) + for (index_type p : num_partitions_list) { assert(p <= 2 * N_size); From c8a6dcbb3f98f5e1251e8d07b0b8f58e25bb442d Mon Sep 17 00:00:00 2001 From: abdourahmanbarry Date: Thu, 6 Aug 2026 05:42:37 +0000 Subject: [PATCH 40/52] Remove co-simulation changes for separate pull request --- .../Solver/Dynamic/MultiStageCosimulation.cpp | 192 ------------------ .../Solver/Dynamic/MultiStageCosimulation.hpp | 179 ---------------- 2 files changed, 371 deletions(-) delete mode 100644 GridKit/Solver/Dynamic/MultiStageCosimulation.cpp delete mode 100644 GridKit/Solver/Dynamic/MultiStageCosimulation.hpp diff --git a/GridKit/Solver/Dynamic/MultiStageCosimulation.cpp b/GridKit/Solver/Dynamic/MultiStageCosimulation.cpp deleted file mode 100644 index 7b1803bca..000000000 --- a/GridKit/Solver/Dynamic/MultiStageCosimulation.cpp +++ /dev/null @@ -1,192 +0,0 @@ - -#include "MultiStageCosimulation.hpp"; - -#include - -#include -#include -#include -#include - -#include "GridKit/Solver/Dynamic/Interpolation.hpp" -#include "GridKit/Solver/Dynamic/Rosenbrock.hpp" - -namespace Integrator -{ - - template - MultiStageCosimulation::MultiStageCosimulation( - std::vector partitions, - std::size_t num_stages, - MemorySpace memspace) - : num_partitions_(partitions.size()), - num_stages_(num_stages), - memspace_(memspace), - partitions_(std::move(partitions)) - { - if (num_partitions_ == 0) - { - throw std::invalid_argument("MultiStageCosimulation requires at least one partition."); - } - - if (num_stages_ == 0) - { - throw std::invalid_argument("MultiStageCosimulation requires at least one stage."); - } - - for (auto* partition : partitions_) - { - if (partition == nullptr) - { - throw std::invalid_argument("Partition pointer cannot be null."); - } - } - } - - // TODO: Complete implementation - template - int MultiStageCosimulation::allocate() - { - num_partitions_ = partitions_.size(); - - x0_local_.resize(num_partitions_); - coupling_mat_.resize(num_partitions_); - linear_workspaces_.resize(num_partitions_); - lin_solver_.resize(num_partitions_); - solvers_.resize(num_partitions_); - - for (size_t p = 0; p < num_partitions_; ++p) - { - auto* part = partitions_[p]; - assert(part != nullptr); - - const IdxT num_states = part->getStateSize(); - const IdxT num_coupling = part->getCouplingSize(); - - x0_local_[p] = std::make_unique(num_states); - x0_local_[p]->allocate(memspace_); - - coupling_mat_[p] = std::make_unique(num_coupling); - coupling_mat_[p]->allocate(memspace_); - - linear_workspaces_[p] = std::make_unique(); - - lin_solver_[p] = std::make_unique( - linear_workspaces_[p].get(), - "klu", - "klu", - "klu"); - - lin_solver_[p]->initialize(); - - solvers_[p] = std::make_unique( - Rosenbrock::Tableau::rodas5p(), - part, - lin_solver_[p].get(), - &vector_handler_); - - solvers_[p]->allocate(); - } - - return 0; - } - - template - int MultiStageCosimulation::distributeLocal(const State& global_y, PartitionEvaluator* partition) - { - auto& internal = partition->getInternalIndecies(); - size_t n = partition->size(); - - matrix_handler_.matvec(); - - return 0; - } - - template - int MultiStageCosimulation::distributeCoupling(const State& global_y, int stage) - { - - auto* global_data = global_y.getData(memspace_); - - for (size_t p = 0; p < num_partitions_; ++p) - { - auto* part = partitions_[p]; - auto& external = part->getExternalIndices(); // TODO: add this function to partition evaluate - size_t n = part->size(); - - auto* local_data = coupling_mat_[p].getData(stage, memspace_); - - for (size_t i = 0; i < n; ++i) - { - local_data[i] = global_data[external[i]]; - } - } - - return 0; - } - - // TODO: Complete implementation - template - int MultiStageCosimulation::timestep(const State& global_y) - { - return 0; - } - - // TODO: Complete implementation - template - int MultiStageCosimulation::partitionStageSolve(int stage, double t0, double dt) - { - - std::vector out_times; - - for (size_t iter = 0; iter < max_iterations_; iter++) - { - for (size_t part = 0; part < num_partitions_; part++) - { - - auto* partitionEvaluator = partitions_[part]; - auto* partition_solver = solvers_[part]; - - if (stage == 0 && iter == 0) - { - distributeLocal(y_, partitionEvaluator); - } - - auto& coupling_stage_values = coupling_mat_[part]; - - GridKit::LagrangeInterpolant interp(coupling_stage_values, t0, dt); - partitionEvaluator->addForcing(interp); - - for (size_t i = 0; i < num_partitions_; i++) - { - partition_solution_[i]->setToZero(memspace_); - } - - int counter = 1; - - auto sol_out = [&](double tt, ReSolve::vector::Vector yp) -> void - { - auto* internal_csr = partition_internal_csr_[part].get(); - auto* internal_sol = partition_solution_[counter].get(); - - double alpha = 1.0; - double beta = 1.0; - - matrix_handler_.matvec(internal_csr, &yp, internal_sol, &alpha, &beta, memspace_); - - counter++; - }; - - partition_solver->integrate(out_times, stepController_, params_, sol_out); - } - - if (stage == 0) - { - break; - } - } - - return 0; - } - -} // namespace Integrator diff --git a/GridKit/Solver/Dynamic/MultiStageCosimulation.hpp b/GridKit/Solver/Dynamic/MultiStageCosimulation.hpp deleted file mode 100644 index ab6aabf08..000000000 --- a/GridKit/Solver/Dynamic/MultiStageCosimulation.hpp +++ /dev/null @@ -1,179 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Integrator -{ - - template - class MultiStageCosimulation - { - using State = ReSolve::vector::Vector; - using RealT = typename GridKit::ScalarTraits::RealT; - using Rosenbrock = Integrator::Rosenbrock; - using PartitionEvaluator = GridKit::Model::PartitionEvaluator; - using MemorySpace = ReSolve::memory::MemorySpace; - - public: - MultiStageCosimulation(std::vector partitions, - size_t num_stages = 2, - size_t max_iterations = 2, - MemorySpace memspace = MemorySpace::HOST); - - int initializeSimulation(RealT t0); - - int runSimulation(RealT tn, IdxT nout, std::optional> step_callback = {}); - - int partitionStageSolve(int, double, double); - - int timestepper(const std::vector&, - std::optional>); - - int distributeLocal(const State&); - - int distributeCoupling(const State&, int); - - int allocate(); - - private: - /** - * @brief - * - */ - std::vector> linear_workspaces_; - - /** - * @brief initial time - */ - RealT t0_; - - /** - * @brief Maximum number of iterations per stage - */ - size_t max_iterations_; - - /** - * - * @brief Number of partitions in the problem - */ - size_t num_partitions_; - - /** - * - * @brief Number of stages used by the method - */ - size_t num_stages_; - - /** - * - * @brief Number of stages used by the method - */ - size_t num_states_; - - /** - * - * @brief The global state of the system - */ - State y_; - - Rosenbrock::Parameters params_; - - Integrator::AdaptiveStep stepController_; - - /** - * - * @brief Memory space used - */ - ReSolve::memory::MemorySpace memspace_; - - /** - * - * @brief Handles vector operations - * - */ - ReSolve::VectorHandler vector_handler_; - - /** - * - * @brief Handles matrix linear algebra operations - * - */ - ReSolve::MatrixHandler matrix_handler_; - - /** - * - * @brief linear solver for the sub-integrator - * - */ - std::vector> lin_solver_; - - /** - * - * @brief Contains vectors of initial conditions for every step - * - */ - std::vector> x0_local_; - - /** - * - * @brief Vectors of coupling matrices for each partition - * - */ - std::vector>> coupling_mat_; - - /** - * - * @brief partition solution multi-dimensional vectors - * - */ - std::vector> partition_solution_; - - /** - * - * @brief partition solution multi-dimensional vectors - * - */ - std::vector partition_external_csr_; - - /** - * - * @brief partition solution multi-dimensional vectors - * - */ - std::vector> partition_internal_csr_; - - /** - * - *@brief Vector of partitions - */ - std::vector partitions_; - - /** - * - *@brief Vector of solvers - */ - std::vector> solvers_; - }; -} // namespace Integrator From f082543661f819967d4d1a8afdbbfe22c04201b9 Mon Sep 17 00:00:00 2001 From: abdourahmanbarry Date: Tue, 11 Aug 2026 17:53:09 +0000 Subject: [PATCH 41/52] Removed PartitionEvaluator --- GridKit/Model/PartitionEvaluator.hpp | 282 --------------------------- 1 file changed, 282 deletions(-) delete mode 100644 GridKit/Model/PartitionEvaluator.hpp diff --git a/GridKit/Model/PartitionEvaluator.hpp b/GridKit/Model/PartitionEvaluator.hpp deleted file mode 100644 index 1ce80b513..000000000 --- a/GridKit/Model/PartitionEvaluator.hpp +++ /dev/null @@ -1,282 +0,0 @@ -#pragma once - -#include - -#include "Evaluator.hpp" - -namespace GridKit -{ - namespace Model - { - - template - class PartitionEvaluator : public Evaluator - { - public: - using Base = Evaluator; - using RealT = typename Base::RealT; - using MatrixT = typename Base::MatrixT; - using CsrMatrixT = typename Base::CsrMatrixT; - - using ForcingFunc = std::function; - - private: - std::vector y_; - std::vector yp_; - std::vector yB_; - std::vector ypB_; - std::vector param_; - std::vector param_up_; - std::vector param_lo_; - std::vector residual_; - std::vector integrand_; - std::vector adj_integrand_; - std::vector adj_residual_; - std::vector tag_; - - MatrixT jacobian_; - - std::vector coupling_; - std::vector forcing_; - - public: - int allocate() override - { - return 0; - } - - int initialize() override - { - return 0; - } - - int tagDifferentiable() override - { - return 0; - } - - int evaluateResidual() override - { - return 0; - } - - int evaluateJacobian() override - { - return 0; - } - - int evaluateIntegrand() override - { - return 0; - } - - int initializeAdjoint() override - { - return 0; - } - - int evaluateAdjointResidual() override - { - return 0; - } - - int evaluateAdjointIntegrand() override - { - return 0; - } - - IdxT size() override - { - return 0; - } - - IdxT nnz() override - { - return 0; - } - - bool hasJacobian() override - { - return false; - } - - IdxT sizeQuadrature() override - { - return 0; - } - - IdxT sizeParams() override - { - return 0; - } - - void updateTime(RealT, RealT) override - { - } - - void setTolerances(RealT&, RealT&) const override - { - } - - void setMaxSteps(IdxT&) const override - { - } - - std::vector& y() override - { - return y_; - } - - const std::vector& y() const override - { - return y_; - } - - std::vector& yp() override - { - return yp_; - } - - const std::vector& yp() const override - { - return yp_; - } - - std::vector& tag() override - { - return tag_; - } - - const std::vector& tag() const override - { - return tag_; - } - - std::vector& yB() override - { - return yB_; - } - - const std::vector& yB() const override - { - return yB_; - } - - std::vector& ypB() override - { - return ypB_; - } - - const std::vector& ypB() const override - { - return ypB_; - } - - std::vector& param() override - { - return param_; - } - - const std::vector& param() const override - { - return param_; - } - - std::vector& param_up() override - { - return param_up_; - } - - const std::vector& param_up() const override - { - return param_up_; - } - - std::vector& param_lo() override - { - return param_lo_; - } - - const std::vector& param_lo() const override - { - return param_lo_; - } - - std::vector& getResidual() override - { - return residual_; - } - - const std::vector& getResidual() const override - { - return residual_; - } - - MatrixT& getJacobian() override - { - return jacobian_; - } - - const MatrixT& getJacobian() const override - { - return jacobian_; - } - - std::vector& getIntegrand() override - { - return integrand_; - } - - const std::vector& getIntegrand() const override - { - return integrand_; - } - - std::vector& getAdjointResidual() override - { - return adj_residual_; - } - - const std::vector& getAdjointResidual() const override - { - return adj_residual_; - } - - std::vector& getAdjointIntegrand() override - { - return adj_integrand_; - } - - const std::vector& getAdjointIntegrand() const override - { - return adj_integrand_; - } - - void addForcing(const ForcingFunc& f) - { - forcing_.push_back(f); - } - - IdxT getCouplingSize() const - { - return static_cast(coupling_.size()); - } - - IdxT getStateSize() const - { - return static_cast(y_.size()); - } - - std::vector& getCoupling() - { - return coupling_; - } - - const std::vector& getCoupling() const - { - return coupling_; - } - }; - - } // namespace Model -} // namespace GridKit From 6bb547267a7cf95d11eeca40c91a03de80e3cd27 Mon Sep 17 00:00:00 2001 From: abdourahmanbarry Date: Tue, 11 Aug 2026 22:32:24 +0000 Subject: [PATCH 42/52] Updated hires test problems --- .../PowerElectronics/Partition/CMakeLists.txt | 6 +- .../PowerElectronics/Partition/HiresBus.hpp | 26 +-- .../Partition/HiresComponent1.hpp | 22 +- .../Partition/HiresComponent3.hpp | 6 +- .../PowerElectronics/Partition/Partition.cpp | 219 ------------------ .../Partition/PartitionHires.cpp | 191 +++++++++++++++ 6 files changed, 220 insertions(+), 250 deletions(-) delete mode 100644 examples/PowerElectronics/Partition/Partition.cpp create mode 100644 examples/PowerElectronics/Partition/PartitionHires.cpp diff --git a/examples/PowerElectronics/Partition/CMakeLists.txt b/examples/PowerElectronics/Partition/CMakeLists.txt index e428f5908..249f07505 100644 --- a/examples/PowerElectronics/Partition/CMakeLists.txt +++ b/examples/PowerElectronics/Partition/CMakeLists.txt @@ -1,9 +1,9 @@ find_package(OpenMP) -add_executable(partition Partition.cpp) +add_executable(partitionHires PartitionHires.cpp) target_link_libraries( - partition + partitionHires PRIVATE GridKit::solvers_dyn GridKit::power_elec_partition_interfaces) add_executable(partitionMicrogrid PartitionMicrogrid.cpp) @@ -19,7 +19,7 @@ target_link_libraries( add_test(NAME PartitionMicrogrid COMMAND partitionMicrogrid) -install(TARGETS partition partitionMicrogrid RUNTIME DESTINATION bin) +install(TARGETS partitionHires partitionMicrogrid RUNTIME DESTINATION bin) if(OpenMP_CXX_FOUND) add_executable(PartitionScaleMicrogrid PartitionScaleMicrogrid.cpp) diff --git a/examples/PowerElectronics/Partition/HiresBus.hpp b/examples/PowerElectronics/Partition/HiresBus.hpp index e3a57c0ca..20c311e6f 100644 --- a/examples/PowerElectronics/Partition/HiresBus.hpp +++ b/examples/PowerElectronics/Partition/HiresBus.hpp @@ -6,7 +6,7 @@ namespace GridKit { /*! - * @brief Declaration of a Hires Bus Component. + * @brief Hires Bus Component. * */ template @@ -56,7 +56,7 @@ namespace GridKit { } - int allocate() + int allocate() final { CircuitComponent::allocate(); @@ -66,32 +66,30 @@ namespace GridKit return 0; } - int initialize() + int initialize() final { return 0; } - int tagDifferentiable() + int tagDifferentiable() final { return 0; } - int evaluateInternalResidual() + int evaluateInternalResidual() final { return 0; } - int evaluateExternalResidual() + int evaluateExternalResidual() final { *f_ext_[0] += -*yp_ext_[0] - *y_ext_[0]; *f_ext_[1] += -*yp_ext_[1] - *y_ext_[1]; - this->getResidual().setDataUpdated(); - return 0; } - int evaluateJacobian() + int evaluateJacobian() final { this->zeroJacMatrix(); @@ -104,22 +102,22 @@ namespace GridKit return 0; } - int evaluateIntegrand() + int evaluateIntegrand() final { return 0; } - int initializeAdjoint() + int initializeAdjoint() final { return 0; } - int evaluateAdjointResidual() + int evaluateAdjointResidual() final { return 0; } - int evaluateAdjointIntegrand() + int evaluateAdjointIntegrand() final { return 0; } @@ -136,7 +134,7 @@ namespace GridKit * This represents a "noise" level close to zero for which pure relative * error cannot be used. */ - int setAbsoluteTolerance(RealT rel_tol) + int setAbsoluteTolerance(RealT rel_tol) final { abs_tol_.setToConst(static_cast(rel_tol)); return 0; diff --git a/examples/PowerElectronics/Partition/HiresComponent1.hpp b/examples/PowerElectronics/Partition/HiresComponent1.hpp index da2707cdb..ec6f28c5a 100644 --- a/examples/PowerElectronics/Partition/HiresComponent1.hpp +++ b/examples/PowerElectronics/Partition/HiresComponent1.hpp @@ -8,7 +8,7 @@ namespace GridKit { /*! - * @brief Declaration of a Hires Component 1 class. + * @brief Hires Component 1 class. * */ template @@ -57,7 +57,7 @@ namespace GridKit { } - int allocate() + int allocate() final { CircuitComponent::allocate(); @@ -67,17 +67,17 @@ namespace GridKit return 0; } - int initialize() + int initialize() final { return 0; } - int tagDifferentiable() + int tagDifferentiable() final { return 0; } - int evaluateInternalResidual() + int evaluateInternalResidual() final { // Internals f_int_[0] = -yp_int_[0] - 1.71 * y_int_[0] + 0.43 * y_int_[1] + 8.32 * y_int_[2] + 0.0007; @@ -96,7 +96,7 @@ namespace GridKit return 0; } - int evaluateJacobian() + int evaluateJacobian() final { this->zeroJacMatrix(); @@ -118,22 +118,22 @@ namespace GridKit return 0; } - int evaluateIntegrand() + int evaluateIntegrand() final { return 0; } - int initializeAdjoint() + int initializeAdjoint() final { return 0; } - int evaluateAdjointResidual() + int evaluateAdjointResidual() final { return 0; } - int evaluateAdjointIntegrand() + int evaluateAdjointIntegrand() final { return 0; } @@ -150,7 +150,7 @@ namespace GridKit * This represents a "noise" level close to zero for which pure relative * error cannot be used. */ - int setAbsoluteTolerance(RealT rel_tol) + int setAbsoluteTolerance(RealT rel_tol) final { abs_tol_.setToConst(static_cast(rel_tol)); return 0; diff --git a/examples/PowerElectronics/Partition/HiresComponent3.hpp b/examples/PowerElectronics/Partition/HiresComponent3.hpp index af1146ae9..060a025aa 100644 --- a/examples/PowerElectronics/Partition/HiresComponent3.hpp +++ b/examples/PowerElectronics/Partition/HiresComponent3.hpp @@ -6,7 +6,7 @@ namespace GridKit { /*! - * @brief Declaration of a Hires Component 3 class. + * @brief Hires Component 3 class. * */ template @@ -89,8 +89,8 @@ namespace GridKit int evaluateExternalResidual() { // Externals - *f_ext_[0] = -0.02 * *y_ext_[0]; - *f_ext_[1] = -0.045 * *y_ext_[1] + 0.43 * y_int_[0] + 0.43 * y_int_[1]; + *f_ext_[0] += -0.02 * *y_ext_[0]; + *f_ext_[1] += -0.045 * *y_ext_[1] + 0.43 * y_int_[0] + 0.43 * y_int_[1]; return 0; } diff --git a/examples/PowerElectronics/Partition/Partition.cpp b/examples/PowerElectronics/Partition/Partition.cpp deleted file mode 100644 index 60280576f..000000000 --- a/examples/PowerElectronics/Partition/Partition.cpp +++ /dev/null @@ -1,219 +0,0 @@ -// #include - -#include - -#define _USE_MATH_DEFINES -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "HiresBus.hpp" -#include "HiresComponent1.hpp" -#include "HiresComponent3.hpp" - -std::vector hires(const std::vector& y, const std::vector& yp); - -int main(int /* argc */, char const** /* argv */) -{ - - using Bus = GridKit::PowerElectronics::MicrogridBus; - Bus bus; - - GridKit::SubsystemModel* partition1 = new GridKit::SubsystemModel(); - GridKit::SubsystemModel* partition2 = new GridKit::SubsystemModel(); - - GridKit::HiresComponent1* comp1 = new GridKit::HiresComponent1(&bus, 1); - GridKit::HiresBus* bus1 = new GridKit::HiresBus(&bus, 2); - GridKit::HiresComponent3* comp3 = new GridKit::HiresComponent3(&bus, 3); - - bus.allocate(); - - GridKit::ExternalConnection connection_node1{ - .y_ = nullptr, - .yp_ = nullptr, - .f_ = nullptr, - .idx_ = 3}; - - GridKit::ExternalConnection connection_node2{ - .y_ = nullptr, - .yp_ = nullptr, - .f_ = nullptr, - .idx_ = 4}; - - bus.setExternalConnectionNodes(0, connection_node1); - bus.setExternalConnectionNodes(1, connection_node2); - - comp1->allocate(); - comp3->allocate(); - bus1->allocate(); - - comp1->setInternalConnectionNodes(2, 0); - comp1->setInternalConnectionNodes(3, 1); - comp1->setInternalConnectionNodes(4, 2); - - comp3->setInternalConnectionNodes(2, 5); - comp3->setInternalConnectionNodes(3, 6); - comp3->setInternalConnectionNodes(4, 7); - - GridKit::HiresComponent3* comp3copy = new GridKit::HiresComponent3(*comp3); - GridKit::BusPartitionInterface* busInterface = new GridKit::BusPartitionInterface(&bus, comp3copy, 4); - - partition1->addComponent(comp1); - partition1->addComponent(bus1); - partition1->addInterface(busInterface); - partition1->addNode(&bus); - - partition2->addComponent(comp3); - - auto subsystems = {partition1, partition2}; - - std::vector y = {1, 2, 3, 4, 5, 6, 7, 8}; - std::vector yp = {1, 2, 3, 4, 5, 6, 7, 8}; - - // Distribute externals to partition 1 - - for (auto* partition : subsystems) - { - partition->allocate(); - partition->initialize(); - partition->updateTime(2, 5); - - for (size_t i = 0; i < partition->getExternSize(); i++) - { - partition->getExternalDataY()[i] = y[partition->getExternalDataIndices()[i]]; - partition->getExternalDataYP()[i] = yp[partition->getExternalDataIndices()[i]]; - } - - auto* partition_y = partition->y().getData(); - auto* partition_yp = partition->yp().getData(); - - for (size_t i = 0; i < partition->getInternalSize(); i++) - { - partition_y[i] = y[partition->getNodeConnection(i)]; - partition_yp[i] = yp[partition->getNodeConnection(i)]; - } - - partition->y().setDataUpdated(); - partition->yp().setDataUpdated(); - - partition->evaluateResidual(); - partition->evaluateJacobian(); - } - - auto printHeader = [](const std::string& title) - { - std::cout << "\n------------- " << title << " -----------\n"; - - std::cout << std::left << std::setw(10) << "Res Values" - << " ---------- " - << std::right << std::setw(10) << "Comp. Index" - << '\n'; - }; - - auto printRow = [](double residual, size_t index) - { - std::cout << std::left << std::setw(10) - << std::defaultfloat << std::setprecision(5) - << residual - << " ---------- " - << std::right << std::setw(10) - << index - << '\n'; - }; - - auto printPartitionResiduals = [&](auto* partition, const std::string& title) - { - printHeader(title); - - auto* residual = partition->getResidual().getData(); - - for (size_t i = 0; i < partition->getInternalSize(); ++i) - { - const auto comp_index = partition->getNodeConnection(i); - printRow(residual[i], comp_index); - } - }; - - printPartitionResiduals(partition1, "Partition 1"); - printPartitionResiduals(partition2, "Partition 2"); - - auto ref = hires(y, yp); - - printHeader("Reference Output"); - for (size_t i = 0; i < ref.size(); ++i) - { - printRow(ref[i], i); - } - - delete comp1; - delete bus1; - delete comp3; - - delete partition1; - delete partition2; - - return 0; -} - -std::vector hires(const std::vector& y, const std::vector& yp) -{ - std::vector f(8); - - f[0] = -yp[0] - 1.71 * y[0] + 0.43 * y[1] + 8.32 * y[2] + 0.0007; - f[1] = -yp[1] + 1.71 * y[0] - 8.75 * y[1]; - f[2] = -yp[2] - 10.03 * y[2] + 0.43 * y[3] + 0.035 * y[4]; - f[3] = -yp[3] + 8.32 * y[1] + 1.71 * y[2] - 1.12 * y[3]; - f[4] = -yp[4] - 1.745 * y[4] + 0.43 * y[5] + 0.43 * y[6]; - f[5] = -yp[5] - 280 * y[5] * y[7] + 0.69 * y[3] + 1.71 * y[4] - 0.43 * y[5] + 0.69 * y[6]; - f[6] = -yp[6] + 280 * y[5] * y[7] - 1.81 * y[6]; - f[7] = -yp[7] - 280 * y[5] * y[7] + 1.81 * y[6]; - - return f; -} - -// template -// int jac_hires(const std::vector& y, [[maybe_unused]] const std::vector& yp, GridKit::LinearAlgebra::DenseMatrix& jac, RealT alpha) -// { - -// jac.setValue(0, 0, -alpha - 1.71); -// jac.setValue(0, 1, 0.43); -// jac.setValue(0, 2, 8.32); - -// jac.setValue(1, 0, 1.71); -// jac.setValue(1, 1, -alpha - 8.75); - -// jac.setValue(2, 2, -alpha - 10.03); -// jac.setValue(2, 3, 0.43); -// jac.setValue(2, 4, 0.035); - -// jac.setValue(3, 1, 8.32); -// jac.setValue(3, 2, 1.71); -// jac.setValue(3, 3, -alpha - 1.12); - -// jac.setValue(4, 4, -alpha - 1.745); -// jac.setValue(4, 5, 0.43); -// jac.setValue(4, 6, 0.43); - -// jac.setValue(5, 3, 0.69); -// jac.setValue(5, 4, 1.71); -// jac.setValue(5, 5, -alpha - 280.0 * y[7] - 0.43); -// jac.setValue(5, 6, 0.69); -// jac.setValue(5, 7, -280.0 * y[5]); - -// jac.setValue(6, 5, 280.0 * y[7]); -// jac.setValue(6, 6, -alpha - 1.81); -// jac.setValue(6, 7, 280.0 * y[5]); - -// jac.setValue(7, 5, -280.0 * y[7]); -// jac.setValue(7, 6, 1.81); -// jac.setValue(7, 7, -alpha - 280.0 * y[5]); - -// return 0; -// } diff --git a/examples/PowerElectronics/Partition/PartitionHires.cpp b/examples/PowerElectronics/Partition/PartitionHires.cpp new file mode 100644 index 000000000..8ed014ed3 --- /dev/null +++ b/examples/PowerElectronics/Partition/PartitionHires.cpp @@ -0,0 +1,191 @@ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "HiresBus.hpp" +#include "HiresComponent1.hpp" +#include "HiresComponent3.hpp" + +std::vector hires(const std::vector& y, const std::vector& yp); +void printRow(double residual, size_t index); +void printHeader(const std::string& title); +void printPartitionResiduals(auto* partition, const std::string& title); + +/** + * This example assembles the HIRES problem and partitions it to demonstrate + * that the partitioned system can reconstruct the full system. + * + * The HIRES problem consists of eight ODE equations and is divided into three + * components. HiresComponent1 contains equations 1, 2, and 3, + * HiresComponent3 contains equations 6, 7, and 8, and HiresBus contains + * equations 4 and 5. HiresBus plays a role similar to MicrogridBusDQ, where + * other components connected to the bus contribute terms to its equations. + * + * In the equations below, terms enclosed in parentheses ( ) are contributions + * from HiresComponent1, terms enclosed in square brackets [ ] are contributions + * from HiresComponent3, and the remaining terms in equations 4 and 5 belong + * to HiresBus. + * + * f_1 = dy_1/dt + 1.71y_1 - 0.43y_2 - 8.32y_3 - 0.0007 + * + * f_2 = dy_2/dt - 1.71y_1 + 8.75y_2 + * + * f_3 = dy_3/dt + 10.03y_3 - 0.43y_4 - 0.035y_5 + * + * f_4 = dy_4/dt + y_4 + (0.1y_4 - 8.32y_2 - 1.71y_3) + [0.02y_4] + * + * f_5 = dy_5/dt + y_5 + (0.7y_5) + [0.045y_5 - 0.43y_6 - 0.43y_7] + * + * f_6 = dy_6/dt - 280y_6y_8 + 0.69y_4 + 1.71y_5 - 0.43y_6 + 0.69y_7 + * + * f_7 = dy_7/dt + 280y_6y_8 - 1.81y_7 + * + * f_8 = dy_8/dt - 280y_6y_8 + 1.81y_7 + * + * + * The assembled system has the following structure: + * + * (HiresComponent1) -------- (HiresBus) -------- (HiresComponent3) + * + * The system is partitioned between HiresBus and HiresComponent3. A bus + * partition interface is introduced in the partition containing HiresBus to + * preserve the contribution of HiresComponent3 to the bus equations. The + * residuals evaluated independently by the partitions are then printed with + * the residual of the full system for eye-ball comparision. + * + * This example will also be useful later for testing the order of accuracy of co-simulation methods. + */ + +int main(int /* argc */, char const** /* argv */) +{ + + using Bus = GridKit::PowerElectronics::MicrogridBus; + Bus bus; + + GridKit::PowerElectronicsModel* sys = new GridKit::PowerElectronicsModel(); + GridKit::SubsystemModel* partition1 = new GridKit::SubsystemModel(); + GridKit::SubsystemModel* partition2 = new GridKit::SubsystemModel(); + + GridKit::HiresComponent1* comp1 = new GridKit::HiresComponent1(&bus, 1); + GridKit::HiresBus* bus1 = new GridKit::HiresBus(&bus, 2); + GridKit::HiresComponent3* comp3 = new GridKit::HiresComponent3(&bus, 3); + + sys->addComponent(comp1); + sys->addComponent(comp3); + sys->addComponent(bus1); + sys->addNode(&bus); + + sys->allocate(); + + std::vector y = {1, 2, 3, 6, 7, 8, 4, 5}; + std::vector yp = {1, 2, 3, 6, 7, 8, 4, 5}; + + auto sys_y = sys->y().getData(); + auto sys_yp = sys->yp().getData(); + + for (size_t i = 0; i < sys->size(); i++) + { + sys_y[i] = y[i]; + sys_yp[i] = yp[i]; + } + + sys->evaluateResidual(); + + GridKit::HiresComponent3* comp3copy = new GridKit::HiresComponent3(*comp3); + GridKit::BusPartitionInterface* busInterface = new GridKit::BusPartitionInterface(&bus, comp3copy, 4); + + partition1->addComponent(comp1); + partition1->addComponent(bus1); + partition1->addInterface(busInterface); + partition1->addNode(&bus); + + partition2->addComponent(comp3); + + auto subsystems = {partition1, partition2}; + + for (auto* partition : subsystems) + { + partition->allocate(); + partition->initialize(); + partition->updateTime(2, 5); + + for (size_t i = 0; i < partition->getExternSize(); i++) + { + partition->getExternalDataY()[i] = y[partition->getExternalDataIndices()[i]]; + partition->getExternalDataYP()[i] = yp[partition->getExternalDataIndices()[i]]; + } + + auto* partition_y = partition->y().getData(); + auto* partition_yp = partition->yp().getData(); + + for (size_t i = 0; i < partition->getInternalSize(); i++) + { + partition_y[i] = y[partition->getNodeConnection(i)]; + partition_yp[i] = yp[partition->getNodeConnection(i)]; + } + + partition->y().setDataUpdated(); + partition->yp().setDataUpdated(); + + partition->evaluateResidual(); + } + + printPartitionResiduals(partition1, "Partition 1"); + printPartitionResiduals(partition2, "Partition 2"); + + printHeader("Reference Output"); + auto* residual = sys->getResidual().getData(); + + for (size_t i = 0; i < sys->getInternalSize(); ++i) + { + printRow(residual[i], i); + } + + delete sys; + delete partition1; + delete partition2; + + return 0; +} + +void printRow(double residual, size_t index) +{ + std::cout << std::left << std::setw(10) + << std::defaultfloat << std::setprecision(5) + << residual + << " ---------- " + << std::right << std::setw(10) + << index + << '\n'; +} + +void printHeader(const std::string& title) +{ + std::cout << "\n------------- " << title << " -----------\n"; + + std::cout << std::left << std::setw(10) << "Res Values" + << " ---------- " + << std::right << std::setw(10) << "Comp. Index" + << '\n'; +} + +void printPartitionResiduals(auto* partition, const std::string& title) +{ + printHeader(title); + + auto* residual = partition->getResidual().getData(); + + for (size_t i = 0; i < partition->getInternalSize(); ++i) + { + const auto comp_index = partition->getNodeConnection(i); + printRow(residual[i], comp_index); + } +} \ No newline at end of file From 367e193b48cb7d7990e1a65fe41be89b25bf8e97 Mon Sep 17 00:00:00 2001 From: abdourahmanbarry Date: Tue, 11 Aug 2026 22:36:57 +0000 Subject: [PATCH 43/52] Apply pre-commit fixes --- examples/PowerElectronics/Partition/PartitionHires.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/PowerElectronics/Partition/PartitionHires.cpp b/examples/PowerElectronics/Partition/PartitionHires.cpp index 8ed014ed3..b34aaf165 100644 --- a/examples/PowerElectronics/Partition/PartitionHires.cpp +++ b/examples/PowerElectronics/Partition/PartitionHires.cpp @@ -188,4 +188,4 @@ void printPartitionResiduals(auto* partition, const std::string& title) const auto comp_index = partition->getNodeConnection(i); printRow(residual[i], comp_index); } -} \ No newline at end of file +} From d87215d58eb87799d0d8f60eadc0d74f63828ab4 Mon Sep 17 00:00:00 2001 From: abdourahmanbarry Date: Sat, 15 Aug 2026 13:40:43 +0000 Subject: [PATCH 44/52] Moved Hires test problem to tests folder - add documentation for hires problem and partitioning - test subsystem jacobian and residuals - Update CMake file --- .../PowerElectronics/Partition/HiresBus.hpp | 146 ---- .../Partition/HiresComponent1.hpp | 162 ---- .../Partition/HiresComponent3.hpp | 174 ----- .../UnitTests/PowerElectronics/CMakeLists.txt | 20 + tests/UnitTests/PowerElectronics/README.md | 139 ++++ .../SubsystemModelWithHiresTest.hpp | 734 ++++++++++++++++++ .../runSubsystemModelWithHiresTest.cpp | 12 + 7 files changed, 905 insertions(+), 482 deletions(-) delete mode 100644 examples/PowerElectronics/Partition/HiresBus.hpp delete mode 100644 examples/PowerElectronics/Partition/HiresComponent1.hpp delete mode 100644 examples/PowerElectronics/Partition/HiresComponent3.hpp create mode 100644 tests/UnitTests/PowerElectronics/README.md create mode 100644 tests/UnitTests/PowerElectronics/SubsystemModelWithHiresTest.hpp create mode 100644 tests/UnitTests/PowerElectronics/runSubsystemModelWithHiresTest.cpp diff --git a/examples/PowerElectronics/Partition/HiresBus.hpp b/examples/PowerElectronics/Partition/HiresBus.hpp deleted file mode 100644 index 20c311e6f..000000000 --- a/examples/PowerElectronics/Partition/HiresBus.hpp +++ /dev/null @@ -1,146 +0,0 @@ -#pragma once - -#include -#include - -namespace GridKit -{ - /*! - * @brief Hires Bus Component. - * - */ - template - class HiresBus : public CircuitComponent - { - - using RealT = typename CircuitComponent::RealT; - using NodeT = typename PowerElectronics::NodeBase; - - using CircuitComponent::size_; - using CircuitComponent::nnz_; - using CircuitComponent::time_; - using CircuitComponent::alpha_; - using CircuitComponent::y_ext_; - using CircuitComponent::y_int_; - using CircuitComponent::yp_ext_; - using CircuitComponent::yp_int_; - using CircuitComponent::tag_; - using CircuitComponent::abs_tol_; - using CircuitComponent::f_ext_; - using CircuitComponent::f_int_; - using CircuitComponent::g_; - using CircuitComponent::yB_; - using CircuitComponent::ypB_; - using CircuitComponent::fB_; - using CircuitComponent::gB_; - using CircuitComponent::param_; - using CircuitComponent::idc_; - - using CircuitComponent::extern_indices_; - using CircuitComponent::n_extern_; - using CircuitComponent::n_intern_; - - public: - HiresBus(NodeT* bus, IdxT id) - : node_ref_(bus) - { - size_ = 2; - n_intern_ = 0; - n_extern_ = 2; - extern_indices_ = {0, 1}; - idc_ = id; - nnz_ = 2; - } - - ~HiresBus() - { - } - - int allocate() final - { - CircuitComponent::allocate(); - - this->setExternalConnectionNodes(0, node_ref_->getNodeConnection(0)); - this->setExternalConnectionNodes(1, node_ref_->getNodeConnection(1)); - - return 0; - } - - int initialize() final - { - return 0; - } - - int tagDifferentiable() final - { - return 0; - } - - int evaluateInternalResidual() final - { - return 0; - } - - int evaluateExternalResidual() final - { - *f_ext_[0] += -*yp_ext_[0] - *y_ext_[0]; - *f_ext_[1] += -*yp_ext_[1] - *y_ext_[1]; - - return 0; - } - - int evaluateJacobian() final - { - this->zeroJacMatrix(); - - std::vector row = {0, 1}; - std::vector col = {0, 1}; - std::vector val = {-alpha_ - 1.0, -alpha_ - 1.0}; - - this->setJacValues(row, col, val); - - return 0; - } - - int evaluateIntegrand() final - { - return 0; - } - - int initializeAdjoint() final - { - return 0; - } - - int evaluateAdjointResidual() final - { - return 0; - } - - int evaluateAdjointIntegrand() final - { - return 0; - } - - /** - * @brief Compute the absolute tolerance for each variable in the model - * - * @param rel_tol The relative tolerance which can be used to pick the - * absolute tolerance. - * @tparam ScalarT Scalar data type - * @tparam IdxT Index data type - * @return int 0 if successful, non-zero otherwise. - * - * This represents a "noise" level close to zero for which pure relative - * error cannot be used. - */ - int setAbsoluteTolerance(RealT rel_tol) final - { - abs_tol_.setToConst(static_cast(rel_tol)); - return 0; - } - - private: - NodeT* node_ref_; - }; -} // namespace GridKit diff --git a/examples/PowerElectronics/Partition/HiresComponent1.hpp b/examples/PowerElectronics/Partition/HiresComponent1.hpp deleted file mode 100644 index ec6f28c5a..000000000 --- a/examples/PowerElectronics/Partition/HiresComponent1.hpp +++ /dev/null @@ -1,162 +0,0 @@ - - -#pragma once - -#include -#include - -namespace GridKit -{ - /*! - * @brief Hires Component 1 class. - * - */ - template - class HiresComponent1 : public CircuitComponent - { - using RealT = typename CircuitComponent::RealT; - using NodeT = typename PowerElectronics::NodeBase; - - using CircuitComponent::size_; - using CircuitComponent::nnz_; - using CircuitComponent::time_; - using CircuitComponent::alpha_; - using CircuitComponent::y_ext_; - using CircuitComponent::y_int_; - using CircuitComponent::yp_ext_; - using CircuitComponent::yp_int_; - using CircuitComponent::tag_; - using CircuitComponent::abs_tol_; - using CircuitComponent::f_ext_; - using CircuitComponent::f_int_; - using CircuitComponent::g_; - using CircuitComponent::yB_; - using CircuitComponent::ypB_; - using CircuitComponent::fB_; - using CircuitComponent::gB_; - using CircuitComponent::param_; - using CircuitComponent::idc_; - - using CircuitComponent::extern_indices_; - using CircuitComponent::n_extern_; - using CircuitComponent::n_intern_; - - public: - HiresComponent1(NodeT* bus, IdxT id) - : node_ref_(bus) - { - size_ = 5; - n_intern_ = 3; - n_extern_ = 2; - extern_indices_ = {0, 1}; - idc_ = id; - nnz_ = 12; - } - - ~HiresComponent1() - { - } - - int allocate() final - { - CircuitComponent::allocate(); - - this->setExternalConnectionNodes(0, node_ref_->getNodeConnection(0)); - this->setExternalConnectionNodes(1, node_ref_->getNodeConnection(1)); - - return 0; - } - - int initialize() final - { - return 0; - } - - int tagDifferentiable() final - { - return 0; - } - - int evaluateInternalResidual() final - { - // Internals - f_int_[0] = -yp_int_[0] - 1.71 * y_int_[0] + 0.43 * y_int_[1] + 8.32 * y_int_[2] + 0.0007; - f_int_[1] = -yp_int_[1] + 1.71 * y_int_[0] - 8.75 * y_int_[1]; - f_int_[2] = -yp_int_[2] - 10.03 * y_int_[2] + 0.43 * *y_ext_[0] + 0.035 * *y_ext_[1]; - - return 0; - } - - int evaluateExternalResidual() - { - // outputs - *f_ext_[0] += 8.32 * y_int_[1] + 1.71 * y_int_[2] - 0.1 * *y_ext_[0]; - *f_ext_[1] += -0.7 * *y_ext_[1]; - - return 0; - } - - int evaluateJacobian() final - { - - this->zeroJacMatrix(); - - // Internal Jacobian Entries - std::vector row = {2, 2, 2, 3, 3, 4, 4, 4}; - std::vector col = {2, 3, 4, 2, 3, 4, 0, 1}; - std::vector val = {-1.71 - alpha_, 0.43, 8.32, 1.71, -8.75 - alpha_, -10.03 - alpha_, 0.43, 0.035}; - - this->setJacValues(row, col, val); - - // External Jacobian Entries - row = {0, 0, 0, 1}; - col = {3, 4, 0, 1}; - val = {8.32, 1.71, -0.1, -0.7}; - - this->setJacValues(row, col, val); - - return 0; - } - - int evaluateIntegrand() final - { - return 0; - } - - int initializeAdjoint() final - { - return 0; - } - - int evaluateAdjointResidual() final - { - return 0; - } - - int evaluateAdjointIntegrand() final - { - return 0; - } - - /** - * @brief Compute the absolute tolerance for each variable in the model - * - * @param rel_tol The relative tolerance which can be used to pick the - * absolute tolerance. - * @tparam ScalarT Scalar data type - * @tparam IdxT Index data type - * @return int 0 if successful, non-zero otherwise. - * - * This represents a "noise" level close to zero for which pure relative - * error cannot be used. - */ - int setAbsoluteTolerance(RealT rel_tol) final - { - abs_tol_.setToConst(static_cast(rel_tol)); - return 0; - } - - private: - NodeT* node_ref_; - }; -} // namespace GridKit diff --git a/examples/PowerElectronics/Partition/HiresComponent3.hpp b/examples/PowerElectronics/Partition/HiresComponent3.hpp deleted file mode 100644 index 060a025aa..000000000 --- a/examples/PowerElectronics/Partition/HiresComponent3.hpp +++ /dev/null @@ -1,174 +0,0 @@ -#pragma once - -#include -#include - -namespace GridKit -{ - /*! - * @brief Hires Component 3 class. - * - */ - template - class HiresComponent3 : public CircuitComponent - { - using RealT = typename CircuitComponent::RealT; - using NodeT = typename PowerElectronics::NodeBase; - - using CircuitComponent::size_; - using CircuitComponent::nnz_; - using CircuitComponent::time_; - using CircuitComponent::alpha_; - using CircuitComponent::y_ext_; - using CircuitComponent::y_int_; - using CircuitComponent::yp_ext_; - using CircuitComponent::yp_int_; - using CircuitComponent::tag_; - using CircuitComponent::abs_tol_; - using CircuitComponent::f_ext_; - using CircuitComponent::f_int_; - using CircuitComponent::g_; - using CircuitComponent::yB_; - using CircuitComponent::ypB_; - using CircuitComponent::fB_; - using CircuitComponent::gB_; - using CircuitComponent::param_; - using CircuitComponent::idc_; - - using CircuitComponent::extern_indices_; - using CircuitComponent::n_extern_; - using CircuitComponent::n_intern_; - - public: - HiresComponent3(NodeT* bus, IdxT id) - : node_ref_(bus) - { - size_ = 5; - n_intern_ = 3; - n_extern_ = 2; - extern_indices_ = {0, 1}; - idc_ = id; - nnz_ = 15; - } - - ~HiresComponent3() - { - } - - int allocate() - { - CircuitComponent::allocate(); - - this->setExternalConnectionNodes(0, node_ref_->getNodeConnection(0)); - this->setExternalConnectionNodes(1, node_ref_->getNodeConnection(1)); - - return 0; - } - - int initialize() - { - return 0; - } - - int tagDifferentiable() - { - return 0; - } - - int evaluateInternalResidual() - { - - // Internals - f_int_[0] = -yp_int_[0] - 280 * y_int_[0] * y_int_[2] + 0.69 * *y_ext_[0] + 1.71 * *y_ext_[1] - 0.43 * y_int_[0] + 0.69 * y_int_[1]; - f_int_[1] = -yp_int_[1] + 280 * y_int_[0] * y_int_[2] - 1.81 * y_int_[1]; - f_int_[2] = -yp_int_[2] - 280 * y_int_[0] * y_int_[2] + 1.81 * y_int_[1]; - - return 0; - } - - int evaluateExternalResidual() - { - // Externals - *f_ext_[0] += -0.02 * *y_ext_[0]; - *f_ext_[1] += -0.045 * *y_ext_[1] + 0.43 * y_int_[0] + 0.43 * y_int_[1]; - - return 0; - } - - int evaluateJacobian() - { - this->zeroJacMatrix(); - - // Internal Jacobian Entries [row 1] - std::vector row = {2, 2, 2, 2, 2}; - std::vector col = {2, 3, 4, 0, 1}; - std::vector val = {-280 * y_int_[2] - 0.43 - alpha_, 0.69, -280 * y_int_[0], 0.69, 1.71}; - - this->setJacValues(row, col, val); - - // Internal Jacobian Entries [row 2] - row = {3, 3, 3}; - col = {2, 3, 4}; - val = {280 * y_int_[2], -1.81 - alpha_, 280 * y_int_[0]}; - - this->setJacValues(row, col, val); - - // Internal Jacobian Entries [row 3] - row = {4, 4, 4}; - col = {2, 3, 4}; - val = {-280 * y_int_[2], 1.81, -280 * y_int_[0] - alpha_}; - - this->setJacValues(row, col, val); - - // External Jacobian Entries - row = {0, 1, 1, 1}; - col = {0, 2, 3, 1}; - val = {-0.02, 0.43, 0.43, -0.045}; - - this->setJacValues(row, col, val); - - return 0; - } - - int evaluateIntegrand() - { - return 0; - } - - int initializeAdjoint() - { - return 0; - } - - int evaluateAdjointResidual() - { - return 0; - } - - int evaluateAdjointIntegrand() - { - return 0; - } - - /** - * @brief Compute the absolute tolerance for each variable in the model - * - * @param rel_tol The relative tolerance which can be used to pick the - * absolute tolerance. - * @tparam ScalarT Scalar data type - * @tparam IdxT Index data type - * @return int 0 if successful, non-zero otherwise. - * - * This represents a "noise" level close to zero for which pure relative - * error cannot be used. - */ - int setAbsoluteTolerance(RealT rel_tol) - { - abs_tol_.setToConst(static_cast(rel_tol)); - return 0; - } - - private: - NodeT* node_ref_; - }; -} // namespace GridKit diff --git a/tests/UnitTests/PowerElectronics/CMakeLists.txt b/tests/UnitTests/PowerElectronics/CMakeLists.txt index 74d50eb1e..88a0bc7f6 100644 --- a/tests/UnitTests/PowerElectronics/CMakeLists.txt +++ b/tests/UnitTests/PowerElectronics/CMakeLists.txt @@ -3,6 +3,26 @@ target_link_libraries( test_power_electronics_node PRIVATE GridKit::power_electronics_circuit_node GridKit::testing) +add_executable(test_subsystem_model_with_hires runSubsystemModelWithHiresTest.cpp) +target_link_libraries( + test_subsystem_model_with_hires + PRIVATE GridKit::power_elec_partition_interfaces + GridKit::testing + GridKit::sparse_matrix) + +add_executable(test_power_electronics_component_clone runComponentCloneTests.cpp) +target_link_libraries( + test_power_electronics_component_clone + PRIVATE GridKit::power_elec_disgen + GridKit::power_elec_microline + GridKit::power_elec_microload + GridKit::power_elec_microbusdq + GridKit::testing) + add_test(NAME PowerElectronicsNodeTest COMMAND $) +add_test(NAME SubsystemModelWithHires COMMAND $) +add_test(NAME PowerElectronicsComponentCloneTest COMMAND $) install(TARGETS test_power_electronics_node RUNTIME DESTINATION bin) +install(TARGETS test_subsystem_model_with_hires RUNTIME DESTINATION bin) +install(TARGETS test_power_electronics_component_clone RUNTIME DESTINATION bin) diff --git a/tests/UnitTests/PowerElectronics/README.md b/tests/UnitTests/PowerElectronics/README.md new file mode 100644 index 000000000..9d03909d4 --- /dev/null +++ b/tests/UnitTests/PowerElectronics/README.md @@ -0,0 +1,139 @@ +## HIRES Partitioning Test Problem + +The HIRES test problem is a simple ODE system with eight variables. To +demonstrate the partitioning machinery in GridKit, the system is divided into +three components. Equations 1--3 belong to **Component 1**, equations 4--5 +belong to **Component 2**, which is modeled as a bus, and equations 6--8 belong +to **Component 3**. + +> **Note:** HIRES is not a circuit problem. In this example, it is modeled to +> resemble GridKit circuit components so that the existing partitioning +> machinery can be used. The example also provides a simple test problem for +> verifying the order of co-simulation methods. + +The full HIRES system is + +$$ +\begin{aligned} +f_1 &= \frac{dy_1}{dt} +1.71y_1 -0.43y_2 -8.32y_3 -0.0007, \\ +f_2 &= \frac{dy_2}{dt} -1.71y_1 +8.75y_2, \\ +f_3 &= \frac{dy_3}{dt} +10.03y_3 -0.43y_4 -0.035y_5, \\ +f_4 &= \frac{dy_4}{dt} -8.32y_2 -1.71y_3 +1.12y_4, \\ +f_5 &= \frac{dy_5}{dt} +1.745y_5 -0.43y_6 -0.43y_7, \\ +f_6 &= \frac{dy_6}{dt} +280y_6y_8 -0.69y_4 -1.71y_5 + +0.43y_6 -0.69y_7, \\ +f_7 &= \frac{dy_7}{dt} -280y_6y_8 +1.81y_7, \\ +f_8 &= \frac{dy_8}{dt} +280y_6y_8 -1.81y_7. +\end{aligned} +$$ + +For the component representation, equations $f_4$ and $f_5$ are decomposed +to expose the contributions from each component: + +$$ +\begin{aligned} +f_4 +&= \frac{dy_4}{dt} -8.32y_2 -1.71y_3 +1.12y_4 \qquad +&\longrightarrow +\left(\frac{dy_4}{dt}+y_4\right) ++\left(0.1y_4-8.32y_2-1.71y_3\right) ++0.02y_4, +\\[6pt] +f_5 +&= \frac{dy_5}{dt}+1.745y_5-0.43y_6-0.43y_7 \qquad +&\longrightarrow +\left(\frac{dy_5}{dt}+y_5\right) ++0.7y_5 ++\left[0.045y_5-0.43y_6-0.43y_7\right]. +\end{aligned} +$$ + +The decomposition does not change the HIRES system. It separates the terms in +the bus equations (conviniently choosen to be equation 4 and 5) according to the component responsible for each contribution. + +### Component 1 + +Component 1 has three internal equations: + +$$ +\begin{aligned} +f_1 &= \frac{dy_1}{dt} +1.71y_1 -0.43y_2 -8.32y_3 -0.0007, \\ +f_2 &= \frac{dy_2}{dt} -1.71y_1 +8.75y_2, \\ +f_3 &= \frac{dy_3}{dt} +10.03y_3 -0.43y_4 -0.035y_5. +\end{aligned} +$$ + +It also contributes the following terms to the bus equations as its external contribution: + +$$ +\begin{aligned} +f_4^{(1)} &= 0.1y_4 -8.32y_2 -1.71y_3, \\ +f_5^{(1)} &= 0.7y_5. +\end{aligned} +$$ + +### Component 2 (HiresBus) + +Component 2 represents the bus and owns the following contributions to +equations 4 and 5: + +$$ +\begin{aligned} +f_4^{(2)} &= \frac{dy_4}{dt} + y_4, \\ +f_5^{(2)} &= \frac{dy_5}{dt} + y_5. +\end{aligned} +$$ + +The remaining terms in these equations are supplied by the components +connected to the bus. + +### Component 3 + +Component 3 has three internal equations: + +$$ +\begin{aligned} +f_6 &= \frac{dy_6}{dt} -280y_6y_8 +0.69y_4 +1.71y_5 + -0.43y_6 +0.69y_7, \\ +f_7 &= \frac{dy_7}{dt} +280y_6y_8 -1.81y_7, \\ +f_8 &= \frac{dy_8}{dt} -280y_6y_8 +1.81y_7. +\end{aligned} +$$ + +It also contributes the following terms to the bus equations as its external contribution: + +$$ +\begin{aligned} +f_4^{(3)} &= 0.02y_4, \\ +f_5^{(3)} &= 0.045y_5 -0.43y_6 -0.43y_7. +\end{aligned} +$$ + +### Bus Residual Assembly + +The complete bus residuals are obtained by adding the contributions from +Components 1, 2, and 3: + +$$ +f_4 = f_4^{(1)} + f_4^{(2)} + f_4^{(3)}, +$$ + +$$ +f_5 = f_5^{(1)} + f_5^{(2)} + f_5^{(3)}. +$$ + +This reconstruction gives exactly the corresponding equations in the original +HIRES system. + +### Partitioning + +The full HIRES system in component form looks like this: + +```text +Component 1 -------- Component 2 (HiresBus) -------- Component 3 +``` + +The system is divided between **Component 2 (HiresBus)** and **Component 3**, and +a `BusPartitionInterface` is added to the first partition. The resulting +partition residuals are then evaluated independently and are then compared with the full-system residual to verify +that the partitioned evaluation reproduces the original system. \ No newline at end of file diff --git a/tests/UnitTests/PowerElectronics/SubsystemModelWithHiresTest.hpp b/tests/UnitTests/PowerElectronics/SubsystemModelWithHiresTest.hpp new file mode 100644 index 000000000..9b30c2785 --- /dev/null +++ b/tests/UnitTests/PowerElectronics/SubsystemModelWithHiresTest.hpp @@ -0,0 +1,734 @@ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace GridKit +{ + + /*! + * @brief Hires Component 1 class. + * + */ + template + class HiresComponent1 : public CircuitComponent + { + using RealT = typename CircuitComponent::RealT; + using NodeT = typename PowerElectronics::NodeBase; + + using CircuitComponent::size_; + using CircuitComponent::nnz_; + using CircuitComponent::alpha_; + using CircuitComponent::y_ext_; + using CircuitComponent::y_int_; + using CircuitComponent::yp_ext_; + using CircuitComponent::yp_int_; + using CircuitComponent::abs_tol_; + using CircuitComponent::f_ext_; + using CircuitComponent::f_int_; + using CircuitComponent::idc_; + + using CircuitComponent::extern_indices_; + using CircuitComponent::n_extern_; + using CircuitComponent::n_intern_; + + public: + HiresComponent1(NodeT* bus, IdxT id) + : node_ref_(bus) + { + size_ = 5; + n_intern_ = 3; + n_extern_ = 2; + extern_indices_ = {0, 1}; + idc_ = id; + nnz_ = 12; + } + + ~HiresComponent1() + { + } + + int allocate() final + { + CircuitComponent::allocate(); + + this->setExternalConnectionNodes(0, node_ref_->getNodeConnection(0)); + this->setExternalConnectionNodes(1, node_ref_->getNodeConnection(1)); + + return 0; + } + + int initialize() final + { + return 0; + } + + int tagDifferentiable() final + { + return 0; + } + + int evaluateInternalResidual() final + { + // Internals + f_int_[0] = -yp_int_[0] - 1.71 * y_int_[0] + 0.43 * y_int_[1] + 8.32 * y_int_[2] + 0.0007; + f_int_[1] = -yp_int_[1] + 1.71 * y_int_[0] - 8.75 * y_int_[1]; + f_int_[2] = -yp_int_[2] - 10.03 * y_int_[2] + 0.43 * *y_ext_[0] + 0.035 * *y_ext_[1]; + + return 0; + } + + int evaluateExternalResidual() + { + // outputs + *f_ext_[0] += 8.32 * y_int_[1] + 1.71 * y_int_[2] - 0.1 * *y_ext_[0]; + *f_ext_[1] += -0.7 * *y_ext_[1]; + + return 0; + } + + int evaluateJacobian() final + { + + this->zeroJacMatrix(); + + // Internal Jacobian Entries + std::vector row = {2, 2, 2, 3, 3, 4, 4, 4}; + std::vector col = {2, 3, 4, 2, 3, 4, 0, 1}; + std::vector val = {-1.71 - alpha_, 0.43, 8.32, 1.71, -8.75 - alpha_, -10.03 - alpha_, 0.43, 0.035}; + + this->setJacValues(row, col, val); + + // External Jacobian Entries + row = {0, 0, 0, 1}; + col = {3, 4, 0, 1}; + val = {8.32, 1.71, -0.1, -0.7}; + + this->setJacValues(row, col, val); + + return 0; + } + + int evaluateIntegrand() final + { + return 0; + } + + int initializeAdjoint() final + { + return 0; + } + + int evaluateAdjointResidual() final + { + return 0; + } + + int evaluateAdjointIntegrand() final + { + return 0; + } + + /** + * @brief Compute the absolute tolerance for each variable in the model + */ + int setAbsoluteTolerance(RealT rel_tol) final + { + abs_tol_.setToConst(static_cast(rel_tol)); + return 0; + } + + private: + NodeT* node_ref_; + }; + + /*! + * @brief Hires Bus Component (Component 2). + * + */ + template + class HiresBus : public CircuitComponent + { + + using RealT = typename CircuitComponent::RealT; + using NodeT = typename PowerElectronics::NodeBase; + + using CircuitComponent::size_; + using CircuitComponent::nnz_; + using CircuitComponent::alpha_; + using CircuitComponent::y_ext_; + using CircuitComponent::y_int_; + using CircuitComponent::yp_ext_; + using CircuitComponent::yp_int_; + using CircuitComponent::abs_tol_; + using CircuitComponent::f_ext_; + using CircuitComponent::f_int_; + using CircuitComponent::idc_; + + using CircuitComponent::extern_indices_; + using CircuitComponent::n_extern_; + using CircuitComponent::n_intern_; + + public: + HiresBus(NodeT* bus, IdxT id) + : node_ref_(bus) + { + size_ = 2; + n_intern_ = 0; + n_extern_ = 2; + extern_indices_ = {0, 1}; + idc_ = id; + nnz_ = 2; + } + + ~HiresBus() + { + } + + int allocate() final + { + CircuitComponent::allocate(); + + this->setExternalConnectionNodes(0, node_ref_->getNodeConnection(0)); + this->setExternalConnectionNodes(1, node_ref_->getNodeConnection(1)); + + return 0; + } + + int initialize() final + { + return 0; + } + + int tagDifferentiable() final + { + return 0; + } + + int evaluateInternalResidual() final + { + return 0; + } + + int evaluateExternalResidual() final + { + *f_ext_[0] += -*yp_ext_[0] - *y_ext_[0]; + *f_ext_[1] += -*yp_ext_[1] - *y_ext_[1]; + + return 0; + } + + int evaluateJacobian() final + { + this->zeroJacMatrix(); + + std::vector row = {0, 1}; + std::vector col = {0, 1}; + std::vector val = {-alpha_ - 1.0, -alpha_ - 1.0}; + + this->setJacValues(row, col, val); + + return 0; + } + + int evaluateIntegrand() final + { + return 0; + } + + int initializeAdjoint() final + { + return 0; + } + + int evaluateAdjointResidual() final + { + return 0; + } + + int evaluateAdjointIntegrand() final + { + return 0; + } + + /** + * @brief Compute the absolute tolerance for each variable in the model + */ + int setAbsoluteTolerance(RealT rel_tol) final + { + abs_tol_.setToConst(static_cast(rel_tol)); + return 0; + } + + private: + NodeT* node_ref_; + }; + + /*! + * @brief Hires Component 3 class. + * + */ + template + class HiresComponent3 : public CircuitComponent + { + using RealT = typename CircuitComponent::RealT; + using NodeT = typename PowerElectronics::NodeBase; + + using CircuitComponent::size_; + using CircuitComponent::nnz_; + using CircuitComponent::alpha_; + using CircuitComponent::y_ext_; + using CircuitComponent::y_int_; + using CircuitComponent::yp_ext_; + using CircuitComponent::yp_int_; + using CircuitComponent::abs_tol_; + using CircuitComponent::f_ext_; + using CircuitComponent::f_int_; + using CircuitComponent::idc_; + + using CircuitComponent::extern_indices_; + using CircuitComponent::n_extern_; + using CircuitComponent::n_intern_; + + public: + HiresComponent3(NodeT* bus, IdxT id) + : node_ref_(bus) + { + size_ = 5; + n_intern_ = 3; + n_extern_ = 2; + extern_indices_ = {0, 1}; + idc_ = id; + nnz_ = 15; + } + + ~HiresComponent3() + { + } + + int allocate() + { + CircuitComponent::allocate(); + + this->setExternalConnectionNodes(0, node_ref_->getNodeConnection(0)); + this->setExternalConnectionNodes(1, node_ref_->getNodeConnection(1)); + + return 0; + } + + int initialize() + { + return 0; + } + + int tagDifferentiable() + { + return 0; + } + + int evaluateInternalResidual() + { + + // Internals + f_int_[0] = -yp_int_[0] - 280 * y_int_[0] * y_int_[2] + 0.69 * *y_ext_[0] + 1.71 * *y_ext_[1] - 0.43 * y_int_[0] + 0.69 * y_int_[1]; + f_int_[1] = -yp_int_[1] + 280 * y_int_[0] * y_int_[2] - 1.81 * y_int_[1]; + f_int_[2] = -yp_int_[2] - 280 * y_int_[0] * y_int_[2] + 1.81 * y_int_[1]; + + return 0; + } + + int evaluateExternalResidual() + { + // Externals + *f_ext_[0] += -0.02 * *y_ext_[0]; + *f_ext_[1] += -0.045 * *y_ext_[1] + 0.43 * y_int_[0] + 0.43 * y_int_[1]; + + return 0; + } + + int evaluateJacobian() + { + this->zeroJacMatrix(); + + // Internal Jacobian Entries [row 1] + std::vector row = {2, 2, 2, 2, 2}; + std::vector col = {2, 3, 4, 0, 1}; + std::vector val = {-280 * y_int_[2] - 0.43 - alpha_, 0.69, -280 * y_int_[0], 0.69, 1.71}; + + this->setJacValues(row, col, val); + + // Internal Jacobian Entries [row 2] + row = {3, 3, 3}; + col = {2, 3, 4}; + val = {280 * y_int_[2], -1.81 - alpha_, 280 * y_int_[0]}; + + this->setJacValues(row, col, val); + + // Internal Jacobian Entries [row 3] + row = {4, 4, 4}; + col = {2, 3, 4}; + val = {-280 * y_int_[2], 1.81, -280 * y_int_[0] - alpha_}; + + this->setJacValues(row, col, val); + + // External Jacobian Entries + row = {0, 1, 1, 1}; + col = {0, 2, 3, 1}; + val = {-0.02, 0.43, 0.43, -0.045}; + + this->setJacValues(row, col, val); + + return 0; + } + + int evaluateIntegrand() + { + return 0; + } + + int initializeAdjoint() + { + return 0; + } + + int evaluateAdjointResidual() + { + return 0; + } + + int evaluateAdjointIntegrand() + { + return 0; + } + + /** + * @brief Compute the absolute tolerance for each variable in the model + * + */ + int setAbsoluteTolerance(RealT rel_tol) + { + abs_tol_.setToConst(static_cast(rel_tol)); + return 0; + } + + private: + NodeT* node_ref_; + }; + + namespace Testing + { + + /** + * This example assembles the HIRES problem and partitions it to demonstrate + * that the partitioned system can reconstruct the full system. + * + * The HIRES problem consists of eight ODE equations and is divided into three + * components. HiresComponent1 contains equations 1, 2, and 3, + * HiresComponent3 contains equations 6, 7, and 8, and HiresBus contains + * equations 4 and 5. HiresBus plays a role similar to MicrogridBusDQ, where + * other components connected to the bus contribute terms to its equations. + * + * In the equations below, terms enclosed in parentheses ( ) are contributions + * from HiresComponent1, terms enclosed in square brackets [ ] are contributions + * from HiresComponent3, and the remaining terms in equations 4 and 5 belong + * to HiresBus. + *\f[ + * f_1 = dy_1/dt + 1.71y_1 - 0.43y_2 - 8.32y_3 - 0.0007 + * + * f_2 = dy_2/dt - 1.71y_1 + 8.75y_2 + * + * f_3 = dy_3/dt + 10.03y_3 - 0.43y_4 - 0.035y_5 + * + * f_4 = dy_4/dt + y_4 + (0.1y_4 - 8.32y_2 - 1.71y_3) + [0.02y_4] + * + * f_5 = dy_5/dt + y_5 + \left(0.7y_5 \right) + \left[0.045y_5 - 0.43y_6 - 0.43y_7 \right] + * + * f_6 = dy_6/dt - 280y_6y_8 + 0.69y_4 + 1.71y_5 - 0.43y_6 + 0.69y_7 + * + * f_7 = dy_7/dt + 280y_6y_8 - 1.81y_7 + * + * f_8 = dy_8/dt - 280y_6y_8 + 1.81y_7 + *\f] + * + * The assembled system has the following structure: + * + * (HiresComponent1) -------- (HiresBus) -------- (HiresComponent3) + * + * The system is partitioned between HiresBus and HiresComponent3. A bus + * partition interface is introduced in the partition containing HiresBus to + * preserve the contribution of HiresComponent3 to the bus equations. The + * residuals evaluated independently by the partitions are then printed with + * the residual of the full system for eye-ball comparision. + * + * This example will also be useful later for testing the order of accuracy of co-simulation methods. + */ + template + class SubsystemModelWithHiresTests + { + using RealT = typename CircuitComponent::RealT; + using Bus = PowerElectronics::MicrogridBus; + using Subsystem = SubsystemModel; + using System = PowerElectronicsModel; + + public: + /** + * @brief Construct the HIRES reference system and subsystem partitions. + */ + SubsystemModelWithHiresTests() + : system_(new System()), + partition1_(new Subsystem()), + partition2_(new Subsystem()), + comp1_(new HiresComponent1(&bus_, 1)), + bus1_(new HiresBus(&bus_, 2)), + comp3_(new HiresComponent3(&bus_, 3)) + { + + y_ = {1, 2, 3, 4, 5, 6, 7, 8}; + yp_ = {1, 2, 3, 4, 5, 6, 7, 8}; + // --------------------------------------------------------------------- + // Assemble and allocate the monolithic reference system + // --------------------------------------------------------------------- + + system_->addComponent(comp1_); + system_->addComponent(comp3_); + system_->addComponent(bus1_); + system_->addNode(&bus_); + + system_->allocate(); + + distributeVariables(y_, yp_); + system_->updateTime(1.0, 2.0); + system_->evaluateResidual(); + + // --------------------------------------------------------------------- + // Construct the partition interface + // --------------------------------------------------------------------- + + auto* comp3_copy = new HiresComponent3(*comp3_); + bus_interface_ = new BusPartitionInterface(&bus_, comp3_copy, 4); + + bus_interface_->allocate(); + + // --------------------------------------------------------------------- + // Assemble the subsystem partitions + // --------------------------------------------------------------------- + + partition1_->addComponent(comp1_); + partition1_->addComponent(bus1_); + partition1_->addInterface(bus_interface_); + partition1_->addNode(&bus_); + + partition2_->addComponent(comp3_); + + partition1_->allocate(); + partition2_->allocate(); + partitions_ = {partition1_, partition2_}; + } + + ~SubsystemModelWithHiresTests() + { + delete partition1_; + delete partition2_; + delete system_; + } + + void distributeVariables(const std::vector& y, const std::vector& yp) + { + auto* system_y = system_->y().getData(); + auto* system_yp = system_->yp().getData(); + + for (size_t i = 0; i < system_->size(); ++i) + { + system_y[i] = y[i]; + system_yp[i] = yp[i]; + } + + system_->y().setDataUpdated(); + system_->yp().setDataUpdated(); + + for (auto* partition : partitions_) + { + for (size_t i = 0; i < partition->getExternSize(); ++i) + { + const auto global_index = partition->getExternalDataIndices()[i]; + + partition->getExternalDataY()[i] = y[global_index]; + partition->getExternalDataYP()[i] = yp[global_index]; + } + + auto* partition_y = partition->y().getData(); + auto* partition_yp = partition->yp().getData(); + + for (size_t i = 0; i < partition->getInternalSize(); ++i) + { + const auto global_index = partition->getNodeConnection(i); + + partition_y[i] = y[global_index]; + partition_yp[i] = yp[global_index]; + } + + partition->y().setDataUpdated(); + partition->yp().setDataUpdated(); + } + } + + /** + * @brief Verify that the partitioned HIRES residual matches the + * monolithic residual. + */ + TestOutcome residual() + { + TestStatus success = true; + + // Distribute variables to all partitions + distributeVariables(y_, yp_); + + std::vector partition_residual(system_->size(), 0.0); + + for (auto* partition : partitions_) + { + partition->updateTime(1.0, 2.0); + partition->evaluateResidual(); + + auto* residual = partition->getResidual().getData(); + + for (size_t i = 0; i < partition->getInternalSize(); ++i) + { + partition_residual[partition->getNodeConnection(i)] = residual[i]; + } + + partition->getResidual().setDataUpdated(); + } + + auto* reference_residual = system_->getResidual().getData(); + + RealT max_error = 0.0; + + for (size_t i = 0; i < system_->size(); ++i) + { + double error = std::abs(partition_residual[i] - reference_residual[i]) / std::abs(reference_residual[i] + 1); + max_error = std::max(max_error, error); + } + + std::cout << "max error " << max_error << std::endl; + + success *= max_error <= std::numeric_limits::epsilon(); + + return success.report(__func__); + } + + /** + * @brief Verify that each subsystem Jacobian matches the corresponding + * entries of the monolithic HIRES Jacobian. + */ + TestOutcome jacobian() + { + TestStatus success = true; + + RealT alpha = 2.0; + + distributeVariables(y_, yp_); + + /* + * GridKit stores the HIRES variables in component assembly order: + * + * System index: 0 1 2 3 4 5 6 7 + * HIRES index: 0 1 2 5 6 7 3 4 + * Variable: y1 y2 y3 y6 y7 y8 y4 y5 + */ + const std::array sysmodel_to_hires = { + 0, 1, 2, 5, 6, 7, 3, 4}; + + const std::array hires_to_sysmodel = { + 0, 1, 2, 6, 7, 3, 4, 5}; + + const RealT y6 = y_[hires_to_sysmodel[5]]; + const RealT y8 = y_[hires_to_sysmodel[7]]; + + std::array, 8> reference_jac = + {{{-alpha - 1.71, 0.43, 8.32, 0.0, 0.0, 0.0, 0.0, 0.0}, + {1.71, -alpha - 8.75, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {0.0, 0.0, -alpha - 10.03, 0.43, 0.035, 0.0, 0.0, 0.0}, + {0.0, 8.32, 1.71, -alpha - 1.12, 0.0, 0.0, 0.0, 0.0}, + {0.0, 0.0, 0.0, 0.0, -alpha - 1.745, 0.43, 0.43, 0.0}, + {0.0, 0.0, 0.0, 0.69, 1.71, -alpha - 280.0 * y8 - 0.43, 0.69, -280.0 * y6}, + {0.0, 0.0, 0.0, 0.0, 0.0, 280.0 * y8, -alpha - 1.81, 280.0 * y6}, + {0.0, 0.0, 0.0, 0.0, 0.0, -280.0 * y8, 1.81, -alpha - 280.0 * y6}}}; + + for (auto* partition : partitions_) + { + partition->updateTime(1.0, alpha); + partition->evaluateJacobian(); + + auto* partition_jac = partition->getCsrJacobian(); + + const auto* row_ptr = partition_jac->getRowData(); + const auto* cols = partition_jac->getColData(); + const auto* vals = partition_jac->getValues(); + + const size_t n = partition->getInternalSize(); + + std::vector> dense_jac(n, std::vector(n, 0.0)); + + // Convert the partition CSR Jacobian to a dense matrix. + for (size_t row = 0; row < n; ++row) + { + for (IdxT k = row_ptr[row]; k < row_ptr[row + 1]; ++k) + { + dense_jac[row][cols[k]] = vals[k]; + } + } + + // Compare with the corresponding entries of the full Jacobian. + for (size_t row = 0; row < n; ++row) + { + const IdxT global_row = partition->getNodeConnection(row); + const IdxT ref_row = sysmodel_to_hires[global_row]; + + for (size_t col = 0; col < n; ++col) + { + const IdxT global_col = partition->getNodeConnection(col); + const IdxT ref_col = sysmodel_to_hires[global_col]; + + success *= std::abs(dense_jac[row][col] - reference_jac[ref_row][ref_col]) <= std::numeric_limits::epsilon(); + } + } + } + + return success.report(__func__); + } + + private: + Bus bus_; + + System* system_; + + Subsystem* partition1_; + Subsystem* partition2_; + + HiresComponent1* comp1_; + HiresBus* bus1_; + HiresComponent3* comp3_; + + BusPartitionInterface* bus_interface_; + + std::vector partitions_; + + std::vector y_; + std::vector yp_; + }; + + } // namespace Testing + +} // namespace GridKit diff --git a/tests/UnitTests/PowerElectronics/runSubsystemModelWithHiresTest.cpp b/tests/UnitTests/PowerElectronics/runSubsystemModelWithHiresTest.cpp new file mode 100644 index 000000000..2b8ef11b0 --- /dev/null +++ b/tests/UnitTests/PowerElectronics/runSubsystemModelWithHiresTest.cpp @@ -0,0 +1,12 @@ +#include "SubsystemModelWithHiresTest.hpp" + +int main() +{ + GridKit::Testing::TestingResults result; + GridKit::Testing::SubsystemModelWithHiresTests test; + + result += test.residual(); + result += test.jacobian(); + + return result.summary(); +} From 7628dc52d99dba1b7fd20e46ca6d13eb1114321d Mon Sep 17 00:00:00 2001 From: abdourahmanbarry Date: Sat, 15 Aug 2026 13:48:21 +0000 Subject: [PATCH 45/52] Add more documentation for SubsystemModel - Add comments to methods and lambda functions - added check to ensure only allocated components can be added to model - Change parameter type in addInterface method to PartitionInterface --- .../Model/PowerElectronics/SubsystemModel.hpp | 204 +++++++++++++----- 1 file changed, 147 insertions(+), 57 deletions(-) diff --git a/GridKit/Model/PowerElectronics/SubsystemModel.hpp b/GridKit/Model/PowerElectronics/SubsystemModel.hpp index 9d64b76c3..1d222b94e 100644 --- a/GridKit/Model/PowerElectronics/SubsystemModel.hpp +++ b/GridKit/Model/PowerElectronics/SubsystemModel.hpp @@ -15,12 +15,35 @@ #include #include #include +#include #include #include namespace GridKit { + /** + * @brief Represents a subset of a PowerElectronicsModel that can be evaluated + * independently. + * + * A SubsystemModel contains a collection of existing GridKit components and + * nodes taken from a larger system. Variables owned by those components and + * nodes become internal variables of the subsystem. Variables needed by those + * components but owned outside the subsystem become external coupling + * variables. + * + * Components normally store connection indices in the global system indexing. + * During subsystem allocation, these indices are temporarily replaced with a + * contiguous local subsystem indexing so that the subsystem can be evaluated + * like an independent PowerElectronicsModel. + * + * External coupling values must be supplied before residual or Jacobian + * evaluation, either directly through the external-data vectors or through a + * forcing function. + * + * @tparam ScalarT Scalar type used by the model. + * @tparam IdxT Index type used for variable and connection indices. + */ template class SubsystemModel : public PowerElectronicsModel { @@ -34,6 +57,7 @@ namespace GridKit using CsrMatrixT = typename CircuitComponent::CsrMatrixT; using component_type = CircuitComponent; using node_type = PowerElectronics::NodeBase; + using interface_type = PartitionInterface; using SystemModel::abs_tol_; using SystemModel::allocated_; @@ -93,14 +117,29 @@ namespace GridKit nodes_.clear(); } + /** + * @brief Allocate the subsystem for independent evaluation. + * + * Converts the selected components and nodes from global system indexing to + * local subsystem indexing, allocates storage for the subsystem's internal + * variables, and creates storage for coupling variables that lie outside the + * subsystem. + * + * Component pointers are then redirected to either the subsystem's internal + * vectors or its external coupling-data vectors. Finally, the subsystem + * Jacobian structure is assembled using only entries whose row and column + * belong to internal subsystem variables. + * + * @pre Components and nodes added to the subsystem must already be allocated + * by the parent system. + * + * @post Component and node connection indices use local subsystem indexing, + * and the subsystem is ready for residual and Jacobian evaluation. + * + * @return 0 on success, otherwise an error code returned during allocation. + */ int allocate() override { - - for (auto* comp : interfaces_) - { - comp->allocate(); - } - if (int err = hold()) { return err; @@ -176,7 +215,9 @@ namespace GridKit { const IdxT connection_index = comp->getNodeConnection(local_index); - // External to the component but internal to the subsystem. + // A variable can be external from the component's point of view while still + // being owned by this subsystem. In that case, connect the component directly + // to the corresponding entry in the subsystem's internal vectors. if (connection_index < n_intern_) { ExternalConnection connection{ @@ -190,7 +231,8 @@ namespace GridKit continue; } - // External to both the component and subsystem. + // Otherwise the variable is owned outside this subsystem. Connect the + // component to the subsystem's external coupling-data storage instead. const IdxT external_offset = connection_index - static_cast(n_intern_); ExternalConnection connection{ @@ -217,6 +259,9 @@ namespace GridKit component->evaluateJacobian(); } + // Check whether a Jacobian entry belongs to the subsystem Jacobian. + // Only entries whose row and column are both internal subsystem + // variables are retained. auto isValidEntry = [this](IdxT row, IdxT col) { if (row == neg1_ || col == neg1_) @@ -316,11 +361,18 @@ namespace GridKit } /** - * @brief Distribute y and y' to each component based of node connection graph + * @brief Update the subsystem external state and derivative data. * - * @post Each component has y and y' set + * If a forcing function is provided, evaluate it at the current subsystem + * time and copy the returned coupling values into the external state vectors. * - * @return int 0 if successful, positive if there's a recoverable error, negative if unrecoverable + * @post y_ext_data_ and yp_ext_data_ contain the external coupling values + * returned by the forcing function, if one is set. + * + * @throws std::runtime_error If the forcing function returns vectors whose + * sizes do not match the subsystem external-data vectors. + * + * @return 0 on success. */ int distributeExternalVectors() { @@ -387,6 +439,12 @@ namespace GridKit */ void addComponent(component_type* component) { + if (!component->isAllocated()) + { + throw std::logic_error( + "SubsystemModel::addComponent: cannot add an unallocated component."); + } + if (connections_are_local_) { throw std::logic_error( @@ -408,6 +466,12 @@ namespace GridKit */ void addNode(node_type* node) { + if (!node->isAllocated()) + { + throw std::logic_error( + "SubsystemModel::addNode: cannot add an unallocated node."); + } + if (connections_are_local_) { throw std::logic_error( @@ -426,22 +490,27 @@ namespace GridKit * * @param component Pointer to the interface component to add. */ - void addInterface(component_type* component) + void addInterface(interface_type* component) { addComponent(component); interfaces_.push_back(component); } /** - * @brief Acquire the subsystem's global-to-local index mappings. + * @brief Prepare the subsystem topology for local evaluation. * - * Builds the internal/external index maps via buildIndexMappings(), - * then converts component/node connections from global to local - * indices via mapGlobalToLocal(). + * Components and nodes initially contain the same global connection indices + * assigned by the parent system. This method determines which of those + * variables belong to the subsystem and which are external coupling variables, + * assigns each a local subsystem index, and replaces the stored global + * connection indices with those local indices. * - * Call this after modifying subsystem topology (adding or removing - * components/nodes), to (re)establish local - * indexing. Pairs with release(), which undoes this. + * This local indexing is used while the subsystem is allocated and evaluated. + * release() restores the original global indices. + * + * @pre Component and node connections use global system indices. + * + * @post Component and node connections use local subsystem indices. * * @return int 0 if successful, positive if there's a recoverable error, negative if unrecoverable */ @@ -458,17 +527,17 @@ namespace GridKit } /** - * @brief Release the subsystem's global-to-local index mappings. + * @brief Restore the subsystem topology to global system indexing. * - * If components/nodes currently hold local indices, they are first - * converted back to their original global indices via - * mapLocalToGlobal(). The internal/external index maps built by - * buildIndexMappings() are then cleared, and the model is marked as - * not allocated. + * Reverses hold() by replacing local subsystem connection indices with the + * original global system indices. The local internal/external mappings are + * then cleared so that components or nodes can safely be added or removed. * + * @pre Component and node connections may use local subsystem indices. * - * Call this before modifying subsystem topology (adding or removing - * components/nodes), so stale local indices aren't left dangling. + * @post Component and node connections use their original global indices, + * the subsystem mappings are cleared, and the subsystem is marked + * unallocated. * * @return int 0 if successful, positive if there's a recoverable error, negative if unrecoverable */ @@ -507,7 +576,7 @@ namespace GridKit return f_ext_data_; } - void setTimeFunction(TimeFunction function) + void setForcingFunction(TimeFunction function) { forcing_function_ = std::move(function); } @@ -524,16 +593,20 @@ namespace GridKit private: /** - * @brief Replace local subsystem connection indices with their original global indices - * for every component and node. + * @brief Restore local subsystem connection indices to global system indices. + * + * Reverses mapGlobalToLocal(). Internal subsystem indices are translated + * through the subsystem connection-node table, while external subsystem + * indices are translated through external_data_indices_. + * + * The component/node connectivity is unchanged; only the index representation + * is restored. * - * Inverse of mapGlobalToLocal(). Internal connections (index < internal - * size) are mapped back through this subsystem's own node-connection - * table; external connections are mapped back through - * @c external_data_indices_. Entries equal to @c neg1_ represent no - * connection and are left unchanged. + * @pre Component and node connections use local subsystem indices. * - * @return Always returns 0. + * @post Component and node connections use their original global indices. + * + * @return 0 on success. */ int mapLocalToGlobal() { @@ -585,20 +658,21 @@ namespace GridKit } /** - * @brief Replace global connection indices with local subsystem indices for every component - * and node. + * @brief Replace global connection indices with subsystem-local indices. + * + * Uses the mappings created by buildIndexMappings() to rewrite the connection + * indices stored by every component and node. Variables owned by the subsystem + * use internal_map_; variables owned outside the subsystem use external_map_. * - * After the internal and external maps have been constructed, each component - * and node still stores the original global connection indices. This function - * traverses every connection and replaces each global index with its - * corresponding local subsystem index. + * This changes only the indexing used to identify connections; it does not + * change the physical component/node connectivity. * - * Internal connections are mapped using @c internal_map_, while connections - * that reference variables outside the subsystem are mapped using - * @c external_map_. Entries equal to @c neg1_ represent no connection and - * are left unchanged. + * @pre internal_map_ and external_map_ have been constructed from global + * connection indices. * - * @return Always returns 0. + * @post All valid component and node connections use local subsystem indices. + * + * @return 0 on success. */ int mapGlobalToLocal() { @@ -650,16 +724,24 @@ namespace GridKit } /** - * @brief Construct the mappings from global variable indices to local - * subsystem indices. + * @brief Build the global-to-local variable mappings for the subsystem. + * + * Examines the connection indices of all components and nodes in the + * subsystem and divides the referenced variables into two groups: * - * The subsystem stores its variables in a contiguous local ordering. This - * function assigns each global variable index a corresponding local index and - * separates them into internal and external variables. + * - Internal variables are owned by a component or node in this subsystem. + * - External variables are required by a subsystem component but are owned + * outside the subsystem. * - * Internal variables are those owned by this subsystem, while external - * variables are owned by neighboring subsystems but are required for residual - * and Jacobian evaluation. + * Internal variables receive local indices first. External coupling variables + * are then assigned indices immediately after the internal range. This gives + * every variable referenced by the subsystem a unique local index while + * preserving its original global index in the corresponding map. + * + * @pre Component and node connections use global system indices. + * + * @post internal_map_ and external_map_ contain the global-to-local mappings + * needed to convert the subsystem topology to local indexing. */ void buildIndexMappings() { @@ -673,7 +755,7 @@ namespace GridKit external_map_.clear(); size_t component_internal_idx = 0; - // First pass: Map global component indices to local subsystem indices. + // Pass 1: Add variables owned internally by subsystem components. for (component_type* comp : components_) { const auto& extern_indices = comp->getExternIndices(); @@ -689,6 +771,7 @@ namespace GridKit } } + // Pass 2: Add variables owned by subsystem nodes. for (node_type* node : nodes_) { @@ -703,6 +786,7 @@ namespace GridKit } } + // Pass 3: Add component dependencies that are owned outside the subsystem. for (component_type* comp : components_) { auto extern_indices = comp->getExternIndices(); @@ -722,10 +806,16 @@ namespace GridKit } } + // Global system index -> local subsystem index for subsystem internal variables std::unordered_map internal_map_; + + // Global system index -> local subsystem index for subsystem external variables std::unordered_map external_map_; - std::vector external_data_indices_; + // Global system index corresponding to each entry in the subsystem external state vectors + std::vector external_data_indices_; + + // subsystem external State, derivative, and residual vectors. std::vector y_ext_data_; std::vector yp_ext_data_; std::vector f_ext_data_; From f5c21341c113d82754ebd5fecad52782a1ed68af Mon Sep 17 00:00:00 2001 From: abdourahmanbarry Date: Sat, 15 Aug 2026 14:17:23 +0000 Subject: [PATCH 46/52] Add clone function to components in PowerElectronics --- .../PowerElectronics/Capacitor/Capacitor.cpp | 31 +++++++++++++++++++ .../PowerElectronics/Capacitor/Capacitor.hpp | 11 +++++-- .../PowerElectronics/CircuitComponent.hpp | 16 +++++++--- .../DistributedGenerator.cpp | 12 +++++++ .../DistributedGenerator.hpp | 25 ++++++++------- .../InductionMotor/InductionMotor.cpp | 31 +++++++++++++++++++ .../InductionMotor/InductionMotor.hpp | 11 +++++-- .../PowerElectronics/Inductor/Inductor.cpp | 12 +++++++ .../PowerElectronics/Inductor/Inductor.hpp | 9 ++++-- .../LinearTransformer/LinearTransformer.cpp | 31 +++++++++++++++++++ .../LinearTransformer/LinearTransformer.hpp | 11 +++++-- .../MicrogridBusDQ/MicrogridBusDQ.cpp | 12 +++++++ .../MicrogridBusDQ/MicrogridBusDQ.hpp | 9 ++++-- .../MicrogridLine/MicrogridLine.cpp | 12 +++++++ .../MicrogridLine/MicrogridLine.hpp | 9 ++++-- .../MicrogridLoad/MicrogridLoad.cpp | 12 +++++++ .../MicrogridLoad/MicrogridLoad.hpp | 9 ++++-- GridKit/Model/PowerElectronics/NodeBase.hpp | 10 ++++++ .../PowerElectronics/Resistor/Resistor.cpp | 12 +++++++ .../PowerElectronics/Resistor/Resistor.hpp | 9 ++++-- .../SynchronousMachine/SynchronousMachine.cpp | 31 +++++++++++++++++++ .../SynchronousMachine/SynchronousMachine.hpp | 11 +++++-- .../TransmissionLine/TransmissionLine.cpp | 31 +++++++++++++++++++ .../TransmissionLine/TransmissionLine.hpp | 11 +++++-- .../VoltageSource/VoltageSource.cpp | 12 +++++++ .../VoltageSource/VoltageSource.hpp | 9 ++++-- 26 files changed, 350 insertions(+), 49 deletions(-) diff --git a/GridKit/Model/PowerElectronics/Capacitor/Capacitor.cpp b/GridKit/Model/PowerElectronics/Capacitor/Capacitor.cpp index aab348679..c9c3d1150 100644 --- a/GridKit/Model/PowerElectronics/Capacitor/Capacitor.cpp +++ b/GridKit/Model/PowerElectronics/Capacitor/Capacitor.cpp @@ -52,6 +52,25 @@ namespace GridKit return 0; } + /** + * @brief Compute the absolute tolerance for each variable in the model + * + * @param rel_tol The relative tolerance which can be used to pick the + * absolute tolerance. + * @tparam ScalarT Scalar data type + * @tparam IdxT Index data type + * @return int 0 if successful, non-zero otherwise. + * + * This represents a "noise" level close to zero for which pure relative + * error cannot be used. + */ + template + int Capacitor::setAbsoluteTolerance(RealT rel_tol) + { + abs_tol_.setToConst(static_cast(rel_tol)); + return 0; + } + /** * @brief Evaluate the resisdual of the Capcitor * @@ -117,6 +136,18 @@ namespace GridKit return 0; } + template + bool Capacitor::isCloneable() const + { + return true; + } + + template + CircuitComponent* Capacitor::clone() const + { + return new Capacitor(*this); + } + // Available template instantiations template class Capacitor; template class Capacitor; diff --git a/GridKit/Model/PowerElectronics/Capacitor/Capacitor.hpp b/GridKit/Model/PowerElectronics/Capacitor/Capacitor.hpp index d7a131a9a..0b3353c47 100644 --- a/GridKit/Model/PowerElectronics/Capacitor/Capacitor.hpp +++ b/GridKit/Model/PowerElectronics/Capacitor/Capacitor.hpp @@ -29,6 +29,7 @@ namespace GridKit using CircuitComponent::y_int_; using CircuitComponent::yp_ext_; using CircuitComponent::yp_int_; + using CircuitComponent::abs_tol_; using CircuitComponent::tag_; using CircuitComponent::f_ext_; using CircuitComponent::f_int_; @@ -50,15 +51,19 @@ namespace GridKit int initialize(); int tagDifferentiable(); + int setAbsoluteTolerance(RealT); int evaluateInternalResidual() final; int evaluateExternalResidual() final; int evaluateJacobian(); int evaluateIntegrand(); - int initializeAdjoint(); - int evaluateAdjointResidual(); + int initializeAdjoint(); + int evaluateAdjointResidual(); // int evaluateAdjointJacobian(); - int evaluateAdjointIntegrand(); + int evaluateAdjointIntegrand(); + bool isCloneable() const; + + CircuitComponent* clone() const; private: RealT C_; diff --git a/GridKit/Model/PowerElectronics/CircuitComponent.hpp b/GridKit/Model/PowerElectronics/CircuitComponent.hpp index 3dacdb055..4dac238b1 100644 --- a/GridKit/Model/PowerElectronics/CircuitComponent.hpp +++ b/GridKit/Model/PowerElectronics/CircuitComponent.hpp @@ -149,6 +149,16 @@ namespace GridKit copyVector(param_lo_, other.param_lo_); } + virtual CircuitComponent* clone() const + { + return nullptr; + } + + virtual bool isCloneable() const + { + return false; + } + /** * @note Cannot be marked final, since it is overriden to recurse in the system model. */ @@ -275,11 +285,7 @@ namespace GridKit yp_ext_ = std::make_unique(static_cast(size_)); f_ext_ = std::make_unique(static_cast(size_)); - // Make the existing allocation code idempotent: - if (!connection_nodes_) - { - connection_nodes_ = std::make_unique(static_cast(size_)); - } + connection_nodes_ = std::make_unique(static_cast(size_)); if (!allocated_) { diff --git a/GridKit/Model/PowerElectronics/DistributedGenerator/DistributedGenerator.cpp b/GridKit/Model/PowerElectronics/DistributedGenerator/DistributedGenerator.cpp index dfa6f1a98..bc12c6fa6 100644 --- a/GridKit/Model/PowerElectronics/DistributedGenerator/DistributedGenerator.cpp +++ b/GridKit/Model/PowerElectronics/DistributedGenerator/DistributedGenerator.cpp @@ -421,6 +421,18 @@ namespace GridKit return 0; } + template + bool DistributedGenerator::isCloneable() const + { + return true; + } + + template + CircuitComponent* DistributedGenerator::clone() const + { + return new DistributedGenerator(*this); + } + // Available template instantiations template class DistributedGenerator; template class DistributedGenerator; diff --git a/GridKit/Model/PowerElectronics/DistributedGenerator/DistributedGenerator.hpp b/GridKit/Model/PowerElectronics/DistributedGenerator/DistributedGenerator.hpp index ab7e340f1..8a5219848 100644 --- a/GridKit/Model/PowerElectronics/DistributedGenerator/DistributedGenerator.hpp +++ b/GridKit/Model/PowerElectronics/DistributedGenerator/DistributedGenerator.hpp @@ -75,18 +75,21 @@ namespace GridKit NodeT* node_bus); virtual ~DistributedGenerator(); - int initialize(); - int allocate() final; - int tagDifferentiable(); - int setAbsoluteTolerance(RealT); - int evaluateInternalResidual() final; - int evaluateExternalResidual() final; - int evaluateJacobian(); - int evaluateIntegrand(); - int initializeAdjoint(); - int evaluateAdjointResidual(); + int initialize(); + int allocate() final; + int tagDifferentiable(); + int setAbsoluteTolerance(RealT); + int evaluateInternalResidual() final; + int evaluateExternalResidual() final; + int evaluateJacobian(); + int evaluateIntegrand(); + int initializeAdjoint(); + int evaluateAdjointResidual(); // int evaluateAdjointJacobian(); - int evaluateAdjointIntegrand(); + int evaluateAdjointIntegrand(); + bool isCloneable() const; + + CircuitComponent* clone() const; private: RealT wb_; diff --git a/GridKit/Model/PowerElectronics/InductionMotor/InductionMotor.cpp b/GridKit/Model/PowerElectronics/InductionMotor/InductionMotor.cpp index 0224e6ebe..a17456b65 100644 --- a/GridKit/Model/PowerElectronics/InductionMotor/InductionMotor.cpp +++ b/GridKit/Model/PowerElectronics/InductionMotor/InductionMotor.cpp @@ -63,6 +63,25 @@ namespace GridKit return 0; } + /** + * @brief Compute the absolute tolerance for each variable in the model + * + * @param rel_tol The relative tolerance which can be used to pick the + * absolute tolerance. + * @tparam ScalarT Scalar data type + * @tparam IdxT Index data type + * @return int 0 if successful, non-zero otherwise. + * + * This represents a "noise" level close to zero for which pure relative + * error cannot be used. + */ + template + int InductionMotor::setAbsoluteTolerance(RealT rel_tol) + { + abs_tol_.setToConst(static_cast(rel_tol)); + return 0; + } + /** * @brief Contributes to the resisdual * @@ -129,6 +148,18 @@ namespace GridKit return 0; } + template + bool InductionMotor::isCloneable() const + { + return true; + } + + template + CircuitComponent* InductionMotor::clone() const + { + return new InductionMotor(*this); + } + // Available template instantiations template class InductionMotor; template class InductionMotor; diff --git a/GridKit/Model/PowerElectronics/InductionMotor/InductionMotor.hpp b/GridKit/Model/PowerElectronics/InductionMotor/InductionMotor.hpp index 122cd4d30..6f1724670 100644 --- a/GridKit/Model/PowerElectronics/InductionMotor/InductionMotor.hpp +++ b/GridKit/Model/PowerElectronics/InductionMotor/InductionMotor.hpp @@ -29,6 +29,7 @@ namespace GridKit using CircuitComponent::y_int_; using CircuitComponent::yp_ext_; using CircuitComponent::yp_int_; + using CircuitComponent::abs_tol_; using CircuitComponent::tag_; using CircuitComponent::f_ext_; using CircuitComponent::f_int_; @@ -50,15 +51,19 @@ namespace GridKit int initialize(); int tagDifferentiable(); + int setAbsoluteTolerance(RealT); int evaluateInternalResidual() final; int evaluateExternalResidual() final; int evaluateJacobian(); int evaluateIntegrand(); - int initializeAdjoint(); - int evaluateAdjointResidual(); + int initializeAdjoint(); + int evaluateAdjointResidual(); // int evaluateAdjointJacobian(); - int evaluateAdjointIntegrand(); + int evaluateAdjointIntegrand(); + bool isCloneable() const; + + CircuitComponent* clone() const; private: RealT Lls_; diff --git a/GridKit/Model/PowerElectronics/Inductor/Inductor.cpp b/GridKit/Model/PowerElectronics/Inductor/Inductor.cpp index b192cebc5..f9e2f5d3d 100644 --- a/GridKit/Model/PowerElectronics/Inductor/Inductor.cpp +++ b/GridKit/Model/PowerElectronics/Inductor/Inductor.cpp @@ -147,6 +147,18 @@ namespace GridKit return 0; } + template + bool Inductor::isCloneable() const + { + return true; + } + + template + CircuitComponent* Inductor::clone() const + { + return new Inductor(*this); + } + // Available template instantiations template class Inductor; template class Inductor; diff --git a/GridKit/Model/PowerElectronics/Inductor/Inductor.hpp b/GridKit/Model/PowerElectronics/Inductor/Inductor.hpp index b82ce1070..30d25db79 100644 --- a/GridKit/Model/PowerElectronics/Inductor/Inductor.hpp +++ b/GridKit/Model/PowerElectronics/Inductor/Inductor.hpp @@ -60,10 +60,13 @@ namespace GridKit int evaluateJacobian(); int evaluateIntegrand(); - int initializeAdjoint(); - int evaluateAdjointResidual(); + int initializeAdjoint(); + int evaluateAdjointResidual(); // int evaluateAdjointJacobian(); - int evaluateAdjointIntegrand(); + int evaluateAdjointIntegrand(); + bool isCloneable() const; + + CircuitComponent* clone() const; private: RealT L_; diff --git a/GridKit/Model/PowerElectronics/LinearTransformer/LinearTransformer.cpp b/GridKit/Model/PowerElectronics/LinearTransformer/LinearTransformer.cpp index fcfc4a02d..b1537c8ba 100644 --- a/GridKit/Model/PowerElectronics/LinearTransformer/LinearTransformer.cpp +++ b/GridKit/Model/PowerElectronics/LinearTransformer/LinearTransformer.cpp @@ -65,6 +65,25 @@ namespace GridKit return 0; } + /** + * @brief Compute the absolute tolerance for each variable in the model + * + * @param rel_tol The relative tolerance which can be used to pick the + * absolute tolerance. + * @tparam ScalarT Scalar data type + * @tparam IdxT Index data type + * @return int 0 if successful, non-zero otherwise. + * + * This represents a "noise" level close to zero for which pure relative + * error cannot be used. + */ + template + int LinearTransformer::setAbsoluteTolerance(RealT rel_tol) + { + abs_tol_.setToConst(static_cast(rel_tol)); + return 0; + } + /** * @brief Computes the component resisdual */ @@ -114,6 +133,18 @@ namespace GridKit return 0; } + template + bool LinearTransformer::isCloneable() const + { + return true; + } + + template + CircuitComponent* LinearTransformer::clone() const + { + return new LinearTransformer(*this); + } + // Available template instantiations template class LinearTransformer; template class LinearTransformer; diff --git a/GridKit/Model/PowerElectronics/LinearTransformer/LinearTransformer.hpp b/GridKit/Model/PowerElectronics/LinearTransformer/LinearTransformer.hpp index 62dc84305..6b75dcc1d 100644 --- a/GridKit/Model/PowerElectronics/LinearTransformer/LinearTransformer.hpp +++ b/GridKit/Model/PowerElectronics/LinearTransformer/LinearTransformer.hpp @@ -29,6 +29,7 @@ namespace GridKit using CircuitComponent::y_int_; using CircuitComponent::yp_ext_; using CircuitComponent::yp_int_; + using CircuitComponent::abs_tol_; using CircuitComponent::tag_; using CircuitComponent::f_ext_; using CircuitComponent::f_int_; @@ -50,15 +51,19 @@ namespace GridKit int initialize(); int tagDifferentiable(); + int setAbsoluteTolerance(RealT); int evaluateInternalResidual() final; int evaluateExternalResidual() final; int evaluateJacobian(); int evaluateIntegrand(); - int initializeAdjoint(); - int evaluateAdjointResidual(); + int initializeAdjoint(); + int evaluateAdjointResidual(); // int evaluateAdjointJacobian(); - int evaluateAdjointIntegrand(); + int evaluateAdjointIntegrand(); + bool isCloneable() const; + + CircuitComponent* clone() const; private: RealT L0_; diff --git a/GridKit/Model/PowerElectronics/MicrogridBusDQ/MicrogridBusDQ.cpp b/GridKit/Model/PowerElectronics/MicrogridBusDQ/MicrogridBusDQ.cpp index ffc02dfb3..765286583 100644 --- a/GridKit/Model/PowerElectronics/MicrogridBusDQ/MicrogridBusDQ.cpp +++ b/GridKit/Model/PowerElectronics/MicrogridBusDQ/MicrogridBusDQ.cpp @@ -154,6 +154,18 @@ namespace GridKit return 0; } + template + bool MicrogridBusDQ::isCloneable() const + { + return true; + } + + template + CircuitComponent* MicrogridBusDQ::clone() const + { + return new MicrogridBusDQ(*this); + } + // Available template instantiations template class MicrogridBusDQ; template class MicrogridBusDQ; diff --git a/GridKit/Model/PowerElectronics/MicrogridBusDQ/MicrogridBusDQ.hpp b/GridKit/Model/PowerElectronics/MicrogridBusDQ/MicrogridBusDQ.hpp index 111ca7be8..2314700a3 100644 --- a/GridKit/Model/PowerElectronics/MicrogridBusDQ/MicrogridBusDQ.hpp +++ b/GridKit/Model/PowerElectronics/MicrogridBusDQ/MicrogridBusDQ.hpp @@ -60,10 +60,13 @@ namespace GridKit int evaluateJacobian(); int evaluateIntegrand(); - int initializeAdjoint(); - int evaluateAdjointResidual(); + int initializeAdjoint(); + int evaluateAdjointResidual(); // int evaluateAdjointJacobian(); - int evaluateAdjointIntegrand(); + int evaluateAdjointIntegrand(); + bool isCloneable() const; + + CircuitComponent* clone() const; private: RealT RN_; diff --git a/GridKit/Model/PowerElectronics/MicrogridLine/MicrogridLine.cpp b/GridKit/Model/PowerElectronics/MicrogridLine/MicrogridLine.cpp index 65c3fb0a0..1292cd045 100644 --- a/GridKit/Model/PowerElectronics/MicrogridLine/MicrogridLine.cpp +++ b/GridKit/Model/PowerElectronics/MicrogridLine/MicrogridLine.cpp @@ -182,6 +182,18 @@ namespace GridKit return 0; } + template + bool MicrogridLine::isCloneable() const + { + return true; + } + + template + CircuitComponent* MicrogridLine::clone() const + { + return new MicrogridLine(*this); + } + // Available template instantiations template class MicrogridLine; template class MicrogridLine; diff --git a/GridKit/Model/PowerElectronics/MicrogridLine/MicrogridLine.hpp b/GridKit/Model/PowerElectronics/MicrogridLine/MicrogridLine.hpp index 1354a9d9b..041562cbb 100644 --- a/GridKit/Model/PowerElectronics/MicrogridLine/MicrogridLine.hpp +++ b/GridKit/Model/PowerElectronics/MicrogridLine/MicrogridLine.hpp @@ -60,10 +60,13 @@ namespace GridKit int evaluateJacobian(); int evaluateIntegrand(); - int initializeAdjoint(); - int evaluateAdjointResidual(); + int initializeAdjoint(); + int evaluateAdjointResidual(); // int evaluateAdjointJacobian(); - int evaluateAdjointIntegrand(); + int evaluateAdjointIntegrand(); + bool isCloneable() const; + + CircuitComponent* clone() const; private: RealT R_; diff --git a/GridKit/Model/PowerElectronics/MicrogridLoad/MicrogridLoad.cpp b/GridKit/Model/PowerElectronics/MicrogridLoad/MicrogridLoad.cpp index a14a038f1..7739b39be 100644 --- a/GridKit/Model/PowerElectronics/MicrogridLoad/MicrogridLoad.cpp +++ b/GridKit/Model/PowerElectronics/MicrogridLoad/MicrogridLoad.cpp @@ -173,6 +173,18 @@ namespace GridKit return 0; } + template + bool MicrogridLoad::isCloneable() const + { + return true; + } + + template + CircuitComponent* MicrogridLoad::clone() const + { + return new MicrogridLoad(*this); + } + // Available template instantiations template class MicrogridLoad; template class MicrogridLoad; diff --git a/GridKit/Model/PowerElectronics/MicrogridLoad/MicrogridLoad.hpp b/GridKit/Model/PowerElectronics/MicrogridLoad/MicrogridLoad.hpp index 931983d50..6189e941c 100644 --- a/GridKit/Model/PowerElectronics/MicrogridLoad/MicrogridLoad.hpp +++ b/GridKit/Model/PowerElectronics/MicrogridLoad/MicrogridLoad.hpp @@ -60,10 +60,13 @@ namespace GridKit int evaluateJacobian(); int evaluateIntegrand(); - int initializeAdjoint(); - int evaluateAdjointResidual(); + int initializeAdjoint(); + int evaluateAdjointResidual(); // int evaluateAdjointJacobian(); - int evaluateAdjointIntegrand(); + int evaluateAdjointIntegrand(); + bool isCloneable() const; + + CircuitComponent* clone() const; private: RealT R_; diff --git a/GridKit/Model/PowerElectronics/NodeBase.hpp b/GridKit/Model/PowerElectronics/NodeBase.hpp index 2e04acff8..11ddb1117 100644 --- a/GridKit/Model/PowerElectronics/NodeBase.hpp +++ b/GridKit/Model/PowerElectronics/NodeBase.hpp @@ -409,6 +409,16 @@ namespace GridKit return gB_; } + /** + * @brief Check whether the Node has already been allocated. + * + * @return true if allocate() has previously completed, false otherwise. + */ + bool isAllocated() const + { + return allocated_; + } + private: void allocateVectors(IdxT n) { diff --git a/GridKit/Model/PowerElectronics/Resistor/Resistor.cpp b/GridKit/Model/PowerElectronics/Resistor/Resistor.cpp index 5c6c4a1cb..45efeab33 100644 --- a/GridKit/Model/PowerElectronics/Resistor/Resistor.cpp +++ b/GridKit/Model/PowerElectronics/Resistor/Resistor.cpp @@ -141,6 +141,18 @@ namespace GridKit return 0; } + template + bool Resistor::isCloneable() const + { + return true; + } + + template + CircuitComponent* Resistor::clone() const + { + return new Resistor(*this); + } + // Available template instantiations template class Resistor; template class Resistor; diff --git a/GridKit/Model/PowerElectronics/Resistor/Resistor.hpp b/GridKit/Model/PowerElectronics/Resistor/Resistor.hpp index 540ab4d20..19c90a190 100644 --- a/GridKit/Model/PowerElectronics/Resistor/Resistor.hpp +++ b/GridKit/Model/PowerElectronics/Resistor/Resistor.hpp @@ -60,10 +60,13 @@ namespace GridKit int evaluateJacobian(); int evaluateIntegrand(); - int initializeAdjoint(); - int evaluateAdjointResidual(); + int initializeAdjoint(); + int evaluateAdjointResidual(); // int evaluateAdjointJacobian(); - int evaluateAdjointIntegrand(); + int evaluateAdjointIntegrand(); + bool isCloneable() const; + + CircuitComponent* clone() const; private: RealT R_; diff --git a/GridKit/Model/PowerElectronics/SynchronousMachine/SynchronousMachine.cpp b/GridKit/Model/PowerElectronics/SynchronousMachine/SynchronousMachine.cpp index c3a647762..b10480f33 100644 --- a/GridKit/Model/PowerElectronics/SynchronousMachine/SynchronousMachine.cpp +++ b/GridKit/Model/PowerElectronics/SynchronousMachine/SynchronousMachine.cpp @@ -80,6 +80,25 @@ namespace GridKit return 0; } + /** + * @brief Compute the absolute tolerance for each variable in the model + * + * @param rel_tol The relative tolerance which can be used to pick the + * absolute tolerance. + * @tparam ScalarT Scalar data type + * @tparam IdxT Index data type + * @return int 0 if successful, non-zero otherwise. + * + * This represents a "noise" level close to zero for which pure relative + * error cannot be used. + */ + template + int SynchronousMachine::setAbsoluteTolerance(RealT rel_tol) + { + abs_tol_.setToConst(static_cast(rel_tol)); + return 0; + } + /** * @brief Compute the resisdual of the component. * @@ -163,6 +182,18 @@ namespace GridKit return 0; } + template + bool SynchronousMachine::isCloneable() const + { + return true; + } + + template + CircuitComponent* SynchronousMachine::clone() const + { + return new SynchronousMachine(*this); + } + // Available template instantiations template class SynchronousMachine; template class SynchronousMachine; diff --git a/GridKit/Model/PowerElectronics/SynchronousMachine/SynchronousMachine.hpp b/GridKit/Model/PowerElectronics/SynchronousMachine/SynchronousMachine.hpp index 80b68eed2..7cf040583 100644 --- a/GridKit/Model/PowerElectronics/SynchronousMachine/SynchronousMachine.hpp +++ b/GridKit/Model/PowerElectronics/SynchronousMachine/SynchronousMachine.hpp @@ -31,6 +31,7 @@ namespace GridKit using CircuitComponent::y_int_; using CircuitComponent::yp_ext_; using CircuitComponent::yp_int_; + using CircuitComponent::abs_tol_; using CircuitComponent::tag_; using CircuitComponent::f_ext_; using CircuitComponent::f_int_; @@ -52,15 +53,19 @@ namespace GridKit int initialize(); int tagDifferentiable(); + int setAbsoluteTolerance(RealT); int evaluateInternalResidual() final; int evaluateExternalResidual() final; int evaluateJacobian(); int evaluateIntegrand(); - int initializeAdjoint(); - int evaluateAdjointResidual(); + int initializeAdjoint(); + int evaluateAdjointResidual(); // int evaluateAdjointJacobian(); - int evaluateAdjointIntegrand(); + int evaluateAdjointIntegrand(); + bool isCloneable() const; + + CircuitComponent* clone() const; private: RealT Lls_; diff --git a/GridKit/Model/PowerElectronics/TransmissionLine/TransmissionLine.cpp b/GridKit/Model/PowerElectronics/TransmissionLine/TransmissionLine.cpp index 3adbbef58..c959c2b5c 100644 --- a/GridKit/Model/PowerElectronics/TransmissionLine/TransmissionLine.cpp +++ b/GridKit/Model/PowerElectronics/TransmissionLine/TransmissionLine.cpp @@ -63,6 +63,25 @@ namespace GridKit return 0; } + /** + * @brief Compute the absolute tolerance for each variable in the model + * + * @param rel_tol The relative tolerance which can be used to pick the + * absolute tolerance. + * @tparam ScalarT Scalar data type + * @tparam IdxT Index data type + * @return int 0 if successful, non-zero otherwise. + * + * This represents a "noise" level close to zero for which pure relative + * error cannot be used. + */ + template + int TransmissionLine::setAbsoluteTolerance(RealT rel_tol) + { + abs_tol_.setToConst(static_cast(rel_tol)); + return 0; + } + /** * @brief Evaluate residual of transmission line * @@ -184,6 +203,18 @@ namespace GridKit return 0; } + template + bool TransmissionLine::isCloneable() const + { + return true; + } + + template + CircuitComponent* TransmissionLine::clone() const + { + return new TransmissionLine(*this); + } + // Available template instantiations template class TransmissionLine; template class TransmissionLine; diff --git a/GridKit/Model/PowerElectronics/TransmissionLine/TransmissionLine.hpp b/GridKit/Model/PowerElectronics/TransmissionLine/TransmissionLine.hpp index 091984c89..c9743e9dd 100644 --- a/GridKit/Model/PowerElectronics/TransmissionLine/TransmissionLine.hpp +++ b/GridKit/Model/PowerElectronics/TransmissionLine/TransmissionLine.hpp @@ -33,6 +33,7 @@ namespace GridKit using CircuitComponent::y_int_; using CircuitComponent::yp_ext_; using CircuitComponent::yp_int_; + using CircuitComponent::abs_tol_; using CircuitComponent::tag_; using CircuitComponent::f_ext_; using CircuitComponent::f_int_; @@ -54,15 +55,19 @@ namespace GridKit int initialize(); int tagDifferentiable(); + int setAbsoluteTolerance(RealT); int evaluateInternalResidual() final; int evaluateExternalResidual() final; int evaluateJacobian(); int evaluateIntegrand(); - int initializeAdjoint(); - int evaluateAdjointResidual(); + int initializeAdjoint(); + int evaluateAdjointResidual(); // int evaluateAdjointJacobian(); - int evaluateAdjointIntegrand(); + int evaluateAdjointIntegrand(); + bool isCloneable() const; + + CircuitComponent* clone() const; private: RealT R_; diff --git a/GridKit/Model/PowerElectronics/VoltageSource/VoltageSource.cpp b/GridKit/Model/PowerElectronics/VoltageSource/VoltageSource.cpp index 9a4accd0b..a0d0d3946 100644 --- a/GridKit/Model/PowerElectronics/VoltageSource/VoltageSource.cpp +++ b/GridKit/Model/PowerElectronics/VoltageSource/VoltageSource.cpp @@ -140,6 +140,18 @@ namespace GridKit return 0; } + template + bool VoltageSource::isCloneable() const + { + return true; + } + + template + CircuitComponent* VoltageSource::clone() const + { + return new VoltageSource(*this); + } + // Available template instantiations template class VoltageSource; template class VoltageSource; diff --git a/GridKit/Model/PowerElectronics/VoltageSource/VoltageSource.hpp b/GridKit/Model/PowerElectronics/VoltageSource/VoltageSource.hpp index 814fa5ec5..ab9125f40 100644 --- a/GridKit/Model/PowerElectronics/VoltageSource/VoltageSource.hpp +++ b/GridKit/Model/PowerElectronics/VoltageSource/VoltageSource.hpp @@ -60,10 +60,13 @@ namespace GridKit int evaluateJacobian() final; int evaluateIntegrand(); - int initializeAdjoint(); - int evaluateAdjointResidual(); + int initializeAdjoint(); + int evaluateAdjointResidual(); // int evaluateAdjointJacobian(); - int evaluateAdjointIntegrand(); + int evaluateAdjointIntegrand(); + bool isCloneable() const; + + CircuitComponent* clone() const; private: RealT V_; From 9053f175827503dce3c7478a131dab90f0837c50 Mon Sep 17 00:00:00 2001 From: abdourahmanbarry Date: Sat, 15 Aug 2026 14:31:42 +0000 Subject: [PATCH 47/52] Add tests for component clone method --- .../PowerElectronics/ComponentCloneTests.hpp | 263 ++++++++++++++++++ .../runComponentCloneTests.cpp | 15 + 2 files changed, 278 insertions(+) create mode 100644 tests/UnitTests/PowerElectronics/ComponentCloneTests.hpp create mode 100644 tests/UnitTests/PowerElectronics/runComponentCloneTests.cpp diff --git a/tests/UnitTests/PowerElectronics/ComponentCloneTests.hpp b/tests/UnitTests/PowerElectronics/ComponentCloneTests.hpp new file mode 100644 index 000000000..5124442d8 --- /dev/null +++ b/tests/UnitTests/PowerElectronics/ComponentCloneTests.hpp @@ -0,0 +1,263 @@ + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace GridKit +{ + template + bool verifyComponentClone(ComponentT& component) + { + using RealT = typename CircuitComponent::RealT; + + bool success = true; + + auto* clone = dynamic_cast(component.clone()); + + if (clone == nullptr) + { + return false; + } + + /************************************************************************** + * Verify the Clone Initially Matches the Original + **************************************************************************/ + + success &= clone != &component; + success &= clone->size() == component.size(); + success &= clone->nnz() == component.nnz(); + success &= clone->getInternalSize() == component.getInternalSize(); + success &= clone->getExternSize() == component.getExternSize(); + success &= clone->getExternIndices() == component.getExternIndices(); + success &= clone->getIDcomponent() == component.getIDcomponent(); + + /************************************************************************** + * Verify Connection Independence + **************************************************************************/ + + for (IdxT i = 0; i < component.size(); ++i) + { + const IdxT connection = component.getNodeConnection(i); + + success &= clone->getNodeConnection(i) == connection; + + clone->setConnectionNodes(i, connection + 1); + + success &= component.getNodeConnection(i) == connection; + success &= clone->getNodeConnection(i) == connection + 1; + + clone->setConnectionNodes(i, connection); + } + + /************************************************************************** + * Verify State Independence + **************************************************************************/ + + auto checkVectorIndependence = [&success](auto& original, auto& copy) + { + success &= original.getSize() == copy.getSize(); + + if (original.getSize() == 0) + { + return; + } + + auto* original_data = original.getData(); + auto* copy_data = copy.getData(); + + success &= original_data != copy_data; + + const auto original_value = original_data[0]; + + copy_data[0] = original_value + 1.0; + + success &= original_data[0] == original_value; + success &= copy_data[0] == original_value + 1.0; + + copy_data[0] = original_value; + }; + + checkVectorIndependence(component.y(), clone->y()); + checkVectorIndependence(component.yp(), clone->yp()); + checkVectorIndependence(component.getResidual(), clone->getResidual()); + checkVectorIndependence(component.absoluteTolerance(), clone->absoluteTolerance()); + checkVectorIndependence(component.param(), clone->param()); + + /************************************************************************** + * Verify Jacobian Independence + **************************************************************************/ + + if (component.nnz() > 0) + { + auto* original_rows = component.jacobianCooRows(); + auto* original_cols = component.jacobianCooCols(); + auto* original_values = component.jacobianCooValues(); + + auto* clone_rows = clone->jacobianCooRows(); + auto* clone_cols = clone->jacobianCooCols(); + auto* clone_values = clone->jacobianCooValues(); + + success &= clone_rows != original_rows; + success &= clone_cols != original_cols; + success &= clone_values != original_values; + + for (IdxT i = 0; i < component.nnz(); ++i) + { + success &= clone_rows[i] == original_rows[i]; + success &= clone_cols[i] == original_cols[i]; + success &= clone_values[i] == original_values[i]; + } + + const IdxT original_row = original_rows[0]; + const IdxT original_col = original_cols[0]; + const RealT original_value = original_values[0]; + + clone_rows[0] = original_row + 1; + clone_cols[0] = original_col + 1; + clone_values[0] = original_value + 1.0; + + success &= original_rows[0] == original_row; + success &= original_cols[0] == original_col; + success &= original_values[0] == original_value; + + clone_rows[0] = original_row; + clone_cols[0] = original_col; + clone_values[0] = original_value; + } + + delete clone; + + return success; + } + + namespace Testing + { + template + class CircuitComponentCloneTests + { + using SignalNode = PowerElectronics::SignalNode; + using Bus = PowerElectronics::MicrogridBus; + using BusDQ = MicrogridBusDQ; + using Generator = DistributedGenerator; + using GeneratorParameters = DistributedGeneratorParameters; + using Line = MicrogridLine; + using Load = MicrogridLoad; + + public: + CircuitComponentCloneTests() + { + /************************************************************************** + * Construct Network Nodes + **************************************************************************/ + + signal_.allocate(); + + bus1_.allocate(); + bus2_.allocate(); + + /************************************************************************** + * Distributed Generator Parameters + **************************************************************************/ + + generator_parameters_.wb_ = 2.0 * M_PI * 50.0; + generator_parameters_.wc_ = 31.41; + generator_parameters_.mp_ = 9.4e-5; + generator_parameters_.Vn_ = 380.0; + generator_parameters_.nq_ = 1.3e-3; + generator_parameters_.F_ = 0.75; + generator_parameters_.Kiv_ = 420.0; + generator_parameters_.Kpv_ = 0.1; + generator_parameters_.Kic_ = 2.0e4; + generator_parameters_.Kpc_ = 15.0; + generator_parameters_.Cf_ = 5.0e-5; + generator_parameters_.rLf_ = 0.1; + generator_parameters_.Lf_ = 1.35e-3; + generator_parameters_.rLc_ = 0.03; + generator_parameters_.Lc_ = 0.35e-3; + + /************************************************************************** + * Construct Components + **************************************************************************/ + + generator_ = new Generator(1, generator_parameters_, true, &signal_, &bus1_); + + line_ = new Line(2, 0.23, 0.1 / (2.0 * M_PI * 50.0), &signal_, &bus1_, &bus2_); + + load_ = new Load(3, 3.0, 2.0 / (2.0 * M_PI * 50.0), &signal_, &bus1_); + + bus_dq_ = new BusDQ(4, 1.0e4, &bus1_); + + /************************************************************************** + * Allocate Components + **************************************************************************/ + + generator_->allocate(); + line_->allocate(); + load_->allocate(); + bus_dq_->allocate(); + } + + ~CircuitComponentCloneTests() + { + delete generator_; + delete line_; + delete load_; + delete bus_dq_; + } + + TestOutcome distributedGeneratorClone() + { + TestStatus success = true; + + success *= verifyComponentClone(*generator_); + + return success.report(__func__); + } + + TestOutcome microgridLineClone() + { + TestStatus success = true; + + success *= verifyComponentClone(*line_); + + return success.report(__func__); + } + + TestOutcome microgridLoadClone() + { + TestStatus success = true; + + success *= verifyComponentClone(*load_); + + return success.report(__func__); + } + + TestOutcome microgridBusDQClone() + { + TestStatus success = true; + + success *= verifyComponentClone(*bus_dq_); + + return success.report(__func__); + } + + private: + SignalNode signal_; + + Bus bus1_; + Bus bus2_; + + GeneratorParameters generator_parameters_; + + Generator* generator_{nullptr}; + Line* line_{nullptr}; + Load* load_{nullptr}; + BusDQ* bus_dq_{nullptr}; + }; + } // namespace Testing +} // namespace GridKit diff --git a/tests/UnitTests/PowerElectronics/runComponentCloneTests.cpp b/tests/UnitTests/PowerElectronics/runComponentCloneTests.cpp new file mode 100644 index 000000000..dfb5fa97f --- /dev/null +++ b/tests/UnitTests/PowerElectronics/runComponentCloneTests.cpp @@ -0,0 +1,15 @@ +#include "ComponentCloneTests.hpp" + +int main() +{ + GridKit::Testing::CircuitComponentCloneTests tests; + + GridKit::Testing::TestingResults result; + + result += tests.distributedGeneratorClone(); + result += tests.microgridLineClone(); + result += tests.microgridLoadClone(); + result += tests.microgridBusDQClone(); + + return result.summary(); +} From 7c19edbc889d40d3e826f60c0b267a49d9a2a625 Mon Sep 17 00:00:00 2001 From: abdourahmanbarry Date: Sat, 15 Aug 2026 14:32:33 +0000 Subject: [PATCH 48/52] Generalize BusPartitionInterface - Partition interface can be applied to all bus types, not just MicrogridBus - New clone functionality allows users to pass components directly without explicitly copying it - Update CMake file with dense vector dependency - Add more documentation - Move suitable code from constructor to allocate --- .../BusPartitionInterface.cpp | 287 +++++++++++------- .../BusPartitionInterface.hpp | 20 +- .../PartitionInterface/CMakeLists.txt | 3 +- .../PartitionInterface/PartitionInterface.hpp | 6 - 4 files changed, 184 insertions(+), 132 deletions(-) diff --git a/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.cpp b/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.cpp index 392f221b4..b83580718 100644 --- a/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.cpp +++ b/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.cpp @@ -11,31 +11,21 @@ namespace GridKit { /** - * @brief Construct a bus partition interface for a circuit component. + * @brief Construct a partition interface between a bus and a circuit component. * - * During partitioning, the interface receives a bus and a component that - * participate in the partition boundary. The interface is added directly - * to the partition containing the bus and uses the component model to - * compute the residual and Jacobian contributions to that bus. + * The interface wraps a copy of a component located across a partition + * boundary and evaluates the contributions that the component makes to the + * connected bus. All interface variables are treated as external variables + * and have the same size as the wrapped component. * - * The component passed to this constructor must be a copy of the original - * component, not the original component itself. This allows the interface - * to modify and evaluate the copied component without affecting the component - * stored in the neighboring partition. * - * For example: - * @code - * CompType* comp_copy = new CompType(*comp); - * auto* interface = new BusPartitionInterface(bus, comp_copy, id); - * @endcode - * - * @param bus Bus belonging to the partition where the interface is added. - * @param component Copy of the circuit component across the partition boundary. - * @param id Unique interface identifier. + * @param bus Bus associated with the partition interface. + * @param component Copy of the component across the partition boundary. + * @param id Unique identifier for the interface. */ template BusPartitionInterface::BusPartitionInterface(node_type* bus, component_type* component, IdxT id) - : component_(component), + : component_(component->clone()), bus_(bus) { size_ = component_->size(); @@ -49,110 +39,128 @@ namespace GridKit extern_indices_.insert(i); } - // The subsystem needs access to the interface connection indices before - // the interface is fully allocated. - connection_nodes_ = std::make_unique(static_cast(size_)); + // Map each global bus connection index to its position in the bus. + std::unordered_map bus_connections; + + for (size_t i = 0; i < static_cast(bus_->size()); ++i) + { + bus_connections[bus_->getNodeConnection(i).idx_] = i; + } + + // The interface Jacobian contains only rows corresponding to variables + // owned by the bus. + const IdxT* coo_rows = component_->jacobianCooRows(); - // Global indices of the two variables associated with the bus. - const IdxT bus_i = bus_->getNodeConnection(0).idx_; - const IdxT bus_j = bus_->getNodeConnection(1).idx_; + nnz_ = 0; - is_external_.resize(static_cast(size_), false); + for (IdxT k = 0; k < component_->nnz(); ++k) + { + const IdxT row_node = component_->getNodeConnection(static_cast(coo_rows[k])); + + if (bus_connections.contains(row_node)) + { + ++nnz_; + jac_map_.push_back(k); // Keep track of the entries so they can be easily extracted + } + } + } - bool port_i_set = false; - bool port_j_set = false; + template + BusPartitionInterface::~BusPartitionInterface() + { + delete component_; + } + + /** + * @brief Allocate storage and initialize the interface mappings. + * + * Identifies the external variables of the wrapped component that are + * connected to the bus and builds the mappings used to transfer residual + * contributions between the component and the interface. Private storage is + * also allocated for the internal variables of the wrapped component. + * + * @return 0 on success and a nonzero value if the component does not contain + * all variables required by the bus interface. + */ + template + int BusPartitionInterface::allocate() + { + + CircuitComponent::allocate(); + + // Build a lookup of the global indices belonging to the bus. + std::unordered_map bus_connections; + + for (size_t i = 0; i < static_cast(bus_->size()); ++i) + { + bus_connections[bus_->getNodeConnection(i).idx_] = i; + } const auto& external_indices = component_->getExternIndices(); - IdxT external_index = 0; - // Copy the wrapped component topology and identify the two bus ports and - // their corresponding output ports in the wrapped component. + is_external_.assign(static_cast(size_), false); + + bus_input_ports_.clear(); + bus_output_ports_.clear(); + + size_t external_index = 0; + + // Identify which component variables are external and which of those external + // variables are connected to the bus. bus_input_ports_ stores the corresponding + // variable position in this interface, while bus_output_ports_ stores its position + // in the wrapped component's external residual vector. for (size_t i = 0; i < static_cast(size_); ++i) { const IdxT connection_index = component_->getNodeConnection(i); this->setConnectionNodes(i, connection_index); - // Skip indices that are not external to the wrapped component. if (!external_indices.contains(static_cast(i))) { continue; } - // Identify the bus ports and their positions in the component's external output. - if (connection_index == bus_i) - { - bus_port_i_ = i; - bus_port_out_i_ = static_cast(external_index); - port_i_set = true; - } - else if (connection_index == bus_j) + is_external_[i] = true; + + if (bus_connections.contains(connection_index)) { - bus_port_j_ = i; - bus_port_out_j_ = static_cast(external_index); - port_j_set = true; + bus_input_ports_.push_back(i); + bus_output_ports_.push_back(external_index); } ++external_index; - is_external_[static_cast(i)] = true; } - // Report an error if either bus connection is missing from the component. - if (!port_i_set || !port_j_set) + // A valid bus interface must contain every variable belonging to the bus. + // If fewer bus variables are found, the wrapped component does not provide + // the complete coupling required by this interface. + const size_t bus_size = static_cast(bus_->size()); + + if (bus_input_ports_.size() != bus_size) { std::cerr << "ERROR: Invalid partition interface detected. " << "Bus(ID=" << bus_->busID() << "), Component(ID=" << component_->getIDcomponent() - << "). Please verify connection-node mappings and " - "internal/external index assignments." + << "). Expected " << bus_size + << " bus connections, but found " + << bus_input_ports_.size() << "." << std::endl; - } - - // Record the Jacobian entries whose residual row belongs to either bus - // variable. jac_map_ stores their positions in the wrapped component's - // COO Jacobian so that the same entries can be extracted during each Jacobian evaluation. - const IdxT* cooRow = component_->jacobianCooRows(); - - nnz_ = 0; - for (IdxT k = 0; k < component_->nnz(); ++k) - { - const IdxT row_node = component_->getNodeConnection(static_cast(cooRow[k])); - - const bool row_is_bus = (row_node == bus_i || row_node == bus_j); - - if (row_is_bus) - { - ++nnz_; - jac_map_.push_back(k); - } + return 1; } - } - - template - BusPartitionInterface::~BusPartitionInterface() - { - delete component_; - } - - /*! - * @brief allocate method - */ - template - int BusPartitionInterface::allocate() - { - CircuitComponent::allocate(); - - const auto n = component_->getInternalSize(); + // The wrapped component is evaluated independently by the interface. + // Allocate storage for its internal variables and residuals and redirect + // its internal pointers to this private storage. + const size_t internal_size = static_cast(component_->getInternalSize()); - y_ptr = std::make_unique(n); - yp_ptr = std::make_unique(n); - f_ptr = std::make_unique(n); + y_ptr_ = std::make_unique(internal_size); + yp_ptr_ = std::make_unique(internal_size); + f_ptr_ = std::make_unique(internal_size); - component_->setInternalPointer(y_ptr.get()); - component_->setInternalDerivativePointer(yp_ptr.get()); - component_->setInternalResidualPointer(f_ptr.get()); + component_->setInternalPointer(y_ptr_.get()); + component_->setInternalDerivativePointer(yp_ptr_.get()); + component_->setInternalResidualPointer(f_ptr_.get()); return 0; } @@ -165,7 +173,9 @@ namespace GridKit } /** - * Initialization of the grid model + * @brief Initialize the partition interface. + * + * @return 0 on success. */ template int BusPartitionInterface::initialize() @@ -173,8 +183,8 @@ namespace GridKit return 0; } - /* - * \brief Identify differential variables + /** + * @brief Identify differential variables */ template int BusPartitionInterface::tagDifferentiable() @@ -191,30 +201,49 @@ namespace GridKit return 0; } + /** + * @brief Evaluate the wrapped component's contributions to the bus residual. + * + * The wrapped component is evaluated using state information supplied through + * the interface. Residual contributions associated with bus variables are + * extracted from the component's external residual and accumulated into the + * corresponding interface residual entries. + * + * @return 0 on success, or the error code returned by the wrapped component. + */ template int BusPartitionInterface::evaluateExternalResidual() { + + // Evaluate all external residual contributions produced by the wrapped + // component. std::vector component_ext_residual(static_cast(component_->getExternSize())); updateComponentPointers(component_ext_residual.data()); - if (int err_code = component_->evaluateResidual()) + if (int err_code = component_->evaluateExternalResidual()) { return err_code; } - *f_ext_[bus_port_i_] += component_ext_residual[bus_port_out_i_]; - *f_ext_[bus_port_j_] += component_ext_residual[bus_port_out_j_]; + // Only contributions associated with the bus are accumulated + // into the interface residual below. + for (size_t i = 0; i < bus_input_ports_.size(); ++i) + { + *f_ext_[bus_input_ports_[i]] += component_ext_residual[bus_output_ports_[i]]; + } return 0; } /** - * @brief Generate Jacobian for + * @brief Evaluate the Jacobian contributions associated with the bus. * - * @tparam ScalarT - * @tparam IdxT - * @return int + * Evaluates the Jacobian of the wrapped component and extracts the entries + * whose residual rows correspond to variables belonging to the connected bus. + * The selected entries are then used to assemble the interface Jacobian. + * + * @return 0 on success. */ template int BusPartitionInterface::evaluateJacobian() @@ -222,8 +251,9 @@ namespace GridKit this->zeroJacMatrix(); - // The Jacobian only requires the component state pointers. Residual - // contributions are redirected to dummy storage. + // The Jacobian only requires the component state pointers. + // Residual output is not needed, so external residual pointers + // are redirected to dummy storage. updateComponentPointers(nullptr); component_->evaluateJacobian(); @@ -232,22 +262,50 @@ namespace GridKit const IdxT* cooCols = component_->jacobianCooCols(); const RealT* cooVals = component_->jacobianCooValues(); - std::vector r = {}; - std::vector c = {}; - std::vector v = {}; + std::vector rows; + std::vector cols; + std::vector vals; + + rows.reserve(jac_map_.size()); + cols.reserve(jac_map_.size()); + vals.reserve(jac_map_.size()); - for (const auto& index : jac_map_) + // Extract only the Jacobian entries whose residual rows belong to the bus. + for (const IdxT index : jac_map_) { - r.push_back(cooRows[index]); - c.push_back(cooCols[index]); - v.push_back(cooVals[index]); + rows.push_back(cooRows[index]); + cols.push_back(cooCols[index]); + vals.push_back(cooVals[index]); } - this->setJacValues(r, c, v); + this->setJacValues(rows, cols, vals); return 0; } + /** + * @brief Update the wrapped component with state and output residual pointers. + * + * Reconstructs the state required to evaluate the wrapped component using + * data supplied through the interface. External component variables are + * connected directly to the interface data, while internal component + * variables are copied into the interface's private storage, which the wrapped + * component already keeps track of for its internal state. + * + * If residual storage is provided, external residual contributions are + * written to that storage. Otherwise, they are redirected to dummy storage. + * + * @pre The interface must be allocated and its external state pointers must + * reference valid state and derivative data. + * + * @post The wrapped component is configured with the state, derivative, and + * residual pointers required for evaluation. + * + * @param residual Storage for the component's external residual contributions, + * or nullptr when residual output is not required. + * + * @return 0 on success. + */ template int BusPartitionInterface::updateComponentPointers(ScalarT* residual) { @@ -256,6 +314,9 @@ namespace GridKit const auto& external_indices = component_->getExternIndices(); + // Reconstruct the state expected by the wrapped component from the + // interface's external data. Route the residual to dummy variable if + // output data is not needed such as when evaluating Jacobians. for (size_t i = 0; i < static_cast(component_->size()); ++i) { if (is_external_[i]) @@ -272,8 +333,8 @@ namespace GridKit } else { - y_ptr[internal_index] = *y_ext_[i]; - yp_ptr[internal_index] = *yp_ext_[i]; + y_ptr_[internal_index] = *y_ext_[i]; + yp_ptr_[internal_index] = *yp_ext_[i]; ++internal_index; } @@ -312,4 +373,4 @@ namespace GridKit template class BusPartitionInterface; template class BusPartitionInterface; -} // namespace GridKit +} // namespace GridKit \ No newline at end of file diff --git a/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.hpp b/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.hpp index 006d8c9f3..0ebf566a4 100644 --- a/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.hpp +++ b/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.hpp @@ -4,8 +4,7 @@ #include #include - -#include "GridKit/Model/PowerElectronics/PartitionInterface/PartitionInterface.hpp" +#include namespace GridKit { @@ -46,12 +45,6 @@ namespace GridKit using CircuitComponent::n_intern_; using CircuitComponent::connection_nodes_; - using PartitionInterface::bus_port_i_; - using PartitionInterface::bus_port_j_; - - using PartitionInterface::bus_port_out_i_; - using PartitionInterface::bus_port_out_j_; - public: BusPartitionInterface(node_type* bus, component_type* component, IdxT id); virtual ~BusPartitionInterface(); @@ -78,9 +71,12 @@ namespace GridKit node_type* bus_; // Storage for the wrapped component's internal variables. - std::unique_ptr y_ptr; - std::unique_ptr yp_ptr; - std::unique_ptr f_ptr; + std::unique_ptr y_ptr_; + std::unique_ptr yp_ptr_; + std::unique_ptr f_ptr_; + + std::vector bus_input_ports_; + std::vector bus_output_ports_; // Dummy storage used when the component residual is not needed. ScalarT dummy_residual_{}; @@ -91,4 +87,4 @@ namespace GridKit // Identifies which wrapped component variables are external. std::vector is_external_; }; -} // namespace GridKit +} // namespace GridKit \ No newline at end of file diff --git a/GridKit/Model/PowerElectronics/PartitionInterface/CMakeLists.txt b/GridKit/Model/PowerElectronics/PartitionInterface/CMakeLists.txt index b339bfa2f..58b4fa884 100644 --- a/GridKit/Model/PowerElectronics/PartitionInterface/CMakeLists.txt +++ b/GridKit/Model/PowerElectronics/PartitionInterface/CMakeLists.txt @@ -1,4 +1,5 @@ gridkit_add_library( power_elec_partition_interfaces SOURCES BusPartitionInterface.cpp - HEADERS BusPartitionInterface.hpp PartitionInterface.hpp) + HEADERS BusPartitionInterface.hpp PartitionInterface.hpp + LINK_LIBRARIES GridKit::dense_vector) diff --git a/GridKit/Model/PowerElectronics/PartitionInterface/PartitionInterface.hpp b/GridKit/Model/PowerElectronics/PartitionInterface/PartitionInterface.hpp index 2df9be323..9a2f0e072 100644 --- a/GridKit/Model/PowerElectronics/PartitionInterface/PartitionInterface.hpp +++ b/GridKit/Model/PowerElectronics/PartitionInterface/PartitionInterface.hpp @@ -19,12 +19,6 @@ namespace GridKit PartitionInterface() = default; ~PartitionInterface() = default; - - protected: - size_t bus_port_i_; - size_t bus_port_j_; - size_t bus_port_out_i_; - size_t bus_port_out_j_; }; } // namespace GridKit From 9f85ee19af3b5766f33469705bf3469113a16949 Mon Sep 17 00:00:00 2001 From: abdourahmanbarry Date: Sat, 15 Aug 2026 15:38:05 +0000 Subject: [PATCH 49/52] Fixed failing test --- .../SubsystemModelWithHiresTest.hpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/UnitTests/PowerElectronics/SubsystemModelWithHiresTest.hpp b/tests/UnitTests/PowerElectronics/SubsystemModelWithHiresTest.hpp index 9b30c2785..fa524bfc1 100644 --- a/tests/UnitTests/PowerElectronics/SubsystemModelWithHiresTest.hpp +++ b/tests/UnitTests/PowerElectronics/SubsystemModelWithHiresTest.hpp @@ -149,6 +149,11 @@ namespace GridKit return 0; } + CircuitComponent* clone() const + { + return new HiresComponent1(*this); + } + private: NodeT* node_ref_; }; @@ -271,6 +276,11 @@ namespace GridKit return 0; } + CircuitComponent* clone() const + { + return new HiresBus(*this); + } + private: NodeT* node_ref_; }; @@ -422,6 +432,11 @@ namespace GridKit return 0; } + CircuitComponent* clone() const + { + return new HiresComponent3(*this); + } + private: NodeT* node_ref_; }; From 93d0442ad44c2c9648ee33b634757076b1869ed1 Mon Sep 17 00:00:00 2001 From: abdourahmanbarry Date: Sat, 15 Aug 2026 15:45:41 +0000 Subject: [PATCH 50/52] Refactored examples to minimize code duplication - move microgrid network builder section to separate file to facilitate code reuse as more examples dependent on it - Add partition utility code to minimize code duplication in partition examples. - Added all helper header files in one location - Remove remaining Hires example files from Partition folder --- .../JacTestHelper.hpp} | 58 +++- .../Common/MicrogridNetwork.hpp | 309 ++++++++++++++++++ .../Common/PartitionUtilities.hpp | 238 ++++++++++++++ .../Partition/PartitionHires.cpp | 191 ----------- 4 files changed, 589 insertions(+), 207 deletions(-) rename examples/PowerElectronics/{Partition/jac_test_helper.hpp => Common/JacTestHelper.hpp} (78%) create mode 100644 examples/PowerElectronics/Common/MicrogridNetwork.hpp create mode 100644 examples/PowerElectronics/Common/PartitionUtilities.hpp delete mode 100644 examples/PowerElectronics/Partition/PartitionHires.cpp diff --git a/examples/PowerElectronics/Partition/jac_test_helper.hpp b/examples/PowerElectronics/Common/JacTestHelper.hpp similarity index 78% rename from examples/PowerElectronics/Partition/jac_test_helper.hpp rename to examples/PowerElectronics/Common/JacTestHelper.hpp index 34b84790e..8cd7db7e2 100644 --- a/examples/PowerElectronics/Partition/jac_test_helper.hpp +++ b/examples/PowerElectronics/Common/JacTestHelper.hpp @@ -2,12 +2,16 @@ #include #include +#include #include #include #include #include #include +#include + +#include "GridKit/Testing/Testing.hpp" namespace GridKit { @@ -26,6 +30,11 @@ namespace GridKit * exists in the subsystem Jacobian, and * - matching entries agree within the specified tolerance. * + * @note When the components in a subsystem are evaluated in a different order + * than in the monolithic reference system, a larger tolerance may be + * required to account for floating-point roundoff introduced by the + * different summation order. + * * @param full_jac Full-system Jacobian. * @param sub_jac Subsystem Jacobian. * @param subsystem Subsystem associated with sub_jac. @@ -40,7 +49,7 @@ namespace GridKit GridKit::LinearAlgebra::CsrMatrix& full_jac, GridKit::LinearAlgebra::CsrMatrix& sub_jac, GridKit::SubsystemModel& subsystem, - RealT tolerance = static_cast(1e-12)) + std::optional tolerance = std::nullopt) { constexpr auto host = memory::HOST; // Full-system CSR data. @@ -151,15 +160,20 @@ namespace GridKit const IdxT full_begin = full_rows[global_row]; const IdxT full_end = full_rows[global_row + 1]; - // Store the subsystem row using global column indices. The subsystem - // CSR row is sorted by local column index, but this ordering may change - // after converting the columns back to global indices. + /* + * Store the current subsystem row using global column indices. + * + * The subsystem Jacobian uses local column indices, while the full-system + * Jacobian uses global column indices. Convert each local column to its + * corresponding global column so the two rows can be compared directly. + */ std::unordered_map sub_row_entries; for (IdxT sub_index = sub_begin; sub_index < sub_end; ++sub_index) { const IdxT local_column = sub_cols[sub_index]; + // Fail if the subsystem column index is outside the valid local range. if (local_column >= num_sub_cols) { std::cout << "Invalid subsystem column index " @@ -173,7 +187,7 @@ namespace GridKit const IdxT global_column = local_to_global[local_column]; - // A CSR row should contain only one entry for each column. + // Fail if the subsystem row contains more than one entry for the same column. if (sub_row_entries.find(global_column) != sub_row_entries.end()) { std::cout << "Duplicate subsystem entry at (" @@ -188,13 +202,11 @@ namespace GridKit } // Compare full-system entries whose columns belong to the subsystem. - for (IdxT full_index = full_begin; - full_index < full_end; - ++full_index) + for (IdxT full_index = full_begin; full_index < full_end; ++full_index) { const IdxT global_column = full_cols[full_index]; - // Ignore entries whose column belongs outside the subsystem. + // No need to check if column belongs outside the subsystem. if (global_to_local.find(global_column) == global_to_local.end()) { continue; @@ -202,8 +214,7 @@ namespace GridKit auto sub_entry = sub_row_entries.find(global_column); - // The full Jacobian contains an entry that is missing from the - // subsystem Jacobian. + // Then it must exist in the subsystem Jacobian; fail otherwise. if (sub_entry == sub_row_entries.end()) { std::cout << "Entry exists only in full Jacobian at (" @@ -218,8 +229,23 @@ namespace GridKit const RealT sub_value = sub_entry->second; const RealT difference = std::abs(full_value - sub_value); - // Compare the values of matching Jacobian entries. - if (difference > tolerance) + // Different component evaluation orders can change the order of floating-point + // summation and introduce small roundoff differences. Use an appropriate + // tolerance when comparing against the monolithic reference. + // if tolerance is not supplied we simply use default machine precision provided + // by GridKit's Test::isEqual + auto isEqual = [&tolerance](RealT value, RealT reference) + { + if (tolerance) + { + return GridKit::Testing::isEqual(value, reference, *tolerance); + } + + return GridKit::Testing::isEqual(value, reference); + }; + + // Then the values must agree, fail otherwise + if (!isEqual(sub_value, full_value)) { std::cout << "Jacobian value mismatch at (" << global_row << ", " @@ -231,12 +257,12 @@ namespace GridKit matches = false; } - // Remove the matched entry. Any entries left after checking the - // full-system row exist only in the subsystem Jacobian. + // Remove the matched entry sub_row_entries.erase(sub_entry); } - // Report entries that exist only in the subsystem Jacobian. + // Any entries remaining must be missing from the + // full-system Jacobian, so this subsystem row contains incorrect entries, fail! for (const auto& entry : sub_row_entries) { const IdxT global_column = entry.first; diff --git a/examples/PowerElectronics/Common/MicrogridNetwork.hpp b/examples/PowerElectronics/Common/MicrogridNetwork.hpp new file mode 100644 index 000000000..03e8fcffb --- /dev/null +++ b/examples/PowerElectronics/Common/MicrogridNetwork.hpp @@ -0,0 +1,309 @@ +// MicrogridNetwork.hpp + +#pragma once + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace GridKit +{ + + using index_type = size_t; + using real_type = double; + + using SignalNode = GridKit::PowerElectronics::SignalNode; + using Bus = GridKit::PowerElectronics::MicrogridBus; + using BusDQ = GridKit::MicrogridBusDQ; + using DGGenerator = GridKit::DistributedGenerator; + using Line = GridKit::MicrogridLine; + using Load = GridKit::MicrogridLoad; + using GenParams = GridKit::DistributedGeneratorParameters; + + /* + * Contains components and nodes that make up the scaled microgrid network. + * + * The network contains 2 * N_size IBRs and stores the physical components + * that make up the scale microgrid. + */ + struct ScaleMicrogridNetwork + { + size_t model_id_next; + size_t N_size; + SignalNode dg_signal; + std::vector buses; + std::vector busesDQ; + std::vector generators; + std::vector lines; + std::vector loads; + std::vector DGParam_list; + + ScaleMicrogridNetwork(size_t n_size) + : model_id_next(0), + N_size(n_size), + buses(2 * n_size), + busesDQ(2 * n_size, nullptr), + generators(2 * n_size, nullptr), + lines(2 * n_size, nullptr), + loads(2 * n_size, nullptr), + DGParam_list(2 * n_size) + { + } + }; + + /** + * @brief Construct all components of a scaled microgrid network. + * + * Builds a microgrid containing @c 2*N_size generators and buses connected + * in a chain by transmission lines. Loads are connected to every other bus, + * and a virtual DQ bus is associated with each physical bus. + * + * The first two generators use the reference generator parameter set, while + * all remaining generators use the second parameter set. Line parameters + * alternate along the network, and the first load uses a different parameter + * set from the remaining loads. + * + * The created components are stored in @p network and assigned unique model + * identifiers using @c network.model_id_next. + * + * @param[in,out] network Network in which the microgrid components are + * constructed and stored. + * + * @pre @c network.N_size is greater than zero. + * @pre The component storage in @p network has been sized consistently with + * @c network.N_size. + * + * @post @p network contains @c 2*N_size generators and virtual DQ buses. + * @post @p network contains @c 2*N_size-1 transmission lines connecting + * consecutive buses. + * @post @p network contains @c N_size loads connected to every other bus. + * @post @c network.DGParam_list contains the parameters associated with each + * generator. + * @post @c network.model_id_next is advanced for every component created. + * + * @note Components are dynamically allocated and their pointers are stored + * in the corresponding network component vectors. + */ + inline void buildScaleMicrogridNetwork(ScaleMicrogridNetwork& network) + { + size_t N_size = network.N_size; + + assert(N_size > 0); + // Every Bus has the same virtual resistance. This is due to numerical stability as mentioned in the paper. + real_type RN = 1.0e4; + + // DG Params Vector + // All DGs have the same set of parameters except for the first two. + GenParams DG_parms1; + DG_parms1.wb_ = 2.0 * M_PI * 50.0; + DG_parms1.wc_ = 31.41; + DG_parms1.mp_ = 9.4e-5; + DG_parms1.Vn_ = 380.0; + DG_parms1.nq_ = 1.3e-3; + DG_parms1.F_ = 0.75; + DG_parms1.Kiv_ = 420.0; + DG_parms1.Kpv_ = 0.1; + DG_parms1.Kic_ = 2.0e4; + DG_parms1.Kpc_ = 15.0; + DG_parms1.Cf_ = 5.0e-5; + DG_parms1.rLf_ = 0.1; + DG_parms1.Lf_ = 1.35e-3; + DG_parms1.rLc_ = 0.03; + DG_parms1.Lc_ = 0.35e-3; + + GenParams DG_parms2; + DG_parms2.wb_ = 2.0 * M_PI * 50.0; + DG_parms2.wc_ = 31.41; + DG_parms2.mp_ = 12.5e-5; + DG_parms2.Vn_ = 380.0; + DG_parms2.nq_ = 1.5e-3; + DG_parms2.F_ = 0.75; + DG_parms2.Kiv_ = 390.0; + DG_parms2.Kpv_ = 0.05; + DG_parms2.Kic_ = 16.0e3; + DG_parms2.Kpc_ = 10.5; + DG_parms2.Cf_ = 50.0e-6; + DG_parms2.rLf_ = 0.1; + DG_parms2.Lf_ = 1.35e-3; + DG_parms2.rLc_ = 0.03; + DG_parms2.Lc_ = 0.35e-3; + + network.DGParam_list.assign(2 * N_size, DG_parms2); + + // First two generators use parameters 1 + if (network.DGParam_list.size() >= 1) + { + network.DGParam_list[0] = DG_parms1; + } + if (network.DGParam_list.size() >= 2) + { + network.DGParam_list[1] = DG_parms1; + } + + // line vector params + // Every odd line has the same parameters and every even line has the same parameters + real_type rline1 = 0.23; + real_type Lline1 = 0.1 / (2.0 * M_PI * 50.0); + real_type rline2 = 0.35; + real_type Lline2 = 0.58 / (2.0 * M_PI * 50.0); + std::vector rline_list(2 * N_size - 1, 0.0); + std::vector Lline_list(2 * N_size - 1, 0.0); + for (index_type i = 0; i < rline_list.size(); i++) + { + rline_list[i] = (i % 2) ? rline2 : rline1; + Lline_list[i] = (i % 2) ? Lline2 : Lline1; + } + + // load parms + // Only the first load has the same paramaters. + real_type rload1 = 3.0; + real_type Lload1 = 2.0 / (2.0 * M_PI * 50.0); + real_type rload2 = 2.0; + real_type Lload2 = 1.0 / (2.0 * M_PI * 50.0); + + std::vector rload_list(N_size, rload2); + std::vector Lload_list(N_size, Lload2); + if (rload_list.size() >= 1) + { + rload_list[0] = rload1; + Lload_list[0] = Lload1; + } + + // Create the reference generator + auto* dg_ref = new DGGenerator(network.model_id_next++, + network.DGParam_list[0], + true, + &network.dg_signal, + &network.buses[0]); + + network.generators[0] = dg_ref; + + // Create the remaining generators. + for (index_type i = 1; i < 2 * N_size; i++) + { + auto* dg = new DGGenerator(network.model_id_next++, + network.DGParam_list[i], + false, + &network.dg_signal, + &network.buses[i]); + + network.generators[i] = dg; + } + + // // Create transmission lines between consecutive buses. + for (index_type i = 0; i < 2 * N_size - 1; i++) + { + auto* line_model = new Line(network.model_id_next++, + rline_list[i], + Lline_list[i], + &network.dg_signal, + &network.buses[i], + &network.buses[i + 1]); + + network.lines[i + 1] = line_model; + } + + // Create loads on every other bus. + for (index_type i = 0; i < N_size; i++) + { + auto* load_model = new Load(network.model_id_next++, + rload_list[i], + Lload_list[i], + &network.dg_signal, + &network.buses[2 * i]); + + network.loads[2 * i] = load_model; + } + + // Create and Add all the microgrid Virtual DQ Buses + for (index_type i = 0; i < 2 * N_size; i++) + { + auto* virDQbus_model = new BusDQ(network.model_id_next++, + RN, + &network.buses[i]); + + network.busesDQ[i] = virDQbus_model; + } + } + + /** + * @brief Assemble a scaled microgrid network into a power electronics model. + * + * Adds the signal node, physical buses, generators, transmission lines, + * loads, and virtual DQ buses stored in @p network to @p sys_model. + * + * This function does not construct or allocate any network components. The + * physical network must already have been created by + * buildScaleMicrogridNetwork(). + * + * @param[in] network Constructed scaled microgrid network whose components + * are added to the system model. + * @param[in,out] sys_model Power electronics model to which the network + * components and nodes are added. + * + * @pre @c network.N_size is greater than zero. + * @pre @p network has been constructed by buildScaleMicrogridNetwork(). + * @pre All component and node pointers referenced by @p network are valid. + * + * @post The signal node and all physical buses in @p network have been added + * to @p sys_model. + * @post All generators, transmission lines, loads, and virtual DQ buses in + * @p network have been added to @p sys_model. + * + * @note This function only assembles the network into the system model. It + * does not call PowerElectronicsModel::allocate(). + */ + inline void assembleSystem(ScaleMicrogridNetwork& network, GridKit::PowerElectronicsModel& sys_model) + { + size_t N_size = network.N_size; + + // Ensure minimum size requirement + assert(N_size > 0); + + // Add all bus nodes + sys_model.addNode(&network.dg_signal); + + for (size_t i = 0; i < 2 * N_size; i++) + { + sys_model.addNode(&network.buses[i]); + } + + // Add all generators + for (index_type i = 0; i < 2 * N_size; i++) + { + sys_model.addComponent(network.generators[i]); + } + + // Load all the Line components + for (index_type i = 1; i < 2 * N_size; i++) + { + sys_model.addComponent(network.lines[i]); + } + + // Load all the Load components + for (index_type i = 0; i < 2 * N_size; i++) + { + if (network.loads[i] != nullptr) + { + sys_model.addComponent(network.loads[i]); + } + } + + // Add all the microgrid Virtual DQ Buses + for (index_type i = 0; i < 2 * N_size; i++) + { + sys_model.addComponent(network.busesDQ[i]); + } + } +} // namespace GridKit \ No newline at end of file diff --git a/examples/PowerElectronics/Common/PartitionUtilities.hpp b/examples/PowerElectronics/Common/PartitionUtilities.hpp new file mode 100644 index 000000000..21e7c9fbf --- /dev/null +++ b/examples/PowerElectronics/Common/PartitionUtilities.hpp @@ -0,0 +1,238 @@ +#pragma once + +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include "MicrogridNetwork.hpp" + +namespace GridKit +{ + + /** + * @brief Partition a scaled microgrid network into subsystem models. + * + * Divides the network into contiguous groups of IBRs and creates one + * subsystem for each group. The IBRs are distributed as evenly as possible, + * with any remainder assigned to the first partitions. + * + * The reference signal node is added to the first subsystem. A partition + * interface is added at the right boundary of every subsystem except the + * last to represent its connection to the neighboring subsystem. + * + * @param[in,out] network Scaled microgrid network to partition. + * @param[out] subsystems Vector populated with the created subsystem models. + * @param[in] num_partitions Number of subsystems to create. + * + * @pre The network has been constructed and contains @c 2*N_size IBRs. + * @pre @p num_partitions is greater than zero and does not exceed the + * number of IBRs in the network. + * @pre The generators, buses, virtual DQ buses, loads, and lines referenced + * by the network have valid lifetimes for use by the created subsystems. + * + * @post @p subsystems contains exactly @p num_partitions subsystem models. + * @post Every IBR in the network belongs to exactly one subsystem. + * @post Every subsystem except the last has a partition interface at its + * right boundary. + * + * @note The subsystem models are dynamically allocated. The caller is + * responsible for releasing and deleting them. + */ + template + void partitionNetwork( + ScaleMicrogridNetwork& network, + std::vector*>& subsystems, + size_t num_partitions) + { + const size_t num_ibrs = 2 * network.N_size; + + assert(num_partitions <= num_ibrs); + + subsystems.resize(num_partitions); + + IdxT q = num_ibrs / num_partitions; + IdxT r = num_ibrs % num_partitions; + IdxT index = 0; + + for (IdxT j = 0; j < num_partitions; j++) + { + auto* partition = + new GridKit::SubsystemModel(); + + // Add the reference signal node to the first partition. + if (j == 0) + { + partition->addNode(&network.dg_signal); + } + + IdxT part_size = q + (j < r ? 1 : 0); + IdxT end = std::min(index + part_size, num_ibrs); + + // Add all components belonging to this partition. + for (; index < end; ++index) + { + partition->addComponent(network.generators[index]); + partition->addComponent(network.busesDQ[index]); + + if (network.loads[index] != nullptr) + { + partition->addComponent(network.loads[index]); + } + + if (network.lines[index] != nullptr) + { + partition->addComponent(network.lines[index]); + } + + partition->addNode(&network.buses[index]); + } + + // Add the interface at the right boundary of the partition. + if (index < num_ibrs) + { + + auto* busInterface = new GridKit::BusPartitionInterface( + &network.buses[index - 1], + network.lines[index], + network.model_id_next++); + + busInterface->allocate(); + partition->addInterface(busInterface); + } + + subsystems[j] = partition; + } + } + + /** + * @brief Evaluate subsystem residuals and reconstruct the global residual. + * + * Distributes the global state and state-derivative vectors to each + * subsystem using its internal and external index mappings. Each subsystem + * residual is then evaluated in parallel, and its internal residual entries + * are gathered into the global residual vector. + * + * @param[in] subsystems Subsystem models to evaluate. + * @param[in] y Global state vector. + * @param[in] yp Global state-derivative vector. + * @param[out] f Global residual vector reconstructed from the subsystem + * residuals. + * @param[in] time Current simulation time. + * @param[in] alpha Jacobian scaling parameter associated with the current + * time-integration evaluation. + * + * @pre All subsystem models have been allocated. + * @pre @p y and @p yp contain all global entries + * @pre @p f is large enough to contain every global residual entry referenced + * by the subsystems. + * @pre The subsystems have disjoint internal index sets so that parallel + * writes to @p f do not overlap. + * + * @post Each subsystem residual has been evaluated at the supplied + * @p time and @p alpha. + * @post @p f contains the reconstructed residual for all internal variables + * represented by the subsystems. + */ + template + void evaluatePartitionResiduals( + const std::vector*>& subsystems, + const std::vector& y, + const std::vector& yp, + std::vector& f, + ScalarT time, + ScalarT alpha) + { +#ifdef _OPENMP +#pragma omp parallel for schedule(guided) +#endif + for (auto* partition : subsystems) + { + partition->updateTime(time, alpha); + + for (size_t i = 0; i < partition->getExternSize(); i++) + { + partition->getExternalDataY()[i] = y[partition->getExternalDataIndices()[i]]; + partition->getExternalDataYP()[i] = yp[partition->getExternalDataIndices()[i]]; + } + + auto* partition_y = partition->y().getData(); + auto* partition_yp = partition->yp().getData(); + + for (size_t i = 0; i < partition->getInternalSize(); i++) + { + partition_y[i] = y[partition->getNodeConnection(i)]; + partition_yp[i] = yp[partition->getNodeConnection(i)]; + } + + partition->y().setDataUpdated(); + partition->yp().setDataUpdated(); + + partition->evaluateResidual(); + + auto* residual = partition->getResidual().getData(); + + for (size_t i = 0; i < partition->getInternalSize(); i++) + { + f[partition->getNodeConnection(i)] = residual[i]; + } + } + } + + /** + * @brief Assemble the scaled microgrid from left to right. + * + * Adds the network components to @p sys_model in physical left-to-right + * order. For each bus location, the generator, virtual DQ bus, load, line, + * and bus associated with that location are added before moving to the next + * location in the network. + * + * This ordering is useful when comparing the monolithic system with + * partition-based evaluations that traverse the network from left to right. + * + * @param[in] network Constructed scaled microgrid network. + * @param[in,out] sys_model Power electronics model to populate. + * + * @pre @c network.N_size is greater than zero. + * @pre @p network has been constructed by buildScaleMicrogridNetwork(). + * @pre All component and node pointers stored in @p network are valid. + * + * @post All nodes and components in @p network have been added to + * @p sys_model in left-to-right network order. + */ + template + void assembleSystemLeftToRight( + ScaleMicrogridNetwork& network, + GridKit::PowerElectronicsModel& sys_model) + { + const size_t num_ibrs = 2 * network.N_size; + + assert(network.N_size > 0); + + sys_model.addNode(&network.dg_signal); + + for (IdxT i = 0; i < num_ibrs; ++i) + { + sys_model.addComponent(network.generators[i]); + sys_model.addComponent(network.busesDQ[i]); + + if (network.loads[i] != nullptr) + { + sys_model.addComponent(network.loads[i]); + } + + if (network.lines[i] != nullptr) + { + sys_model.addComponent(network.lines[i]); + } + + sys_model.addNode(&network.buses[i]); + } + } +} // namespace GridKit \ No newline at end of file diff --git a/examples/PowerElectronics/Partition/PartitionHires.cpp b/examples/PowerElectronics/Partition/PartitionHires.cpp deleted file mode 100644 index b34aaf165..000000000 --- a/examples/PowerElectronics/Partition/PartitionHires.cpp +++ /dev/null @@ -1,191 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "HiresBus.hpp" -#include "HiresComponent1.hpp" -#include "HiresComponent3.hpp" - -std::vector hires(const std::vector& y, const std::vector& yp); -void printRow(double residual, size_t index); -void printHeader(const std::string& title); -void printPartitionResiduals(auto* partition, const std::string& title); - -/** - * This example assembles the HIRES problem and partitions it to demonstrate - * that the partitioned system can reconstruct the full system. - * - * The HIRES problem consists of eight ODE equations and is divided into three - * components. HiresComponent1 contains equations 1, 2, and 3, - * HiresComponent3 contains equations 6, 7, and 8, and HiresBus contains - * equations 4 and 5. HiresBus plays a role similar to MicrogridBusDQ, where - * other components connected to the bus contribute terms to its equations. - * - * In the equations below, terms enclosed in parentheses ( ) are contributions - * from HiresComponent1, terms enclosed in square brackets [ ] are contributions - * from HiresComponent3, and the remaining terms in equations 4 and 5 belong - * to HiresBus. - * - * f_1 = dy_1/dt + 1.71y_1 - 0.43y_2 - 8.32y_3 - 0.0007 - * - * f_2 = dy_2/dt - 1.71y_1 + 8.75y_2 - * - * f_3 = dy_3/dt + 10.03y_3 - 0.43y_4 - 0.035y_5 - * - * f_4 = dy_4/dt + y_4 + (0.1y_4 - 8.32y_2 - 1.71y_3) + [0.02y_4] - * - * f_5 = dy_5/dt + y_5 + (0.7y_5) + [0.045y_5 - 0.43y_6 - 0.43y_7] - * - * f_6 = dy_6/dt - 280y_6y_8 + 0.69y_4 + 1.71y_5 - 0.43y_6 + 0.69y_7 - * - * f_7 = dy_7/dt + 280y_6y_8 - 1.81y_7 - * - * f_8 = dy_8/dt - 280y_6y_8 + 1.81y_7 - * - * - * The assembled system has the following structure: - * - * (HiresComponent1) -------- (HiresBus) -------- (HiresComponent3) - * - * The system is partitioned between HiresBus and HiresComponent3. A bus - * partition interface is introduced in the partition containing HiresBus to - * preserve the contribution of HiresComponent3 to the bus equations. The - * residuals evaluated independently by the partitions are then printed with - * the residual of the full system for eye-ball comparision. - * - * This example will also be useful later for testing the order of accuracy of co-simulation methods. - */ - -int main(int /* argc */, char const** /* argv */) -{ - - using Bus = GridKit::PowerElectronics::MicrogridBus; - Bus bus; - - GridKit::PowerElectronicsModel* sys = new GridKit::PowerElectronicsModel(); - GridKit::SubsystemModel* partition1 = new GridKit::SubsystemModel(); - GridKit::SubsystemModel* partition2 = new GridKit::SubsystemModel(); - - GridKit::HiresComponent1* comp1 = new GridKit::HiresComponent1(&bus, 1); - GridKit::HiresBus* bus1 = new GridKit::HiresBus(&bus, 2); - GridKit::HiresComponent3* comp3 = new GridKit::HiresComponent3(&bus, 3); - - sys->addComponent(comp1); - sys->addComponent(comp3); - sys->addComponent(bus1); - sys->addNode(&bus); - - sys->allocate(); - - std::vector y = {1, 2, 3, 6, 7, 8, 4, 5}; - std::vector yp = {1, 2, 3, 6, 7, 8, 4, 5}; - - auto sys_y = sys->y().getData(); - auto sys_yp = sys->yp().getData(); - - for (size_t i = 0; i < sys->size(); i++) - { - sys_y[i] = y[i]; - sys_yp[i] = yp[i]; - } - - sys->evaluateResidual(); - - GridKit::HiresComponent3* comp3copy = new GridKit::HiresComponent3(*comp3); - GridKit::BusPartitionInterface* busInterface = new GridKit::BusPartitionInterface(&bus, comp3copy, 4); - - partition1->addComponent(comp1); - partition1->addComponent(bus1); - partition1->addInterface(busInterface); - partition1->addNode(&bus); - - partition2->addComponent(comp3); - - auto subsystems = {partition1, partition2}; - - for (auto* partition : subsystems) - { - partition->allocate(); - partition->initialize(); - partition->updateTime(2, 5); - - for (size_t i = 0; i < partition->getExternSize(); i++) - { - partition->getExternalDataY()[i] = y[partition->getExternalDataIndices()[i]]; - partition->getExternalDataYP()[i] = yp[partition->getExternalDataIndices()[i]]; - } - - auto* partition_y = partition->y().getData(); - auto* partition_yp = partition->yp().getData(); - - for (size_t i = 0; i < partition->getInternalSize(); i++) - { - partition_y[i] = y[partition->getNodeConnection(i)]; - partition_yp[i] = yp[partition->getNodeConnection(i)]; - } - - partition->y().setDataUpdated(); - partition->yp().setDataUpdated(); - - partition->evaluateResidual(); - } - - printPartitionResiduals(partition1, "Partition 1"); - printPartitionResiduals(partition2, "Partition 2"); - - printHeader("Reference Output"); - auto* residual = sys->getResidual().getData(); - - for (size_t i = 0; i < sys->getInternalSize(); ++i) - { - printRow(residual[i], i); - } - - delete sys; - delete partition1; - delete partition2; - - return 0; -} - -void printRow(double residual, size_t index) -{ - std::cout << std::left << std::setw(10) - << std::defaultfloat << std::setprecision(5) - << residual - << " ---------- " - << std::right << std::setw(10) - << index - << '\n'; -} - -void printHeader(const std::string& title) -{ - std::cout << "\n------------- " << title << " -----------\n"; - - std::cout << std::left << std::setw(10) << "Res Values" - << " ---------- " - << std::right << std::setw(10) << "Comp. Index" - << '\n'; -} - -void printPartitionResiduals(auto* partition, const std::string& title) -{ - printHeader(title); - - auto* residual = partition->getResidual().getData(); - - for (size_t i = 0; i < partition->getInternalSize(); ++i) - { - const auto comp_index = partition->getNodeConnection(i); - printRow(residual[i], comp_index); - } -} From 54af398afaae1c26c887aa4890d302f47b1a70bd Mon Sep 17 00:00:00 2001 From: abdourahmanbarry Date: Sat, 15 Aug 2026 15:54:15 +0000 Subject: [PATCH 51/52] Cleaned up Microgrid examples - Refactored common code in the Microgrid, ScaleMicrogrid, and ScaleMicrogridArbitrary examples into reusable helper functions - Improved documentation across the PowerElectronics examples - Updated CMake files --- .../PowerElectronics/Microgrid/CMakeLists.txt | 6 + .../PowerElectronics/Microgrid/Microgrid.cpp | 157 +--- .../PowerElectronics/Partition/CMakeLists.txt | 45 +- .../Partition/PartitionMicrogrid.cpp | 478 +++++------- .../Partition/PartitionScaleMicrogrid.cpp | 698 ++++++++---------- .../ScaleMicrogrid/CMakeLists.txt | 11 + .../ScaleMicrogrid/ScaleMicrogrid.cpp | 168 +---- .../ScaleMicrogridArbitrary.cpp | 173 +---- 8 files changed, 572 insertions(+), 1164 deletions(-) diff --git a/examples/PowerElectronics/Microgrid/CMakeLists.txt b/examples/PowerElectronics/Microgrid/CMakeLists.txt index 6eae26be3..0ca695b05 100644 --- a/examples/PowerElectronics/Microgrid/CMakeLists.txt +++ b/examples/PowerElectronics/Microgrid/CMakeLists.txt @@ -1,4 +1,10 @@ add_executable(microgrid Microgrid.cpp) + +target_include_directories( + microgrid + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/.. +) + target_link_libraries( microgrid GridKit::power_elec_disgen diff --git a/examples/PowerElectronics/Microgrid/Microgrid.cpp b/examples/PowerElectronics/Microgrid/Microgrid.cpp index 74497fc41..2805a0b2d 100644 --- a/examples/PowerElectronics/Microgrid/Microgrid.cpp +++ b/examples/PowerElectronics/Microgrid/Microgrid.cpp @@ -1,21 +1,13 @@ #include -#include -#include -#include +#include #include -#include -#include -#include -#include -#include -#include -#include -#include #include #include #include +#include "Common/MicrogridNetwork.hpp" + int main(int /* argc */, char const** /* argv */) { /// @todo Needs to be modified. Some components are small relative to others thus @@ -28,139 +20,16 @@ int main(int /* argc */, char const** /* argv */) // Create model auto* sysmodel = new GridKit::PowerElectronicsModel(use_jac); - // Modeled after the problem in the paper - double RN = 1.0e4; - - // DG Params - static constexpr auto pi = std::numbers::pi_v; - - GridKit::DistributedGeneratorParameters parms1; - parms1.wb_ = 2.0 * pi * 50.0; - parms1.wc_ = 31.41; - parms1.mp_ = 9.4e-5; - parms1.Vn_ = 380.0; - parms1.nq_ = 1.3e-3; - parms1.F_ = 0.75; - parms1.Kiv_ = 420.0; - parms1.Kpv_ = 0.1; - parms1.Kic_ = 2.0e4; - parms1.Kpc_ = 15.0; - parms1.Cf_ = 5.0e-5; - parms1.rLf_ = 0.1; - parms1.Lf_ = 1.35e-3; - parms1.rLc_ = 0.03; - parms1.Lc_ = 0.35e-3; - - GridKit::DistributedGeneratorParameters parms2; - // Parameters from MATLAB Microgrid code for first DG - parms2.wb_ = 2.0 * pi * 50.0; - parms2.wc_ = 31.41; - parms2.mp_ = 12.5e-5; - parms2.Vn_ = 380.0; - parms2.nq_ = 1.5e-3; - parms2.F_ = 0.75; - parms2.Kiv_ = 390.0; - parms2.Kpv_ = 0.05; - parms2.Kic_ = 16.0e3; - parms2.Kpc_ = 10.5; - parms2.Cf_ = 50.0e-6; - parms2.rLf_ = 0.1; - parms2.Lf_ = 1.35e-3; - parms2.rLc_ = 0.03; - parms2.Lc_ = 0.35e-3; - - // Line params - double rline1 = 0.23; - double Lline1 = 0.1 / (2.0 * pi * 50.0); - - double rline2 = 0.35; - double Lline2 = 0.58 / (2.0 * pi * 50.0); - - double rline3 = 0.23; - double Lline3 = 0.1 / (2.0 * pi * 50.0); - - // load parms - double rload1 = 3.0; - double Lload1 = 2.0 / (2.0 * pi * 50.0); - - double rload2 = 2.0; - double Lload2 = 1.0 / (2.0 * pi * 50.0); - - using SignalNode = GridKit::PowerElectronics::SignalNode; - SignalNode dg_signal; - - sysmodel->addNode(&dg_signal); - - using Bus = GridKit::PowerElectronics::MicrogridBus; - Bus bus1; - Bus bus2; - Bus bus3; - Bus bus4; - - sysmodel->addNode(&bus1); - sysmodel->addNode(&bus2); - sysmodel->addNode(&bus3); - sysmodel->addNode(&bus4); - - // dg 1 - GridKit::DistributedGenerator* dg1 = new GridKit::DistributedGenerator( - 0, parms1, true, &dg_signal, &bus1); - sysmodel->addComponent(dg1); - - // dg 2 - GridKit::DistributedGenerator* dg2 = new GridKit::DistributedGenerator( - 1, parms1, false, &dg_signal, &bus2); - sysmodel->addComponent(dg2); - - // dg 3 - GridKit::DistributedGenerator* dg3 = new GridKit::DistributedGenerator( - 2, parms2, false, &dg_signal, &bus3); - sysmodel->addComponent(dg3); - - // dg 4 - GridKit::DistributedGenerator* dg4 = new GridKit::DistributedGenerator( - 3, parms2, false, &dg_signal, &bus4); - sysmodel->addComponent(dg4); - - // Lines - - // line 1 - GridKit::MicrogridLine* l1 = new GridKit::MicrogridLine( - 4, rline1, Lline1, &dg_signal, &bus1, &bus2); - sysmodel->addComponent(l1); - - // line 2 - GridKit::MicrogridLine* l2 = new GridKit::MicrogridLine( - 5, rline2, Lline2, &dg_signal, &bus2, &bus3); - sysmodel->addComponent(l2); - - // line 3 - GridKit::MicrogridLine* l3 = new GridKit::MicrogridLine( - 6, rline3, Lline3, &dg_signal, &bus3, &bus4); - sysmodel->addComponent(l3); - - // loads - - // load 1 - GridKit::MicrogridLoad* load1 = new GridKit::MicrogridLoad(7, rload1, Lload1, &dg_signal, &bus1); - sysmodel->addComponent(load1); - - // load 2 - GridKit::MicrogridLoad* load2 = new GridKit::MicrogridLoad(8, rload2, Lload2, &dg_signal, &bus3); - sysmodel->addComponent(load2); - - // Virtual PQ Buses - GridKit::MicrogridBusDQ* bus_para_1 = new GridKit::MicrogridBusDQ(9, RN, &bus1); - sysmodel->addComponent(bus_para_1); - - GridKit::MicrogridBusDQ* bus_para_2 = new GridKit::MicrogridBusDQ(10, RN, &bus2); - sysmodel->addComponent(bus_para_2); + // Build the four-generator microgrid network. + size_t N_size = 2; + GridKit::ScaleMicrogridNetwork network(N_size); - GridKit::MicrogridBusDQ* bus_para_3 = new GridKit::MicrogridBusDQ(11, RN, &bus3); - sysmodel->addComponent(bus_para_3); + GridKit::buildScaleMicrogridNetwork(network); + GridKit::assembleSystem(network, *sysmodel); - GridKit::MicrogridBusDQ* bus_para_4 = new GridKit::MicrogridBusDQ(12, RN, &bus4); - sysmodel->addComponent(bus_para_4); + // Generator parameters used to construct the initial conditions. + const auto& parms1 = network.DGParam_list[0]; + const auto& parms2 = network.DGParam_list[2]; sysmodel->allocate(); @@ -192,7 +61,7 @@ int main(int /* argc */, char const** /* argv */) } // since the intial P_com = 0 - y[dg_signal.getNodeConnection(0).idx_] = parms1.wb_; + y[network.dg_signal.getNodeConnection(0).idx_] = parms1.wb_; sysmodel->y().setDataUpdated(); sysmodel->yp().setDataUpdated(); @@ -348,4 +217,4 @@ int main(int /* argc */, char const** /* argv */) delete sysmodel; return 0; -} +} \ No newline at end of file diff --git a/examples/PowerElectronics/Partition/CMakeLists.txt b/examples/PowerElectronics/Partition/CMakeLists.txt index 249f07505..c92d2b180 100644 --- a/examples/PowerElectronics/Partition/CMakeLists.txt +++ b/examples/PowerElectronics/Partition/CMakeLists.txt @@ -1,15 +1,13 @@ find_package(OpenMP) -add_executable(partitionHires PartitionHires.cpp) +add_executable(PartitionMicrogrid PartitionMicrogrid.cpp) +target_include_directories( + PartitionMicrogrid + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/.. +) target_link_libraries( - partitionHires - PRIVATE GridKit::solvers_dyn GridKit::power_elec_partition_interfaces) - -add_executable(partitionMicrogrid PartitionMicrogrid.cpp) - -target_link_libraries( - partitionMicrogrid + PartitionMicrogrid PRIVATE GridKit::power_elec_disgen GridKit::power_elec_microline GridKit::power_elec_microload @@ -17,24 +15,29 @@ target_link_libraries( GridKit::power_elec_microbusdq GridKit::power_elec_partition_interfaces) -add_test(NAME PartitionMicrogrid COMMAND partitionMicrogrid) - -install(TARGETS partitionHires partitionMicrogrid RUNTIME DESTINATION bin) - -if(OpenMP_CXX_FOUND) - add_executable(PartitionScaleMicrogrid PartitionScaleMicrogrid.cpp) - - target_link_libraries( +add_executable(PartitionScaleMicrogrid PartitionScaleMicrogrid.cpp) +target_include_directories( + PartitionScaleMicrogrid + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/.. +) + +target_link_libraries( PartitionScaleMicrogrid PRIVATE GridKit::power_elec_disgen GridKit::power_elec_microline GridKit::power_elec_microload GridKit::solvers_dyn GridKit::power_elec_microbusdq - GridKit::power_elec_partition_interfaces - OpenMP::OpenMP_CXX) - - add_test(NAME PartitionScaleMicrogrid COMMAND PartitionScaleMicrogrid) + GridKit::power_elec_partition_interfaces) - install(TARGETS PartitionScaleMicrogrid RUNTIME DESTINATION bin) +if(OpenMP_CXX_FOUND) + target_link_libraries( + PartitionScaleMicrogrid + PRIVATE OpenMP::OpenMP_CXX) endif() + +add_test(NAME PartitionMicrogrid COMMAND PartitionMicrogrid) +add_test(NAME PartitionScaleMicrogrid COMMAND PartitionScaleMicrogrid) + +install(TARGETS PartitionMicrogrid RUNTIME DESTINATION bin) +install(TARGETS PartitionScaleMicrogrid RUNTIME DESTINATION bin) \ No newline at end of file diff --git a/examples/PowerElectronics/Partition/PartitionMicrogrid.cpp b/examples/PowerElectronics/Partition/PartitionMicrogrid.cpp index 9782f92f7..53f30281c 100644 --- a/examples/PowerElectronics/Partition/PartitionMicrogrid.cpp +++ b/examples/PowerElectronics/Partition/PartitionMicrogrid.cpp @@ -1,14 +1,11 @@ - +#include #include +#include #include #include #include #include -#define _USE_MATH_DEFINES -#include -#include - #include #include #include @@ -21,380 +18,250 @@ #include #include -#include "jac_test_helper.hpp" - -/** - * Partitioned Residual Evaluation - * - * Partition the microgrid into subsystems and evaluate each subsystem - * independently using the appropriate internal and external state values. - * - * The subsystem residuals are gathered into a global residual vector and - * compared with the monolithic residual to verify that the partitioned - * evaluation reproduces the full-system result. - */ +#include "Common/JacTestHelper.hpp" +#include "Common/MicrogridNetwork.hpp" +#include "Common/PartitionUtilities.hpp" using Component = GridKit::CircuitComponent; using Node = GridKit::PowerElectronics::NodeBase; +using Subsystem = GridKit::SubsystemModel; +using System = GridKit::PowerElectronicsModel; std::vector getComponentConnections(const std::vector& components); std::vector getNodeConnections(const std::vector& nodes); +/* + * Verify partitioned residual and Jacobian evaluation against the + * monolithic microgrid model. + * + * The microgrid is manually divided into two subsystems. A partition interface + * is introduced at the boundary between bus 1 and the line connecting buses 1 + * and 2. Each subsystem is evaluated independently, and its residuals are gathered + * and compared with the monolithic reference. + */ int main() { - bool use_jac = true; - - // Create model - auto* sysmodel = new GridKit::PowerElectronicsModel(use_jac); - - // Modeled after the problem in the paper - double RN = 1.0e4; - - // DG Params - GridKit::DistributedGeneratorParameters parms1; - parms1.wb_ = 2.0 * M_PI * 50.0; - parms1.wc_ = 31.41; - parms1.mp_ = 9.4e-5; - parms1.Vn_ = 380.0; - parms1.nq_ = 1.3e-3; - parms1.F_ = 0.75; - parms1.Kiv_ = 420.0; - parms1.Kpv_ = 0.1; - parms1.Kic_ = 2.0e4; - parms1.Kpc_ = 15.0; - parms1.Cf_ = 5.0e-5; - parms1.rLf_ = 0.1; - parms1.Lf_ = 1.35e-3; - parms1.rLc_ = 0.03; - parms1.Lc_ = 0.35e-3; - - GridKit::DistributedGeneratorParameters parms2; - // Parameters from MATLAB Microgrid code for first DG - parms2.wb_ = 2.0 * M_PI * 50.0; - parms2.wc_ = 31.41; - parms2.mp_ = 12.5e-5; - parms2.Vn_ = 380.0; - parms2.nq_ = 1.5e-3; - parms2.F_ = 0.75; - parms2.Kiv_ = 390.0; - parms2.Kpv_ = 0.05; - parms2.Kic_ = 16.0e3; - parms2.Kpc_ = 10.5; - parms2.Cf_ = 50.0e-6; - parms2.rLf_ = 0.1; - parms2.Lf_ = 1.35e-3; - parms2.rLc_ = 0.03; - parms2.Lc_ = 0.35e-3; - - // Line params - double rline1 = 0.23; - double Lline1 = 0.1 / (2.0 * M_PI * 50.0); - - double rline2 = 0.35; - double Lline2 = 0.58 / (2.0 * M_PI * 50.0); - - double rline3 = 0.23; - double Lline3 = 0.1 / (2.0 * M_PI * 50.0); - - // load parms - double rload1 = 3.0; - double Lload1 = 2.0 / (2.0 * M_PI * 50.0); - - double rload2 = 2.0; - double Lload2 = 1.0 / (2.0 * M_PI * 50.0); - - using SignalNode = GridKit::PowerElectronics::SignalNode; - using Subsystem = GridKit::SubsystemModel; - - SignalNode dg_signal; - - sysmodel->addNode(&dg_signal); - - using Bus = GridKit::PowerElectronics::MicrogridBus; - Bus bus1; - Bus bus2; - Bus bus3; - Bus bus4; - - sysmodel->addNode(&bus1); - sysmodel->addNode(&bus2); - sysmodel->addNode(&bus3); - sysmodel->addNode(&bus4); - - // dg 1 - GridKit::DistributedGenerator* dg1 = new GridKit::DistributedGenerator( - 0, parms1, true, &dg_signal, &bus1); - sysmodel->addComponent(dg1); - - // dg 2 - GridKit::DistributedGenerator* dg2 = new GridKit::DistributedGenerator( - 1, parms1, false, &dg_signal, &bus2); - sysmodel->addComponent(dg2); - - // dg 3 - GridKit::DistributedGenerator* dg3 = new GridKit::DistributedGenerator( - 2, parms2, false, &dg_signal, &bus3); - sysmodel->addComponent(dg3); - - // dg 4 - GridKit::DistributedGenerator* dg4 = new GridKit::DistributedGenerator( - 3, parms2, false, &dg_signal, &bus4); - sysmodel->addComponent(dg4); - - // Lines - - // line 1 - GridKit::MicrogridLine* l1 = new GridKit::MicrogridLine( - 4, rline1, Lline1, &dg_signal, &bus1, &bus2); - sysmodel->addComponent(l1); - - // line 2 - GridKit::MicrogridLine* l2 = new GridKit::MicrogridLine( - 5, rline2, Lline2, &dg_signal, &bus2, &bus3); - sysmodel->addComponent(l2); - - // line 3 - GridKit::MicrogridLine* l3 = new GridKit::MicrogridLine( - 6, rline3, Lline3, &dg_signal, &bus3, &bus4); - sysmodel->addComponent(l3); - - // loads - - // load 1 - GridKit::MicrogridLoad* load1 = new GridKit::MicrogridLoad(7, rload1, Lload1, &dg_signal, &bus1); - sysmodel->addComponent(load1); - - // load 2 - GridKit::MicrogridLoad* load2 = new GridKit::MicrogridLoad(8, rload2, Lload2, &dg_signal, &bus3); - sysmodel->addComponent(load2); - - // Virtual PQ Buses - GridKit::MicrogridBusDQ* bus_para_1 = new GridKit::MicrogridBusDQ(9, RN, &bus1); - sysmodel->addComponent(bus_para_1); - - GridKit::MicrogridBusDQ* bus_para_2 = new GridKit::MicrogridBusDQ(10, RN, &bus2); - sysmodel->addComponent(bus_para_2); - - GridKit::MicrogridBusDQ* bus_para_3 = new GridKit::MicrogridBusDQ(11, RN, &bus3); - sysmodel->addComponent(bus_para_3); - - GridKit::MicrogridBusDQ* bus_para_4 = new GridKit::MicrogridBusDQ(12, RN, &bus4); - sysmodel->addComponent(bus_para_4); - - sysmodel->allocate(); - - Subsystem* partition1 = new Subsystem(); - Subsystem* partition2 = new Subsystem(); - - GridKit::MicrogridLine* l2copy = new GridKit::MicrogridLine(*l2); - GridKit::BusPartitionInterface* busInterface1 = new GridKit::BusPartitionInterface(&bus2, l2copy, 14); - - std::vector partition1_components = {dg1, dg2, l1, load1, busInterface1, bus_para_1, bus_para_2}; - std::vector partition2_components = {dg3, dg4, l2, l3, load2, bus_para_4, bus_para_3}; - std::vector partition1_nodes = {&dg_signal, &bus1, &bus2}; - std::vector partition2_nodes = {&bus3, &bus4}; + constexpr size_t num_network_sections = 2; + constexpr double time = 0.1; + constexpr double alpha = 0.1; - std::vector component_global_indices_1 = getComponentConnections(partition1_components); - std::vector component_global_indices_2 = getComponentConnections(partition2_components); - std::vector node_global_indices_1 = getNodeConnections(partition1_nodes); - std::vector node_global_indices_2 = getNodeConnections(partition2_nodes); - - // --------------------------------------------------------------------------- - // Populate partition 1 - // -------------------------------------------------------------------------- - partition1->addNode(&dg_signal); - partition1->addComponent(dg1); - partition1->addComponent(dg2); - partition1->addComponent(l1); - partition1->addComponent(load1); - partition1->addInterface(busInterface1); - partition1->addComponent(bus_para_1); - partition1->addComponent(bus_para_2); - partition1->addNode(&bus1); - partition1->addNode(&bus2); + bool use_jac = true; // --------------------------------------------------------------------------- - // Populate partition 2 + // build the grid network and assemble the system model // --------------------------------------------------------------------------- - partition2->addComponent(dg3); - partition2->addComponent(dg4); - partition2->addComponent(l2); - partition2->addComponent(l3); - partition2->addComponent(load2); - partition2->addComponent(bus_para_4); - partition2->addComponent(bus_para_3); - partition2->addNode(&bus3); - partition2->addNode(&bus4); + GridKit::ScaleMicrogridNetwork network(num_network_sections); - // --------------------------------------------------------------------------- - // Initialize the full-system state and evaluate residuals and system Jacobian - // --------------------------------------------------------------------------- + GridKit::buildScaleMicrogridNetwork(network); + + auto* system = new System(use_jac); - std::vector y; - std::vector yp; + GridKit::assembleSystemLeftToRight(network, *system); + system->allocate(); - for (size_t i = 0; i < sysmodel->size(); i++) + std::vector y(system->size()); + std::vector yp(system->size()); + + for (size_t i = 0; i < system->size(); ++i) { - y.push_back(static_cast(i + 1)); - yp.push_back(static_cast(i + 1)); + y[i] = static_cast(i + 1); + yp[i] = static_cast(i + 1); } - auto* system_y = sysmodel->y().getData(); - auto* system_yp = sysmodel->yp().getData(); + auto* system_y = system->y().getData(); + auto* system_yp = system->yp().getData(); - for (size_t i = 0; i < sysmodel->size(); i++) + for (size_t i = 0; i < system->size(); ++i) { system_y[i] = y[i]; system_yp[i] = yp[i]; } - sysmodel->y().setDataUpdated(); - sysmodel->yp().setDataUpdated(); - - sysmodel->updateTime(2, 5); - sysmodel->evaluateResidual(); - sysmodel->evaluateJacobian(); - - auto full_jac = sysmodel->getCsrJacobian(); - - auto* f_sysmodel = sysmodel->getResidual().getData(); - - std::vector*> partitions = {partition1, partition2}; + system->y().setDataUpdated(); + system->yp().setDataUpdated(); + + system->updateTime(time, alpha); + system->evaluateResidual(); + system->evaluateJacobian(); + + auto* system_jacobian = system->getCsrJacobian(); + auto* system_residual = system->getResidual().getData(); + + //------------------------------------------------------------------------------ + // Gather all global indices belonging partition 1 and 2 to test release() later + //------------------------------------------------------------------------------ + std::vector components = { + network.generators[0], + network.generators[1], + network.lines[1], + network.loads[0], + network.busesDQ[0], + network.busesDQ[1], + network.generators[2], + network.generators[3], + network.lines[2], + network.lines[3], + network.loads[2], + network.busesDQ[2], + network.busesDQ[3]}; + + std::vector nodes = { + &network.dg_signal, + &network.buses[0], + &network.buses[1], + &network.buses[2], + &network.buses[3]}; + + const auto original_component_connections = getComponentConnections(components); + const auto original_node_connections = getNodeConnections(nodes); + + // -------------------------------------------------------------------------------- + // Create 2 Partitions and Partition interfaces + // -------------------------------------------------------------------------------- + + auto* bus_interface = new GridKit::BusPartitionInterface( + &network.buses[1], + network.lines[2], + 14); + + bus_interface->allocate(); + + auto* partition1 = new Subsystem(); + auto* partition2 = new Subsystem(); + + // -------------------------------------------------------------------------------- + // Manually add components, nodes and a bus partition interface to Partition 1 + // -------------------------------------------------------------------------------- + + partition1->addNode(&network.dg_signal); + partition1->addComponent(network.generators[0]); + partition1->addComponent(network.busesDQ[0]); + partition1->addComponent(network.loads[0]); + partition1->addNode(&network.buses[0]); + partition1->addComponent(network.lines[1]); + partition1->addComponent(network.generators[1]); + partition1->addComponent(network.busesDQ[1]); + partition1->addInterface(bus_interface); + partition1->addNode(&network.buses[1]); // --------------------------------------------------------------------------- - // Allocate and evaluate the subsystem partitions + // Manually add components and nodes to Partition 2 // --------------------------------------------------------------------------- - // Distribute externals to partition 1 + partition2->addComponent(network.generators[2]); + partition2->addComponent(network.busesDQ[2]); + partition2->addComponent(network.loads[2]); + partition2->addComponent(network.lines[2]); + partition2->addNode(&network.buses[2]); + partition2->addComponent(network.generators[3]); + partition2->addComponent(network.busesDQ[3]); + partition2->addComponent(network.lines[3]); + partition2->addNode(&network.buses[3]); + + std::vector partitions = {partition1, partition2}; + for (auto* partition : partitions) { partition->allocate(); - partition->initialize(); - - partition->updateTime(2, 5); - - // Test setTimeFunction function - auto forcing = [&]([[maybe_unused]] double t) -> Subsystem::ForcingData - { - auto ext_size = partition->getExternSize(); - - std::vector y_ext(ext_size); - std::vector yp_ext(ext_size); - - for (size_t i = 0; i < partition->getExternSize(); i++) - { - y_ext[i] = y[partition->getExternalDataIndices()[i]]; - yp_ext[i] = yp[partition->getExternalDataIndices()[i]]; - } - - return {y_ext, yp_ext}; - }; - - partition->setTimeFunction(forcing); - - auto* partition_y = partition->y().getData(); - auto* partition_yp = partition->yp().getData(); - - for (size_t i = 0; i < partition->getInternalSize(); i++) - { - partition_y[i] = y[partition->getNodeConnection(i)]; - partition_yp[i] = yp[partition->getNodeConnection(i)]; - } + } - partition->y().setDataUpdated(); - partition->yp().setDataUpdated(); + std::vector partition_residual(system->size(), 0.0); - partition->evaluateResidual(); - partition->evaluateJacobian(); - } + GridKit::evaluatePartitionResiduals(partitions, y, yp, partition_residual, time, alpha); // --------------------------------------------------------------------------- // Verify the subsystem Jacobians // --------------------------------------------------------------------------- - auto partition1_jac = partition1->getCsrJacobian(); - auto partition2_jac = partition2->getCsrJacobian(); - bool jac_matched_1 = GridKit::Testing::verifySubsystemJacobian(*full_jac, *partition1_jac, *partition1); - bool jac_matched_2 = GridKit::Testing::verifySubsystemJacobian(*full_jac, *partition2_jac, *partition2); + bool jacobians_match = true; - std::cout << "Jacobian Matched: " << jac_matched_1 * jac_matched_2 << std::endl; + for (auto* partition : partitions) + { + partition->evaluateJacobian(); - if (!jac_matched_1 || !jac_matched_2) + auto* partition_jacobian = partition->getCsrJacobian(); + + jacobians_match = jacobians_match && GridKit::Testing::verifySubsystemJacobian(*system_jacobian, *partition_jacobian, *partition); + } + + if (!jacobians_match) { - std::cout << "ERROR: At least one subsystem Jacobian is incorrect!" << std::endl; + std::cout << "ERROR: At least one subsystem Jacobian is incorrect!\n"; return 1; } // --------------------------------------------------------------------------- - // Gather and verify the subsystem residuals + // Gather and verify the subsystem residuals against monolithic residuals // --------------------------------------------------------------------------- - std::vector f(sysmodel->size(), 0.0); - std::vector error(sysmodel->size(), 1.0); - // Get internal residuals from partition 1 - for (auto* partition : partitions) - { - const auto* residual = partition->getResidual().getData(); + double max_error = 0.0; - for (size_t i = 0; i < partition->getInternalSize(); i++) - { + for (size_t i = 0; i < system->size(); ++i) + { + const double error = std::abs(system_residual[i] - partition_residual[i]) / std::abs(system_residual[i] + 1); - f[partition->getNodeConnection(i)] = residual[i]; - } + max_error = std::max(max_error, error); } - double max_error = 0; - for (size_t i = 0; i < sysmodel->size(); i++) - { - error[i] = (f_sysmodel[i] - f[i]) / f_sysmodel[i]; + const double machine_epsilon = + std::numeric_limits::epsilon(); - if (max_error < std::abs(error[i])) - { - max_error = std::abs(error[i]); - } - } + const bool residuals_match = max_error <= machine_epsilon; - std::cout << "\nMax Error of Reference and Partition Evaluation: " << max_error << std::endl; + std::cout << "\nPartition Microgrid Validation\n"; + std::cout << "------------------------------\n"; - if (max_error > std::numeric_limits::epsilon()) - { - std::cout << "ERROR: Max Error too high!" << std::endl; - return 1; - } + std::cout << std::left + << std::setw(32) << "Maximum residual error:" + << std::setprecision(16) + << max_error << '\n'; + + std::cout << std::left + << std::setw(32) << "Machine epsilon:" + << machine_epsilon << '\n'; + + std::cout << std::left + << std::setw(32) << "Residuals matched:" + << (residuals_match ? "True" : "False") + << '\n'; // --------------------------------------------------------------------------- // Verify subsystem release() from SubsystemModel // --------------------------------------------------------------------------- - partitions[0]->release(); - partitions[1]->release(); - if (getComponentConnections(partition1_components) != component_global_indices_1 - || getNodeConnections(partition1_nodes) != node_global_indices_1) + for (auto* partition : partitions) { - std::cout << "ERROR: Subsystem release did not restore " - "the original global connection indices!\n"; - return 1; + partition->release(); } - if (getComponentConnections(partition2_components) != component_global_indices_2 - || getNodeConnections(partition2_nodes) != node_global_indices_2) + const bool components_restored = getComponentConnections(components) == original_component_connections; + const bool nodes_restored = getNodeConnections(nodes) == original_node_connections; + + if (!components_restored || !nodes_restored) { std::cout << "ERROR: Subsystem release did not restore " "the original global connection indices!\n"; + return 1; } // --------------------------------------------------------------------------- // Clean up // --------------------------------------------------------------------------- - delete sysmodel; - delete partition1; - delete partition2; + delete system; + + for (auto* partition : partitions) + { + delete partition; + } - return 0; + return residuals_match ? 0 : 1; } +/** + * @brief Collect the connection indices of a set of components. + */ std::vector getComponentConnections(const std::vector& components) { std::vector connections; @@ -410,6 +277,9 @@ std::vector getComponentConnections(const std::vector& compo return connections; } +/** + * @brief Collect the connection indices of a set of nodes. + */ std::vector getNodeConnections(const std::vector& nodes) { std::vector connections; @@ -422,4 +292,4 @@ std::vector getNodeConnections(const std::vector& nodes) } return connections; -} +} \ No newline at end of file diff --git a/examples/PowerElectronics/Partition/PartitionScaleMicrogrid.cpp b/examples/PowerElectronics/Partition/PartitionScaleMicrogrid.cpp index 0b87fdc3c..116e7836f 100644 --- a/examples/PowerElectronics/Partition/PartitionScaleMicrogrid.cpp +++ b/examples/PowerElectronics/Partition/PartitionScaleMicrogrid.cpp @@ -21,482 +21,408 @@ #include #include -#include "jac_test_helper.hpp" +#include "Common/JacTestHelper.hpp" +#include "Common/MicrogridNetwork.hpp" +#include "Common/PartitionUtilities.hpp" using index_type = size_t; using real_type = double; +using SignalNode = GridKit::PowerElectronics::SignalNode; +using Bus = GridKit::PowerElectronics::MicrogridBus; +using BusDQ = GridKit::MicrogridBusDQ; +using DGGenerator = GridKit::DistributedGenerator; +using Line = GridKit::MicrogridLine; +using Load = GridKit::MicrogridLoad; + +/* + * Output data for each parallel function evaluation run + */ struct RunResult { bool success = true; index_type num_partitions; - real_type partition_time; // seconds - real_type monolithic_time; // seconds - real_type speedup; // monolithic / partition + real_type partition_eval_time; // seconds + real_type monolithic_eval_time; // seconds + real_type speedup; // monolithic / partition real_type max_error; - std::string subsys_jac; + std::string jacobian_status; }; -RunResult printMicrogridSystems(index_type N_size, index_type num_partitions); +/* + * Reference data from the monolithic system evaluation. + * + * The monolithic system is evaluated once and its states, residual, and + * evaluation time are stored here. Each partition configuration reuses this + * data for validation and performance comparison. + */ +struct MonolithicReference +{ + std::vector y; + std::vector yp; + std::vector residual; + real_type monolithic_eval_time; +}; /** - * @brief Run Scale Microgrid for a specific N given by the user. + * @brief Evaluate the monolithic system and store reference data. + * + * Initializes the global state and state-derivative vectors with deterministic + * values, evaluates and times the monolithic residual, evaluates the full + * Jacobian, and stores the resulting data for later comparison with + * partitioned evaluations. + * + * The residual evaluation time is measured independently from the Jacobian + * evaluation so that the stored timing represents only the cost of the + * monolithic residual evaluation. + * + * @param[in,out] system Allocated monolithic power electronics system. * - * @param[in] argc should be 1 if no N given, 2 if N given - * @param[in] argv can contain the N value (argv[1]) - * @return int + * @return MonolithicReference containing the state vector, state-derivative + * vector, residual, and residual evaluation time. + * + * @pre @p system is non-null. + * @pre @p system has already been assembled and allocated. + * + * @post The state and state-derivative vectors of @p system contain the + * deterministic reference values. + * @post The monolithic residual and Jacobian have been evaluated. + * @post The returned reference contains an independent copy of the + * monolithic residual. */ -int main() +MonolithicReference evaluateMonolithicSystem(GridKit::PowerElectronicsModel* system) { - index_type N_size = 5; // Number of IBRs/2 - std::vector num_partitions_list = {1, 2, 3, 4, 5}; + MonolithicReference reference; - std::cout << std::format("{:<16}{:>16}{:>18}{:>12}{:>14}{:>16}\n", - "num_partitions", - "partition_time", - "monolithic_time", - "speedup", - "error", - "Jacobians"); + // --------------------------------------------------------------------------- + // Initialize the reference state and state-derivative vectors + // --------------------------------------------------------------------------- + reference.y.resize(system->size()); + reference.yp.resize(system->size()); - std::cout << std::string(93, '-') << "\n"; + for (size_t i = 0; i < system->size(); ++i) + { + reference.y[i] = static_cast(i + 1); + reference.yp[i] = static_cast(i + 1); + } - for (index_type p : num_partitions_list) + // Copy the reference values into the vectors owned by the system model. + auto* system_y = system->y().getData(); + auto* system_yp = system->yp().getData(); + + for (size_t i = 0; i < system->size(); ++i) { - assert(p <= 2 * N_size); + system_y[i] = reference.y[i]; + system_yp[i] = reference.yp[i]; + } - RunResult r = printMicrogridSystems(N_size, p); + system->y().setDataUpdated(); + system->yp().setDataUpdated(); - if (!r.success) - { - return 1; - } + // --------------------------------------------------------------------------- + // Evaluate and time the monolithic residual + // --------------------------------------------------------------------------- - std::cout << std::format("{:<16d}{:>14.4f} s{:>16.4f} s{:>11.2f}x{:>14.3e} {:>16s}\n", - r.num_partitions, - r.partition_time, - r.monolithic_time, - r.speedup, - r.max_error, - r.subsys_jac); - } + auto start_time = std::chrono::high_resolution_clock::now(); - return 0; + system->updateTime(0.1, 0.1); + system->evaluateResidual(); + + auto end_time = std::chrono::high_resolution_clock::now(); + + reference.monolithic_eval_time = std::chrono::duration(end_time - start_time).count(); + + // evaluate the monolithic Jacobian + system->evaluateJacobian(); + + // Store a copy of the residual return reference data + auto* residual = system->getResidual().getData(); + + reference.residual.assign(residual, residual + system->size()); + + return reference; } /** - * @brief Tests network of distributed generators. + * @brief Evaluate one partition configuration and validate it against the + * monolithic reference system. + * + * Creates the requested subsystem decomposition, allocates each subsystem, + * evaluates the partitioned residual, verifies each subsystem Jacobian against + * the monolithic Jacobian, and compares the reconstructed global residual with + * the stored monolithic residual. + * + * The partitioned residual evaluation is timed independently and compared with + * the previously measured monolithic residual evaluation time to compute the + * resulting speedup. + * + * @param[in,out] network Scaled microgrid network to partition. + * @param[in] system Allocated monolithic system used as the reference. + * @param[in] reference Stored monolithic state, derivative, residual, and + * timing information. + * @param[in] num_partitions Number of subsystem partitions to create. + * + * @return Performance and validation results for the requested partition count. + * + * @pre @p system is non-null and has already been evaluated. + * @pre @p network has been constructed using buildScaleMicrogridNetwork(). + * @pre @p num_partitions is greater than zero and does not exceed the number + * of IBRs in the network. + * @pre @p reference contains state and derivative vectors consistent with the + * size of @p system. * - * @param[in] N_size - The number of DG line load cobinations to generate for scale - * @return int returns 0 if successful, >0 otherwise + * @post All temporary subsystem models created by this function are released + * and deleted before returning. + * @post The returned result reports the partition timing, speedup, residual + * error, and subsystem Jacobian validation status. */ -RunResult printMicrogridSystems(index_type N_size, index_type num_partitions) +RunResult evaluatePartitioning( + GridKit::ScaleMicrogridNetwork& network, + GridKit::PowerElectronicsModel* system, + const MonolithicReference& reference, + index_type num_partitions) { - using namespace GridKit; - const index_type num_ibrs = 2 * N_size; + // --------------------------------------------------------------------------- + // Create and allocate subsystem partitions + // --------------------------------------------------------------------------- - assert(num_partitions <= num_ibrs); + std::vector*> subsystems; - bool use_jac = true; + GridKit::partitionNetwork(network, subsystems, num_partitions); - // Create circuit model - auto* sys_model_control = new PowerElectronicsModel(use_jac); - - // Ensure minimum size requirement - assert(N_size >= 1); - - // Modeled after the problem in the paper - // Every Bus has the same virtual resistance. This is due to numerical stability as mentioned in the paper. - real_type RN = 1.0e4; - - // DG Params Vector - // All DGs have the same set of parameters except for the first two. - GridKit::DistributedGeneratorParameters DG_parms1; - DG_parms1.wb_ = 2.0 * M_PI * 50.0; - DG_parms1.wc_ = 31.41; - DG_parms1.mp_ = 9.4e-5; - DG_parms1.Vn_ = 380.0; - DG_parms1.nq_ = 1.3e-3; - DG_parms1.F_ = 0.75; - DG_parms1.Kiv_ = 420.0; - DG_parms1.Kpv_ = 0.1; - DG_parms1.Kic_ = 2.0e4; - DG_parms1.Kpc_ = 15.0; - DG_parms1.Cf_ = 5.0e-5; - DG_parms1.rLf_ = 0.1; - DG_parms1.Lf_ = 1.35e-3; - DG_parms1.rLc_ = 0.03; - DG_parms1.Lc_ = 0.35e-3; - - GridKit::DistributedGeneratorParameters DG_parms2; - DG_parms2.wb_ = 2.0 * M_PI * 50.0; - DG_parms2.wc_ = 31.41; - DG_parms2.mp_ = 12.5e-5; - DG_parms2.Vn_ = 380.0; - DG_parms2.nq_ = 1.5e-3; - DG_parms2.F_ = 0.75; - DG_parms2.Kiv_ = 390.0; - DG_parms2.Kpv_ = 0.05; - DG_parms2.Kic_ = 16.0e3; - DG_parms2.Kpc_ = 10.5; - DG_parms2.Cf_ = 50.0e-6; - DG_parms2.rLf_ = 0.1; - DG_parms2.Lf_ = 1.35e-3; - DG_parms2.rLc_ = 0.03; - DG_parms2.Lc_ = 0.35e-3; - - std::vector> DGParams_list(2 * N_size, DG_parms2); - - // First two generators use parameters 1 - if (DGParams_list.size() >= 1) - { - DGParams_list[0] = DG_parms1; - } - if (DGParams_list.size() >= 2) + for (auto* partition : subsystems) { - DGParams_list[1] = DG_parms1; + partition->allocate(); } - // line vector params - // Every odd line has the same parameters and every even line has the same parameters - real_type rline1 = 0.23; - real_type Lline1 = 0.1 / (2.0 * M_PI * 50.0); - real_type rline2 = 0.35; - real_type Lline2 = 0.58 / (2.0 * M_PI * 50.0); - std::vector rline_list(2 * N_size - 1, 0.0); - std::vector Lline_list(2 * N_size - 1, 0.0); - for (index_type i = 0; i < rline_list.size(); i++) - { - rline_list[i] = (i % 2) ? rline2 : rline1; - Lline_list[i] = (i % 2) ? Lline2 : Lline1; - } + // Global residual reconstructed from the subsystem residuals. + std::vector f(system->size(), 1.0); + // Elementwise error between the monolithic and reconstructed residuals. + std::vector error(system->size(), 1.0); - // load parms - // Only the first load has the same paramaters. - real_type rload1 = 3.0; - real_type Lload1 = 2.0 / (2.0 * M_PI * 50.0); - real_type rload2 = 2.0; - real_type Lload2 = 1.0 / (2.0 * M_PI * 50.0); + // --------------------------------------------------------------------------- + // Evaluate and time the partitioned residual + // --------------------------------------------------------------------------- + auto start_time = std::chrono::high_resolution_clock::now(); - std::vector rload_list(N_size, rload2); - std::vector Lload_list(N_size, Lload2); - if (rload_list.size() >= 1) - { - rload_list[0] = rload1; - Lload_list[0] = Lload1; - } + GridKit::evaluatePartitionResiduals(subsystems, reference.y, reference.yp, f, 0.1, 0.1); - using SignalNode = GridKit::PowerElectronics::SignalNode; - using Bus = GridKit::PowerElectronics::MicrogridBus; - using BusDQ = MicrogridBusDQ; - using Generator = DistributedGenerator; - using Line = MicrogridLine; - using Load = MicrogridLoad; - - std::vector buses(num_ibrs); - std::vector busesDQ(num_ibrs, nullptr); - std::vector generators(num_ibrs, nullptr); - std::vector lines(num_ibrs, nullptr); - std::vector loads(num_ibrs, nullptr); - std::vector linesCopies; - - SignalNode dg_signal; - // sys_model_control->addNode(&dg_signal); - - // for (size_t i = 0; i < 2 * N_size; i++) - // { - // buses[i] = new Bus(); - // sys_model_control->addNode(buses[i]); - // } - - // Create the reference DG - auto* dg_ref = new DistributedGenerator(0, - DGParams_list[0], - true, - &dg_signal, - &buses[0]); - // sys_model_control->addComponent(dg_ref); - - generators[0] = dg_ref; - - // Keep track of models and index location - index_type model_id = 1; - // Add all other DGs - for (index_type i = 1; i < 2 * N_size; i++) - { - // current DG to add - auto* dg = new DistributedGenerator(model_id++, - DGParams_list[i], - false, - &dg_signal, - &buses[i]); - - generators[i] = dg; - // sys_model_control->addComponent(dg); - } + auto end_time = std::chrono::high_resolution_clock::now(); - // Load all the Line components - for (index_type i = 0; i < 2 * N_size - 1; i++) - { - // line - auto* line_model = new MicrogridLine(model_id++, - rline_list[i], - Lline_list[i], - &dg_signal, - &buses[i], - &buses[i + 1]); - lines[i + 1] = line_model; - // sys_model_control->addComponent(line_model); - } + auto partition_eval_time = std::chrono::duration(end_time - start_time); - // Load all the Load components - for (index_type i = 0; i < N_size; i++) - { - auto* load_model = new MicrogridLoad(model_id++, - rload_list[i], - Lload_list[i], - &dg_signal, - &buses[2 * i]); - loads[2 * i] = load_model; - // sys_model_control->addComponent(load_model); - } + // --------------------------------------------------------------------------- + // Verify subsystem Jacobians against the monolithic Jacobian + // --------------------------------------------------------------------------- - // Add all the microgrid Virtual DQ Buses - for (index_type i = 0; i < 2 * N_size; i++) + auto* system_jacobian = system->getCsrJacobian(); + + bool jacobian_match = true; + + for (auto* partition : subsystems) { - auto* virDQbus_model = new MicrogridBusDQ(model_id++, RN, &buses[i]); + partition->evaluateJacobian(); - busesDQ[i] = virDQbus_model; - // sys_model_control->addComponent(virDQbus_model); + jacobian_match = jacobian_match && GridKit::Testing::verifySubsystemJacobian(*system_jacobian, *partition->getCsrJacobian(), *partition); } - sys_model_control->addNode(&dg_signal); - for (index_type i = 0; i < num_ibrs; ++i) - { - sys_model_control->addComponent(generators[i]); - sys_model_control->addComponent(busesDQ[i]); + // --------------------------------------------------------------------------- + // Compare the reconstructed and monolithic residuals + // --------------------------------------------------------------------------- + real_type max_error = 0.0; - if (loads[i] != nullptr) - { - sys_model_control->addComponent(loads[i]); - } + for (size_t i = 0; i < system->size(); ++i) + { + error[i] = std::abs(f[i] - reference.residual[i]) / (reference.residual[i] + 1.0); - if (lines[i] != nullptr) + if (max_error < error[i]) { - sys_model_control->addComponent(lines[i]); + max_error = error[i]; } - sys_model_control->addNode(&buses[i]); } - sys_model_control->allocate(); - - std::vector y; - std::vector yp; + // --------------------------------------------------------------------------- + // Store performance and validation results + // --------------------------------------------------------------------------- + RunResult result; - for (size_t i = 0; i < sys_model_control->size(); i++) + result.num_partitions = num_partitions; + result.partition_eval_time = partition_eval_time.count(); + result.monolithic_eval_time = reference.monolithic_eval_time; + result.speedup = reference.monolithic_eval_time / partition_eval_time.count(); + result.max_error = max_error; + result.jacobian_status = jacobian_match ? "Correct" : "Wrong"; + + // --------------------------------------------------------------------------- + // Check validation results + // --------------------------------------------------------------------------- + if (!jacobian_match) { - y.push_back(static_cast(i + 1)); - yp.push_back(static_cast(i + 1)); - } - - auto start_time = std::chrono::high_resolution_clock::now(); + std::cout << "ERROR: At least one subsystem Jacobian is incorrect!" + << std::endl; - auto* sys_model_y = sys_model_control->y().getData(); - auto* sys_model_yp = sys_model_control->yp().getData(); + result.success = false; + } - for (size_t i = 0; i < sys_model_control->size(); i++) + if (max_error > std::numeric_limits::epsilon()) { - sys_model_y[i] = y[i]; - sys_model_yp[i] = yp[i]; + std::cout << "ERROR: Max Error too high!: " << max_error << std::endl; + result.success = false; } - sys_model_control->y().setDataUpdated(); - sys_model_control->yp().setDataUpdated(); - - sys_model_control->updateTime(2, 5); - sys_model_control->evaluateResidual(); + // --------------------------------------------------------------------------- + // Release and destroy temporary subsystem models + // --------------------------------------------------------------------------- - auto end_time = std::chrono::high_resolution_clock::now(); - auto monolithic_time = std::chrono::duration(end_time - start_time); - - sys_model_control->evaluateJacobian(); - - auto* f_sysmodel_control = sys_model_control->getResidual().getData(); - auto* full_jac = sys_model_control->getCsrJacobian(); - - //--------------------------------------------------------------- - // Partition the monolithic network into independent subsystem - // models. Each subsystem owns a contiguous block of generators, - // buses, lines, and loads. Whenever a partition boundary is - // encountered, a BusPartitionInterface is inserted to expose the - // neighboring partition's boundary variables while maintaining - // the same electrical connectivity as the original network. - //--------------------------------------------------------------- - - std::vector*> subsystems(num_partitions); - - // Partition the system - index_type q = (num_ibrs) / num_partitions; - index_type r = (num_ibrs) % num_partitions; - index_type index = 0; - for (index_type j = 0; j < num_partitions; j++) + for (auto* partition : subsystems) { - auto* partition = new SubsystemModel(); - - // add Reference rotor to the first partition - if (j == 0) - { - partition->addNode(&dg_signal); - } + partition->release(); + delete partition; + } - index_type part_size = q + (j < r ? 1 : 0); - index_type end = std::min(index + part_size, num_ibrs); + return result; +} - // Add all components belonging to this partition - for (; index < end; ++index) +/** + * @brief Benchmark and validate partitioned residual evaluation of a large + * scaled microgrid. It also confirms the correctness of the subsystem + * Jacobian. + * + * Builds a scaled microgrid once, constructs a monolithic reference system, + * and compares several subsystem decompositions of the same physical network. + * + * For each requested partition count, the benchmark measures the partitioned + * residual evaluation time, computes its speedup relative to the monolithic + * evaluation, verifies the reconstructed residual, and checks every subsystem + * Jacobian against the monolithic Jacobian. + * + * The physical network and monolithic system are constructed only once. + * Individual subsystem models are created and destroyed separately for each + * partition count. + * + * @return 0 if all residual and Jacobian validation checks pass; otherwise 1. + */ +int main(int argc, char const* argv[]) +{ + index_type N_size = 5000; + + std::vector num_partitions_list = {500}; + + /* + * If command-line arguments are provided, the first argument specifies + * N_size and all remaining arguments specify partition counts. + * + * Example: + * ./PartitionScaleMicrogrid 5000 10 48 100 500 + */ + if (argc > 1) + { + try { - partition->addComponent(generators[index]); - partition->addComponent(busesDQ[index]); + N_size = static_cast(std::stoull(argv[1])); - if (loads[index] != nullptr) + if (N_size < 1) { - partition->addComponent(loads[index]); + std::cerr << "ERROR: N_size must be at least 1.\n"; + return 1; } - if (lines[index] != nullptr) + // When N_size is supplied explicitly, at least one partition count + // must also be supplied. + if (argc < 3) { - partition->addComponent(lines[index]); + std::cerr << "ERROR: At least one partition count must be provided " + << "when N_size is specified.\n"; + return 1; } - partition->addNode(&buses[index]); + num_partitions_list.clear(); + + for (int i = 2; i < argc; ++i) + { + index_type num_partitions = static_cast(std::stoull(argv[i])); + + if (num_partitions < 1) + { + std::cerr << "ERROR: Number of partitions must be at least 1.\n"; + return 1; + } + + if (num_partitions > 2 * N_size) + { + std::cerr << "ERROR: Number of partitions (" + << num_partitions + << ") cannot exceed the number of IBRs (" + << 2 * N_size + << ").\n"; + return 1; + } + + num_partitions_list.push_back(num_partitions); + } } - // Add the partition interface to the left partition - if (index < num_ibrs) + catch (const std::exception& e) { - auto* linecopy = new GridKit::MicrogridLine(*lines[index]); - auto* busInterface = new GridKit::BusPartitionInterface(&buses[index - 1], linecopy, model_id++); - partition->addInterface(busInterface); - } - - subsystems[j] = partition; - } - - //--------------------------------------------------------------- - // Allocate each subsystem and evaluate the partitioned residual. - // - // The global state vectors (y and yp) are scattered into the - // corresponding internal and external vectors of every subsystem. - // After evaluating the residuals independently, the local - // residuals are gathered back into the global residual vector for - // comparison with the monolithic evaluation. - //--------------------------------------------------------------- + std::cerr + << "ERROR: Invalid command-line argument: " + << e.what() + << "\n"; - std::vector f(sys_model_control->size(), 1.0); - std::vector error(sys_model_control->size(), 1.0); - - for (auto* partition : subsystems) - { - partition->allocate(); - partition->updateTime(2, 5); - } - - start_time = std::chrono::high_resolution_clock::now(); - -// Distribute externals to partition 1 -#pragma omp parallel for schedule(guided) - for (auto* partition : subsystems) - { - // Distribute external variables - for (size_t i = 0; i < partition->getExternSize(); i++) - { - partition->getExternalDataY()[i] = y[partition->getExternalDataIndices()[i]]; - partition->getExternalDataYP()[i] = yp[partition->getExternalDataIndices()[i]]; + return 1; } + } - auto* partition_y = partition->y().getData(); - auto* partition_yp = partition->yp().getData(); - - // Distribute internal variables - for (size_t i = 0; i < partition->getInternalSize(); i++) - { - partition_y[i] = y[partition->getNodeConnection(i)]; - partition_yp[i] = yp[partition->getNodeConnection(i)]; - } + bool use_jac = true; - partition->y().setDataUpdated(); - partition->yp().setDataUpdated(); + // Build the physical network once. + GridKit::ScaleMicrogridNetwork network(N_size); + GridKit::buildScaleMicrogridNetwork(network); - // Evaluate residual of this partition - partition->evaluateResidual(); + // Build, assemble and allocate the monolithic system once. + auto* system = new GridKit::PowerElectronicsModel(use_jac); - auto* residual = partition->getResidual().getData(); - // Reconstructs the monolithic residual from the partition residuals - for (size_t i = 0; i < partition->getInternalSize(); i++) - { - f[partition->getNodeConnection(i)] = residual[i]; - } - } + GridKit::assembleSystemLeftToRight(network, *system); + system->allocate(); - end_time = std::chrono::high_resolution_clock::now(); - auto partition_time = std::chrono::duration(end_time - start_time); + // Evaluate the monolithic reference once. + MonolithicReference reference = evaluateMonolithicSystem(system); - //--------------------------------------------------------------- - // Compare the reconstructed partition residual against the - // reference monolithic residual. The per-entry error is computed - // and the maximum error is reported to verify that the partitioned - // formulation reproduces the monolithic model within numerical - // roundoff. - //--------------------------------------------------------------- + std::cout << std::format("{:<16}{:>16}{:>18}{:>12}{:>14}{:>16}\n", + "num_partitions", + "partition_time", + "monolithic_time", + "speedup", + "error", + "Jacobians"); - bool matched = true; + std::cout << std::string(93, '-') << "\n"; - for (auto* partition : subsystems) + // Only the partitioned system is rebuilt and evaluated for each partition count. + for (index_type p : num_partitions_list) { - partition->evaluateJacobian(); - - matched &= GridKit::Testing::verifySubsystemJacobian( - *full_jac, - *partition->getCsrJacobian(), - *partition); - } + assert(p <= 2 * N_size); - real_type max_error = 0; - for (size_t i = 0; i < sys_model_control->size(); i++) - { - error[i] = abs(f[i] - f_sysmodel_control[i]) / (f_sysmodel_control[i] + 1); + // Takes in the network and partition it into p partitions, and performs parallel function eval + RunResult r = evaluatePartitioning(network, system, reference, p); - if (max_error < error[i]) + if (!r.success) { - max_error = error[i]; + delete system; + return 1; } - } - RunResult result; - result.num_partitions = num_partitions; - result.partition_time = partition_time.count(); - result.monolithic_time = monolithic_time.count(); - result.speedup = monolithic_time.count() / partition_time.count(); - result.max_error = max_error; - result.subsys_jac = matched ? "Correct" : "Wrong"; - - if (!matched) - { - std::cout << "ERROR: At least one subsystem Jacobian is incorrect!" << std::endl; - result.success = false; - } - - if (max_error > std::numeric_limits::epsilon()) - { - std::cout << "ERROR: Max Error too high!" << std::endl; - result.success = false; + // Output the results from partition evaluation + std::cout << std::format("{:<16d}{:>14.4f} s{:>16.4f} s{:>11.2f}x{:>14.3e} {:>16s}\n", + r.num_partitions, + r.partition_eval_time, + r.monolithic_eval_time, + r.speedup, + r.max_error, + r.jacobian_status); } - for (auto* partition : subsystems) - { - delete partition; - } - delete sys_model_control; + delete system; - return result; -} + return 0; +} \ No newline at end of file diff --git a/examples/PowerElectronics/ScaleMicrogrid/CMakeLists.txt b/examples/PowerElectronics/ScaleMicrogrid/CMakeLists.txt index 0334a939b..d495ef484 100644 --- a/examples/PowerElectronics/ScaleMicrogrid/CMakeLists.txt +++ b/examples/PowerElectronics/ScaleMicrogrid/CMakeLists.txt @@ -1,5 +1,16 @@ add_executable(scalemicrogrid ScaleMicrogrid.cpp) add_executable(scalemicrogridarbitrary ScaleMicrogridArbitrary.cpp) + +target_include_directories( + scalemicrogrid + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/.. +) + +target_include_directories( + scalemicrogridarbitrary + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/.. +) + target_link_libraries( scalemicrogrid GridKit::power_elec_disgen diff --git a/examples/PowerElectronics/ScaleMicrogrid/ScaleMicrogrid.cpp b/examples/PowerElectronics/ScaleMicrogrid/ScaleMicrogrid.cpp index cc8dc28ee..beea9f640 100644 --- a/examples/PowerElectronics/ScaleMicrogrid/ScaleMicrogrid.cpp +++ b/examples/PowerElectronics/ScaleMicrogrid/ScaleMicrogrid.cpp @@ -1,22 +1,14 @@ #include -#include -#include -#include #include -#include -#include - -#include -#include -#include -#include -#include -#include +#include + #include #include #include #include +#include "Common/MicrogridNetwork.hpp" + using index_type = size_t; using real_type = double; @@ -94,143 +86,11 @@ int test(index_type Nsize, real_type error_tol, bool debug_output) std::cout << "Using default Nsize = 8.\n"; } - // Modeled after the problem in the paper - // Every Bus has the same virtual resistance. This is due to the numerical stability as mentioned in the paper. - real_type RN = 1.0e4; - - // DG Params Vector - // All DGs have the same set of parameters except for the first two. - static constexpr auto pi = std::numbers::pi_v; - - GridKit::DistributedGeneratorParameters DG_parms1; - DG_parms1.wb_ = 2.0 * pi * 50.0; - DG_parms1.wc_ = 31.41; - DG_parms1.mp_ = 9.4e-5; - DG_parms1.Vn_ = 380.0; - DG_parms1.nq_ = 1.3e-3; - DG_parms1.F_ = 0.75; - DG_parms1.Kiv_ = 420.0; - DG_parms1.Kpv_ = 0.1; - DG_parms1.Kic_ = 2.0e4; - DG_parms1.Kpc_ = 15.0; - DG_parms1.Cf_ = 5.0e-5; - DG_parms1.rLf_ = 0.1; - DG_parms1.Lf_ = 1.35e-3; - DG_parms1.rLc_ = 0.03; - DG_parms1.Lc_ = 0.35e-3; - - GridKit::DistributedGeneratorParameters DG_parms2; - DG_parms2.wb_ = 2.0 * pi * 50.0; - DG_parms2.wc_ = 31.41; - DG_parms2.mp_ = 12.5e-5; - DG_parms2.Vn_ = 380.0; - DG_parms2.nq_ = 1.5e-3; - DG_parms2.F_ = 0.75; - DG_parms2.Kiv_ = 390.0; - DG_parms2.Kpv_ = 0.05; - DG_parms2.Kic_ = 16.0e3; - DG_parms2.Kpc_ = 10.5; - DG_parms2.Cf_ = 50.0e-6; - DG_parms2.rLf_ = 0.1; - DG_parms2.Lf_ = 1.35e-3; - DG_parms2.rLc_ = 0.03; - DG_parms2.Lc_ = 0.35e-3; - - std::vector> DGParams_list(2 * Nsize, DG_parms2); - - DGParams_list[0] = DG_parms1; - DGParams_list[1] = DG_parms1; - - // line vector params - // Every odd line has the same parameters and every even line has the same parameters - real_type rline1 = 0.23; - real_type Lline1 = 0.1 / (2.0 * pi * 50.0); - real_type rline2 = 0.35; - real_type Lline2 = 0.58 / (2.0 * pi * 50.0); - std::vector rline_list(2 * Nsize - 1, 0.0); - std::vector Lline_list(2 * Nsize - 1, 0.0); - for (index_type i = 0; i < rline_list.size(); i++) - { - rline_list[i] = (i % 2) ? rline2 : rline1; - Lline_list[i] = (i % 2) ? Lline2 : Lline1; - } + // Build and assemble the scaled microgrid network. + ScaleMicrogridNetwork network(Nsize); - // load parms - // Only the first load has the same paramaters. - real_type rload1 = 3.0; - real_type Lload1 = 2.0 / (2.0 * pi * 50.0); - real_type rload2 = 2.0; - real_type Lload2 = 1.0 / (2.0 * pi * 50.0); - - std::vector rload_list(Nsize, rload2); - std::vector Lload_list(Nsize, Lload2); - rload_list[0] = rload1; - Lload_list[0] = Lload1; - - using SignalNode = GridKit::PowerElectronics::SignalNode; - SignalNode dg_signal; - sys_model->addNode(&dg_signal); - - using Bus = GridKit::PowerElectronics::MicrogridBus; - std::unique_ptr[]> buses = std::make_unique[]>(2 * Nsize); - for (size_t i = 0; i < 2 * Nsize; i++) - { - buses[i] = std::make_unique(); - sys_model->addNode(buses[i].get()); - } - - // Create the reference DG - auto* dg_ref = new DistributedGenerator(0, - DGParams_list[0], - true, - &dg_signal, - buses[0].get()); - sys_model->addComponent(dg_ref); - - // Keep track of models and index location - index_type model_id = 1; - // Add all other DGs - for (index_type i = 1; i < 2 * Nsize; i++) - { - // current DG to add - auto* dg = new DistributedGenerator(model_id++, - DGParams_list[i], - false, - &dg_signal, - buses[i].get()); - sys_model->addComponent(dg); - } - - // Load all the Line compoenents - for (index_type i = 0; i < 2 * Nsize - 1; i++) - { - // line - auto* line_model = new MicrogridLine(model_id++, - rline_list[i], - Lline_list[i], - &dg_signal, - buses[i].get(), - buses[i + 1].get()); - sys_model->addComponent(line_model); - } - - // Load all the Load components - for (index_type i = 0; i < Nsize; i++) - { - auto* load_model = new MicrogridLoad(model_id++, - rload_list[i], - Lload_list[i], - &dg_signal, - buses[2 * i].get()); - sys_model->addComponent(load_model); - } - - // Add all the microgrid Virtual DQ Buses - for (index_type i = 0; i < 2 * Nsize; i++) - { - auto* virDQbus_model = new MicrogridBusDQ(model_id++, RN, buses[i].get()); - sys_model->addComponent(virDQbus_model); - } + buildScaleMicrogridNetwork(network); + assembleSystem(network, *sys_model); // allocate all the intial conditions sys_model->allocate(); @@ -253,13 +113,15 @@ int test(index_type Nsize, real_type error_tol, bool debug_output) // Create initial derivatives specifics generated in MATLAB for (index_type i = 0; i < 2 * Nsize; i++) { - yp[13 * i - 1 + 2] = DGParams_list[i].Vn_; - yp[13 * i - 1 + 4] = DGParams_list[i].Kpv_ * DGParams_list[i].Vn_; - yp[13 * i - 1 + 6] = (DGParams_list[i].Kpc_ * DGParams_list[i].Kpv_ * DGParams_list[i].Vn_) / DGParams_list[i].Lf_; + const auto& params = network.DGParam_list[i]; + + yp[13 * i - 1 + 2] = params.Vn_; + yp[13 * i - 1 + 4] = params.Kpv_ * params.Vn_; + yp[13 * i - 1 + 6] = (params.Kpc_ * params.Kpv_ * params.Vn_) / params.Lf_; } // since the intial P_com = 0, the set the intial vector to the reference frame - y[dg_signal.getNodeConnection(0).idx_] = DG_parms1.wb_; + y[network.dg_signal.getNodeConnection(0).idx_] = network.DGParam_list[0].wb_; sys_model->y().setDataUpdated(); sys_model->yp().setDataUpdated(); @@ -345,4 +207,4 @@ int test(index_type Nsize, real_type error_tol, bool debug_output) std::cout << "Test with Nsize = " << Nsize << " fails!\n"; return 1; } -} +} \ No newline at end of file diff --git a/examples/PowerElectronics/ScaleMicrogrid/ScaleMicrogridArbitrary.cpp b/examples/PowerElectronics/ScaleMicrogrid/ScaleMicrogridArbitrary.cpp index a8ab6a4a1..6007b6746 100644 --- a/examples/PowerElectronics/ScaleMicrogrid/ScaleMicrogridArbitrary.cpp +++ b/examples/PowerElectronics/ScaleMicrogrid/ScaleMicrogridArbitrary.cpp @@ -1,20 +1,13 @@ #include -#include -#include -#include #include -#include -#include -#include -#include -#include -#include #include #include #include #include +#include "Common/MicrogridNetwork.hpp" + using index_type = size_t; using real_type = double; @@ -79,149 +72,11 @@ int printMicrogridSystems(index_type N_size) return 1; } - // Modeled after the problem in the paper - // Every Bus has the same virtual resistance. This is due to numerical stability as mentioned in the paper. - real_type RN = 1.0e4; - - // DG Params Vector - // All DGs have the same set of parameters except for the first two. - static constexpr auto pi = std::numbers::pi_v; - - GridKit::DistributedGeneratorParameters DG_parms1; - DG_parms1.wb_ = 2.0 * pi * 50.0; - DG_parms1.wc_ = 31.41; - DG_parms1.mp_ = 9.4e-5; - DG_parms1.Vn_ = 380.0; - DG_parms1.nq_ = 1.3e-3; - DG_parms1.F_ = 0.75; - DG_parms1.Kiv_ = 420.0; - DG_parms1.Kpv_ = 0.1; - DG_parms1.Kic_ = 2.0e4; - DG_parms1.Kpc_ = 15.0; - DG_parms1.Cf_ = 5.0e-5; - DG_parms1.rLf_ = 0.1; - DG_parms1.Lf_ = 1.35e-3; - DG_parms1.rLc_ = 0.03; - DG_parms1.Lc_ = 0.35e-3; - - GridKit::DistributedGeneratorParameters DG_parms2; - DG_parms2.wb_ = 2.0 * pi * 50.0; - DG_parms2.wc_ = 31.41; - DG_parms2.mp_ = 12.5e-5; - DG_parms2.Vn_ = 380.0; - DG_parms2.nq_ = 1.5e-3; - DG_parms2.F_ = 0.75; - DG_parms2.Kiv_ = 390.0; - DG_parms2.Kpv_ = 0.05; - DG_parms2.Kic_ = 16.0e3; - DG_parms2.Kpc_ = 10.5; - DG_parms2.Cf_ = 50.0e-6; - DG_parms2.rLf_ = 0.1; - DG_parms2.Lf_ = 1.35e-3; - DG_parms2.rLc_ = 0.03; - DG_parms2.Lc_ = 0.35e-3; - - std::vector> DGParams_list(2 * N_size, DG_parms2); - - // First two generators use parameters 1 - if (DGParams_list.size() >= 1) - DGParams_list[0] = DG_parms1; - if (DGParams_list.size() >= 2) - DGParams_list[1] = DG_parms1; - - // line vector params - // Every odd line has the same parameters and every even line has the same parameters - real_type rline1 = 0.23; - real_type Lline1 = 0.1 / (2.0 * pi * 50.0); - real_type rline2 = 0.35; - real_type Lline2 = 0.58 / (2.0 * pi * 50.0); - std::vector rline_list(2 * N_size - 1, 0.0); - std::vector Lline_list(2 * N_size - 1, 0.0); - for (index_type i = 0; i < rline_list.size(); i++) - { - rline_list[i] = (i % 2) ? rline2 : rline1; - Lline_list[i] = (i % 2) ? Lline2 : Lline1; - } - - // load parms - // Only the first load has the same paramaters. - real_type rload1 = 3.0; - real_type Lload1 = 2.0 / (2.0 * pi * 50.0); - real_type rload2 = 2.0; - real_type Lload2 = 1.0 / (2.0 * pi * 50.0); - - std::vector rload_list(N_size, rload2); - std::vector Lload_list(N_size, Lload2); - if (rload_list.size() >= 1) - { - rload_list[0] = rload1; - Lload_list[0] = Lload1; - } - - using SignalNode = GridKit::PowerElectronics::SignalNode; - SignalNode dg_signal; - sys_model.addNode(&dg_signal); - - using Bus = GridKit::PowerElectronics::MicrogridBus; - std::unique_ptr[]> buses = std::make_unique[]>(2 * N_size); - for (size_t i = 0; i < 2 * N_size; i++) - { - buses[i] = std::make_unique(); - sys_model.addNode(buses[i].get()); - } - - // Create the reference DG - auto* dg_ref = new DistributedGenerator(0, - DGParams_list[0], - true, - &dg_signal, - buses[0].get()); - sys_model.addComponent(dg_ref); - - // Keep track of models and index location - index_type model_id = 1; - // Add all other DGs - for (index_type i = 1; i < 2 * N_size; i++) - { - // current DG to add - auto* dg = new DistributedGenerator(model_id++, - DGParams_list[i], - false, - &dg_signal, - buses[i].get()); - sys_model.addComponent(dg); - } - - // Load all the Line components - for (index_type i = 0; i < 2 * N_size - 1; i++) - { - // line - auto* line_model = new MicrogridLine(model_id++, - rline_list[i], - Lline_list[i], - &dg_signal, - buses[i].get(), - buses[i + 1].get()); - sys_model.addComponent(line_model); - } - - // Load all the Load components - for (index_type i = 0; i < N_size; i++) - { - auto* load_model = new MicrogridLoad(model_id++, - rload_list[i], - Lload_list[i], - &dg_signal, - buses[2 * i].get()); - sys_model.addComponent(load_model); - } + // Build and assemble the scaled microgrid network. + ScaleMicrogridNetwork network(N_size); - // Add all the microgrid Virtual DQ Buses - for (index_type i = 0; i < 2 * N_size; i++) - { - auto* virDQbus_model = new MicrogridBusDQ(model_id++, RN, buses[i].get()); - sys_model.addComponent(virDQbus_model); - } + buildScaleMicrogridNetwork(network); + assembleSystem(network, sys_model); // allocate all the initial conditions sys_model.allocate(); @@ -236,16 +91,22 @@ int printMicrogridSystems(index_type N_size) yp[i] = 0.0; } + //------------------------------------------------------------------- // Create Initial derivatives specifics generated in MATLAB + //------------------------------------------------------------------- for (index_type i = 0; i < 2 * N_size; i++) { - yp[13 * i - 1 + 3] = DGParams_list[i].Vn_; - yp[13 * i - 1 + 5] = DGParams_list[i].Kpv_ * DGParams_list[i].Vn_; - yp[13 * i - 1 + 7] = (DGParams_list[i].Kpc_ * DGParams_list[i].Kpv_ * DGParams_list[i].Vn_) / DGParams_list[i].Lf_; + const auto& params = network.DGParam_list[i]; + + yp[13 * i - 1 + 3] = params.Vn_; + yp[13 * i - 1 + 5] = params.Kpv_ * params.Vn_; + yp[13 * i - 1 + 7] = (params.Kpc_ * params.Kpv_ * params.Vn_) / params.Lf_; } + //--------------------------------------------------------------------------- // since the initial P_com = 0, set the initial vector to the reference frame - y[dg_signal.getNodeConnection(0).idx_] = DG_parms1.wb_; + //--------------------------------------------------------------------------- + y[network.dg_signal.getNodeConnection(0).idx_] = network.DGParam_list[0].wb_; sys_model.y().setDataUpdated(); sys_model.yp().setDataUpdated(); @@ -271,4 +132,4 @@ int printMicrogridSystems(index_type N_size) idas.runSimulation(t_final); return 0; -} +} \ No newline at end of file From 938a7fff059fdb9d45ada02b3bd80dc3109af499 Mon Sep 17 00:00:00 2001 From: abdourahmanbarry Date: Sat, 15 Aug 2026 16:30:16 +0000 Subject: [PATCH 52/52] Clean up --- .../BusPartitionInterface.cpp | 2 +- .../BusPartitionInterface.hpp | 2 +- .../Common/MicrogridNetwork.hpp | 2 +- .../Common/PartitionUtilities.hpp | 2 +- .../PowerElectronics/Microgrid/CMakeLists.txt | 3 +- .../PowerElectronics/Microgrid/Microgrid.cpp | 2 +- .../PowerElectronics/Partition/CMakeLists.txt | 28 ++++++++----------- .../Partition/PartitionMicrogrid.cpp | 2 +- .../Partition/PartitionScaleMicrogrid.cpp | 2 +- .../ScaleMicrogrid/CMakeLists.txt | 6 ++-- .../ScaleMicrogrid/ScaleMicrogrid.cpp | 2 +- .../ScaleMicrogridArbitrary.cpp | 2 +- tests/UnitTests/PowerElectronics/README.md | 2 +- 13 files changed, 25 insertions(+), 32 deletions(-) diff --git a/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.cpp b/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.cpp index b83580718..98c5ac704 100644 --- a/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.cpp +++ b/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.cpp @@ -373,4 +373,4 @@ namespace GridKit template class BusPartitionInterface; template class BusPartitionInterface; -} // namespace GridKit \ No newline at end of file +} // namespace GridKit diff --git a/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.hpp b/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.hpp index 0ebf566a4..0a61407e4 100644 --- a/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.hpp +++ b/GridKit/Model/PowerElectronics/PartitionInterface/BusPartitionInterface.hpp @@ -87,4 +87,4 @@ namespace GridKit // Identifies which wrapped component variables are external. std::vector is_external_; }; -} // namespace GridKit \ No newline at end of file +} // namespace GridKit diff --git a/examples/PowerElectronics/Common/MicrogridNetwork.hpp b/examples/PowerElectronics/Common/MicrogridNetwork.hpp index 03e8fcffb..84bc1b118 100644 --- a/examples/PowerElectronics/Common/MicrogridNetwork.hpp +++ b/examples/PowerElectronics/Common/MicrogridNetwork.hpp @@ -306,4 +306,4 @@ namespace GridKit sys_model.addComponent(network.busesDQ[i]); } } -} // namespace GridKit \ No newline at end of file +} // namespace GridKit diff --git a/examples/PowerElectronics/Common/PartitionUtilities.hpp b/examples/PowerElectronics/Common/PartitionUtilities.hpp index 21e7c9fbf..d55119da4 100644 --- a/examples/PowerElectronics/Common/PartitionUtilities.hpp +++ b/examples/PowerElectronics/Common/PartitionUtilities.hpp @@ -235,4 +235,4 @@ namespace GridKit sys_model.addNode(&network.buses[i]); } } -} // namespace GridKit \ No newline at end of file +} // namespace GridKit diff --git a/examples/PowerElectronics/Microgrid/CMakeLists.txt b/examples/PowerElectronics/Microgrid/CMakeLists.txt index 0ca695b05..86dea6678 100644 --- a/examples/PowerElectronics/Microgrid/CMakeLists.txt +++ b/examples/PowerElectronics/Microgrid/CMakeLists.txt @@ -2,8 +2,7 @@ add_executable(microgrid Microgrid.cpp) target_include_directories( microgrid - PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/.. -) + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/..) target_link_libraries( microgrid diff --git a/examples/PowerElectronics/Microgrid/Microgrid.cpp b/examples/PowerElectronics/Microgrid/Microgrid.cpp index 2805a0b2d..5e844e91d 100644 --- a/examples/PowerElectronics/Microgrid/Microgrid.cpp +++ b/examples/PowerElectronics/Microgrid/Microgrid.cpp @@ -217,4 +217,4 @@ int main(int /* argc */, char const** /* argv */) delete sysmodel; return 0; -} \ No newline at end of file +} diff --git a/examples/PowerElectronics/Partition/CMakeLists.txt b/examples/PowerElectronics/Partition/CMakeLists.txt index c92d2b180..cd182dcd3 100644 --- a/examples/PowerElectronics/Partition/CMakeLists.txt +++ b/examples/PowerElectronics/Partition/CMakeLists.txt @@ -3,8 +3,7 @@ find_package(OpenMP) add_executable(PartitionMicrogrid PartitionMicrogrid.cpp) target_include_directories( PartitionMicrogrid - PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/.. -) + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/..) target_link_libraries( PartitionMicrogrid @@ -18,26 +17,23 @@ target_link_libraries( add_executable(PartitionScaleMicrogrid PartitionScaleMicrogrid.cpp) target_include_directories( PartitionScaleMicrogrid - PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/.. -) - + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/..) + target_link_libraries( - PartitionScaleMicrogrid - PRIVATE GridKit::power_elec_disgen - GridKit::power_elec_microline - GridKit::power_elec_microload - GridKit::solvers_dyn - GridKit::power_elec_microbusdq - GridKit::power_elec_partition_interfaces) + PartitionScaleMicrogrid + PRIVATE GridKit::power_elec_disgen + GridKit::power_elec_microline + GridKit::power_elec_microload + GridKit::solvers_dyn + GridKit::power_elec_microbusdq + GridKit::power_elec_partition_interfaces) if(OpenMP_CXX_FOUND) - target_link_libraries( - PartitionScaleMicrogrid - PRIVATE OpenMP::OpenMP_CXX) + target_link_libraries(PartitionScaleMicrogrid PRIVATE OpenMP::OpenMP_CXX) endif() add_test(NAME PartitionMicrogrid COMMAND PartitionMicrogrid) add_test(NAME PartitionScaleMicrogrid COMMAND PartitionScaleMicrogrid) install(TARGETS PartitionMicrogrid RUNTIME DESTINATION bin) -install(TARGETS PartitionScaleMicrogrid RUNTIME DESTINATION bin) \ No newline at end of file +install(TARGETS PartitionScaleMicrogrid RUNTIME DESTINATION bin) diff --git a/examples/PowerElectronics/Partition/PartitionMicrogrid.cpp b/examples/PowerElectronics/Partition/PartitionMicrogrid.cpp index 53f30281c..72e638ca0 100644 --- a/examples/PowerElectronics/Partition/PartitionMicrogrid.cpp +++ b/examples/PowerElectronics/Partition/PartitionMicrogrid.cpp @@ -292,4 +292,4 @@ std::vector getNodeConnections(const std::vector& nodes) } return connections; -} \ No newline at end of file +} diff --git a/examples/PowerElectronics/Partition/PartitionScaleMicrogrid.cpp b/examples/PowerElectronics/Partition/PartitionScaleMicrogrid.cpp index 116e7836f..1834cd2e3 100644 --- a/examples/PowerElectronics/Partition/PartitionScaleMicrogrid.cpp +++ b/examples/PowerElectronics/Partition/PartitionScaleMicrogrid.cpp @@ -425,4 +425,4 @@ int main(int argc, char const* argv[]) delete system; return 0; -} \ No newline at end of file +} diff --git a/examples/PowerElectronics/ScaleMicrogrid/CMakeLists.txt b/examples/PowerElectronics/ScaleMicrogrid/CMakeLists.txt index d495ef484..6bee549d3 100644 --- a/examples/PowerElectronics/ScaleMicrogrid/CMakeLists.txt +++ b/examples/PowerElectronics/ScaleMicrogrid/CMakeLists.txt @@ -3,13 +3,11 @@ add_executable(scalemicrogridarbitrary ScaleMicrogridArbitrary.cpp) target_include_directories( scalemicrogrid - PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/.. -) + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/..) target_include_directories( scalemicrogridarbitrary - PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/.. -) + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/..) target_link_libraries( scalemicrogrid diff --git a/examples/PowerElectronics/ScaleMicrogrid/ScaleMicrogrid.cpp b/examples/PowerElectronics/ScaleMicrogrid/ScaleMicrogrid.cpp index beea9f640..1e97cd56a 100644 --- a/examples/PowerElectronics/ScaleMicrogrid/ScaleMicrogrid.cpp +++ b/examples/PowerElectronics/ScaleMicrogrid/ScaleMicrogrid.cpp @@ -207,4 +207,4 @@ int test(index_type Nsize, real_type error_tol, bool debug_output) std::cout << "Test with Nsize = " << Nsize << " fails!\n"; return 1; } -} \ No newline at end of file +} diff --git a/examples/PowerElectronics/ScaleMicrogrid/ScaleMicrogridArbitrary.cpp b/examples/PowerElectronics/ScaleMicrogrid/ScaleMicrogridArbitrary.cpp index 6007b6746..c83ce2979 100644 --- a/examples/PowerElectronics/ScaleMicrogrid/ScaleMicrogridArbitrary.cpp +++ b/examples/PowerElectronics/ScaleMicrogrid/ScaleMicrogridArbitrary.cpp @@ -132,4 +132,4 @@ int printMicrogridSystems(index_type N_size) idas.runSimulation(t_final); return 0; -} \ No newline at end of file +} diff --git a/tests/UnitTests/PowerElectronics/README.md b/tests/UnitTests/PowerElectronics/README.md index 9d03909d4..be5c30248 100644 --- a/tests/UnitTests/PowerElectronics/README.md +++ b/tests/UnitTests/PowerElectronics/README.md @@ -136,4 +136,4 @@ Component 1 -------- Component 2 (HiresBus) -------- Component 3 The system is divided between **Component 2 (HiresBus)** and **Component 3**, and a `BusPartitionInterface` is added to the first partition. The resulting partition residuals are then evaluated independently and are then compared with the full-system residual to verify -that the partitioned evaluation reproduces the original system. \ No newline at end of file +that the partitioned evaluation reproduces the original system.