diff --git a/GridKit/Model/PowerElectronics/Capacitor/Capacitor.cpp b/GridKit/Model/PowerElectronics/Capacitor/Capacitor.cpp index 6c0b5fdf6..7d53e309f 100644 --- a/GridKit/Model/PowerElectronics/Capacitor/Capacitor.cpp +++ b/GridKit/Model/PowerElectronics/Capacitor/Capacitor.cpp @@ -54,6 +54,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 * @@ -119,6 +138,12 @@ namespace GridKit return 0; } + 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..201597fa0 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,6 +51,7 @@ namespace GridKit int initialize(); int tagDifferentiable(); + int setAbsoluteTolerance(RealT); int evaluateInternalResidual() final; int evaluateExternalResidual() final; int evaluateJacobian(); @@ -60,6 +62,8 @@ namespace GridKit // int evaluateAdjointJacobian(); int evaluateAdjointIntegrand(); + CircuitComponent* clone() const; + private: RealT C_; }; diff --git a/GridKit/Model/PowerElectronics/CircuitComponent.hpp b/GridKit/Model/PowerElectronics/CircuitComponent.hpp index a033b46e7..c3e33792a 100644 --- a/GridKit/Model/PowerElectronics/CircuitComponent.hpp +++ b/GridKit/Model/PowerElectronics/CircuitComponent.hpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -27,6 +28,176 @@ namespace GridKit CircuitComponent() = default; + protected: + /** + * @brief Constructs a copy of a circuit component. + * + * Copies the component metadata, local vector data, connection-node mapping, + * and COO Jacobian storage. Dynamically allocated component-owned data is + * deep-copied so that the new component does not share ownership of this + * storage with @p other. + * + * Pointers to state, state-derivative, and residual storage supplied by a + * parent system are copied as-is. Consequently, the copied component initially + * references the same parent-system storage as @p other. The pointer arrays + * used for external variables are independently allocated, but their entries + * point to the same external state, state-derivative, and residual storage as + * the original component. + * + * Local vectors, including the state, state derivative, residual, tolerances, + * quadrature data, adjoint data, and parameter vectors, retain the values of + * the original component. + * + * @param other Component to copy. + * + * @note If the copied component is subsequently attached to a different parent + * system, its state, state-derivative, and residual pointers must be + * reassigned to the storage provided by that system before evaluation. + */ + 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_), + y_int_(other.y_int_), + yp_int_(other.yp_int_), + f_int_(other.f_int_), + tag_(other.tag_), + time_(other.time_), + alpha_(other.alpha_), + max_steps_(other.max_steps_), + idc_(other.idc_), + allocated_(other.allocated_) + { + + auto copyVector = [](VectorT& destination, const VectorT& source) + { + const IdxT source_size = source.getSize(); + + if (source_size == 0) + { + return; + } + + destination.resize(source_size); + destination.copyFromExternal(source); + }; + + /* + * Deep-copy the local-to-global connection mapping. + */ + if (other.connection_nodes_) + { + connection_nodes_ = std::make_unique(static_cast(size_)); + + for (size_t i = 0; i < static_cast(size_); ++i) + { + connection_nodes_[i] = other.connection_nodes_[i]; + } + } + + /* + * Deep-copy the COO Jacobian row indices. + */ + if (other.jacobian_coo_rows_) + { + jacobian_coo_rows_ = std::make_unique(static_cast(nnz_)); + + for (size_t i = 0; i < static_cast(nnz_); ++i) + { + jacobian_coo_rows_[i] = other.jacobian_coo_rows_[i]; + } + } + + /* + * Deep-copy the COO Jacobian column indices. + */ + if (other.jacobian_coo_cols_) + { + jacobian_coo_cols_ = std::make_unique(static_cast(nnz_)); + + for (size_t i = 0; i < static_cast(nnz_); ++i) + { + jacobian_coo_cols_[i] = other.jacobian_coo_cols_[i]; + } + } + + /* + * Deep-copy the COO Jacobian values. + */ + if (other.jacobian_coo_values_) + { + jacobian_coo_values_ = std::make_unique(static_cast(nnz_)); + + for (size_t i = 0; i < static_cast(nnz_); ++i) + { + jacobian_coo_values_[i] = other.jacobian_coo_values_[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 (size_t i = 0; i < static_cast(size_); ++i) + { + y_ext_[i] = other.y_ext_[i]; + yp_ext_[i] = other.yp_ext_[i]; + f_ext_[i] = other.f_ext_[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_); + copyVector(g_, other.g_); + copyVector(yB_, other.yB_); + copyVector(ypB_, other.ypB_); + copyVector(fB_, other.fB_); + copyVector(gB_, other.gB_); + copyVector(param_, other.param_); + copyVector(param_up_, other.param_up_); + copyVector(param_lo_, other.param_lo_); + } + + public: + /** + * @brief Create an independent copy of this component. + * + * The clone preserves the component's model configuration, parameters, + * topology, and structural data, but does not preserve bindings to + * system-owned state or residual storage. + * + * @note By default, the cloned component's state, state-derivative, and + * residual pointers are not set. The user is responsible for setting these + * pointers to the appropriate storage before evaluating the residual. + */ + virtual CircuitComponent* clone() const + { + throw std::runtime_error("clone() is not supported for this component."); + } + + /** + * @brief Indicates whether this component supports cloning. + * + * Derived components that implement clone() should override this method + * and return true. + * + * @return true if the component can be cloned, false otherwise. + */ + virtual bool isCloneable() const + { + return false; + } + /** * @note Cannot be marked final, since it is overriden to recurse in the system model. */ @@ -51,7 +222,7 @@ namespace GridKit return this->n_intern_; } - std::set getExternIndices() + std::set getExternIndices() { return this->extern_indices_; } @@ -69,7 +240,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; } @@ -88,10 +259,27 @@ namespace GridKit int setExternalConnectionNodes(size_t local_index, ExternalConnection connection) { assert(extern_indices_.contains(local_index)); - y_ext_[local_index] = connection.y_; - yp_ext_[local_index] = connection.yp_; - f_ext_[local_index] = connection.f_; - connection_nodes_[local_index] = connection.idx_; + y_ext_[local_index] = connection.y_; + yp_ext_[local_index] = connection.yp_; + f_ext_[local_index] = connection.f_; + setConnectionNodes(local_index, connection.idx_); + return 0; + } + + /** + * @brief Update the connection index for a variable. + * + * Sets 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; } @@ -132,9 +320,10 @@ 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_)); + 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_)); tag_.resize(static_cast(size_)); @@ -441,6 +630,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. @@ -491,7 +690,6 @@ namespace GridKit */ std::unique_ptr connection_nodes_; - protected: /// The number of variables in this component. Should be equal to \ref n_extern_ plus \ref n_intern_. \see size() IdxT size_{0}; /// The number of nonzero elements in this component's Jacobian. \see nnz() diff --git a/GridKit/Model/PowerElectronics/DistributedGenerator/DistributedGenerator.cpp b/GridKit/Model/PowerElectronics/DistributedGenerator/DistributedGenerator.cpp index aa538933f..db4a280a5 100644 --- a/GridKit/Model/PowerElectronics/DistributedGenerator/DistributedGenerator.cpp +++ b/GridKit/Model/PowerElectronics/DistributedGenerator/DistributedGenerator.cpp @@ -422,6 +422,12 @@ namespace GridKit return 0; } + 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..11d6f9e97 100644 --- a/GridKit/Model/PowerElectronics/DistributedGenerator/DistributedGenerator.hpp +++ b/GridKit/Model/PowerElectronics/DistributedGenerator/DistributedGenerator.hpp @@ -88,6 +88,8 @@ namespace GridKit // int evaluateAdjointJacobian(); int evaluateAdjointIntegrand(); + CircuitComponent* clone() const; + private: RealT wb_; RealT wc_; diff --git a/GridKit/Model/PowerElectronics/InductionMotor/InductionMotor.cpp b/GridKit/Model/PowerElectronics/InductionMotor/InductionMotor.cpp index 74aabb549..0c7c87761 100644 --- a/GridKit/Model/PowerElectronics/InductionMotor/InductionMotor.cpp +++ b/GridKit/Model/PowerElectronics/InductionMotor/InductionMotor.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 InductionMotor::setAbsoluteTolerance(RealT rel_tol) + { + abs_tol_.setToConst(static_cast(rel_tol)); + return 0; + } + /** * @brief Contributes to the resisdual * @@ -131,6 +150,12 @@ namespace GridKit return 0; } + 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..13ed162ec 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,6 +51,7 @@ namespace GridKit int initialize(); int tagDifferentiable(); + int setAbsoluteTolerance(RealT); int evaluateInternalResidual() final; int evaluateExternalResidual() final; int evaluateJacobian(); @@ -60,6 +62,8 @@ namespace GridKit // int evaluateAdjointJacobian(); int evaluateAdjointIntegrand(); + CircuitComponent* clone() const; + private: RealT Lls_; RealT Rs_; diff --git a/GridKit/Model/PowerElectronics/Inductor/Inductor.cpp b/GridKit/Model/PowerElectronics/Inductor/Inductor.cpp index 0e6e7ea28..42c15c6a5 100644 --- a/GridKit/Model/PowerElectronics/Inductor/Inductor.cpp +++ b/GridKit/Model/PowerElectronics/Inductor/Inductor.cpp @@ -149,6 +149,12 @@ namespace GridKit return 0; } + 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..7440037c9 100644 --- a/GridKit/Model/PowerElectronics/Inductor/Inductor.hpp +++ b/GridKit/Model/PowerElectronics/Inductor/Inductor.hpp @@ -65,6 +65,8 @@ namespace GridKit // int evaluateAdjointJacobian(); int evaluateAdjointIntegrand(); + CircuitComponent* clone() const; + private: RealT L_; NodeT* node1_; diff --git a/GridKit/Model/PowerElectronics/LinearTransformer/LinearTransformer.cpp b/GridKit/Model/PowerElectronics/LinearTransformer/LinearTransformer.cpp index 455ef202f..b3b909ebb 100644 --- a/GridKit/Model/PowerElectronics/LinearTransformer/LinearTransformer.cpp +++ b/GridKit/Model/PowerElectronics/LinearTransformer/LinearTransformer.cpp @@ -67,6 +67,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 */ @@ -116,6 +135,12 @@ namespace GridKit return 0; } + 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..0785dcb32 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,6 +51,7 @@ namespace GridKit int initialize(); int tagDifferentiable(); + int setAbsoluteTolerance(RealT); int evaluateInternalResidual() final; int evaluateExternalResidual() final; int evaluateJacobian(); @@ -60,6 +62,8 @@ namespace GridKit // int evaluateAdjointJacobian(); int evaluateAdjointIntegrand(); + CircuitComponent* clone() const; + private: RealT L0_; RealT L1_; diff --git a/GridKit/Model/PowerElectronics/MicrogridBusDQ/MicrogridBusDQ.cpp b/GridKit/Model/PowerElectronics/MicrogridBusDQ/MicrogridBusDQ.cpp index 462c200f8..a918eaf9d 100644 --- a/GridKit/Model/PowerElectronics/MicrogridBusDQ/MicrogridBusDQ.cpp +++ b/GridKit/Model/PowerElectronics/MicrogridBusDQ/MicrogridBusDQ.cpp @@ -152,6 +152,12 @@ namespace GridKit return 0; } + 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..806dd2751 100644 --- a/GridKit/Model/PowerElectronics/MicrogridBusDQ/MicrogridBusDQ.hpp +++ b/GridKit/Model/PowerElectronics/MicrogridBusDQ/MicrogridBusDQ.hpp @@ -65,6 +65,8 @@ namespace GridKit // int evaluateAdjointJacobian(); int evaluateAdjointIntegrand(); + CircuitComponent* clone() const; + private: RealT RN_; NodeT* node1_; diff --git a/GridKit/Model/PowerElectronics/MicrogridLine/MicrogridLine.cpp b/GridKit/Model/PowerElectronics/MicrogridLine/MicrogridLine.cpp index efa5c2a36..7dc2f6b80 100644 --- a/GridKit/Model/PowerElectronics/MicrogridLine/MicrogridLine.cpp +++ b/GridKit/Model/PowerElectronics/MicrogridLine/MicrogridLine.cpp @@ -184,6 +184,12 @@ namespace GridKit return 0; } + 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..0749e5ceb 100644 --- a/GridKit/Model/PowerElectronics/MicrogridLine/MicrogridLine.hpp +++ b/GridKit/Model/PowerElectronics/MicrogridLine/MicrogridLine.hpp @@ -65,6 +65,8 @@ namespace GridKit // int evaluateAdjointJacobian(); int evaluateAdjointIntegrand(); + CircuitComponent* clone() const; + private: RealT R_; RealT L_; diff --git a/GridKit/Model/PowerElectronics/MicrogridLoad/MicrogridLoad.cpp b/GridKit/Model/PowerElectronics/MicrogridLoad/MicrogridLoad.cpp index 36ac6c375..34de349d1 100644 --- a/GridKit/Model/PowerElectronics/MicrogridLoad/MicrogridLoad.cpp +++ b/GridKit/Model/PowerElectronics/MicrogridLoad/MicrogridLoad.cpp @@ -175,6 +175,12 @@ namespace GridKit return 0; } + 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..12935d671 100644 --- a/GridKit/Model/PowerElectronics/MicrogridLoad/MicrogridLoad.hpp +++ b/GridKit/Model/PowerElectronics/MicrogridLoad/MicrogridLoad.hpp @@ -65,6 +65,8 @@ namespace GridKit // int evaluateAdjointJacobian(); int evaluateAdjointIntegrand(); + CircuitComponent* clone() const; + private: RealT R_; RealT L_; diff --git a/GridKit/Model/PowerElectronics/NodeBase.hpp b/GridKit/Model/PowerElectronics/NodeBase.hpp index 077854b97..b39539781 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_); @@ -392,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 8fba09a55..75b3ec18b 100644 --- a/GridKit/Model/PowerElectronics/Resistor/Resistor.cpp +++ b/GridKit/Model/PowerElectronics/Resistor/Resistor.cpp @@ -139,6 +139,12 @@ namespace GridKit return 0; } + 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..354074bc9 100644 --- a/GridKit/Model/PowerElectronics/Resistor/Resistor.hpp +++ b/GridKit/Model/PowerElectronics/Resistor/Resistor.hpp @@ -65,6 +65,8 @@ namespace GridKit // int evaluateAdjointJacobian(); int evaluateAdjointIntegrand(); + CircuitComponent* clone() const; + private: RealT R_; NodeT* node1_; diff --git a/GridKit/Model/PowerElectronics/SynchronousMachine/SynchronousMachine.cpp b/GridKit/Model/PowerElectronics/SynchronousMachine/SynchronousMachine.cpp index 5ec5b3b91..0616977e2 100644 --- a/GridKit/Model/PowerElectronics/SynchronousMachine/SynchronousMachine.cpp +++ b/GridKit/Model/PowerElectronics/SynchronousMachine/SynchronousMachine.cpp @@ -82,6 +82,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. * @@ -165,6 +184,12 @@ namespace GridKit return 0; } + 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..55356f191 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,6 +53,7 @@ namespace GridKit int initialize(); int tagDifferentiable(); + int setAbsoluteTolerance(RealT); int evaluateInternalResidual() final; int evaluateExternalResidual() final; int evaluateJacobian(); @@ -62,6 +64,8 @@ namespace GridKit // int evaluateAdjointJacobian(); int evaluateAdjointIntegrand(); + CircuitComponent* clone() const; + private: RealT Lls_; std::tuple Llkq_; diff --git a/GridKit/Model/PowerElectronics/TransmissionLine/TransmissionLine.cpp b/GridKit/Model/PowerElectronics/TransmissionLine/TransmissionLine.cpp index f4700e275..f3be694d4 100644 --- a/GridKit/Model/PowerElectronics/TransmissionLine/TransmissionLine.cpp +++ b/GridKit/Model/PowerElectronics/TransmissionLine/TransmissionLine.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 TransmissionLine::setAbsoluteTolerance(RealT rel_tol) + { + abs_tol_.setToConst(static_cast(rel_tol)); + return 0; + } + /** * @brief Evaluate residual of transmission line * @@ -186,6 +205,12 @@ namespace GridKit return 0; } + 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..7a0bfb230 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,6 +55,7 @@ namespace GridKit int initialize(); int tagDifferentiable(); + int setAbsoluteTolerance(RealT); int evaluateInternalResidual() final; int evaluateExternalResidual() final; int evaluateJacobian(); @@ -64,6 +66,8 @@ namespace GridKit // int evaluateAdjointJacobian(); int evaluateAdjointIntegrand(); + CircuitComponent* clone() const; + private: RealT R_; RealT X_; diff --git a/GridKit/Model/PowerElectronics/VoltageSource/VoltageSource.cpp b/GridKit/Model/PowerElectronics/VoltageSource/VoltageSource.cpp index 52f021a5a..900d2fc64 100644 --- a/GridKit/Model/PowerElectronics/VoltageSource/VoltageSource.cpp +++ b/GridKit/Model/PowerElectronics/VoltageSource/VoltageSource.cpp @@ -142,6 +142,12 @@ namespace GridKit return 0; } + 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..0cc94f66c 100644 --- a/GridKit/Model/PowerElectronics/VoltageSource/VoltageSource.hpp +++ b/GridKit/Model/PowerElectronics/VoltageSource/VoltageSource.hpp @@ -65,6 +65,8 @@ namespace GridKit // int evaluateAdjointJacobian(); int evaluateAdjointIntegrand(); + CircuitComponent* clone() const; + private: RealT V_; NodeT* node1_; diff --git a/tests/UnitTests/PowerElectronics/CMakeLists.txt b/tests/UnitTests/PowerElectronics/CMakeLists.txt index 74d50eb1e..94cd4fd91 100644 --- a/tests/UnitTests/PowerElectronics/CMakeLists.txt +++ b/tests/UnitTests/PowerElectronics/CMakeLists.txt @@ -3,6 +3,18 @@ target_link_libraries( test_power_electronics_node PRIVATE GridKit::power_electronics_circuit_node GridKit::testing) +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 PowerElectronicsComponentCloneTest + COMMAND $) install(TARGETS test_power_electronics_node RUNTIME DESTINATION bin) +install(TARGETS test_power_electronics_component_clone RUNTIME DESTINATION bin) diff --git a/tests/UnitTests/PowerElectronics/ComponentCloneTests.hpp b/tests/UnitTests/PowerElectronics/ComponentCloneTests.hpp new file mode 100644 index 000000000..77ea9a623 --- /dev/null +++ b/tests/UnitTests/PowerElectronics/ComponentCloneTests.hpp @@ -0,0 +1,263 @@ + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace GridKit +{ + 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: + 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; + } + + 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(); +}