diff --git a/dpsim-models/include/dpsim-models/Base/Base_ReducedOrderSynchronGenerator.h b/dpsim-models/include/dpsim-models/Base/Base_ReducedOrderSynchronGenerator.h index b070f562eb..f825ee0772 100644 --- a/dpsim-models/include/dpsim-models/Base/Base_ReducedOrderSynchronGenerator.h +++ b/dpsim-models/include/dpsim-models/Base/Base_ReducedOrderSynchronGenerator.h @@ -60,7 +60,7 @@ namespace Base { virtual ~ReducedOrderSynchronGenerator() { } /// modelAsCurrentSource=true --> SG is modeled as current source, otherwise as voltage source /// Both implementations are equivalent, but the current source implementation is more efficient - virtual void setModelAsCurrentSource(Bool modelAsCurrentSource); + virtual void setModelAsNortonSource(Bool modelAsCurrentSource); /// void setBaseParameters(Real nomPower, Real nomVolt, Real nomFreq); /// Initialization for 3 Order SynGen @@ -113,7 +113,7 @@ namespace Base { void initializeFromNodesAndTerminals(Real frequency); /// Function to initialize the specific variables of each SG model virtual void specificInitialization() = 0; - /// + /// Model specific step virtual void stepInPerUnit() = 0; // ### MNA Section ### @@ -128,9 +128,8 @@ namespace Base { virtual void mnaCompPostStep(const Matrix& leftVector) = 0; /// Stamps system matrix virtual void mnaCompApplySystemMatrixStamp(SparseMatrixRow& systemMatrix) = 0; - /// Model flag indicating whether the machine is modeled as current or voltage source - /// Default: currentsource (recommended) - Bool mModelAsCurrentSource = true; + /// Model flag indicating whether the machine is modelled as Norton or Thevenin equivalent + Bool mModelAsNortonSource; // Model flag indicating the SG order to be used SGOrder mSGOrder; diff --git a/dpsim-models/include/dpsim-models/Components.h b/dpsim-models/include/dpsim-models/Components.h index 1e4af743f9..3326608c27 100644 --- a/dpsim-models/include/dpsim-models/Components.h +++ b/dpsim-models/include/dpsim-models/Components.h @@ -31,7 +31,6 @@ #include #include #include -#include #include #include @@ -58,8 +57,11 @@ #include #include #include +#include +#include #include #include +#include #include #include #include @@ -97,6 +99,7 @@ #include #include #include +#include #include #include #include diff --git a/dpsim-models/include/dpsim-models/DP/DP_Ph1_DPDQInterface.h b/dpsim-models/include/dpsim-models/DP/DP_Ph1_DPDQInterface.h new file mode 100644 index 0000000000..b3a1d28883 --- /dev/null +++ b/dpsim-models/include/dpsim-models/DP/DP_Ph1_DPDQInterface.h @@ -0,0 +1,52 @@ +/* Copyright 2017-2021 Institute for Automation of Complex Power Systems, + * EONERC, RWTH Aachen University + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + *********************************************************************************/ + +#pragma once + +#include + +namespace CPS { +namespace DP { +namespace Ph1 { + /// @brief Class providing interface with DP domain for all components in DQ domain + class DPDQInterface { + + protected: + /// Transform from DQ to DP domain + MatrixFixedSize<2,2> mDQToDPTransform; + /// Transform from DP to DQ domain + MatrixFixedSize<2,2> mDPToDQTransform; + + /// Shifting frequency of the DP domain (assumed to be constant) + Real mOmegaShift; + + public: + /// Constructor + DPDQInterface() {} + + /// Setter for shit frequency + void setDPShiftFrequency(const Real & omegaShift); + + /// Getter for transform from DQ to DP domain + MatrixFixedSize<2,2> DQToDPTransform() { return mDQToDPTransform; }; + /// Getter for transform from DP to DQ domain + MatrixFixedSize<2,2> DPToDQTransform() { return mDPToDQTransform; }; + + /// Update transformation matrix from DQ to DP + void updateDQToDPTransform(const Real & thetaDQ, const Real & simTime); + /// Update transformation matrix from DP to DQ + void updateDPToDQTransform(const Real & thetaDQ, const Real & simTime); + + /// Apply transform to obtain current complex DP + Complex applyDQToDPTransform(const MatrixFixedSize<2,1> & dqMatrix); + /// Apply transform to obtain current DQ vector + MatrixFixedSize<2,1> applyDPToDQTransform(const Complex& dpComplex); + }; +} +} +} diff --git a/dpsim-models/include/dpsim-models/DP/DP_Ph1_ReducedOrderSynchronGeneratorVBR.h b/dpsim-models/include/dpsim-models/DP/DP_Ph1_ReducedOrderSynchronGeneratorVBR.h index f6f45661d8..55f5d058ea 100644 --- a/dpsim-models/include/dpsim-models/DP/DP_Ph1_ReducedOrderSynchronGeneratorVBR.h +++ b/dpsim-models/include/dpsim-models/DP/DP_Ph1_ReducedOrderSynchronGeneratorVBR.h @@ -10,6 +10,7 @@ #include #include +#include namespace CPS { namespace DP { @@ -27,30 +28,12 @@ namespace Ph1 { Complex mIvbr; protected: + /// Interface used to transform between DP and DQ vars + DPDQInterface mDomainInterface; /// Resistance matrix in dq reference frame Matrix mResistanceMatrixDq; /// Conductance matrix phase A MatrixFixedSize<2, 2> mConductanceMatrix; - /// Ka Matrix - MatrixComp mKa; - /// Kb Matrix - MatrixComp mKb; - /// Kb Matrix - MatrixComp mKc; - /// Constant part of Resistance matrix in abc reference frame - Matrix mResistanceMatrix_const; - /// phase a equivalent part of mKa - Complex mKa_1ph; - /// phase a equivalent part of mKb - Complex mKb_1ph; - /// phase a equivalent part of mResistanceMatrix_const - Complex mR_const_1ph; - /// Vector to create abc vector from a component - MatrixComp mShiftVector; - /// complex conjugate of mShiftVector - MatrixComp mShiftVectorConj; - /// Matrix to convert Evbr from dq domain to abc domain (only phase a) - MatrixComp mKvbr; /// Constructor ReducedOrderSynchronGeneratorVBR(const String & uid, const String & name, Logger::Level logLevel); @@ -59,16 +42,12 @@ namespace Ph1 { // #### General Functions #### /// Specific component initialization virtual void specificInitialization() override =0; - /// - void initializeResistanceMatrix() override; /// virtual void stepInPerUnit() override =0; /// - void calculateConductanceMatrix(); - /// Calculate Ka, Kb and Kvbr - void calculateAuxiliarVariables(); + virtual void calculateConductanceMatrix(); /// - Matrix get_parkTransformMatrix() const; + void initializeResistanceMatrix() final {}; // ### MNA Section ### void mnaCompApplySystemMatrixStamp(SparseMatrixRow& systemMatrix) override; diff --git a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderPCM.h b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderPCM.h new file mode 100644 index 0000000000..593435d75f --- /dev/null +++ b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderPCM.h @@ -0,0 +1,96 @@ +/* Copyright 2017-2021 Institute for Automation of Complex Power Systems, + * EONERC, RWTH Aachen University + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + *********************************************************************************/ + +#pragma once + +#include +#include +#include + +namespace CPS { +namespace DP { +namespace Ph1 { + /// @brief 4 Order Synchronous generator model for transient stability analysis + /// + /// This model is based on Eremia section 2.1.6. + /// Modeling approach: delayed current injection + predictor corrector method + class SynchronGenerator4OrderPCM : + public Base::ReducedOrderSynchronGenerator, + public MNASyncGenInterface, + public SharedFactory { + public: + /// + SynchronGenerator4OrderPCM(const String& uid, const String& name, Logger::Level logLevel = Logger::Level::off); + /// + SynchronGenerator4OrderPCM(const String& name, Logger::Level logLevel = Logger::Level::off); + /// + SimPowerComp::Ptr clone(const String& name); + + // #### General Functions #### + /// + void specificInitialization() override; + /// + void calculateStateSpaceMatrices(); + /// + void stepInPerUnit() override; + // + void correctorStep() override; + /// + void updateVoltage(const Matrix& leftVector) override; + /// + bool requiresIteration() override; + /// + void initializeResistanceMatrix() final {}; + + /// Warning if this method is applied: the model is exclusively implemented as current source and this setter will have no impact! + void setModelAsNortonSource(Bool modelAsCurrentSource) override { + SPDLOG_LOGGER_WARN(mSLog, "This model can exclusively be used as current source. The setter setModelAsNortonSource will have no impact on the model!"); + } + + // #### MNA Functions #### + /// + void mnaCompApplyRightSideVectorStamp(Matrix& rightVector) final; + /// + void mnaCompPostStep(const Matrix& leftVector) final; + /// + void mnaCompApplySystemMatrixStamp(SparseMatrixRow& systemMatrix) final {}; + + protected: + /// Interface used to transform between DP and DQ vars + DPDQInterface mDomainInterface; + + // #### Model specific variables #### + /// Transient emf + const Attribute::Ptr mEdq_t; + + // Variables saving values for later use + /// Trapedzoidal based state space matrix Ad + Matrix mAdTrapezoidal; + /// Trapedzoidal based state space matrix Bd + Matrix mBdTrapezoidal; + /// Trapedzoidal based state space matrix Cd + Matrix mCdTrapezoidal; + /// Edqt at k + Matrix mEdqtPrevStep; + /// Idq at k + Matrix mIdqPrevStep; + /// Vdq at k + Matrix mVdqPrevStep; + /// Vdq at j-1 + Matrix mVdqPrevIter; + + /// A matrix of continuous time state space model + Matrix mAStateSpace = Matrix::Zero(2,2); + /// B matrix of continuous time state space model + Matrix mBStateSpace = Matrix::Zero(2,2); + /// C matrix of continuous time state space model + Matrix mCStateSpace = Matrix::Zero(2,1); + }; +} +} +} diff --git a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderTPM.h b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderTPM.h new file mode 100644 index 0000000000..7bae3d9c9d --- /dev/null +++ b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator4OrderTPM.h @@ -0,0 +1,108 @@ +/* Copyright 2017-2021 Institute for Automation of Complex Power Systems, + * EONERC, RWTH Aachen University + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + *********************************************************************************/ + +#pragma once + +#include +#include +#include + +namespace CPS { +namespace DP { +namespace Ph1 { + /// @brief 4 Order Synchronous generator model for transient stability analysis + /// + /// This model is based on Eremia section 2.1.6. + /// Modeling approach: thevenin prediction method + class SynchronGenerator4OrderTPM : + public Base::ReducedOrderSynchronGenerator, + public MNASyncGenInterface, + public SharedFactory { + protected: + /// Interface used to transform between DP and DQ vars + DPDQInterface mDomainInterface; + + /// Constant part as conductance matrix + Matrix mConductanceMatrixConst = Matrix::Zero(2,2); + /// Varying part as resistance matrix + Matrix mResistanceMatrixVarying = Matrix::Zero(2,2); + + // #### Model specific variables #### + /// Transient emf + const Attribute::Ptr mEdq_t; + /// Original history voltage of VBR model + Matrix mEh = Matrix::Zero(2,1); + /// Modified history voltage of TPM model + const Attribute::Ptr mEhMod; + /// Norton equivalent current of EhMod + Complex mIhMod; + + // Variables saving values for later use + /// Trapedzoidal based state space matrix Ad + Matrix mAdStateSpace; + /// Trapedzoidal based state space matrix Bd + Matrix mBdStateSpace; + /// Trapedzoidal based state space matrix Cd + Matrix mCdStateSpace; + /// Idp at k-1 + MatrixComp mIdpTwoPrevStep; + /// Vdq at k + Matrix mVdqPrevStep; + /// Vdq at j-1 + Matrix mVdqPrevIter; + /// Edq_t at k + Matrix mEdqtPrevStep; + + /// A matrix of continuous time state space model + Matrix mAStateSpace = Matrix::Zero(2,2); + /// B matrix of continuous time state space model + Matrix mBStateSpace = Matrix::Zero(2,2); + /// C matrix of continuous time state space model + Matrix mCStateSpace = Matrix::Zero(2,1); + public: + /// + SynchronGenerator4OrderTPM(String uid, String name, Logger::Level logLevel = Logger::Level::off); + /// + SynchronGenerator4OrderTPM(String name, Logger::Level logLevel = Logger::Level::off); + /// + SimPowerComp::Ptr clone(String name); + + // #### General Functions #### + /// Initializes component from power flow data + void specificInitialization() override; + /// + void calculateStateSpaceMatrices(); + /// + void calculateConstantConductanceMatrix(); + /// + void stepInPerUnit() override; + /// + void correctorStep() override; + /// + void updateVoltage(const Matrix& leftVector) override; + /// + bool requiresIteration() override; + + /// + void initializeResistanceMatrix() final {}; + + // #### MNA Functions #### + /// + void mnaCompApplyRightSideVectorStamp(Matrix& rightVector) override; + /// + void mnaCompPostStep(const Matrix& leftVector) override; + /// + void mnaCompApplySystemMatrixStamp(SparseMatrixRow& systemMatrix) override; + + void setOperationalParametersPerUnit(Real nomPower, + Real nomVolt, Real nomFreq, Real H, Real Ld, Real Lq, Real L0, + Real Ld_t, Real Lq_t, Real Td0_t, Real Tq0_t); + }; +} +} +} diff --git a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator6OrderPCM.h b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator6OrderPCM.h new file mode 100644 index 0000000000..5ec200091f --- /dev/null +++ b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGenerator6OrderPCM.h @@ -0,0 +1,96 @@ +/* Copyright 2017-2021 Institute for Automation of Complex Power Systems, + * EONERC, RWTH Aachen University + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + *********************************************************************************/ + +#pragma once + +#include +#include +#include +namespace CPS { +namespace DP { +namespace Ph1 { + /// @brief 6 Order Synchronous generator model for transient stability analysis + /// + /// This model is based on Eremia section 2.1.6. + class SynchronGenerator6OrderPCM : + public Base::ReducedOrderSynchronGenerator, + public MNASyncGenInterface, + public SharedFactory { + public: + /// + SynchronGenerator6OrderPCM(const String& uid, const String& name, Logger::Level logLevel = Logger::Level::off); + /// + SynchronGenerator6OrderPCM(const String& name, Logger::Level logLevel = Logger::Level::off); + /// + SimPowerComp::Ptr clone(const String& name); + + // #### General Functions #### + /// + void specificInitialization() override; + /// + void calculateStateSpaceMatrices(); + /// + void stepInPerUnit() override; + // + void correctorStep() override; + /// + void updateVoltage(const Matrix& leftVector) override; + /// + bool requiresIteration() override; + /// + void initializeResistanceMatrix() final {}; + + /// Warning if this method is applied: the model is exclusively implemented as current source and this setter will have no impact! + void setModelAsNortonSource(Bool modelAsCurrentSource) override { + SPDLOG_LOGGER_WARN(mSLog, "This model can exclusively be used as current source. The setter setModelAsNortonSource will have no impact on the model!"); + } + + // #### MNA Functions #### + /// + void mnaCompApplyRightSideVectorStamp(Matrix& rightVector) final; + /// + void mnaCompPostStep(const Matrix& leftVector) final; + /// + void mnaCompApplySystemMatrixStamp(SparseMatrixRow& systemMatrix) final {}; + + protected: + /// Interface used to transform between DP and DQ vars + DPDQInterface mDomainInterface; + + // #### Model specific variables #### + /// Subransient emf + const Attribute::Ptr mEdq_s; + /// Transient emf + const Attribute::Ptr mEdq_t; + /// + Matrix mEdqts = Matrix::Zero(4,1); + + // Variables saving values for later use + /// Trapedzoidal based state space matrix Ad + Matrix mAdTrapezoidal; + /// Trapedzoidal based state space matrix Bd + Matrix mBdTrapezoidal; + /// Trapedzoidal based state space matrix Cd + Matrix mCdTrapezoidal; + /// Edqts at k + Matrix mEdqtsPrevStep; + /// Vdq at j-1 + Matrix mVdqPrevIter; + /// Idq at k + Matrix mIdqPrevStep; + + /// A matrix of continuous time state space model + Matrix mAStateSpace = Matrix::Zero(4,4); + /// B matrix of continuous time state space model + Matrix mBStateSpace = Matrix::Zero(4,2); + /// C matrix of continuous time state space model + Matrix mCStateSpace = Matrix::Zero(4,1); + }; +} +} +} diff --git a/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGeneratorIter.h b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGeneratorIter.h new file mode 100644 index 0000000000..43d97ca3af --- /dev/null +++ b/dpsim-models/include/dpsim-models/DP/DP_Ph1_SynchronGeneratorIter.h @@ -0,0 +1,58 @@ +/* Copyright 2017-2021 Institute for Automation of Complex Power Systems, + * EONERC, RWTH Aachen University + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + *********************************************************************************/ + +#pragma once + +#include +#include + +namespace CPS { +namespace DP { +namespace Ph1 { + /// @brief Base class for DP VBR synchronous generator model single phase + class SynchronGeneratorIter : + public Base::ReducedOrderSynchronGenerator, + public MNASyncGenInterface { + public: + private: + + protected: + /// Constructor + SynchronGeneratorIter(const String& uid, const String& name, Logger::Level logLevel); + SynchronGeneratorIter(const String& name, Logger::Level logLevel); + + // #### General Functions #### + /// + virtual void specificInitialization() = 0; + /// + virtual void stepInPerUnit() = 0; + // + virtual void correctorStep() = 0; + /// + void updateVoltage(const Matrix& leftVector); + /// + bool requiresIteration(); + /// + Matrix parkTransform(Real theta, const Matrix& abcVector); + + // ### MNA Section ### + void mnaCompApplySystemMatrixStamp(SparseMatrixRow& systemMatrix); + void mnaCompApplyRightSideVectorStamp(Matrix& rightVector); + void mnaCompPostStep(const Matrix& leftVector); + void mnaCompInitialize(Real omega, Real timeStep, Attribute::Ptr leftVector); + + public: + virtual ~SynchronGeneratorIter(); + + /// Mark that parameter changes so that system matrix is updated + Bool hasParameterChanged() override { return 1; }; + + }; +} +} +} diff --git a/dpsim-models/include/dpsim-models/Definitions.h b/dpsim-models/include/dpsim-models/Definitions.h index 723775c7a4..5d133673d9 100644 --- a/dpsim-models/include/dpsim-models/Definitions.h +++ b/dpsim-models/include/dpsim-models/Definitions.h @@ -108,10 +108,9 @@ namespace CPS { enum class PhaseType { A, B, C, ABC, Single }; enum class Domain { SP, DP, EMT }; enum class PowerflowBusType { PV, PQ, VD, None }; - enum class GeneratorType {PVNode, IdealVoltageSource, IdealCurrentSource, TransientStability, FullOrder, FullOrderVBR, SG6aOrderVBR, SG6bOrderVBR, SG4OrderVBR, SG3OrderVBR, None}; + enum class GeneratorType {PVNode, IdealVoltageSource, IdealCurrentSource, TransientStability, FullOrder, FullOrderVBR, SG6aOrderVBR, SG6bOrderVBR, SG4OrderVBR, SG3OrderVBR, SG4OrderPCM, SG4OrderTPM, SG6OrderPCM, None}; enum class SGOrder {SG3Order, SG4Order, SG6aOrder, SG6bOrder}; - // ### Exceptions ### class Exception : public std::exception { }; class AccessException : public Exception { }; diff --git a/dpsim-models/include/dpsim-models/EMT/EMT_Ph3_ReducedOrderSynchronGeneratorVBR.h b/dpsim-models/include/dpsim-models/EMT/EMT_Ph3_ReducedOrderSynchronGeneratorVBR.h index df657734a1..485713f54f 100644 --- a/dpsim-models/include/dpsim-models/EMT/EMT_Ph3_ReducedOrderSynchronGeneratorVBR.h +++ b/dpsim-models/include/dpsim-models/EMT/EMT_Ph3_ReducedOrderSynchronGeneratorVBR.h @@ -38,8 +38,8 @@ namespace Ph3 { Matrix mDq0ToAbc; /// Constructor - ReducedOrderSynchronGeneratorVBR(const String & uid, const String & name, Logger::Level logLevel); - ReducedOrderSynchronGeneratorVBR(const String & name, Logger::Level logLevel); + ReducedOrderSynchronGeneratorVBR(const String& uid, const String& name, Logger::Level logLevel); + ReducedOrderSynchronGeneratorVBR(const String& name, Logger::Level logLevel); // #### General Functions #### /// Specific component initialization diff --git a/dpsim-models/include/dpsim-models/EMT/EMT_Ph3_SynchronGenerator4OrderPCM.h b/dpsim-models/include/dpsim-models/EMT/EMT_Ph3_SynchronGenerator4OrderPCM.h new file mode 100644 index 0000000000..52acec9689 --- /dev/null +++ b/dpsim-models/include/dpsim-models/EMT/EMT_Ph3_SynchronGenerator4OrderPCM.h @@ -0,0 +1,97 @@ +/* Copyright 2017-2021 Institute for Automation of Complex Power Systems, + * EONERC, RWTH Aachen University + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + *********************************************************************************/ + +#pragma once + +#include +#include + +namespace CPS { +namespace EMT { +namespace Ph3 { + /// @brief 4 Order Synchronous generator model for transient stability analysis + /// + /// This model is based on Eremia section 2.1.6. + /// Modeling approach: delayed current injection + predictor corrector method + class SynchronGenerator4OrderPCM : + public Base::ReducedOrderSynchronGenerator, + public MNASyncGenInterface, + public SharedFactory { + + public: + /// + SynchronGenerator4OrderPCM(const String& uid, const String& name, Logger::Level logLevel = Logger::Level::off); + /// + SynchronGenerator4OrderPCM(const String& name, Logger::Level logLevel = Logger::Level::off); + + // #### General Functions #### + /// + void specificInitialization() override; + /// + void calculateStateSpaceMatrices(); + /// + void stepInPerUnit() override; + // + void correctorStep() override; + /// + void updateVoltage(const Matrix& leftVector) override; + /// + bool requiresIteration() override; + /// + Matrix parkTransform(Real theta, const Matrix& abcVector); + /// + Matrix inverseParkTransform(Real theta, const Matrix& dq0Vector); + /// + void initializeResistanceMatrix() final {}; + + /// Warning if this method is applied: the model is exclusively implemented as current source and this setter will have no impact! + void setModelAsNortonSource(Bool modelAsCurrentSource) override { + SPDLOG_LOGGER_WARN(mSLog, "This model can exclusively be used as current source. The setter setModelAsNortonSource will have no impact on the model!"); + } + + // #### MNA Functions #### + /// + void mnaCompApplyRightSideVectorStamp(Matrix& rightVector) final; + /// + void mnaCompPostStep(const Matrix& leftVector) final; + /// + void mnaCompApplySystemMatrixStamp(SparseMatrixRow& systemMatrix) final {}; + + protected: + // #### Model specific variables #### + /// Transient emf + const Attribute::Ptr mEdq0_t; + + // Variables saving values for later use + /// Trapedzoidal based state space matrix Ad + Matrix mAdTrapezoidal; + /// Trapedzoidal based state space matrix Bd + Matrix mBdTrapezoidal; + /// Trapedzoidal based state space matrix Cd + Matrix mCdTrapezoidal; + /// Edqt at k + Matrix mEdq0tPrevStep; + /// Idq at k + Matrix mIdq0PrevStep; + /// Vdq at k + Matrix mVdq0PrevStep; + /// Vdq at j-1 + Matrix mVdq0PrevIter; + + /// A matrix of continuous time state space model + Matrix mAStateSpace = Matrix::Zero(3,3); + /// B matrix of continuous time state space model + Matrix mBStateSpace = Matrix::Zero(3,3); + /// C matrix of continuous time state space model + Matrix mCStateSpace = Matrix::Zero(3,1); + + + }; +} +} +} diff --git a/dpsim-models/include/dpsim-models/Factory.h b/dpsim-models/include/dpsim-models/Factory.h new file mode 100644 index 0000000000..7b35a2b52f --- /dev/null +++ b/dpsim-models/include/dpsim-models/Factory.h @@ -0,0 +1,99 @@ +#include +#include +#include + +#include +#include +#include +#include + +#pragma once + + +template +class Creator { + + public: + virtual ~Creator(){} + + virtual std::shared_ptr Create(const std::string & name, CPS::Logger::Level logLevel = CPS::Logger::Level::debug) = 0; +}; + +template +class DerivedCreator : public Creator { + + public: + std::shared_ptr Create(const std::string & name, CPS::Logger::Level logLevel = CPS::Logger::Level::debug) { + return std::shared_ptr(new DerivedClass(name, logLevel)); + } +}; + +template +class Factory { + public: + static Factory & get() { + static Factory instance; + return instance; + } + + std::vector getItems() { + + std::vector items; + for (auto g : functionMap) { + items.push_back(g.first); + } + + return items; + } + + std::shared_ptr create( + std::string type, const std::string & name, + CPS::Logger::Level logLevel = CPS::Logger::Level::debug) { + + auto it = functionMap.find(type); + if (it != functionMap.end()) + return it->second->Create(name, logLevel); + else + throw CPS::SystemError("Unsupported type '" + type + "'!"); + } + + void registerExciter( + const std::string& type, + Creator* Fn) { + + functionMap[type] = Fn; + } + + private: + Factory() { } + Factory(const Factory&); + ~Factory() { + auto i = functionMap.begin(); + while (i != functionMap.end()) { + delete (*i).second; + ++i; + } + } + + std::map*> functionMap; +}; + +template +class FactoryRegistration { + public: + FactoryRegistration(std::string type, Creator* Fn) { + Factory::get().registerExciter(type, Fn); + } +}; + +namespace SynchronGeneratorFactory { +namespace DP { +namespace Ph1 { + void registerSynchronGenerators() { + FactoryRegistration> _4OrderDPIter("4PCM", new DerivedCreator>); + FactoryRegistration> _4OrderDPTPM("4TPM", new DerivedCreator>); + FactoryRegistration> _6OrderDPIter("6PCM", new DerivedCreator>); + } +} +} +} \ No newline at end of file diff --git a/dpsim-models/include/dpsim-models/MathUtils.h b/dpsim-models/include/dpsim-models/MathUtils.h index 56579c1ed6..cbd4c81b7d 100644 --- a/dpsim-models/include/dpsim-models/MathUtils.h +++ b/dpsim-models/include/dpsim-models/MathUtils.h @@ -88,6 +88,11 @@ namespace CPS { static Real StateSpaceEuler(Real states, Real A, Real B, Real dt, Real u); static Real StateSpaceEuler(Real states, Real A, Real B, Real C, Real dt, Real u); + /// Calculate the discretized state space matrices Ad, Bd, Cd using trapezoidal rule + static void calculateStateSpaceTrapezoidalMatrices(const Matrix & A, const Matrix & B, const Matrix & C, const Real & dt, Matrix & Ad, Matrix & Bd, Matrix & Cd); + /// Apply the trapezoidal based state space matrices Ad, Bd, Cd to get the states at the current time step + static Matrix applyStateSpaceTrapezoidalMatrices(const Matrix & Ad, const Matrix & Bd, const Matrix & Cd, const Matrix & statesPrevStep, const Matrix & inputCurrStep, const Matrix & inputPrevStep); + static void FFT(std::vector& samples); static Complex rotatingFrame2to1(Complex f2, Real theta1, Real theta2); diff --git a/dpsim-models/include/dpsim-models/SP/SP_Ph1_SynchronGenerator4OrderDCIM.h b/dpsim-models/include/dpsim-models/SP/SP_Ph1_SynchronGenerator4OrderDCIM.h deleted file mode 100644 index cb152c7c9c..0000000000 --- a/dpsim-models/include/dpsim-models/SP/SP_Ph1_SynchronGenerator4OrderDCIM.h +++ /dev/null @@ -1,73 +0,0 @@ -/* Copyright 2017-2021 Institute for Automation of Complex Power Systems, - * EONERC, RWTH Aachen University - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - *********************************************************************************/ - -#pragma once - -#include - -namespace CPS { -namespace SP { -namespace Ph1 { - /// @brief Delayed-Current-Injection (DCIM) implementation - /// of 4th order synchronous generator model - class SynchronGenerator4OrderDCIM : - public Base::ReducedOrderSynchronGenerator, - public SharedFactory { - - public: - // ### State variables [p.u.]### - /// voltage behing the transient reactance - const Attribute::Ptr mEdq_t; - - protected: - /// state representation matrix - /// - Matrix mA; - /// - Matrix mB; - /// - Matrix mC; - - /// - void calculateStateMatrix(); - - /// Park Transformation - /// - Matrix mDqToComplexA; - /// - Matrix mComplexAToDq; - /// - Matrix get_DqToComplexATransformMatrix(); - - // #### General Functions #### - /// Specific component initialization - void specificInitialization() final; - /// - void initializeResistanceMatrix() final {}; - /// - void stepInPerUnit() final; - - // ### MNA Section ### - /// - void mnaCompApplySystemMatrixStamp(SparseMatrixRow& systemMatrix) override; - void mnaCompApplyRightSideVectorStamp(Matrix& rightVector) override; - void mnaCompPostStep(const Matrix& leftVector) override; - - public: - /// - SynchronGenerator4OrderDCIM(const String & uid, const String & name, Logger::Level logLevel = Logger::Level::off); - /// - SynchronGenerator4OrderDCIM(const String & name, Logger::Level logLevel = Logger::Level::off); - /// DCIM is only implmented as current source! - void setModelAsCurrentSource(Bool modelAsCurrentSource) const { - SPDLOG_LOGGER_DEBUG(mSLog, "DCIM model can only be used as current source!"); - } - }; -} -} -} diff --git a/dpsim-models/include/dpsim-models/Solver/MNASyncGenInterface.h b/dpsim-models/include/dpsim-models/Solver/MNASyncGenInterface.h new file mode 100644 index 0000000000..70c9e79c33 --- /dev/null +++ b/dpsim-models/include/dpsim-models/Solver/MNASyncGenInterface.h @@ -0,0 +1,48 @@ +/* Copyright 2017-2020 Institute for Automation of Complex Power Systems, + * EONERC, RWTH Aachen University + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + *********************************************************************************/ + +#pragma once + +#include +#include +#include + +namespace CPS { + /// Interface to be used by synchronous generators + class MNASyncGenInterface { + protected: + /// + Attribute::Ptr mNumIter; + /// + Int mMaxIter = 25; + /// + Real mTolerance = 1e-6; + + public: + typedef std::shared_ptr Ptr; + typedef std::vector List; + + // Solver functions + /// + virtual void correctorStep()=0; + /// + virtual void updateVoltage(const Matrix& leftVector)=0; + /// + virtual bool requiresIteration() {return false;} + + /// Setters + /// + void setMaxIterations(Int maxIterations) {mMaxIter = maxIterations;} + /// + void setTolerance(Real Tolerance) {mTolerance = Tolerance;} + + protected: + /// Constructor + MNASyncGenInterface() { } + }; +} diff --git a/dpsim-models/src/Base/Base_ReducedOrderSynchronGenerator.cpp b/dpsim-models/src/Base/Base_ReducedOrderSynchronGenerator.cpp index 9c8925cfb6..18277ec813 100644 --- a/dpsim-models/src/Base/Base_ReducedOrderSynchronGenerator.cpp +++ b/dpsim-models/src/Base/Base_ReducedOrderSynchronGenerator.cpp @@ -28,6 +28,10 @@ Base::ReducedOrderSynchronGenerator::ReducedOrderSynchronGenerator( // declare state variables **mVdq0 = Matrix::Zero(3,1); **mIdq0 = Matrix::Zero(3,1); + + // default model is Norton equivalent + mModelAsNortonSource = true; + SPDLOG_LOGGER_INFO(this->mSLog, "SG per default modelled as Norton equivalent"); } template <> @@ -49,15 +53,23 @@ Base::ReducedOrderSynchronGenerator::ReducedOrderSynchronGenerator( ///FIXME: The mVdq0 and mVdq member variables are mutually exclusive and carry the same attribute name. Maybe they can be unified? **mVdq = Matrix::Zero(2,1); **mIdq = Matrix::Zero(2,1); + + // default model is Norton equivalent + mModelAsNortonSource = true; + SPDLOG_LOGGER_INFO(this->mSLog, "SG per default modelled as Norton equivalent"); } template -void Base::ReducedOrderSynchronGenerator::setModelAsCurrentSource(Bool modelAsCurrentSource) { - mModelAsCurrentSource = modelAsCurrentSource; +void Base::ReducedOrderSynchronGenerator::setModelAsNortonSource(Bool modelAsCurrentSource) { + mModelAsNortonSource = modelAsCurrentSource; - if (!mModelAsCurrentSource) - // SG is modeled as voltage source + if (mModelAsNortonSource) { + this->setVirtualNodeNumber(0); + SPDLOG_LOGGER_INFO(this->mSLog, "Setting SG model to Norton equivalent"); + } else { this->setVirtualNodeNumber(2); + SPDLOG_LOGGER_INFO(this->mSLog, "Setting SG model to Thevenin equivalent"); + } } template @@ -437,22 +449,30 @@ void Base::ReducedOrderSynchronGenerator::mnaCompPreStep(Real time, Int **mMechTorque = mTurbineGovernor->step(**mOmMech, mTimeStep); } - // calculate mechanical variables at t=k+1 with forward euler - if (mSimTime>0.0) { - **mElecTorque = ((**mVdq)(0,0) * (**mIdq)(0,0) + (**mVdq)(1,0) * (**mIdq)(1,0)); + // predict mechanical vars for all reduced-order models in the same manner + if (mSimTime > 0.0) { + // predict omega at t=k+1 (forward euler) + **mElecTorque = (**mVdq)(0,0) * (**mIdq)(0,0) + (**mVdq)(1,0) * (**mIdq)(1,0); **mOmMech = **mOmMech + mTimeStep * (1. / (2. * mH) * (mMechTorque_prev - **mElecTorque)); + + // predict theta and delta at t=k+1 (backward euler) **mThetaMech = **mThetaMech + mTimeStep * (**mOmMech * mBase_OmMech); **mDelta = **mDelta + mTimeStep * (**mOmMech - 1.) * mBase_OmMech; } + // model specific calculation of electrical vars stepInPerUnit(); + + // stamp model specific right side vector after calculation of electrical vars (**mRightVector).setZero(); - mnaApplyRightSideVectorStamp(**mRightVector); + mnaCompApplyRightSideVectorStamp(**mRightVector); } template <> void Base::ReducedOrderSynchronGenerator::mnaCompPreStep(Real time, Int timeStepCount) { mSimTime = time; + + // update controller variables if (mHasExciter) { mEf_prev = **mEf; **mEf = mExciter->step((**mVdq0)(0,0), (**mVdq0)(1,0), mTimeStep); @@ -462,17 +482,23 @@ void Base::ReducedOrderSynchronGenerator::mnaCompPreStep(Real time, Int ti **mMechTorque = mTurbineGovernor->step(**mOmMech, mTimeStep); } - // calculate mechanical variables at t=k+1 with forward euler - if (mSimTime>0.0) { - **mElecTorque = ((**mVdq0)(0,0) * (**mIdq0)(0,0) + (**mVdq0)(1,0) * (**mIdq0)(1,0)); + // predict mechanical vars for all reduced-order models in the same manner + if (mSimTime > 0.0) { + // predict omega at t=k+1 (forward euler) + **mElecTorque = (**mVdq0)(0,0) * (**mIdq0)(0,0) + (**mVdq0)(1,0) * (**mIdq0)(1,0); **mOmMech = **mOmMech + mTimeStep * (1. / (2. * mH) * (mMechTorque_prev - **mElecTorque)); + + // predict theta and delta at t=k+1 (backward euler) **mThetaMech = **mThetaMech + mTimeStep * (**mOmMech * mBase_OmMech); **mDelta = **mDelta + mTimeStep * (**mOmMech - 1.) * mBase_OmMech; } + // model specific calculation of electrical vars stepInPerUnit(); + + // stamp model specific right side vector after calculation of electrical vars (**mRightVector).setZero(); - mnaApplyRightSideVectorStamp(**mRightVector); + mnaCompApplyRightSideVectorStamp(**mRightVector); } template @@ -486,7 +512,7 @@ void Base::ReducedOrderSynchronGenerator::mnaCompPreStep(Real time, Int mSimTime = time; stepInPerUnit(); (**mRightVector).setZero(); - this->mnaApplyRightSideVectorStamp(**mRightVector); + mnaCompApplyRightSideVectorStamp(**mRightVector); } template diff --git a/dpsim-models/src/CIM/Reader.cpp b/dpsim-models/src/CIM/Reader.cpp index 89345f6552..9686736b34 100644 --- a/dpsim-models/src/CIM/Reader.cpp +++ b/dpsim-models/src/CIM/Reader.cpp @@ -520,11 +520,14 @@ TopologicalPowerComp::Ptr Reader::mapSynchronousMachine(CIMPP::SynchronousMachin if (mDomain == Domain::DP) { SPDLOG_LOGGER_INFO(mSLog, " Create generator in DP domain."); - if (mGeneratorType == GeneratorType::TransientStability + if (mGeneratorType == GeneratorType::TransientStability || mGeneratorType == GeneratorType::SG6aOrderVBR || mGeneratorType == GeneratorType::SG6bOrderVBR || mGeneratorType == GeneratorType::SG4OrderVBR - || mGeneratorType == GeneratorType::SG3OrderVBR) { + || mGeneratorType == GeneratorType::SG3OrderVBR + || mGeneratorType == GeneratorType::SG4OrderPCM + || mGeneratorType == GeneratorType::SG4OrderTPM + || mGeneratorType == GeneratorType::SG6OrderPCM) { Real ratedPower = unitValue(machine->ratedS.value, UnitMultiplier::M); Real ratedVoltage = unitValue(machine->ratedU.value, UnitMultiplier::k); @@ -537,7 +540,7 @@ TopologicalPowerComp::Ptr Reader::mapSynchronousMachine(CIMPP::SynchronousMachin // stator Real Rs = genDyn->statorResistance.value; Real Ll = genDyn->statorLeakageReactance.value; - + // reactances Real Ld = genDyn->xDirectSync.value; Real Lq = genDyn->xQuadSync.value; @@ -545,7 +548,7 @@ TopologicalPowerComp::Ptr Reader::mapSynchronousMachine(CIMPP::SynchronousMachin Real Lq_t = genDyn->xQuadTrans.value; Real Ld_s = genDyn->xDirectSubtrans.value; Real Lq_s = genDyn->xQuadSubtrans.value; - + // time constants Real Td0_t = genDyn->tpdo.value; Real Tq0_t = genDyn->tpqo.value; @@ -570,7 +573,7 @@ TopologicalPowerComp::Ptr Reader::mapSynchronousMachine(CIMPP::SynchronousMachin gen->setOperationalParametersPerUnit( ratedPower, ratedVoltage, mFrequency, H, Ld, Lq, Ll, Ld_t, Lq_t, Td0_t, Tq0_t, - Ld_s, Lq_s, Td0_s, Tq0_s); + Ld_s, Lq_s, Td0_s, Tq0_s); return gen; } else if (mGeneratorType == GeneratorType::SG6bOrderVBR) { SPDLOG_LOGGER_INFO(mSLog, " GeneratorType is SynchronGenerator6bOrderVBR."); @@ -578,21 +581,39 @@ TopologicalPowerComp::Ptr Reader::mapSynchronousMachine(CIMPP::SynchronousMachin gen->setOperationalParametersPerUnit( ratedPower, ratedVoltage, mFrequency, H, Ld, Lq, Ll, Ld_t, Lq_t, Td0_t, Tq0_t, - Ld_s, Lq_s, Td0_s, Tq0_s); + Ld_s, Lq_s, Td0_s, Tq0_s); return gen; } else if (mGeneratorType == GeneratorType::SG4OrderVBR) { SPDLOG_LOGGER_INFO(mSLog, " GeneratorType is SynchronGenerator4OrderVBR."); auto gen = std::make_shared( machine->mRID, machine->name, mComponentLogLevel); gen->setOperationalParametersPerUnit(ratedPower, ratedVoltage, mFrequency, H, - Ld, Lq, Ll, Ld_t, Lq_t, Td0_t, Tq0_t); + Ld, Lq, Ll, Ld_t, Lq_t, Td0_t, Tq0_t); return gen; } else if (mGeneratorType == GeneratorType::SG3OrderVBR) { SPDLOG_LOGGER_INFO(mSLog, " GeneratorType is SynchronGenerator3OrderVBR."); auto gen = std::make_shared(machine->mRID, machine->name, mComponentLogLevel); gen->setOperationalParametersPerUnit( ratedPower, ratedVoltage, mFrequency, H, - Ld, Lq, Ll, Ld_t, Td0_t); + Ld, Lq, Ll, Ld_t, Td0_t); + return gen; + } else if (mGeneratorType == GeneratorType::SG4OrderPCM) { + mSLog->info(" GeneratorType is SynchronGenerator4OrderPCM."); + auto gen = std::make_shared(machine->mRID, machine->name, mComponentLogLevel); + gen->setOperationalParametersPerUnit(ratedPower, ratedVoltage, mFrequency, H, + Ld, Lq, Ll, Ld_t, Lq_t, Td0_t, Tq0_t); + return gen; + } else if (mGeneratorType == GeneratorType::SG4OrderTPM) { + mSLog->info(" GeneratorType is SynchronGenerator4OrderTPM."); + auto gen = std::make_shared(machine->mRID, machine->name, mComponentLogLevel); + gen->setOperationalParametersPerUnit(ratedPower, ratedVoltage, mFrequency, H, + Ld, Lq, Ll, Ld_t, Lq_t, Td0_t, Tq0_t); + return gen; + } else if (mGeneratorType == GeneratorType::SG6OrderPCM) { + mSLog->info(" GeneratorType is SynchronGenerator6OrderPCM."); + auto gen = std::make_shared(machine->mRID, machine->name, mComponentLogLevel); + gen->setOperationalParametersPerUnit(ratedPower, ratedVoltage, mFrequency, H, + Ld, Lq, Ll, Ld_t, Lq_t, Td0_t, Tq0_t, Ld_s, Lq_s, Td0_s, Tq0_s); return gen; } } @@ -608,7 +629,7 @@ TopologicalPowerComp::Ptr Reader::mapSynchronousMachine(CIMPP::SynchronousMachin } } else if (mDomain == Domain::SP) { SPDLOG_LOGGER_INFO(mSLog, " Create generator in SP domain."); - if (mGeneratorType == GeneratorType::TransientStability + if (mGeneratorType == GeneratorType::TransientStability || mGeneratorType == GeneratorType::SG6aOrderVBR || mGeneratorType == GeneratorType::SG6bOrderVBR || mGeneratorType == GeneratorType::SG4OrderVBR @@ -625,7 +646,7 @@ TopologicalPowerComp::Ptr Reader::mapSynchronousMachine(CIMPP::SynchronousMachin // stator Real Rs = genDyn->statorResistance.value; Real Ll = genDyn->statorLeakageReactance.value; - + // reactances Real Ld = genDyn->xDirectSync.value; Real Lq = genDyn->xQuadSync.value; @@ -633,7 +654,7 @@ TopologicalPowerComp::Ptr Reader::mapSynchronousMachine(CIMPP::SynchronousMachin Real Lq_t = genDyn->xQuadTrans.value; Real Ld_s = genDyn->xDirectSubtrans.value; Real Lq_s = genDyn->xQuadSubtrans.value; - + // time constants Real Td0_t = genDyn->tpdo.value; Real Tq0_t = genDyn->tpqo.value; @@ -658,7 +679,7 @@ TopologicalPowerComp::Ptr Reader::mapSynchronousMachine(CIMPP::SynchronousMachin gen->setOperationalParametersPerUnit( ratedPower, ratedVoltage, mFrequency, H, Ld, Lq, Ll, Ld_t, Lq_t, Td0_t, Tq0_t, - Ld_s, Lq_s, Td0_s, Tq0_s); + Ld_s, Lq_s, Td0_s, Tq0_s); return gen; } else if (mGeneratorType == GeneratorType::SG6bOrderVBR) { SPDLOG_LOGGER_INFO(mSLog, " GeneratorType is SynchronGenerator6bOrderVBR."); @@ -666,21 +687,21 @@ TopologicalPowerComp::Ptr Reader::mapSynchronousMachine(CIMPP::SynchronousMachin gen->setOperationalParametersPerUnit( ratedPower, ratedVoltage, mFrequency, H, Ld, Lq, Ll, Ld_t, Lq_t, Td0_t, Tq0_t, - Ld_s, Lq_s, Td0_s, Tq0_s); + Ld_s, Lq_s, Td0_s, Tq0_s); return gen; } else if (mGeneratorType == GeneratorType::SG4OrderVBR) { SPDLOG_LOGGER_INFO(mSLog, " GeneratorType is SynchronGenerator4OrderVBR."); auto gen = std::make_shared( machine->mRID, machine->name, mComponentLogLevel); gen->setOperationalParametersPerUnit(ratedPower, ratedVoltage, mFrequency, H, - Ld, Lq, Ll, Ld_t, Lq_t, Td0_t, Tq0_t); + Ld, Lq, Ll, Ld_t, Lq_t, Td0_t, Tq0_t); return gen; } else if (mGeneratorType == GeneratorType::SG3OrderVBR) { SPDLOG_LOGGER_INFO(mSLog, " GeneratorType is SynchronGenerator3OrderVBR."); auto gen = std::make_shared(machine->mRID, machine->name, mComponentLogLevel); gen->setOperationalParametersPerUnit( ratedPower, ratedVoltage, mFrequency, H, - Ld, Lq, Ll, Ld_t, Td0_t); + Ld, Lq, Ll, Ld_t, Td0_t); return gen; } } @@ -736,9 +757,13 @@ TopologicalPowerComp::Ptr Reader::mapSynchronousMachine(CIMPP::SynchronousMachin } } else { SPDLOG_LOGGER_INFO(mSLog, " Create generator in EMT domain."); - if (mGeneratorType == GeneratorType::FullOrder || mGeneratorType == GeneratorType::FullOrderVBR - || mGeneratorType == GeneratorType::SG4OrderVBR) { - + if (mGeneratorType == GeneratorType::FullOrder + || mGeneratorType == GeneratorType::FullOrderVBR + || mGeneratorType == GeneratorType::SG3OrderVBR + || mGeneratorType == GeneratorType::SG4OrderVBR + || mGeneratorType == GeneratorType::SG6aOrderVBR + || mGeneratorType == GeneratorType::SG6bOrderVBR) { + Real ratedPower = unitValue(machine->ratedS.value, UnitMultiplier::M); Real ratedVoltage = unitValue(machine->ratedU.value, UnitMultiplier::k); @@ -747,11 +772,11 @@ TopologicalPowerComp::Ptr Reader::mapSynchronousMachine(CIMPP::SynchronousMachin if (CIMPP::SynchronousMachineTimeConstantReactance* genDyn = dynamic_cast(obj)) { if (genDyn->SynchronousMachine->mRID == machine->mRID) { - + // stator Real Rs = genDyn->statorResistance.value; Real Ll = genDyn->statorLeakageReactance.value; - + // reactances Real Ld = genDyn->xDirectSync.value; Real Lq = genDyn->xQuadSync.value; @@ -759,7 +784,7 @@ TopologicalPowerComp::Ptr Reader::mapSynchronousMachine(CIMPP::SynchronousMachin Real Lq_t = genDyn->xQuadTrans.value; Real Ld_s = genDyn->xDirectSubtrans.value; Real Lq_s = genDyn->xQuadSubtrans.value; - + // time constants Real Td0_t = genDyn->tpdo.value; Real Tq0_t = genDyn->tpqo.value; @@ -778,8 +803,8 @@ TopologicalPowerComp::Ptr Reader::mapSynchronousMachine(CIMPP::SynchronousMachin auto gen = std::make_shared(machine->mRID, machine->name, mComponentLogLevel); gen->setParametersOperationalPerUnit( ratedPower, ratedVoltage, mFrequency, poleNum, nomFieldCurr, - Rs, Ld, Lq, Ld_t, Lq_t, Ld_s, Lq_s, Ll, - Td0_t, Tq0_t, Td0_s, Tq0_s, H); + Rs, Ld, Lq, Ld_t, Lq_t, Ld_s, Lq_s, Ll, + Td0_t, Tq0_t, Td0_s, Tq0_s, H); return gen; } else if (mGeneratorType == GeneratorType::FullOrderVBR) { SPDLOG_LOGGER_INFO(mSLog, " GeneratorType is FullOrderVBR."); @@ -787,7 +812,7 @@ TopologicalPowerComp::Ptr Reader::mapSynchronousMachine(CIMPP::SynchronousMachin gen->setBaseAndOperationalPerUnitParameters( ratedPower, ratedVoltage, mFrequency, poleNum, nomFieldCurr, Rs, Ld, Lq, Ld_t, Lq_t, Ld_s, - Lq_s, Ll, Td0_t, Tq0_t, Td0_s, Tq0_s, H); + Lq_s, Ll, Td0_t, Tq0_t, Td0_s, Tq0_s, H); return gen; } else if (mGeneratorType == GeneratorType::SG6aOrderVBR) { SPDLOG_LOGGER_INFO(mSLog, " GeneratorType is SynchronGenerator6aOrderVBR."); @@ -795,7 +820,7 @@ TopologicalPowerComp::Ptr Reader::mapSynchronousMachine(CIMPP::SynchronousMachin gen->setOperationalParametersPerUnit( ratedPower, ratedVoltage, mFrequency, H, Ld, Lq, Ll, Ld_t, Lq_t, Td0_t, Tq0_t, - Ld_s, Lq_s, Td0_s, Tq0_s); + Ld_s, Lq_s, Td0_s, Tq0_s); return gen; } else if (mGeneratorType == GeneratorType::SG6bOrderVBR) { SPDLOG_LOGGER_INFO(mSLog, " GeneratorType is SynchronGenerator6bOrderVBR."); @@ -803,21 +828,21 @@ TopologicalPowerComp::Ptr Reader::mapSynchronousMachine(CIMPP::SynchronousMachin gen->setOperationalParametersPerUnit( ratedPower, ratedVoltage, mFrequency, H, Ld, Lq, Ll, Ld_t, Lq_t, Td0_t, Tq0_t, - Ld_s, Lq_s, Td0_s, Tq0_s); + Ld_s, Lq_s, Td0_s, Tq0_s); return gen; } else if (mGeneratorType == GeneratorType::SG4OrderVBR) { SPDLOG_LOGGER_INFO(mSLog, " GeneratorType is SynchronGenerator4OrderVBR."); auto gen = std::make_shared(machine->mRID, machine->name, mComponentLogLevel); gen->setOperationalParametersPerUnit( ratedPower, ratedVoltage, mFrequency, H, - Ld, Lq, Ll, Ld_t, Lq_t, Td0_t, Tq0_t); + Ld, Lq, Ll, Ld_t, Lq_t, Td0_t, Tq0_t); return gen; } else if (mGeneratorType == GeneratorType::SG3OrderVBR) { SPDLOG_LOGGER_INFO(mSLog, " GeneratorType is SynchronGenerator3OrderVBR."); auto gen = std::make_shared(machine->mRID, machine->name, mComponentLogLevel); gen->setOperationalParametersPerUnit( ratedPower, ratedVoltage, mFrequency, H, - Ld, Lq, Ll, Ld_t, Td0_t); + Ld, Lq, Ll, Ld_t, Td0_t); return gen; } } diff --git a/dpsim-models/src/CMakeLists.txt b/dpsim-models/src/CMakeLists.txt index 3a333308bd..30639f8dad 100644 --- a/dpsim-models/src/CMakeLists.txt +++ b/dpsim-models/src/CMakeLists.txt @@ -37,12 +37,16 @@ list(APPEND MODELS_SOURCES DP/DP_Ph1_ReducedOrderSynchronGeneratorVBR.cpp DP/DP_Ph1_SynchronGenerator3OrderVBR.cpp DP/DP_Ph1_SynchronGenerator4OrderVBR.cpp + DP/DP_Ph1_SynchronGenerator4OrderPCM.cpp + DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp + DP/DP_Ph1_SynchronGenerator6OrderPCM.cpp DP/DP_Ph1_SynchronGenerator6aOrderVBR.cpp DP/DP_Ph1_SynchronGenerator6bOrderVBR.cpp DP/DP_Ph1_Inverter.cpp DP/DP_Ph1_AvVoltageSourceInverterDQ.cpp DP/DP_Ph1_NetworkInjection.cpp DP/DP_Ph1_varResSwitch.cpp + DP/DP_Ph1_DPDQInterface.cpp DP/DP_Ph3_VoltageSource.cpp DP/DP_Ph3_Capacitor.cpp @@ -86,6 +90,7 @@ list(APPEND MODELS_SOURCES EMT/EMT_Ph3_SynchronGenerator6aOrderVBR.cpp EMT/EMT_Ph3_SynchronGenerator6bOrderVBR.cpp EMT/EMT_Ph3_SynchronGeneratorDQ.cpp + EMT/EMT_Ph3_SynchronGenerator4OrderPCM.cpp EMT/EMT_Ph3_SynchronGeneratorDQTrapez.cpp # EMT/EMT_Ph3_SynchronGeneratorDQSmpl.cpp # EMT/EMT_Ph3_SynchronGeneratorDQSmplCompSource.cpp @@ -113,7 +118,6 @@ list(APPEND MODELS_SOURCES SP/SP_Ph1_SynchronGenerator4OrderVBR.cpp SP/SP_Ph1_SynchronGenerator6aOrderVBR.cpp SP/SP_Ph1_SynchronGenerator6bOrderVBR.cpp - SP/SP_Ph1_SynchronGenerator4OrderDCIM.cpp SP/SP_Ph1_PQNode.cpp SP/SP_Ph1_PVNode.cpp SP/SP_Ph1_VDNode.cpp diff --git a/dpsim-models/src/DP/DP_Ph1_DPDQInterface.cpp b/dpsim-models/src/DP/DP_Ph1_DPDQInterface.cpp new file mode 100644 index 0000000000..54e125e7dc --- /dev/null +++ b/dpsim-models/src/DP/DP_Ph1_DPDQInterface.cpp @@ -0,0 +1,38 @@ +/* Copyright 2017-2021 Institute for Automation of Complex Power Systems, + * EONERC, RWTH Aachen University + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + *********************************************************************************/ + +#include + +using namespace CPS; + +void DP::Ph1::DPDQInterface::setDPShiftFrequency(const Real & omegaShift) { + mOmegaShift = omegaShift; +} + +void DP::Ph1::DPDQInterface::updateDQToDPTransform(const Real & thetaDQ, const Real & simTime) { + mDQToDPTransform << cos(thetaDQ - mOmegaShift * simTime), -sin(thetaDQ - mOmegaShift * simTime), + sin(thetaDQ - mOmegaShift * simTime), cos(thetaDQ - mOmegaShift * simTime); +} + +void DP::Ph1::DPDQInterface::updateDPToDQTransform(const Real & thetaDQ, const Real & simTime) { + mDPToDQTransform << cos(thetaDQ - mOmegaShift * simTime), sin(thetaDQ - mOmegaShift * simTime), + -sin(thetaDQ - mOmegaShift * simTime), cos(thetaDQ - mOmegaShift * simTime); +} + +Complex DP::Ph1::DPDQInterface::applyDQToDPTransform(const MatrixFixedSize<2,1> & dqMatrix) { + Complex dpComplex; + dpComplex = Complex((mDQToDPTransform*dqMatrix)(0,0),(mDQToDPTransform*dqMatrix)(1,0)); + return dpComplex; +} + +MatrixFixedSize<2,1> DP::Ph1::DPDQInterface::applyDPToDQTransform(const Complex& dpComplex) { + MatrixFixedSize<2,1> dqMatrix; + dqMatrix(0,0) = mDPToDQTransform(0,0) * dpComplex.real() + mDPToDQTransform(0,1) * dpComplex.imag(); + dqMatrix(1,0) = mDPToDQTransform(1,0) * dpComplex.real() + mDPToDQTransform(1,1) * dpComplex.imag(); + return dqMatrix; +} diff --git a/dpsim-models/src/DP/DP_Ph1_ReducedOrderSynchronGeneratorVBR.cpp b/dpsim-models/src/DP/DP_Ph1_ReducedOrderSynchronGeneratorVBR.cpp index b182e7135f..c9b2afe711 100644 --- a/dpsim-models/src/DP/DP_Ph1_ReducedOrderSynchronGeneratorVBR.cpp +++ b/dpsim-models/src/DP/DP_Ph1_ReducedOrderSynchronGeneratorVBR.cpp @@ -23,12 +23,6 @@ DP::Ph1::ReducedOrderSynchronGeneratorVBR::ReducedOrderSynchronGeneratorVBR // initialize conductance Matrix mConductanceMatrix = Matrix::Zero(2,2); - - // - mShiftVector = Matrix::Zero(3,1); - mShiftVector << Complex(1., 0), SHIFT_TO_PHASE_B, SHIFT_TO_PHASE_C; - mShiftVectorConj = Matrix::Zero(3,1); - mShiftVectorConj << Complex(1., 0), std::conj(SHIFT_TO_PHASE_B), std::conj(SHIFT_TO_PHASE_C); } DP::Ph1::ReducedOrderSynchronGeneratorVBR::ReducedOrderSynchronGeneratorVBR @@ -36,50 +30,25 @@ DP::Ph1::ReducedOrderSynchronGeneratorVBR::ReducedOrderSynchronGeneratorVBR : ReducedOrderSynchronGeneratorVBR(name, name, logLevel) { } -void DP::Ph1::ReducedOrderSynchronGeneratorVBR::initializeResistanceMatrix() { - // constant part of ABC resistance matrix - mResistanceMatrix_const = Matrix::Zero(1,3); - mResistanceMatrix_const << -mL0, -sqrt(3) / 2. * (mA - mB) - mL0, sqrt(3) / 2. * (mA - mB) - mL0; - mResistanceMatrix_const = (-1. / 3.) * mResistanceMatrix_const; - mR_const_1ph = (mResistanceMatrix_const * mShiftVector)(0,0); - - // - mKc = Matrix::Zero(1,3); - mKc << Complex(cos(PI/2.), -sin(PI/2.)), Complex(cos(7.*PI/6.), -sin(7.*PI/6.)), Complex(cos(PI/6.), sin(PI/6.)); - mKc = (-1. / 6.) * (mA + mB) * mKc; -} - void DP::Ph1::ReducedOrderSynchronGeneratorVBR::calculateConductanceMatrix() { MatrixFixedSize<2, 2> resistanceMatrix = MatrixFixedSize<2, 2>::Zero(2,2); - resistanceMatrix(0,0) = mR_const_1ph.real() + mKa_1ph.real() + mKb_1ph.real(); - resistanceMatrix(0,1) = -mR_const_1ph.imag() - mKa_1ph.imag() + mKb_1ph.imag(); - resistanceMatrix(1,0) = mR_const_1ph.imag() + mKa_1ph.imag() + mKb_1ph.imag(); - resistanceMatrix(1,1) = mR_const_1ph.real() + mKa_1ph.real() - mKb_1ph.real(); + Real DeltaTheta = **mThetaMech - mBase_OmMech * mSimTime; + resistanceMatrix(0,0) = - (mA + mB) / 2.0 * sin(2*DeltaTheta); + resistanceMatrix(0,1) = (mA - mB) / 2.0 + (mA + mB) / 2.0 * cos(2*DeltaTheta); + resistanceMatrix(1,0) = - (mA - mB) / 2.0 + (mA + mB) / 2.0 * cos(2*DeltaTheta); + resistanceMatrix(1,1) = (mA + mB) / 2.0 * sin(2*DeltaTheta); resistanceMatrix = resistanceMatrix * mBase_Z; mConductanceMatrix = resistanceMatrix.inverse(); } -void DP::Ph1::ReducedOrderSynchronGeneratorVBR::calculateAuxiliarVariables() { - mKa = Matrix::Zero(1,3); - mKa = mKc * Complex(cos(2. * **mThetaMech), sin(2. * **mThetaMech)); - mKa_1ph = (mKa * mShiftVector)(0,0); - - mKb = Matrix::Zero(1,3); - Real arg = 2. * **mThetaMech - 2. * mBase_OmMech * mSimTime ; - mKb = mKc * Complex(cos(arg), sin(arg)); - mKb_1ph = (mKb * mShiftVectorConj)(0,0); - - mKvbr = Matrix::Zero(1,2); - mKvbr(0,0) = Complex(cos(**mThetaMech - mBase_OmMech * mSimTime), sin(**mThetaMech - mBase_OmMech * mSimTime)); - mKvbr(0,1) = -Complex(cos(**mThetaMech - mBase_OmMech * mSimTime - PI/2.), sin(**mThetaMech - mBase_OmMech * mSimTime - PI/2.)); -} - void DP::Ph1::ReducedOrderSynchronGeneratorVBR::mnaCompInitialize(Real omega, Real timeStep, Attribute::Ptr leftVector) { Base::ReducedOrderSynchronGenerator::mnaCompInitialize(omega, timeStep, leftVector); - if (mModelAsCurrentSource) { + mDomainInterface.setDPShiftFrequency(mBase_OmMech); + + if (mModelAsNortonSource) { // FIXME set variable matrix entries accordingly as shown below mVariableSystemMatrixEntries.push_back(std::make_pair(matrixNodeIndex(0, 0), matrixNodeIndex(0, 0))); } else { @@ -117,18 +86,15 @@ void DP::Ph1::ReducedOrderSynchronGeneratorVBR::mnaCompInitialize(Real omega, } void DP::Ph1::ReducedOrderSynchronGeneratorVBR::mnaCompApplySystemMatrixStamp(SparseMatrixRow& systemMatrix) { - if (mModelAsCurrentSource) { + if (mModelAsNortonSource) { // Stamp conductance matrix - // set bottom right block Math::addToMatrixElement(systemMatrix, matrixNodeIndex(0, 0), matrixNodeIndex(0, 0), mConductanceMatrix); - } else { // Stamp voltage source Math::setMatrixElement(systemMatrix, mVirtualNodes[0]->matrixNodeIndex(), mVirtualNodes[1]->matrixNodeIndex(), Complex(-1, 0)); Math::setMatrixElement(systemMatrix, mVirtualNodes[1]->matrixNodeIndex(), mVirtualNodes[0]->matrixNodeIndex(), Complex(1, 0)); // Stamp conductance matrix - // set upper left block Math::addToMatrixElement(systemMatrix, mVirtualNodes[0]->matrixNodeIndex(), mVirtualNodes[0]->matrixNodeIndex(), mConductanceMatrix); @@ -142,14 +108,14 @@ void DP::Ph1::ReducedOrderSynchronGeneratorVBR::mnaCompApplySystemMatrixStamp(Sp } void DP::Ph1::ReducedOrderSynchronGeneratorVBR::mnaCompApplyRightSideVectorStamp(Matrix& rightVector) { - if (mModelAsCurrentSource) { - // compute equivalent northon circuit in abc reference frame + if (mModelAsNortonSource) { + // Determine equivalent Norton current mIvbr = Complex(mConductanceMatrix(0,0) * mEvbr.real() + mConductanceMatrix(0,1) * mEvbr.imag(), mConductanceMatrix(1,0) * mEvbr.real() + mConductanceMatrix(1,1) * mEvbr.imag()); - + // Stamp Norton current Math::setVectorElement(rightVector, matrixNodeIndex(0,0), mIvbr); - } else { + // Stamp history voltage Math::setVectorElement(rightVector, mVirtualNodes[1]->matrixNodeIndex(), mEvbr); } } @@ -158,34 +124,17 @@ void DP::Ph1::ReducedOrderSynchronGeneratorVBR::mnaCompPostStep(const Matrix& le // update armature voltage (**mIntfVoltage)(0, 0) = Math::complexFromVectorElement(leftVector, matrixNodeIndex(0, 0)); - // convert armature voltage into dq reference frame - Matrix parkTransform = get_parkTransformMatrix(); - MatrixComp Vabc_ = (**mIntfVoltage)(0, 0) * mShiftVector * Complex(cos(mNomOmega * mSimTime), sin(mNomOmega * mSimTime)); - auto Vabc = Matrix(3,1); - Vabc << Vabc_(0,0).real(), Vabc_(1,0).real(), Vabc_(2,0).real(); - **mVdq = parkTransform * Vabc / mBase_V_RMS; + // convert armature voltage to dq reference frame + **mVdq = mDomainInterface.applyDPToDQTransform((**mIntfVoltage)(0, 0)) / mBase_V_RMS; // update armature current - if (mModelAsCurrentSource) { + if (mModelAsNortonSource) { (**mIntfCurrent)(0, 0) = mIvbr - Complex(mConductanceMatrix(0,0) * (**mIntfVoltage)(0, 0).real() + mConductanceMatrix(0,1) * (**mIntfVoltage)(0, 0).imag(), mConductanceMatrix(1,0) * (**mIntfVoltage)(0, 0).real() + mConductanceMatrix(1,1) * (**mIntfVoltage)(0, 0).imag()); } else { (**mIntfCurrent)(0, 0) = Math::complexFromVectorElement(leftVector, mVirtualNodes[1]->matrixNodeIndex()); } - // convert armature current into dq reference frame - MatrixComp Iabc_ = (**mIntfCurrent)(0, 0) * mShiftVector * Complex(cos(mNomOmega * mSimTime), sin(mNomOmega * mSimTime)); - auto Iabc = Matrix(3,1); - Iabc << Iabc_(0,0).real(), Iabc_(1,0).real(), Iabc_(2,0).real(); - **mIdq = parkTransform * Iabc / mBase_I_RMS; -} - -Matrix DP::Ph1::ReducedOrderSynchronGeneratorVBR::get_parkTransformMatrix() const { - Matrix abcToDq0(2, 3); - - abcToDq0 << - 2./3.*cos(**mThetaMech), 2./3.*cos(**mThetaMech - 2.*PI/3.), 2./3.*cos(**mThetaMech + 2.*PI/3.), - -2./3.*sin(**mThetaMech), -2./3.*sin(**mThetaMech - 2.*PI/3.), -2./3.*sin(**mThetaMech + 2.*PI/3.); - - return abcToDq0; + // convert armature current to dq reference frame + **mIdq = mDomainInterface.applyDPToDQTransform((**mIntfCurrent)(0, 0)) / mBase_I_RMS; } diff --git a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator3OrderVBR.cpp b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator3OrderVBR.cpp index 293f333272..32d3a6a99f 100644 --- a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator3OrderVBR.cpp +++ b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator3OrderVBR.cpp @@ -34,7 +34,7 @@ void DP::Ph1::SynchronGenerator3OrderVBR::specificInitialization() { (**mEdq_t)(0,0) = 0.0; (**mEdq_t)(1,0) = (**mVdq)(1,0) + (**mIdq)(0,0) * mLd_t; - SPDLOG_LOGGER_INFO(mSLog, + SPDLOG_LOGGER_INFO(mSLog, "\n--- Model specific initialization ---" "\nInitial Ed_t (per unit): {:f}" "\nInitial Eq_t (per unit): {:f}" @@ -47,14 +47,20 @@ void DP::Ph1::SynchronGenerator3OrderVBR::specificInitialization() { void DP::Ph1::SynchronGenerator3OrderVBR::stepInPerUnit() { + // update DP-DQ transforms + mDomainInterface.updateDQToDPTransform(**mThetaMech, mSimTime); + mDomainInterface.updateDPToDQTransform(**mThetaMech, mSimTime); + // update Edq_t (**mEdq_t)(1,0) = (**mVdq)(1,0) + (**mIdq)(0,0) * mLd_t; - // VBR history voltage - calculateAuxiliarVariables(); + + // Update time-varying reactance matrix calculateConductanceMatrix(); + + // VBR history voltage mEh_vbr(0,0) = 0.0; mEh_vbr(1,0) = mAq_t * (**mIdq)(0,0) + mBq_t * (**mEdq_t)(1,0) + mDq_t * mEf_prev + mDq_t * (**mEf); // convert Edq_t into the abc reference frame - mEvbr = (mKvbr * mEh_vbr * mBase_V_RMS)(0,0); + mEvbr = mDomainInterface.applyDQToDPTransform(mEh_vbr) * mBase_V_RMS; } diff --git a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderPCM.cpp b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderPCM.cpp new file mode 100644 index 0000000000..e28b3db6b0 --- /dev/null +++ b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderPCM.cpp @@ -0,0 +1,159 @@ +/* Copyright 2017-2021 Institute for Automation of Complex Power Systems, + * EONERC, RWTH Aachen University + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + *********************************************************************************/ + +#include + +using namespace CPS; + +DP::Ph1::SynchronGenerator4OrderPCM::SynchronGenerator4OrderPCM + (const String& uid, const String& name, Logger::Level logLevel) + : Base::ReducedOrderSynchronGenerator(uid, name, logLevel), + mEdq_t(mAttributes->create("Edq_t")) { + + mSGOrder = SGOrder::SG4Order; + mPhaseType = PhaseType::Single; + setTerminalNumber(1); + + /// initialize attributes + mNumIter = mAttributes->create("NIterations", 0); + + // model variables + **mEdq_t = Matrix::Zero(2,1); +} + +DP::Ph1::SynchronGenerator4OrderPCM::SynchronGenerator4OrderPCM + (const String& name, Logger::Level logLevel) + : SynchronGenerator4OrderPCM(name, name, logLevel) { +} + +SimPowerComp::Ptr DP::Ph1::SynchronGenerator4OrderPCM::clone(const String& name) { + auto copy = SynchronGenerator4OrderPCM::make(name, mLogLevel); + + return copy; +} + +void DP::Ph1::SynchronGenerator4OrderPCM::specificInitialization() { + // calculate state representation matrix + calculateStateSpaceMatrices(); + + // initial voltage behind the transient reactance in the dq0 reference frame + (**mEdq_t)(0,0) = (**mVdq)(0,0) - (**mIdq)(1,0) * mLq_t; + (**mEdq_t)(1,0) = (**mVdq)(1,0) + (**mIdq)(0,0) * mLd_t; + + mSLog->info( + "\n--- Model specific initialization ---" + "\nInitial Ed_t (per unit): {:f}" + "\nInitial Eq_t (per unit): {:f}" + "\nMax number of iterations: {:d}" + "\nTolerance: {:f}" + "\nSG Model: 4 Order PCM" + "\n--- Model specific initialization finished ---", + + (**mEdq_t)(0,0), + (**mEdq_t)(1,0), + mMaxIter, + mTolerance + ); + mSLog->flush(); + + mDomainInterface.setDPShiftFrequency(mBase_OmMech); +} + +void DP::Ph1::SynchronGenerator4OrderPCM::calculateStateSpaceMatrices() { + // Initialize matrices of state representation + mAStateSpace << -mLq / mTq0_t / mLq_t, 0, + 0, -mLd / mTd0_t / mLd_t; + mBStateSpace << (mLq-mLq_t) / mTq0_t / mLq_t, 0.0, + 0.0, (mLd-mLd_t) / mTd0_t / mLd_t; + mCStateSpace << 0, + 1 / mTd0_t; + + // Precalculate trapezoidal based matrices (avoids redundant matrix inversions in correction steps) + Math::calculateStateSpaceTrapezoidalMatrices(mAStateSpace, mBStateSpace, mCStateSpace, mTimeStep, mAdTrapezoidal, mBdTrapezoidal, mCdTrapezoidal); +} + +void DP::Ph1::SynchronGenerator4OrderPCM::stepInPerUnit() { + // set number of iterations equal to zero + **mNumIter = 0; + + // store values currently at t=k for later use + mEdqtPrevStep = **mEdq_t; + mIdqPrevStep = **mIdq; + mVdqPrevStep = **mVdq; + + // update DQ-DP transforms according to mThetaMech + mDomainInterface.updateDQToDPTransform(**mThetaMech, mSimTime); + mDomainInterface.updateDPToDQTransform(**mThetaMech, mSimTime); + + // predict emf at t=k+1 (euler) using + (**mEdq_t) = Math::StateSpaceEuler(**mEdq_t, mAStateSpace, mBStateSpace, mCStateSpace * **mEf, mTimeStep, **mVdq); + + // predict stator currents at t=k+1 (assuming Vdq(k+1)=Vdq(k)) + (**mIdq)(0,0) = ((**mEdq_t)(1,0) - (**mVdq)(1,0)) / mLd_t; + (**mIdq)(1,0) = ((**mVdq)(0,0) - (**mEdq_t)(0,0)) / mLq_t; + + // convert currents to dp domain + (**mIntfCurrent)(0,0) = mDomainInterface.applyDQToDPTransform(**mIdq) * mBase_I_RMS; +} + +void DP::Ph1::SynchronGenerator4OrderPCM::mnaCompApplyRightSideVectorStamp(Matrix& rightVector) { + Math::setVectorElement(rightVector, matrixNodeIndex(0,0), (**mIntfCurrent)(0, 0)); +} + +void DP::Ph1::SynchronGenerator4OrderPCM::correctorStep() { + + // increase number of iterations + **mNumIter = **mNumIter + 1; + + // correct electrical vars + // calculate emf at j and k+1 (trapezoidal rule) + (**mEdq_t) = Math::applyStateSpaceTrapezoidalMatrices(mAdTrapezoidal, mBdTrapezoidal, mCdTrapezoidal * **mEf, mEdqtPrevStep, **mVdq, mVdqPrevStep); + + // calculate corrected stator currents at t=k+1 (assuming Vdq(k+1)=VdqPrevIter(k+1)) + (**mIdq)(0,0) = ((**mEdq_t)(1,0) - (**mVdq)(1,0) ) / mLd_t; + (**mIdq)(1,0) = ((**mVdq)(0,0) - (**mEdq_t)(0,0) ) / mLq_t; + + // convert corrected currents to dp domain + (**mIntfCurrent)(0,0) = mDomainInterface.applyDQToDPTransform(**mIdq) * mBase_I_RMS; + + // stamp currents + mnaCompApplyRightSideVectorStamp(**mRightVector); +} + +void DP::Ph1::SynchronGenerator4OrderPCM::updateVoltage(const Matrix& leftVector) { + + // store voltage value currently at j-1 for later use + mVdqPrevIter = **mVdq; + + // + (**mIntfVoltage)(0, 0) = Math::complexFromVectorElement(leftVector, matrixNodeIndex(0, 0)); + + // convert armature voltage into dq reference frame + **mVdq = mDomainInterface.applyDPToDQTransform((**mIntfVoltage)(0, 0)) / mBase_V_RMS; +} + +bool DP::Ph1::SynchronGenerator4OrderPCM::requiresIteration() { + if (**mNumIter >= mMaxIter) { + // maximum number of iterations reached + return false; + } else if (**mNumIter == 0) { + // no corrector step has been performed yet, + // convergence cannot be confirmed + return true; + } else { + // check voltage convergence according to tolerance + Matrix voltageDifference = **mVdq - mVdqPrevIter; + if (Math::abs(voltageDifference(0,0)) > mTolerance || Math::abs(voltageDifference(1,0)) > mTolerance) + return true; + else + return false; + } +} + +void DP::Ph1::SynchronGenerator4OrderPCM::mnaCompPostStep(const Matrix& leftVector) { +} diff --git a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp new file mode 100644 index 0000000000..d3e9166eec --- /dev/null +++ b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderTPM.cpp @@ -0,0 +1,267 @@ +/* Copyright 2017-2021 Institute for Automation of Complex Power Systems, + * EONERC, RWTH Aachen University + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + *********************************************************************************/ + +#include + +using namespace CPS; + +DP::Ph1::SynchronGenerator4OrderTPM::SynchronGenerator4OrderTPM + (String uid, String name, Logger::Level logLevel) + : Base::ReducedOrderSynchronGenerator(uid, name, logLevel), + mEhMod(mAttributes->create("Ehmod")), + mEdq_t(mAttributes->create("Edq_t")) { + + mSGOrder = SGOrder::SG4Order; + mPhaseType = PhaseType::Single; + setTerminalNumber(1); + + // initialize attributes + mNumIter = mAttributes->create("NIterations", 0); + + // model variables + **mEdq_t = Matrix::Zero(2,1); +} + +DP::Ph1::SynchronGenerator4OrderTPM::SynchronGenerator4OrderTPM + (String name, Logger::Level logLevel) + : SynchronGenerator4OrderTPM(name, name, logLevel) { +} + +SimPowerComp::Ptr DP::Ph1::SynchronGenerator4OrderTPM::clone(String name) { + auto copy = SynchronGenerator4OrderTPM::make(name, mLogLevel); + + return copy; +} + +void DP::Ph1::SynchronGenerator4OrderTPM::setOperationalParametersPerUnit(Real nomPower, + Real nomVolt, Real nomFreq, Real H, Real Ld, Real Lq, Real L0, + Real Ld_t, Real Lq_t, Real Td0_t, Real Tq0_t) { + + Base::ReducedOrderSynchronGenerator::setOperationalParametersPerUnit(nomPower, + nomVolt, nomFreq, H, Ld, Lq, L0, + Ld_t, Lq_t, Td0_t, Tq0_t); + + mSLog->info("Set base parameters: \n" + "nomPower: {:e}\nnomVolt: {:e}\nnomFreq: {:e}\n", + nomPower, nomVolt, nomFreq); + + mSLog->info("Set operational parameters in per unit: \n" + "inertia: {:e}\n" + "Ld: {:e}\nLq: {:e}\nL0: {:e}\n" + "Ld_t: {:e}\nLq_t: {:e}\n" + "Td0_t: {:e}\nTq0_t: {:e}\n", + H, Ld, Lq, L0, + Ld_t, Lq_t, + Td0_t, Tq0_t); +}; + +void DP::Ph1::SynchronGenerator4OrderTPM::calculateStateSpaceMatrices() { + mAStateSpace << -mLq / mTq0_t / mLq_t, 0, + 0, -mLd / mTd0_t / mLd_t; + mBStateSpace << (mLq-mLq_t) / mTq0_t / mLq_t, 0.0, + 0.0, (mLd-mLd_t) / mTd0_t / mLd_t; + mCStateSpace << 0, + 1 / mTd0_t; + Math::calculateStateSpaceTrapezoidalMatrices(mAStateSpace, mBStateSpace, mCStateSpace, mTimeStep, mAdStateSpace, mBdStateSpace, mCdStateSpace); +} + +void DP::Ph1::SynchronGenerator4OrderTPM::specificInitialization() { + // initial emf in the dq reference frame + (**mEdq_t)(0,0) = (**mVdq)(0,0) - (**mIdq)(1,0) * mLq_t; + (**mEdq_t)(1,0) = (**mVdq)(1,0) + (**mIdq)(0,0) * mLd_t; + SPDLOG_LOGGER_INFO(mSLog, + "\n--- Model specific initialization ---" + "\nInitial Ed_t (per unit): {:f}" + "\nInitial Eq_t (per unit): {:f}" + "\nSG Model: 4 Order TPM" + "\n--- Model specific initialization finished ---", + (**mEdq_t)(0,0), + (**mEdq_t)(1,0) + ); + + calculateStateSpaceMatrices(); + + mDomainInterface.setDPShiftFrequency(mBase_OmMech); +} + +void DP::Ph1::SynchronGenerator4OrderTPM::calculateConstantConductanceMatrix() { + Matrix resistanceMatrix = Matrix::Zero(2,2); + resistanceMatrix(0,0) = 0; + resistanceMatrix(0,1) = (mA - mB) / 2.0; + resistanceMatrix(1,0) = - (mA - mB) / 2.0; + resistanceMatrix(1,1) = 0; + + SPDLOG_LOGGER_INFO(mSLog, "\nR_const [pu]: {}", Logger::matrixToString(resistanceMatrix)); + + resistanceMatrix = resistanceMatrix * mBase_Z; + SPDLOG_LOGGER_INFO(mSLog, "\nR_const [Ohm]: {}", Logger::matrixToString(resistanceMatrix)); + + mConductanceMatrixConst = resistanceMatrix.inverse(); + SPDLOG_LOGGER_INFO(mSLog, "\nG_const [S]: {}", Logger::matrixToString(mConductanceMatrixConst)); +} + +void DP::Ph1::SynchronGenerator4OrderTPM::mnaCompApplySystemMatrixStamp(SparseMatrixRow& systemMatrix) { + updateMatrixNodeIndices(); + + calculateConstantConductanceMatrix(); + + if (mModelAsNortonSource) { + // Stamp conductance matrix + Math::addToMatrixElement(systemMatrix, matrixNodeIndex(0, 0), matrixNodeIndex(0, 0), mConductanceMatrixConst); + } else { + // Stamp voltage source + Math::setMatrixElement(systemMatrix, mVirtualNodes[0]->matrixNodeIndex(), mVirtualNodes[1]->matrixNodeIndex(), Complex(-1, 0)); + Math::setMatrixElement(systemMatrix, mVirtualNodes[1]->matrixNodeIndex(), mVirtualNodes[0]->matrixNodeIndex(), Complex(1, 0)); + + // Stamp conductance + // set upper left block + Math::addToMatrixElement(systemMatrix, mVirtualNodes[0]->matrixNodeIndex(), mVirtualNodes[0]->matrixNodeIndex(), mConductanceMatrixConst); + + // set buttom right block + Math::addToMatrixElement(systemMatrix, matrixNodeIndex(0, 0), matrixNodeIndex(0, 0), mConductanceMatrixConst); + + // Set off diagonal blocks + Math::addToMatrixElement(systemMatrix, mVirtualNodes[0]->matrixNodeIndex(), matrixNodeIndex(0, 0), -mConductanceMatrixConst); + Math::addToMatrixElement(systemMatrix, matrixNodeIndex(0, 0), mVirtualNodes[0]->matrixNodeIndex(), -mConductanceMatrixConst); + } +} + +void DP::Ph1::SynchronGenerator4OrderTPM::stepInPerUnit() { + // set number of iteratios equal to zero + **mNumIter = 0; + + // update DP-DQ transforms + mDomainInterface.updateDQToDPTransform(**mThetaMech, mSimTime); + mDomainInterface.updateDPToDQTransform(**mThetaMech, mSimTime); + + // update varying resistance matrix part + Real DeltaTheta = **mThetaMech - mBase_OmMech * mSimTime; + mResistanceMatrixVarying(0,0) = - (mA + mB) / 2.0 * sin(2*DeltaTheta); + mResistanceMatrixVarying(0,1) = (mA + mB) / 2.0 * cos(2*DeltaTheta); + mResistanceMatrixVarying(1,0) = (mA + mB) / 2.0 * cos(2*DeltaTheta); + mResistanceMatrixVarying(1,1) = (mA + mB) / 2.0 * sin(2*DeltaTheta); + SPDLOG_LOGGER_DEBUG(mSLog, "\nR_var [pu] (t={:f}): {:s}", mSimTime, Logger::matrixToString(mResistanceMatrixVarying)); + + // predict electrical vars + // set previous values of stator current at simulation start + if (mSimTime == 0.0) { + (**mIntfCurrent)(0,0) = std::conj(mInitElecPower / (mInitVoltage * mBase_V_RMS)); + mIdpTwoPrevStep = **mIntfCurrent; + } + + // predict stator current (linear extrapolation) + Matrix IdpPrediction = Matrix::Zero(2,1); + IdpPrediction(0,0) = 2 * (**mIntfCurrent)(0,0).real() - mIdpTwoPrevStep(0,0).real(); + IdpPrediction(1,0) = 2 * (**mIntfCurrent)(0,0).imag() - mIdpTwoPrevStep(0,0).imag(); + + // calculate emf at t=k + (**mEdq_t)(0,0) = (**mVdq)(0,0) - (**mIdq)(1,0) * mLq_t; + (**mEdq_t)(1,0) = (**mVdq)(1,0) + (**mIdq)(0,0) * mLd_t; + + // calculate original history voltage of VBR model (trapezoidal rule) + mEh(0,0) = mAd_t * (**mIdq)(1,0) + mBd_t * (**mEdq_t)(0,0); + mEh(1,0) = mAq_t * (**mIdq)(0,0) + mBq_t * (**mEdq_t)(1,0) + mDq_t * mEf_prev + mDq_t * (**mEf); + + // set to original history voltage in dp domain + **mEhMod = mDomainInterface.applyDQToDPTransform(mEh) * mBase_V_RMS; + + // add current prediction based component to modified history voltage of TPM model in dp domain + **mEhMod += - Complex(mBase_Z * (mResistanceMatrixVarying * IdpPrediction)(0,0), 0); + **mEhMod += - Complex(0, mBase_Z * (mResistanceMatrixVarying * IdpPrediction)(1,0)); + + // store values currently at t=k for later use + mIdpTwoPrevStep = **mIntfCurrent; + mVdqPrevStep = **mVdq; + mEdqtPrevStep = **mEdq_t; +} + +void DP::Ph1::SynchronGenerator4OrderTPM::mnaCompApplyRightSideVectorStamp(Matrix& rightVector) { + if (mModelAsNortonSource) { + // Determine equivalent Norton current + mIhMod = Complex(mConductanceMatrixConst(0,0) * (**mEhMod).real() + mConductanceMatrixConst(0,1) * (**mEhMod).imag(), + mConductanceMatrixConst(1,0) * (**mEhMod).real() + mConductanceMatrixConst(1,1) * (**mEhMod).imag()); + // Stamp Norton current + Math::setVectorElement(rightVector, matrixNodeIndex(0,0), mIhMod); + } else { + // Stamp modified history voltage + Math::setVectorElement(rightVector, mVirtualNodes[1]->matrixNodeIndex(), **mEhMod); + } +} + +void DP::Ph1::SynchronGenerator4OrderTPM::correctorStep() { + // increase number of iterations + **mNumIter = **mNumIter + 1; + + // correct electrical vars + // calculate emf at j and k+1 (trapezoidal rule) + (**mEdq_t) = Math::applyStateSpaceTrapezoidalMatrices(mAdStateSpace, mBdStateSpace, mCdStateSpace * **mEf, mEdqtPrevStep, **mVdq, mVdqPrevStep); + + // calculate stator currents at j and k+1 + (**mIdq)(0,0) = ((**mEdq_t)(1,0) - (**mVdq)(1,0) ) / mLd_t; + (**mIdq)(1,0) = ((**mVdq)(0,0) - (**mEdq_t)(0,0) ) / mLq_t; + + // convert corrected currents to dp domain + Complex IdpCorrectionComplex = mDomainInterface.applyDQToDPTransform(**mIdq) * mBase_I_RMS; + + Matrix IdpCorrection = Matrix::Zero(2,1); + IdpCorrection(0,0) = IdpCorrectionComplex.real(); + IdpCorrection(1,0) = IdpCorrectionComplex.imag(); + + // reset to original history voltage of VBR model in dp domain + **mEhMod = mDomainInterface.applyDQToDPTransform(mEh) * mBase_V_RMS; + + // add current correction based component to modified history voltage of TPM model in dp domain + **mEhMod += - Complex(mBase_Z * (mResistanceMatrixVarying * IdpCorrection)(0,0), 0); + **mEhMod += - Complex(0, mBase_Z * (mResistanceMatrixVarying * IdpCorrection)(1,0)); + + // stamp currents + (**mRightVector).setZero(); + mnaCompApplyRightSideVectorStamp(**mRightVector); + + // store value currently at j-1 for later use + mVdqPrevIter = **mVdq; +} + +void DP::Ph1::SynchronGenerator4OrderTPM::updateVoltage(const Matrix& leftVector) { + // update armature voltage + (**mIntfVoltage)(0, 0) = Math::complexFromVectorElement(leftVector, matrixNodeIndex(0, 0)); + + // convert armature voltage to dq reference frame + **mVdq = mDomainInterface.applyDPToDQTransform((**mIntfVoltage)(0, 0)) / mBase_V_RMS; +} + +bool DP::Ph1::SynchronGenerator4OrderTPM::requiresIteration() { + if (**mNumIter >= mMaxIter) { + // maximum number of iterations reached + return false; + } else if (**mNumIter == 0) { + // no corrector step has been performed yet, + // convergence cannot be confirmed + return true; + } else { + // check voltage convergence according to tolerance + Matrix voltageDifference = **mVdq - mVdqPrevIter; + if (Math::abs(voltageDifference(0,0)) > mTolerance || Math::abs(voltageDifference(1,0)) > mTolerance) + return true; + else + return false; + } +} + +void DP::Ph1::SynchronGenerator4OrderTPM::mnaCompPostStep(const Matrix& leftVector) { + // update armature current + if (mModelAsNortonSource) { + (**mIntfCurrent)(0, 0) = mIhMod - Complex(mConductanceMatrixConst(0,0) * (**mIntfVoltage)(0, 0).real() + mConductanceMatrixConst(0,1) * (**mIntfVoltage)(0, 0).imag(), + mConductanceMatrixConst(1,0) * (**mIntfVoltage)(0, 0).real() + mConductanceMatrixConst(1,1) * (**mIntfVoltage)(0, 0).imag()); + } else { + (**mIntfCurrent)(0, 0) = Math::complexFromVectorElement(leftVector, mVirtualNodes[1]->matrixNodeIndex()); + } + + // convert armature current to dq reference frame + **mIdq = mDomainInterface.applyDPToDQTransform((**mIntfCurrent)(0, 0)) / mBase_I_RMS; +} diff --git a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderVBR.cpp b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderVBR.cpp index 8a0f179556..e15b8427cc 100644 --- a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderVBR.cpp +++ b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator4OrderVBR.cpp @@ -34,7 +34,7 @@ void DP::Ph1::SynchronGenerator4OrderVBR::specificInitialization() { (**mEdq_t)(0,0) = (**mVdq)(0,0) - (**mIdq)(1,0) * mLq_t; (**mEdq_t)(1,0) = (**mVdq)(1,0) + (**mIdq)(0,0) * mLd_t; - SPDLOG_LOGGER_INFO(mSLog, + SPDLOG_LOGGER_INFO(mSLog, "\n--- Model specific initialization ---" "\nInitial Ed_t (per unit): {:f}" "\nInitial Eq_t (per unit): {:f}" @@ -47,16 +47,21 @@ void DP::Ph1::SynchronGenerator4OrderVBR::specificInitialization() { void DP::Ph1::SynchronGenerator4OrderVBR::stepInPerUnit() { + // update DP-DQ transforms + mDomainInterface.updateDQToDPTransform(**mThetaMech, mSimTime); + mDomainInterface.updateDPToDQTransform(**mThetaMech, mSimTime); + // calculate Edq_t at t=k (**mEdq_t)(0,0) = (**mVdq)(0,0) - (**mIdq)(1,0) * mLq_t; (**mEdq_t)(1,0) = (**mVdq)(1,0) + (**mIdq)(0,0) * mLd_t; - // VBR history voltage - calculateAuxiliarVariables(); + // Update time-varying reactance matrix calculateConductanceMatrix(); + + // VBR history voltage mEh_vbr(0,0) = mAd_t * (**mIdq)(1,0) + mBd_t * (**mEdq_t)(0,0); mEh_vbr(1,0) = mAq_t * (**mIdq)(0,0) + mBq_t * (**mEdq_t)(1,0) + mDq_t * mEf_prev + mDq_t * (**mEf); // convert Edq_t into the abc reference frame - mEvbr = (mKvbr * mEh_vbr * mBase_V_RMS)(0,0); + mEvbr = mDomainInterface.applyDQToDPTransform(mEh_vbr) * mBase_V_RMS; } diff --git a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderPCM.cpp b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderPCM.cpp new file mode 100644 index 0000000000..d57f183cc9 --- /dev/null +++ b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6OrderPCM.cpp @@ -0,0 +1,181 @@ +/* Copyright 2017-2021 Institute for Automation of Complex Power Systems, + * EONERC, RWTH Aachen University + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + *********************************************************************************/ + +#include + +using namespace CPS; + +DP::Ph1::SynchronGenerator6OrderPCM::SynchronGenerator6OrderPCM + (const String& uid, const String& name, Logger::Level logLevel) + : Base::ReducedOrderSynchronGenerator(uid, name, logLevel), + mEdq_t(mAttributes->create("Edq_t")), + mEdq_s(mAttributes->create("Edq_s")) { + + mSGOrder = SGOrder::SG6bOrder; + mPhaseType = PhaseType::Single; + setTerminalNumber(1); + + /// initialize attributes + mNumIter = mAttributes->create("NIterations", 0); + + // model variables + **mEdq_t = Matrix::Zero(2,1); + **mEdq_s = Matrix::Zero(2,1); +} + +DP::Ph1::SynchronGenerator6OrderPCM::SynchronGenerator6OrderPCM + (const String& name, Logger::Level logLevel) + : SynchronGenerator6OrderPCM(name, name, logLevel) { +} + +SimPowerComp::Ptr DP::Ph1::SynchronGenerator6OrderPCM::clone(const String& name) { + auto copy = SynchronGenerator6OrderPCM::make(name, mLogLevel); + + return copy; +} + +void DP::Ph1::SynchronGenerator6OrderPCM::specificInitialization() { + + // calculate state representation matrix + calculateStateSpaceMatrices(); + + // initial voltage behind the transient reactance in the dq0 reference frame + (**mEdq_t)(0,0) = (mLq - mLq_t) * (**mIdq)(1,0); + (**mEdq_t)(1,0) = - (mLd- mLd_t) * (**mIdq)(0,0) + (**mEf); + (**mEdq_s)(0,0) = (**mEdq_t)(0,0) + (mLq_t - mLq_s) * (**mIdq)(1,0); + (**mEdq_s)(1,0) = (**mEdq_t)(1,0) - (mLd_t - mLd_s) * (**mIdq)(0,0); + + // + mEdqts << **mEdq_t, **mEdq_s; + + mSLog->info( + "\n--- Model specific initialization ---" + "\nInitial Ed_t (per unit): {:f}" + "\nInitial Eq_t (per unit): {:f}" + "\nInitial Ed_s (per unit): {:f}" + "\nInitial Eq_s (per unit): {:f}" + "\nMax number of iterations: {:d}" + "\nTolerance: {:f}" + "\nSG Model: 6 Order (Anderson-Fouad) PCM" + "\n--- Model specific initialization finished ---", + + (**mEdq_t)(0,0), + (**mEdq_t)(1,0), + (**mEdq_s)(0,0), + (**mEdq_s)(1,0), + mMaxIter, + mTolerance + ); + mSLog->flush(); + + mDomainInterface.setDPShiftFrequency(mBase_OmMech); +} + +void DP::Ph1::SynchronGenerator6OrderPCM::calculateStateSpaceMatrices() { + // auxiliar constants + Real Bd_t = 1. / mTq0_t * (mLq - mLq_t); + Real Bq_t = 1. / mTd0_t * (mLd - mLd_t); + Real Bd_s = 1. / mTq0_s * (mLq_t - mLq_s); + Real Bq_s = 1. / mTd0_s * (mLd_t - mLd_s); + + // Initialize matrices of state representation of predictor step + mAStateSpace << -1. / mTq0_t, 0., 0., 0., + 0., -1. / mTd0_t, 0., 0., + 1. / mTq0_s, 0., -1. / mTq0_s, 0., + 0., 1. / mTd0_s, 0., -1. / mTd0_s; + mBStateSpace << 0., Bd_t, + -Bq_t, 0., + 0., Bd_s, + -Bq_s, 0.; + mCStateSpace << 0., + 1 / mTd0_t, + 0., + 0.; + + // Precalculate trapezoidal based matrices (avoids redundant matrix inversions in correction steps) + Math::calculateStateSpaceTrapezoidalMatrices(mAStateSpace, mBStateSpace, mCStateSpace, mTimeStep, mAdTrapezoidal, mBdTrapezoidal, mCdTrapezoidal); +} + +void DP::Ph1::SynchronGenerator6OrderPCM::stepInPerUnit() { + // set number of iteratios equal to zero + **mNumIter = 0; + + // store values currently at t=k for later use + mIdqPrevStep = **mIdq; + mEdqtsPrevStep = mEdqts; + + // update DQ-DP transforms according to mThetaMech + mDomainInterface.updateDQToDPTransform(**mThetaMech, mSimTime); + mDomainInterface.updateDPToDQTransform(**mThetaMech, mSimTime); + + // predict emf at t=k+1 (euler) using + mEdqts = Math::StateSpaceEuler(mEdqts, mAStateSpace, mBStateSpace, mCStateSpace * **mEf, mTimeStep, **mIdq); + + // predict armature currents for at t=k+1 + (**mIdq)(0,0) = (mEdqts(3,0) - (**mVdq)(1,0) ) / mLd_s; + (**mIdq)(1,0) = ((**mVdq)(0,0) - mEdqts(2,0) ) / mLq_s; + + // convert currents to dp domain + (**mIntfCurrent)(0,0) = mDomainInterface.applyDQToDPTransform(**mIdq) * mBase_I_RMS; +} + +void DP::Ph1::SynchronGenerator6OrderPCM::mnaCompApplyRightSideVectorStamp(Matrix& rightVector) { + Math::setVectorElement(rightVector, matrixNodeIndex(0,0), (**mIntfCurrent)(0, 0)); +} + +void DP::Ph1::SynchronGenerator6OrderPCM::correctorStep() { + // corrector step (trapezoidal rule) + **mNumIter = **mNumIter + 1; + + // correct emf at t=k+1 (trapezoidal rule) + mEdqts = Math::applyStateSpaceTrapezoidalMatrices(mAdTrapezoidal, mBdTrapezoidal, mCdTrapezoidal * **mEf, mEdqtsPrevStep, **mIdq, mIdqPrevStep); + + // calculate corrected stator currents at t=k+1 (assuming Vdq(k+1)=VdqPrevIter(k+1)) + (**mIdq)(0,0) = (mEdqts(3,0) - (**mVdq)(1,0) ) / mLd_s; + (**mIdq)(1,0) = ((**mVdq)(0,0) - mEdqts(2,0) ) / mLq_s; + + // convert corrected currents to dp domain + (**mIntfCurrent)(0,0) = mDomainInterface.applyDQToDPTransform(**mIdq) * mBase_I_RMS; + + // stamp currents + mnaCompApplyRightSideVectorStamp(**mRightVector); +} + +void DP::Ph1::SynchronGenerator6OrderPCM::updateVoltage(const Matrix& leftVector) { + // store voltage value currently at j-1 for later use + mVdqPrevIter = **mVdq; + + // + (**mIntfVoltage)(0, 0) = Math::complexFromVectorElement(leftVector, matrixNodeIndex(0, 0)); + + // convert armature voltage into dq reference frame + **mVdq = mDomainInterface.applyDPToDQTransform((**mIntfVoltage)(0, 0)) / mBase_V_RMS; +} + +bool DP::Ph1::SynchronGenerator6OrderPCM::requiresIteration() { + if (**mNumIter == 0) + // if no corrector step has been performed yet + return true; + + Matrix voltageDifference = **mVdq - mVdqPrevIter; + if (Math::abs(voltageDifference(0,0)) > mTolerance || Math::abs(voltageDifference(1,0)) > mTolerance) { + if (**mNumIter == mMaxIter) { + return false; + } else { + return true; + } + } else { + return false; + } +} + +void DP::Ph1::SynchronGenerator6OrderPCM::mnaCompPostStep(const Matrix& leftVector) { + **mEdq_t << mEdqts(0,0), mEdqts(1,0); + **mEdq_s << mEdqts(2,0), mEdqts(3,0); +} + diff --git a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6aOrderVBR.cpp b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6aOrderVBR.cpp index 72834dfd2e..edc33e4978 100644 --- a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6aOrderVBR.cpp +++ b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6aOrderVBR.cpp @@ -41,7 +41,7 @@ void DP::Ph1::SynchronGenerator6aOrderVBR::specificInitialization() { (**mEdq_s)(0,0) = (**mVdq)(0,0) - mLq_s * (**mIdq)(1,0); (**mEdq_s)(1,0) = (**mVdq)(1,0) + mLd_s * (**mIdq)(0,0); - SPDLOG_LOGGER_INFO(mSLog, + SPDLOG_LOGGER_INFO(mSLog, "\n--- Model specific initialization ---" "\nInitial Ed_t (per unit): {:f}" "\nInitial Eq_t (per unit): {:f}" @@ -58,6 +58,10 @@ void DP::Ph1::SynchronGenerator6aOrderVBR::specificInitialization() { void DP::Ph1::SynchronGenerator6aOrderVBR::stepInPerUnit() { + // update DP-DQ transforms + mDomainInterface.updateDQToDPTransform(**mThetaMech, mSimTime); + mDomainInterface.updateDPToDQTransform(**mThetaMech, mSimTime); + if (mSimTime>0.0){ // calculate Edq_t at t=k (**mEdq_t)(0,0) = mAd_t * (**mIdq)(1,0) + mEh_t(0,0); @@ -68,10 +72,10 @@ void DP::Ph1::SynchronGenerator6aOrderVBR::stepInPerUnit() { (**mEdq_s)(1,0) = (**mIdq)(0,0) * mLd_s + (**mVdq)(1,0); } - // VBR history voltage - calculateAuxiliarVariables(); + // Update time-varying reactance matrix calculateConductanceMatrix(); + // VBR history voltage // calculate history term behind the transient reactance mEh_t(0,0) = mAd_t * (**mIdq)(1,0) + mBd_t * (**mEdq_t)(0,0); mEh_t(1,0) = mAq_t * (**mIdq)(0,0) + mBq_t * (**mEdq_t)(1,0) + mDq_t * (**mEf) + mDq_t * mEf_prev; @@ -81,5 +85,5 @@ void DP::Ph1::SynchronGenerator6aOrderVBR::stepInPerUnit() { mEh_s(1,0) = mAq_s * (**mIdq)(0,0) + mBq_s * (**mEdq_t)(1,0) + mCq_s * (**mEdq_s)(1,0) + mDq_s * (**mEf) + mDq_s * mEf_prev; // convert Edq_t into the abc reference frame - mEvbr = (mKvbr * mEh_s * mBase_V_RMS)(0,0); + mEvbr = mDomainInterface.applyDQToDPTransform(mEh_s) * mBase_V_RMS; } diff --git a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6bOrderVBR.cpp b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6bOrderVBR.cpp index 666331ea1c..d119bd8452 100644 --- a/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6bOrderVBR.cpp +++ b/dpsim-models/src/DP/DP_Ph1_SynchronGenerator6bOrderVBR.cpp @@ -41,7 +41,7 @@ void DP::Ph1::SynchronGenerator6bOrderVBR::specificInitialization() { (**mEdq_s)(0,0) = (**mVdq)(0,0) - mLq_s * (**mIdq)(1,0); (**mEdq_s)(1,0) = (**mVdq)(1,0) + mLd_s * (**mIdq)(0,0); - SPDLOG_LOGGER_INFO(mSLog, + SPDLOG_LOGGER_INFO(mSLog, "\n--- Model specific initialization ---" "\nInitial Ed_t (per unit): {:f}" "\nInitial Eq_t (per unit): {:f}" @@ -59,6 +59,10 @@ void DP::Ph1::SynchronGenerator6bOrderVBR::specificInitialization() { void DP::Ph1::SynchronGenerator6bOrderVBR::stepInPerUnit() { + // update DP-DQ transforms + mDomainInterface.updateDQToDPTransform(**mThetaMech, mSimTime); + mDomainInterface.updateDPToDQTransform(**mThetaMech, mSimTime); + if (mSimTime>0.0){ // calculate Edq_t at t=k (**mEdq_t)(0,0) = mAd_t * (**mIdq)(1,0) + mEh_t(0,0); @@ -69,10 +73,10 @@ void DP::Ph1::SynchronGenerator6bOrderVBR::stepInPerUnit() { (**mEdq_s)(1,0) = (**mIdq)(0,0) * mLd_s + (**mVdq)(1,0); } - // VBR history voltage - calculateAuxiliarVariables(); + // Update time-varying reactance matrix calculateConductanceMatrix(); + // VBR history voltage // calculate history term behind the transient reactance mEh_t(0,0) = mAd_t * (**mIdq)(1,0) + mBd_t * (**mEdq_t)(0,0); mEh_t(1,0) = mAq_t * (**mIdq)(0,0) + mBq_t * (**mEdq_t)(1,0) + mDq_t * (**mEf) + mDq_t * mEf_prev; @@ -82,5 +86,5 @@ void DP::Ph1::SynchronGenerator6bOrderVBR::stepInPerUnit() { mEh_s(1,0) = mAq_s * (**mIdq)(0,0) + mBq_s * (**mEdq_t)(1,0) + mCq_s * (**mEdq_s)(1,0) + mDq_s * (**mEf) + mDq_s * mEf_prev; // convert Edq_t into the abc reference frame - mEvbr = (mKvbr * mEh_s * mBase_V_RMS)(0,0); + mEvbr = mDomainInterface.applyDQToDPTransform(mEh_s) * mBase_V_RMS; } diff --git a/dpsim-models/src/EMT/EMT_Ph3_ReducedOrderSynchronGeneratorVBR.cpp b/dpsim-models/src/EMT/EMT_Ph3_ReducedOrderSynchronGeneratorVBR.cpp index 5d3027a2a2..5b392189c9 100644 --- a/dpsim-models/src/EMT/EMT_Ph3_ReducedOrderSynchronGeneratorVBR.cpp +++ b/dpsim-models/src/EMT/EMT_Ph3_ReducedOrderSynchronGeneratorVBR.cpp @@ -49,7 +49,7 @@ void EMT::Ph3::ReducedOrderSynchronGeneratorVBR::mnaCompInitialize(Real omega, Base::ReducedOrderSynchronGenerator::mnaCompInitialize(omega, timeStep, leftVector); - if (mModelAsCurrentSource) { + if (mModelAsNortonSource) { mVariableSystemMatrixEntries.push_back(std::make_pair(matrixNodeIndex(0, 0), matrixNodeIndex(0, 0))); mVariableSystemMatrixEntries.push_back(std::make_pair(matrixNodeIndex(0, 0), matrixNodeIndex(0, 1))); mVariableSystemMatrixEntries.push_back(std::make_pair(matrixNodeIndex(0, 0), matrixNodeIndex(0, 2))); @@ -111,7 +111,7 @@ void EMT::Ph3::ReducedOrderSynchronGeneratorVBR::mnaCompInitialize(Real omega, void EMT::Ph3::ReducedOrderSynchronGeneratorVBR::mnaCompApplySystemMatrixStamp(SparseMatrixRow& systemMatrix) { - if (mModelAsCurrentSource) { + if (mModelAsNortonSource) { // Stamp conductance matrix Math::addToMatrixElement(systemMatrix, matrixNodeIndex(0, 0), matrixNodeIndex(0, 0), mConductanceMatrix(0, 0)); Math::addToMatrixElement(systemMatrix, matrixNodeIndex(0, 0), matrixNodeIndex(0, 1), mConductanceMatrix(0, 1)); @@ -180,7 +180,7 @@ void EMT::Ph3::ReducedOrderSynchronGeneratorVBR::mnaCompApplySystemMatrixStamp(S } void EMT::Ph3::ReducedOrderSynchronGeneratorVBR::mnaCompApplyRightSideVectorStamp(Matrix& rightVector) { - if (mModelAsCurrentSource) { + if (mModelAsNortonSource) { // compute equivalent northon circuit in abc reference frame mIvbr = mConductanceMatrix * mEvbr; @@ -205,7 +205,7 @@ void EMT::Ph3::ReducedOrderSynchronGeneratorVBR::mnaCompPostStep(const Matrix& l **mVdq0 = mAbcToDq0 * **mIntfVoltage / mBase_V; // update armature current - if (mModelAsCurrentSource) { + if (mModelAsNortonSource) { Matrix Iconductance = mConductanceMatrix * **mIntfVoltage; (**mIntfCurrent) = mIvbr - Iconductance; } diff --git a/dpsim-models/src/EMT/EMT_Ph3_SynchronGenerator4OrderPCM.cpp b/dpsim-models/src/EMT/EMT_Ph3_SynchronGenerator4OrderPCM.cpp new file mode 100644 index 0000000000..e9c3cb53ef --- /dev/null +++ b/dpsim-models/src/EMT/EMT_Ph3_SynchronGenerator4OrderPCM.cpp @@ -0,0 +1,188 @@ +/* Copyright 2017-2021 Institute for Automation of Complex Power Systems, + * EONERC, RWTH Aachen University + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + *********************************************************************************/ + +#include + +using namespace CPS; + +EMT::Ph3::SynchronGenerator4OrderPCM::SynchronGenerator4OrderPCM + (const String& uid, const String& name, Logger::Level logLevel) + : Base::ReducedOrderSynchronGenerator(uid, name, logLevel), + mEdq0_t(mAttributes->create("Edq0_t")) { + + mPhaseType = PhaseType::ABC; + setTerminalNumber(1); + + // Initialize attributes + mNumIter = mAttributes->create("NIterations", 0); + + // model variables + **mEdq0_t = Matrix::Zero(3,1); +} + +EMT::Ph3::SynchronGenerator4OrderPCM::SynchronGenerator4OrderPCM + (const String& name, Logger::Level logLevel) + : SynchronGenerator4OrderPCM(name, name, logLevel) { +} + +void EMT::Ph3::SynchronGenerator4OrderPCM::specificInitialization() { + + // calculate state representation matrix + calculateStateSpaceMatrices(); + + // initial voltage behind the transient reactance in the dq0 reference frame + (**mEdq0_t)(0,0) = (**mVdq0)(0,0) - (**mIdq0)(1,0) * mLq_t; + (**mEdq0_t)(1,0) = (**mVdq0)(1,0) + (**mIdq0)(0,0) * mLd_t; + (**mEdq0_t)(2,0) = 0.0; + + mSLog->info( + "\n--- Model specific initialization ---" + "\nInitial Ed_t (per unit): {:f}" + "\nInitial Eq_t (per unit): {:f}" + "\nMax number of iterations: {:d}" + "\nTolerance: {:f}" + "\nSG Model: 4 Order PCM" + "\n--- Model specific initialization finished ---", + + (**mEdq0_t)(0,0), + (**mEdq0_t)(1,0), + mMaxIter, + mTolerance + ); + mSLog->flush(); +} + +void EMT::Ph3::SynchronGenerator4OrderPCM::calculateStateSpaceMatrices() { + // Initialize matrices of state representation + mAStateSpace << -mLq / mTq0_t / mLq_t, 0.0 , 0.0, + 0 , -mLd / mTd0_t / mLd_t, 0.0, + 0 , 0.0 , 0.0; + mBStateSpace << (mLq-mLq_t) / mTq0_t / mLq_t, 0.0 , 0.0, + 0.0 , (mLd-mLd_t) / mTd0_t / mLd_t, 0.0, + 0.0 , 0.0 , 0.0; + mCStateSpace << 0.0, + 1. / mTd0_t, + 0.0; + // Precalculate trapezoidal based matrices (avoids redundant matrix inversions in correction steps) + Math::calculateStateSpaceTrapezoidalMatrices(mAStateSpace, mBStateSpace, mCStateSpace, mTimeStep, mAdTrapezoidal, mBdTrapezoidal, mCdTrapezoidal); +} + +void EMT::Ph3::SynchronGenerator4OrderPCM::stepInPerUnit() { + // set number of iterations equal to zero + **mNumIter = 0; + + // Predictor step (euler) + + // store values currently at t=k for later use + mEdq0tPrevStep = **mEdq0_t; + mIdq0PrevStep = **mIdq0; + mVdq0PrevStep = **mVdq0; + + // predict emf at t=k+1 (euler) using + (**mEdq0_t) = Math::StateSpaceEuler(**mEdq0_t, mAStateSpace, mBStateSpace, mCStateSpace * **mEf, mTimeStep, **mVdq0); + + // predict stator currents at t=k+1 (assuming Vdq0(k+1)=Vdq0(k)) + (**mIdq0)(0,0) = ((**mEdq0_t)(1,0) - (**mVdq0)(1,0)) / mLd_t; + (**mIdq0)(1,0) = ((**mVdq0)(0,0) - (**mEdq0_t)(0,0)) / mLq_t; + (**mIdq0)(2,0) = 0.0; + + // convert currents into the abc domain + **mIntfCurrent = inverseParkTransform(**mThetaMech, **mIdq0); + **mIntfCurrent = **mIntfCurrent * mBase_I; +} + +void EMT::Ph3::SynchronGenerator4OrderPCM::mnaCompApplyRightSideVectorStamp(Matrix& rightVector) { + Math::setVectorElement(rightVector, matrixNodeIndex(0,0), (**mIntfCurrent)(0, 0)); + Math::setVectorElement(rightVector, matrixNodeIndex(0,1), (**mIntfCurrent)(1, 0)); + Math::setVectorElement(rightVector, matrixNodeIndex(0,2), (**mIntfCurrent)(2, 0)); +} + +void EMT::Ph3::SynchronGenerator4OrderPCM::correctorStep() { + // increase number of iterations + **mNumIter = **mNumIter + 1; + + // correct electrical vars + // calculate emf at j and k+1 (trapezoidal rule) + (**mEdq0_t) = Math::applyStateSpaceTrapezoidalMatrices(mAdTrapezoidal, mBdTrapezoidal, mCdTrapezoidal * **mEf, mEdq0tPrevStep, **mVdq0, mVdq0PrevStep); + + + // calculate corrected stator currents at t=k+1 (assuming Vdq(k+1)=VdqPrevIter(k+1)) + (**mIdq0)(0,0) = ((**mEdq0_t)(1,0) - (**mVdq0)(1,0) ) / mLd_t; + (**mIdq0)(1,0) = ((**mVdq0)(0,0) - (**mEdq0_t)(0,0) ) / mLq_t; + + // convert currents into the abc domain + **mIntfCurrent = inverseParkTransform(**mThetaMech, **mIdq0); + **mIntfCurrent = **mIntfCurrent * mBase_I; + + // stamp currents + mnaCompApplyRightSideVectorStamp(**mRightVector); +} + +void EMT::Ph3::SynchronGenerator4OrderPCM::updateVoltage(const Matrix& leftVector) { + // store voltage value currently at j-1 for later use + mVdq0PrevIter = **mVdq0; + + (**mIntfVoltage)(0, 0) = Math::realFromVectorElement(leftVector, matrixNodeIndex(0, 0)); + (**mIntfVoltage)(1, 0) = Math::realFromVectorElement(leftVector, matrixNodeIndex(0, 1)); + (**mIntfVoltage)(2, 0) = Math::realFromVectorElement(leftVector, matrixNodeIndex(0, 2)); + + // convert Vdq into abc domain + **mVdq0 = parkTransform(**mThetaMech, **mIntfVoltage); + **mVdq0 = **mVdq0 / mBase_V; +} + +bool EMT::Ph3::SynchronGenerator4OrderPCM::requiresIteration() { + if (**mNumIter >= mMaxIter) { + // maximum number of iterations reached + return false; + } else if (**mNumIter == 0) { + // no corrector step has been performed yet, + // convergence cannot be confirmed + return true; + } else { + // check voltage convergence according to tolerance + Matrix voltageDifference = **mVdq0 - mVdq0PrevIter; + if (Math::abs(voltageDifference(0,0)) > mTolerance || Math::abs(voltageDifference(1,0)) > mTolerance) + return true; + else + return false; + } +} + +void EMT::Ph3::SynchronGenerator4OrderPCM::mnaCompPostStep(const Matrix& leftVector) { +} + +Matrix EMT::Ph3::SynchronGenerator4OrderPCM::parkTransform(Real theta, const Matrix& abcVector) { + Matrix dq0Vector(3, 1); + Matrix abcToDq0(3, 3); + + // Park transform according to Kundur + abcToDq0 << + 2./3.*cos(theta), 2./3.*cos(theta - 2.*PI/3.), 2./3.*cos(theta + 2.*PI/3.), + -2./3.*sin(theta), -2./3.*sin(theta - 2.*PI/3.), -2./3.*sin(theta + 2.*PI/3.), + 1./3., 1./3., 1./3.; + + dq0Vector = abcToDq0 * abcVector; + + return dq0Vector; +} + +Matrix EMT::Ph3::SynchronGenerator4OrderPCM::inverseParkTransform(Real theta, const Matrix& dq0Vector) { + Matrix abcVector(3, 1); + Matrix dq0ToAbc(3, 3); + + // Park transform according to Kundur + dq0ToAbc << + cos(theta), -sin(theta), 1., + cos(theta - 2.*PI/3.), -sin(theta - 2.*PI/3.), 1., + cos(theta + 2.*PI/3.), -sin(theta + 2.*PI/3.), 1.; + + abcVector = dq0ToAbc * dq0Vector; + + return abcVector; +} diff --git a/dpsim-models/src/MathUtils.cpp b/dpsim-models/src/MathUtils.cpp index a07fe332ec..f2f147a237 100644 --- a/dpsim-models/src/MathUtils.cpp +++ b/dpsim-models/src/MathUtils.cpp @@ -308,6 +308,23 @@ Matrix Math::StateSpaceEuler(Matrix states, Matrix A, Matrix input, Real dt) { return states + dt * ( A*states + input ); } +void Math::calculateStateSpaceTrapezoidalMatrices(const Matrix & A, const Matrix & B, const Matrix & C, const Real & dt, Matrix & Ad, Matrix & Bd, Matrix & Cd) { + Matrix::Index n = A.rows(); + Matrix I = Matrix::Identity(n, n); + + Matrix F1 = I + (dt/2.) * A; + Matrix F2 = I - (dt/2.) * A; + Matrix F2inv = F2.inverse(); + + Ad = F2inv*F1; + Bd = F2inv*(dt/2.)*B; + Cd = F2inv*dt*C; +} + +Matrix Math::applyStateSpaceTrapezoidalMatrices(const Matrix & Ad, const Matrix & Bd, const Matrix & Cd, const Matrix & statesPrevStep, const Matrix & inputCurrStep, const Matrix & inputPrevStep) { + return Ad*statesPrevStep + Bd*(inputCurrStep + inputPrevStep) + Cd; +} + void Math::FFT(std::vector& samples) { // DFT size_t N = samples.size(); diff --git a/dpsim-models/src/SP/SP_Ph1_ReducedOrderSynchronGeneratorVBR.cpp b/dpsim-models/src/SP/SP_Ph1_ReducedOrderSynchronGeneratorVBR.cpp index 770a786dbf..c4cffc95e1 100644 --- a/dpsim-models/src/SP/SP_Ph1_ReducedOrderSynchronGeneratorVBR.cpp +++ b/dpsim-models/src/SP/SP_Ph1_ReducedOrderSynchronGeneratorVBR.cpp @@ -48,7 +48,7 @@ void SP::Ph1::ReducedOrderSynchronGeneratorVBR::mnaCompInitialize(Real omega, Base::ReducedOrderSynchronGenerator::mnaCompInitialize(omega, timeStep, leftVector); - if (mModelAsCurrentSource) { + if (mModelAsNortonSource) { mVariableSystemMatrixEntries.push_back(std::make_pair(matrixNodeIndex(0, 0), matrixNodeIndex(0, 0))); } else { // upper left @@ -69,7 +69,7 @@ void SP::Ph1::ReducedOrderSynchronGeneratorVBR::mnaCompInitialize(Real omega, void SP::Ph1::ReducedOrderSynchronGeneratorVBR::mnaCompApplySystemMatrixStamp(SparseMatrixRow& systemMatrix) { - if (mModelAsCurrentSource) { + if (mModelAsNortonSource) { // Stamp conductance matrix // set buttom right block Math::addToMatrixElement(systemMatrix, matrixNodeIndex(0, 0), matrixNodeIndex(0, 0), mConductanceMatrix); @@ -94,7 +94,7 @@ void SP::Ph1::ReducedOrderSynchronGeneratorVBR::mnaCompApplySystemMatrixStamp(Sp } void SP::Ph1::ReducedOrderSynchronGeneratorVBR::mnaCompApplyRightSideVectorStamp(Matrix& rightVector) { - if (mModelAsCurrentSource) { + if (mModelAsNortonSource) { // compute equivalent northon circuit in abc reference frame mIvbr = Complex(mConductanceMatrix(0,0) * mEvbr.real() + mConductanceMatrix(0,1) * mEvbr.imag(), mConductanceMatrix(1,0) * mEvbr.real() + mConductanceMatrix(1,1) * mEvbr.imag()); @@ -116,7 +116,7 @@ void SP::Ph1::ReducedOrderSynchronGeneratorVBR::mnaCompPostStep(const Matrix& le **mVdq = mComplexAToDq * Vabc / mBase_V_RMS; // update armature current - if (mModelAsCurrentSource) { + if (mModelAsNortonSource) { (**mIntfCurrent)(0, 0) = mIvbr - Complex(mConductanceMatrix(0,0) * (**mIntfVoltage)(0, 0).real() + mConductanceMatrix(0,1) * (**mIntfVoltage)(0, 0).imag(), mConductanceMatrix(1,0) * (**mIntfVoltage)(0, 0).real() + mConductanceMatrix(1,1) * (**mIntfVoltage)(0, 0).imag()); } else { diff --git a/dpsim-models/src/SP/SP_Ph1_SynchronGenerator4OrderDCIM.cpp b/dpsim-models/src/SP/SP_Ph1_SynchronGenerator4OrderDCIM.cpp deleted file mode 100644 index ae732762c1..0000000000 --- a/dpsim-models/src/SP/SP_Ph1_SynchronGenerator4OrderDCIM.cpp +++ /dev/null @@ -1,106 +0,0 @@ -/* Copyright 2017-2021 Institute for Automation of Complex Power Systems, - * EONERC, RWTH Aachen University - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - *********************************************************************************/ - -#include - -using namespace CPS; - -SP::Ph1::SynchronGenerator4OrderDCIM::SynchronGenerator4OrderDCIM - (const String & uid, const String & name, Logger::Level logLevel) - : Base::ReducedOrderSynchronGenerator(uid, name, logLevel), - mEdq_t(mAttributes->create("Edq_t")) { - - // - setTerminalNumber(1); - - // model variables - **mEdq_t = Matrix::Zero(2,1); -} - -SP::Ph1::SynchronGenerator4OrderDCIM::SynchronGenerator4OrderDCIM - (const String & name, Logger::Level logLevel) - : SynchronGenerator4OrderDCIM(name, name, logLevel) { -} - -void SP::Ph1::SynchronGenerator4OrderDCIM::specificInitialization() { - - // initial voltage behind the transient reactance in the dq reference frame - (**mEdq_t)(0,0) = (**mVdq)(0,0) - (**mIdq)(1,0) * mLq_t; - (**mEdq_t)(1,0) = (**mVdq)(1,0) + (**mIdq)(0,0) * mLd_t; - - // Initialize matrix of state representation - mA = Matrix::Zero(2,2); - mB = Matrix::Zero(2,2); - mC = Matrix::Zero(2,1); - calculateStateMatrix(); - - SPDLOG_LOGGER_INFO(mSLog, - "\n--- Model specific initialization ---" - "\nInitial Ed_t (per unit): {:f}" - "\nInitial Eq_t (per unit): {:f}" - "\n--- Model specific initialization finished ---", - - (**mEdq_t)(0,0), - (**mEdq_t)(1,0) - ); - mSLog->flush(); -} - -void SP::Ph1::SynchronGenerator4OrderDCIM::calculateStateMatrix() { - Real Td_t = mTd0_t * (mLd_t / mLd); - Real Tq_t = mTq0_t * (mLq_t / mLq); - mA << -1. / Tq_t, 0, - 0 , -1 / Td_t; - mB << (1. / Tq_t) * (mLq-mLq_t) / mLq, 0.0, - 0.0, (1. / Td_t) * (mLd-mLd_t) / mLd; - mC << 0, - (1. / Td_t) * **mEf * (mLd_t / mLd); -} - -void SP::Ph1::SynchronGenerator4OrderDCIM::mnaCompApplySystemMatrixStamp(SparseMatrixRow& systemMatrix) { -} - -void SP::Ph1::SynchronGenerator4OrderDCIM::stepInPerUnit() { - // get transformation matrix - mDqToComplexA = get_DqToComplexATransformMatrix(); - mComplexAToDq = mDqToComplexA.transpose(); - - // calculate Edq at t=k+1. Assumption: Vdq(k) = Vdq(k+1) - (**mEdq_t) = Math::StateSpaceTrapezoidal(**mEdq_t, mA, mB, mC, mTimeStep, **mVdq); - - // armature currents for at t=k+1 - (**mIdq)(0,0) = ((**mEdq_t)(1,0) - (**mVdq)(1,0) ) / mLd_t; - (**mIdq)(1,0) = ((**mVdq)(0,0) - (**mEdq_t)(0,0) ) / mLq_t; - - // convert currents into the abc domain - Matrix Ia = mDqToComplexA * **mIdq; - (**mIntfCurrent)(0,0) = Complex(Ia(0,0), Ia(1,0)) * mBase_I_RMS; -} - -void SP::Ph1::SynchronGenerator4OrderDCIM::mnaCompApplyRightSideVectorStamp(Matrix& rightVector) { - Math::setVectorElement(rightVector, matrixNodeIndex(0), (**mIntfCurrent)(0, 0)); -} - -void SP::Ph1::SynchronGenerator4OrderDCIM::mnaCompPostStep(const Matrix& leftVector) { - // update armature voltage - (**mIntfVoltage)(0, 0) = Math::complexFromVectorElement(leftVector, matrixNodeIndex(0)); - Matrix Vabc = Matrix::Zero(2,1); - Vabc << (**mIntfVoltage)(0, 0).real(), (**mIntfVoltage)(0, 0).imag(); - **mVdq = mComplexAToDq * Vabc / mBase_V_RMS; - - mSimTime = mSimTime + mTimeStep; -} - -Matrix SP::Ph1::SynchronGenerator4OrderDCIM::get_DqToComplexATransformMatrix() { - Matrix dqToComplexA(2, 2); - dqToComplexA << - cos(**mThetaMech - mBase_OmMech * mSimTime), -sin(**mThetaMech - mBase_OmMech * mSimTime), - sin(**mThetaMech - mBase_OmMech * mSimTime), cos(**mThetaMech - mBase_OmMech * mSimTime); - - return dqToComplexA; -} diff --git a/dpsim/examples/cxx/CIM/DP_WSCC9bus_SGReducedOrderIter.cpp b/dpsim/examples/cxx/CIM/DP_WSCC9bus_SGReducedOrderIter.cpp new file mode 100644 index 0000000000..d7e049571b --- /dev/null +++ b/dpsim/examples/cxx/CIM/DP_WSCC9bus_SGReducedOrderIter.cpp @@ -0,0 +1,188 @@ +/* Copyright 2017-2020 Institute for Automation of Complex Power Systems, + * EONERC, RWTH Aachen University + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + *********************************************************************************/ + +#include +#include + +#include +#include + + +using namespace DPsim; +using namespace CPS::DP; +using namespace CPS::CIM; + + +int main(int argc, char *argv[]) { + + // Simulation parameters + String simName = "DP_WSCC9bus_SGReducedOrderIter"; + Real timeStep = 1e-9; + Real finalTime = 0.01; + String SGModel = "4PCM"; // 4PCM or 4TPM + Bool withFault = true; + Real startTimeFault = 0.2; + Real endTimeFault = 0.3; + String faultBusName= "BUS6"; + Real inertiaScalingFactor = 1.0; + String logDirectory = "logs"; + Real tolerance = 1e-10; + Int maxIter = 10; + + // Find CIM files + std::list filenames; + CommandLineArgs args(argc, argv); + if (argc <= 1) { + filenames = Utils::findFiles({ + "WSCC-09_RX_DI.xml", + "WSCC-09_RX_EQ.xml", + "WSCC-09_RX_SV.xml", + "WSCC-09_RX_TP.xml" + }, "WSCC-09_Dyn_Full", "CIMPATH"); + } + else { + filenames = args.positionalPaths(); + timeStep = args.timeStep; + finalTime = args.duration; + + if (args.name != "dpsim") + simName = args.name; + if (args.options.find("logDirectory") != args.options.end()) + logDirectory = args.getOptionString("logDirectory"); + + if (args.options.find("withFault") != args.options.end()) + withFault = args.getOptionBool("withFault"); + if (args.options.find("startTimeFault") != args.options.end()) + startTimeFault = args.getOptionReal("startTimeFault"); + if (args.options.find("endTimeFault") != args.options.end()) + endTimeFault = args.getOptionReal("endTimeFault"); + if (args.options.find("faultBus") != args.options.end()) + faultBusName = args.getOptionString("faultBus"); + + if (args.options.find("SGModel") != args.options.end()) + SGModel = args.getOptionString("SGModel"); + if (args.options.find("inertiaScalingFactor") != args.options.end()) + inertiaScalingFactor = args.getOptionReal("inertiaScalingFactor"); + if (args.options.find("Tolerance") != args.options.end()) + tolerance = args.getOptionReal("Tolerance"); + if (args.options.find("MaxIter") != args.options.end()) + maxIter = int(args.getOptionReal("MaxIter")); + } + + // Configure logging + Logger::Level logLevel = Logger::Level::info; + + // apply downsampling for simulation step sizes lower than 10us + Real logDownSampling; + if (timeStep < 10e-6) + logDownSampling = floor((10e-6) / timeStep); + else + logDownSampling = 1.0; + + // ----- POWERFLOW FOR INITIALIZATION ----- + // read original network topology + String simNamePF = simName + "_PF"; + Logger::setLogDir(logDirectory + "/" + simNamePF); + CPS::CIM::Reader reader(simNamePF, logLevel, logLevel); + SystemTopology systemPF = reader.loadCIM(60, filenames, Domain::SP, PhaseType::Single, CPS::GeneratorType::PVNode); + systemPF.component("GEN1")->modifyPowerFlowBusType(CPS::PowerflowBusType::VD); + + // define logging + auto loggerPF = DPsim::DataLogger::make(simNamePF); + for (auto node : systemPF.mNodes) + loggerPF->logAttribute(node->name() + ".V", node->attribute("v")); + + // run powerflow + Simulation simPF(simNamePF, logLevel); + simPF.setSystem(systemPF); + simPF.setTimeStep(finalTime); + simPF.setFinalTime(2*finalTime); + simPF.setDomain(Domain::SP); + simPF.setSolverType(Solver::Type::NRP); + simPF.setSolverAndComponentBehaviour(Solver::Behaviour::Initialization); + simPF.doInitFromNodesAndTerminals(true); + simPF.addLogger(loggerPF); + simPF.run(); + + // ----- DYNAMIC SIMULATION ----- + Logger::setLogDir(logDirectory + "/" + simName); + + CPS::CIM::Reader reader2(simName, logLevel, logLevel); + SystemTopology sys; + if (SGModel=="4PCM") + sys = reader2.loadCIM(60, filenames, Domain::DP, PhaseType::Single, CPS::GeneratorType::SG4OrderPCM); + else if (SGModel=="4TPM") + sys = reader2.loadCIM(60, filenames, Domain::DP, PhaseType::Single, CPS::GeneratorType::SG4OrderTPM); + else + throw CPS::SystemError("Unsupported reduced-order SG type!"); + + // set tolerances and max iterations + + for (auto comp : sys.mComponents) { + if (std::dynamic_pointer_cast>(comp)) { + std::dynamic_pointer_cast(comp)->setMaxIterations(maxIter); + std::dynamic_pointer_cast(comp)->setTolerance(tolerance); + } + } + + // Optionally extend topology with switch + auto faultDP = Ph1::Switch::make("Fault", logLevel); + if (withFault) { + faultDP->setParameters(1e12,0.02*529); + faultDP->connect({ SimNode::GND, sys.node(faultBusName) }); + faultDP->open(); + sys.addComponent(faultDP); + } + + sys.initWithPowerflow(systemPF); + for (auto comp : sys.mComponents) { + if (auto genReducedOrder = std::dynamic_pointer_cast>(comp)) { + auto genPF = systemPF.component(comp->name()); + genReducedOrder->terminal(0)->setPower(-genPF->getApparentPower()); + genReducedOrder->scaleInertiaConstant(inertiaScalingFactor); + } + } + + // Logging + // log node voltage + auto logger = DataLogger::make(simName, true, logDownSampling); + for (auto node : sys.mNodes) + logger->logAttribute(node->name() + ".V", node->attribute("v")); + + // log generator vars + for (auto comp : sys.mComponents) { + if (auto genReducedOrder = std::dynamic_pointer_cast>(comp)){ + logger->logAttribute(genReducedOrder->name() + ".Tm", genReducedOrder->attribute("Tm")); + logger->logAttribute(genReducedOrder->name() + ".Te", genReducedOrder->attribute("Te")); + logger->logAttribute(genReducedOrder->name() + ".omega", genReducedOrder->attribute("w_r")); + logger->logAttribute(genReducedOrder->name() + ".delta", genReducedOrder->attribute("delta")); + logger->logAttribute(genReducedOrder->name() + ".N", genReducedOrder->attribute("NIterations")); + } + } + + Simulation sim(simName, logLevel); + sim.setSystem(sys); + sim.setDomain(Domain::DP); + sim.setSolverType(Solver::Type::MNA); + sim.setTimeStep(timeStep); + sim.setFinalTime(finalTime); + sim.setDirectLinearSolverImplementation(DPsim::DirectLinearSolverImpl::SparseLU); + sim.addLogger(logger); + + // Optionally add switch event + if (withFault) { + auto faultEvent1 = SwitchEvent::make(startTimeFault, faultDP, true); + auto faultEvent2 = SwitchEvent::make(endTimeFault, faultDP, false); + sim.addEvent(faultEvent1); + sim.addEvent(faultEvent2); + } + + sim.run(); + + return 0; +} diff --git a/dpsim/examples/cxx/CIM/DP_WSCC9bus_SGReducedOrderVBR.cpp b/dpsim/examples/cxx/CIM/DP_WSCC9bus_SGReducedOrderVBR.cpp index 529bbdb3d1..84ff3df067 100644 --- a/dpsim/examples/cxx/CIM/DP_WSCC9bus_SGReducedOrderVBR.cpp +++ b/dpsim/examples/cxx/CIM/DP_WSCC9bus_SGReducedOrderVBR.cpp @@ -141,7 +141,7 @@ int main(int argc, char *argv[]) { auto genPF = systemPF.component(comp->name()); genReducedOrder->terminal(0)->setPower(-genPF->getApparentPower()); genReducedOrder->scaleInertiaConstant(inertiaScalingFactor); - genReducedOrder->setModelAsCurrentSource(false); + genReducedOrder->setModelAsNortonSource(false); } } diff --git a/dpsim/examples/cxx/CIM/EMT_WSCC9bus_SGReducedOrderVBR.cpp b/dpsim/examples/cxx/CIM/EMT_WSCC9bus_SGReducedOrderVBR.cpp index 2142cd145e..95ceb78115 100644 --- a/dpsim/examples/cxx/CIM/EMT_WSCC9bus_SGReducedOrderVBR.cpp +++ b/dpsim/examples/cxx/CIM/EMT_WSCC9bus_SGReducedOrderVBR.cpp @@ -138,7 +138,7 @@ int main(int argc, char *argv[]) { auto genPF = systemPF.component(comp->name()); genReducedOrder->terminal(0)->setPower(-genPF->getApparentPower()); genReducedOrder->scaleInertiaConstant(inertiaScalingFactor); - genReducedOrder->setModelAsCurrentSource(false); + genReducedOrder->setModelAsNortonSource(false); } } diff --git a/dpsim/examples/cxx/CIM/SP_WSCC9bus_SGReducedOrderVBR.cpp b/dpsim/examples/cxx/CIM/SP_WSCC9bus_SGReducedOrderVBR.cpp index 9c3450c4d0..655749b08a 100644 --- a/dpsim/examples/cxx/CIM/SP_WSCC9bus_SGReducedOrderVBR.cpp +++ b/dpsim/examples/cxx/CIM/SP_WSCC9bus_SGReducedOrderVBR.cpp @@ -26,7 +26,7 @@ int main(int argc, char *argv[]) { Bool withFault = true; Real startTimeFault = 0.2; Real endTimeFault = 0.3; - String faultBusName= "BUS6"; + String faultBusName= "BUS5"; Real inertiaScalingFactor = 1.0; String logDirectory = "logs"; @@ -51,16 +51,16 @@ int main(int argc, char *argv[]) { if (args.options.find("sgType") != args.options.end()) sgType = args.getOptionString("sgType"); - + if (args.options.find("withFault") != args.options.end()) withFault = args.getOptionBool("withFault"); if (args.options.find("startTimeFault") != args.options.end()) startTimeFault = args.getOptionReal("startTimeFault"); - + if (args.options.find("endTimeFault") != args.options.end()) endTimeFault = args.getOptionReal("endTimeFault"); - + if (args.options.find("faultBus") != args.options.end()) faultBusName = args.getOptionString("faultBus"); @@ -113,11 +113,11 @@ int main(int argc, char *argv[]) { SystemTopology sys; if (sgType=="3") sys = reader2.loadCIM(60, filenames, Domain::SP, PhaseType::Single, CPS::GeneratorType::SG3OrderVBR); - else if (sgType=="4") + else if (sgType=="4") sys = reader2.loadCIM(60, filenames, Domain::SP, PhaseType::Single, CPS::GeneratorType::SG4OrderVBR); - else if (sgType=="6b") + else if (sgType=="6b") sys = reader2.loadCIM(60, filenames, Domain::SP, PhaseType::Single, CPS::GeneratorType::SG6bOrderVBR); - else + else throw CPS::SystemError("Unsupported reduced-order SG type!"); // Optionally extend topology with switch diff --git a/dpsim/examples/cxx/CMakeLists.txt b/dpsim/examples/cxx/CMakeLists.txt index 424c1e9666..504490cc76 100644 --- a/dpsim/examples/cxx/CMakeLists.txt +++ b/dpsim/examples/cxx/CMakeLists.txt @@ -74,12 +74,14 @@ set(CIRCUIT_SOURCES Circuits/SP_ReducedOrderSG_VBR_Load_Fault.cpp Circuits/DP_ReducedOrderSG_VBR_Load_Fault.cpp Circuits/EMT_ReducedOrderSG_VBR_Load_Fault.cpp - Circuits/SP_SynGen4OrderDCIM_SMIB_Fault.cpp + Circuits/EMT_SynGen4OrderIter_SMIB_Fault.cpp # SMIB Reduced Order - Load step Circuits/SP_SMIB_ReducedOrderSG_LoadStep.cpp Circuits/DP_SMIB_ReducedOrderSG_LoadStep.cpp Circuits/EMT_SMIB_ReducedOrderSG_LoadStep.cpp + Circuits/EMT_SMIB_ReducedOrderSGIterative_LoadStep.cpp + Circuits/DP_SMIB_ReducedOrderSGIterative_LoadStep.cpp #3Bus System Circuits/SP_SynGenTrStab_3Bus_SteadyState.cpp @@ -128,6 +130,8 @@ list(APPEND TEST_SOURCES Circuits/EMT_SynGenDQ7odTrapez_SMIB_Fault.cpp Circuits/EMT_Slack_PiLine_VSI_Ramp_with_PF_Init.cpp Circuits/EMT_DP_SP_Slack_PiLine_PQLoad_FrequencyRamp_CosineFM.cpp + Circuits/DP_SMIB_ReducedOrderSG_LoadStep.cpp + Circuits/DP_SMIB_ReducedOrderSGIterative_LoadStep.cpp ) if(WITH_SUNDIALS) @@ -170,6 +174,7 @@ if(WITH_CIM) CIM/SP_WSCC9bus_SGReducedOrderVBR.cpp CIM/EMT_WSCC9bus_SGReducedOrderVBR.cpp CIM/DP_WSCC9bus_SGReducedOrderVBR.cpp + CIM/DP_WSCC9bus_SGReducedOrderIter.cpp # PF(Power Flow) example CIM/Slack_Trafo_Load.cpp @@ -198,6 +203,7 @@ if(WITH_CIM) CIM/DP_WSCC-9bus_IdealVS.cpp CIM/EMT_WSCC-9bus_IdealCS.cpp CIM/EMT_WSCC-9bus_IdealVS.cpp + CIM/SP_WSCC9bus_SGReducedOrderVBR.cpp ) if(WITH_RT) diff --git a/dpsim/examples/cxx/Circuits/DP_ReducedOrderSG_SMIB_Fault.cpp b/dpsim/examples/cxx/Circuits/DP_ReducedOrderSG_SMIB_Fault.cpp index 96b5c42f8f..93188b502a 100644 --- a/dpsim/examples/cxx/Circuits/DP_ReducedOrderSG_SMIB_Fault.cpp +++ b/dpsim/examples/cxx/Circuits/DP_ReducedOrderSG_SMIB_Fault.cpp @@ -13,7 +13,7 @@ const Examples::Grids::SMIB::ReducedOrderSynchronGenerator::Scenario4::GridParam const Examples::Components::SynchronousGeneratorKundur::MachineParameters syngenKundur; -int main(int argc, char* argv[]) { +int main(int argc, char* argv[]) { //Simultion parameters Real startTimeFault = 30.0; @@ -62,7 +62,7 @@ int main(int argc, char* argv[]) { //Synchronous generator ideal model auto genPF = SP::Ph1::SynchronGenerator::make("Generator", Logger::Level::debug); - genPF->setParameters(syngenKundur.nomPower, GridParams.VnomMV, GridParams.setPointActivePower, + genPF->setParameters(syngenKundur.nomPower, GridParams.VnomMV, GridParams.setPointActivePower, GridParams.setPointVoltage, PowerflowBusType::PV); genPF->setBaseVoltage(GridParams.VnomMV); genPF->modifyPowerFlowBusType(PowerflowBusType::PV); @@ -72,10 +72,10 @@ int main(int argc, char* argv[]) { extnetPF->setParameters(GridParams.VnomMV); extnetPF->setBaseVoltage(GridParams.VnomMV); extnetPF->modifyPowerFlowBusType(PowerflowBusType::VD); - + //Line auto linePF = SP::Ph1::PiLine::make("PiLine", Logger::Level::debug); - linePF->setParameters(GridParams.lineResistance, GridParams.lineInductance, + linePF->setParameters(GridParams.lineResistance, GridParams.lineInductance, GridParams.lineCapacitance, GridParams.lineConductance); linePF->setBaseVoltage(GridParams.VnomMV); @@ -107,7 +107,7 @@ int main(int argc, char* argv[]) { // ----- Dynamic simulation ------ String simNameDP = simName; Logger::setLogDir("logs/" + simNameDP); - + // Extract relevant powerflow results Real initActivePower = genPF->getApparentPower().real(); Real initReactivePower = genPF->getApparentPower().imag(); @@ -125,11 +125,11 @@ int main(int argc, char* argv[]) { genDP->setOperationalParametersPerUnit( syngenKundur.nomPower, syngenKundur.nomVoltage, syngenKundur.nomFreq, H, - syngenKundur.Ld, syngenKundur.Lq, syngenKundur.Ll, + syngenKundur.Ld, syngenKundur.Lq, syngenKundur.Ll, syngenKundur.Ld_t, syngenKundur.Lq_t, syngenKundur.Td0_t, syngenKundur.Tq0_t, - syngenKundur.Ld_s, syngenKundur.Lq_s, syngenKundur.Td0_s, syngenKundur.Tq0_s); + syngenKundur.Ld_s, syngenKundur.Lq_s, syngenKundur.Td0_s, syngenKundur.Tq0_s); genDP->setInitialValues(initElecPower, initMechPower, n1PF->voltage()(0,0)); - genDP->setModelAsCurrentSource(true); + genDP->setModelAsNortonSource(true); //Grid bus as Slack auto extnetDP = DP::Ph1::NetworkInjection::make("Slack", logLevel); @@ -137,9 +137,9 @@ int main(int argc, char* argv[]) { // Line auto lineDP = DP::Ph1::PiLine::make("PiLine", logLevel); - lineDP->setParameters(GridParams.lineResistance, GridParams.lineInductance, + lineDP->setParameters(GridParams.lineResistance, GridParams.lineInductance, GridParams.lineCapacitance, GridParams.lineConductance); - + //Breaker auto fault = CPS::DP::Ph1::Switch::make("Br_fault", logLevel); fault->setParameters(switchOpen, switchClosed); @@ -181,6 +181,6 @@ int main(int argc, char* argv[]) { auto sw2 = SwitchEvent::make(endTimeFault, fault, false); simDP.addEvent(sw2); - + simDP.run(); -} \ No newline at end of file +} diff --git a/dpsim/examples/cxx/Circuits/DP_ReducedOrderSG_VBR_Load_Fault.cpp b/dpsim/examples/cxx/Circuits/DP_ReducedOrderSG_VBR_Load_Fault.cpp index 5b98155d31..47fa7d7cd3 100644 --- a/dpsim/examples/cxx/Circuits/DP_ReducedOrderSG_VBR_Load_Fault.cpp +++ b/dpsim/examples/cxx/Circuits/DP_ReducedOrderSG_VBR_Load_Fault.cpp @@ -18,7 +18,7 @@ const Examples::Components::ExcitationSystemEremia::Parameters excitationEremia; // Turbine Goverour const Examples::Components::TurbineGovernor::TurbineGovernorPSAT1 turbineGovernor; -int main(int argc, char* argv[]) { +int main(int argc, char* argv[]) { //Simultion parameters Real switchClosed = GridParams.SwitchClosed; @@ -66,10 +66,10 @@ int main(int argc, char* argv[]) { // ----- Dynamic simulation ------ String simNameDP = simName; Logger::setLogDir("logs/" + simNameDP); - + // Nodes auto initVoltN1 = std::vector({ - Complex(GridParams.VnomMV * cos(GridParams.initVoltAngle), + Complex(GridParams.VnomMV * cos(GridParams.initVoltAngle), GridParams.VnomMV * sin(GridParams.initVoltAngle))}); auto n1DP = SimNode::make("n1DP", PhaseType::Single, initVoltN1); @@ -78,21 +78,21 @@ int main(int argc, char* argv[]) { genDP->setOperationalParametersPerUnit( syngenKundur.nomPower, syngenKundur.nomVoltage, syngenKundur.nomFreq, H, - syngenKundur.Ld, syngenKundur.Lq, syngenKundur.Ll, + syngenKundur.Ld, syngenKundur.Lq, syngenKundur.Ll, syngenKundur.Ld_t, syngenKundur.Lq_t, syngenKundur.Td0_t, syngenKundur.Tq0_t, - syngenKundur.Ld_s, syngenKundur.Lq_s, syngenKundur.Td0_s, syngenKundur.Tq0_s); - genDP->setInitialValues(GridParams.initComplexElectricalPower, GridParams.mechPower, - Complex(GridParams.VnomMV * cos(GridParams.initVoltAngle), + syngenKundur.Ld_s, syngenKundur.Lq_s, syngenKundur.Td0_s, syngenKundur.Tq0_s); + genDP->setInitialValues(GridParams.initComplexElectricalPower, GridParams.mechPower, + Complex(GridParams.VnomMV * cos(GridParams.initVoltAngle), GridParams.VnomMV * sin(GridParams.initVoltAngle))); - genDP->setModelAsCurrentSource(true); + genDP->setModelAsNortonSource(true); // Exciter std::shared_ptr exciterDP = nullptr; if (withExciter) { exciterDP = Signal::Exciter::make("SynGen_Exciter", logLevel); - exciterDP->setParameters(excitationEremia.Ta, excitationEremia.Ka, - excitationEremia.Te, excitationEremia.Ke, - excitationEremia.Tf, excitationEremia.Kf, + exciterDP->setParameters(excitationEremia.Ta, excitationEremia.Ka, + excitationEremia.Te, excitationEremia.Ke, + excitationEremia.Tf, excitationEremia.Kf, excitationEremia.Tr); genDP->addExciter(exciterDP); } @@ -101,15 +101,15 @@ int main(int argc, char* argv[]) { std::shared_ptr turbineGovernorDP = nullptr; if (withTurbineGovernor) { turbineGovernorDP = Signal::TurbineGovernorType1::make("SynGen_TurbineGovernor", logLevel); - turbineGovernorDP->setParameters(turbineGovernor.T3, turbineGovernor.T4, - turbineGovernor.T5, turbineGovernor.Tc, turbineGovernor.Ts, turbineGovernor.R, + turbineGovernorDP->setParameters(turbineGovernor.T3, turbineGovernor.T4, + turbineGovernor.T5, turbineGovernor.Tc, turbineGovernor.Ts, turbineGovernor.R, turbineGovernor.Tmin, turbineGovernor.Tmax, turbineGovernor.OmegaRef); genDP->addGovernor(turbineGovernorDP); } // Load auto load = CPS::DP::Ph1::RXLoad::make("Load", logLevel); - load->setParameters(GridParams.initActivePower, GridParams.initReactivePower, + load->setParameters(GridParams.initActivePower, GridParams.initReactivePower, GridParams.VnomMV); //Breaker @@ -134,7 +134,7 @@ int main(int argc, char* argv[]) { loggerDP->logAttribute("w_r", genDP->attribute("w_r")); loggerDP->logAttribute("Vdq0", genDP->attribute("Vdq0")); loggerDP->logAttribute("Idq0", genDP->attribute("Idq0")); - + // Exciter if (withExciter) { loggerDP->logAttribute("Ef", exciterDP->attribute("Ef")); @@ -161,6 +161,6 @@ int main(int argc, char* argv[]) { auto sw2 = SwitchEvent::make(endTimeFault, fault, false); simDP.addEvent(sw2); - + simDP.run(); -} \ No newline at end of file +} diff --git a/dpsim/examples/cxx/Circuits/DP_SMIB_ReducedOrderSGIterative_LoadStep.cpp b/dpsim/examples/cxx/Circuits/DP_SMIB_ReducedOrderSGIterative_LoadStep.cpp new file mode 100644 index 0000000000..b969105100 --- /dev/null +++ b/dpsim/examples/cxx/Circuits/DP_SMIB_ReducedOrderSGIterative_LoadStep.cpp @@ -0,0 +1,209 @@ +#include +#include +#include "../Examples.h" + +using namespace DPsim; +using namespace CPS; +using namespace CPS::CIM; +using namespace Examples::Grids::SMIB::ReducedOrderSynchronGenerator; + +// Default configuration of scenario +Scenario6::Config defaultConfig; + +// Grid parameters +Scenario6::GridParams gridParams; + +// Generator parameters +Examples::Components::SynchronousGeneratorKundur::MachineParameters syngenKundur; + +int main(int argc, char* argv[]) { + + // initiaize gen factory + SynchronGeneratorFactory::DP::Ph1::registerSynchronGenerators(); + + // Simulation parameters + String simName = "DP_SMIB_ReducedOrderSGIterative_LoadStep"; + Real timeStep = 1e-3; + Real finalTime = 35; + + // Default configuration + Real loadStepEventTime = defaultConfig.loadStepEventTime; + Real H = syngenKundur.H; + Real tolerance = defaultConfig.tolerance; + int maxIter = defaultConfig.maxIter; + String SGModel = defaultConfig.sgType + "Iter"; + SGModel = "4TPM"; // options: "4PCM", "4TPM", "6PCM" + + // Command line args processing + CommandLineArgs args(argc, argv); + if (argc > 1) { + if (args.options.find("SimName") != args.options.end()) + simName = args.getOptionString("SimName"); + if (args.options.find("TimeStep") != args.options.end()) + timeStep = args.getOptionReal("TimeStep"); + if (args.options.find("Tolerance") != args.options.end()) + tolerance = args.getOptionReal("Tolerance"); + if (args.options.find("MaxIter") != args.options.end()) + maxIter = int(args.getOptionReal("MaxIter")); + if (args.options.find("loadStepEventTime") != args.options.end()) + loadStepEventTime = args.getOptionReal("loadStepEventTime"); + if (args.options.find("inertia") != args.options.end()) + H = args.getOptionReal("inertia"); + if (args.options.find("SGModel") != args.options.end()) + SGModel = args.getOptionString("SGModel"); + } + + std::cout << "Simulation Parameters: " << std::endl; + std::cout << "SimName: " << simName << std::endl; + std::cout << "Time Step: " << timeStep << std::endl; + std::cout << "Tolerance: " << tolerance << std::endl; + std::cout << "Max N° of Iterations: " << maxIter << std::endl; + std::cout << "SG: " << SGModel << std::endl; + + // Configure logging + Logger::Level logLevel = Logger::Level::info; + + // apply downsampling for simulation step sizes lower than 10us + Real logDownSampling; + if (timeStep < 10e-6) + logDownSampling = floor((10e-6) / timeStep); + else + logDownSampling = 1.0; + + // ----- POWERFLOW FOR INITIALIZATION ----- + String simNamePF = simName + "_PF"; + Logger::setLogDir("logs/" + simNamePF); + + // Components + auto n1PF = SimNode::make("n1", PhaseType::Single); + auto n2PF = SimNode::make("n2", PhaseType::Single); + + //Synchronous generator ideal model + auto genPF = SP::Ph1::SynchronGenerator::make("Generator", logLevel); + genPF->setParameters(syngenKundur.nomPower, gridParams.VnomMV, gridParams.setPointActivePower, + gridParams.setPointVoltage, PowerflowBusType::PV); + genPF->setBaseVoltage(gridParams.VnomMV); + genPF->modifyPowerFlowBusType(PowerflowBusType::PV); + + //Grid bus as Slack + auto extnetPF = SP::Ph1::NetworkInjection::make("Slack", logLevel); + extnetPF->setParameters(gridParams.VnomMV); + extnetPF->setBaseVoltage(gridParams.VnomMV); + extnetPF->modifyPowerFlowBusType(PowerflowBusType::VD); + + //Line + auto linePF = SP::Ph1::PiLine::make("PiLine", logLevel); + linePF->setParameters(gridParams.lineResistance, gridParams.lineInductance, + gridParams.lineCapacitance, gridParams.lineConductance); + linePF->setBaseVoltage(gridParams.VnomMV); + + // Topology + genPF->connect({ n1PF }); + linePF->connect({ n1PF, n2PF }); + extnetPF->connect({ n2PF }); + auto systemPF = SystemTopology(gridParams.nomFreq, + SystemNodeList{n1PF, n2PF}, + SystemComponentList{genPF, linePF, extnetPF}); + + // Logging + auto loggerPF = DataLogger::make(simNamePF); + loggerPF->logAttribute("v1", n1PF->attribute("v")); + loggerPF->logAttribute("v2", n2PF->attribute("v")); + + // Simulation + Simulation simPF(simNamePF, logLevel); + simPF.setSystem(systemPF); + simPF.setTimeStep(0.1); + simPF.setFinalTime(0.1); + simPF.setDomain(Domain::SP); + simPF.setSolverType(Solver::Type::NRP); + simPF.setSolverAndComponentBehaviour(Solver::Behaviour::Initialization); + simPF.doInitFromNodesAndTerminals(false); + simPF.addLogger(loggerPF); + simPF.run(); + + + // ----- Dynamic simulation ------ + String simNameDP = simName; + Logger::setLogDir("logs/" + simNameDP); + + // Extract relevant powerflow results + Real initActivePower = genPF->getApparentPower().real(); + Real initReactivePower = genPF->getApparentPower().imag(); + Complex initElecPower = Complex(initActivePower, initReactivePower); + Real initMechPower = initActivePower; + + // Nodes + std::vector initialVoltage_n1{ n1PF->voltage()(0,0)}; + auto n1DP = SimNode::make("n1DP", PhaseType::Single, initialVoltage_n1); + std::vector initialVoltage_n2{ n2PF->voltage()(0,0)}; + auto n2DP = SimNode::make("n2DP", PhaseType::Single, initialVoltage_n2); + + // Synchronous generator + auto genDP = Factory>::get().create(SGModel, "SynGen", logLevel); + genDP->setOperationalParametersPerUnit( + syngenKundur.nomPower, syngenKundur.nomVoltage, + syngenKundur.nomFreq, H, + syngenKundur.Ld, syngenKundur.Lq, syngenKundur.Ll, + syngenKundur.Ld_t, syngenKundur.Lq_t, syngenKundur.Td0_t, syngenKundur.Tq0_t, + syngenKundur.Ld_s, syngenKundur.Lq_s, syngenKundur.Td0_s, syngenKundur.Tq0_s); + genDP->setInitialValues(initElecPower, initMechPower, n1PF->voltage()(0,0)); + genDP->setModelAsNortonSource(true); + std::dynamic_pointer_cast(genDP)->setMaxIterations(maxIter); + std::dynamic_pointer_cast(genDP)->setTolerance(tolerance); + + //Grid bus as Slack + auto extnetDP = DP::Ph1::NetworkInjection::make("Slack", logLevel); + extnetDP->setParameters(gridParams.VnomMV); + + // Line + auto lineDP = DP::Ph1::PiLine::make("PiLine", logLevel); + lineDP->setParameters(gridParams.lineResistance, + gridParams.lineInductance, + gridParams.lineCapacitance, + gridParams.lineConductance); + + // Topology + genDP->connect({ n1DP }); + lineDP->connect({ n1DP, n2DP }); + extnetDP->connect({ n2DP }); + SystemTopology systemDP = SystemTopology(gridParams.nomFreq, + SystemNodeList{n1DP, n2DP}, + SystemComponentList{genDP, lineDP, extnetDP}); + + // Logging + auto logger = DataLogger::make(simName, true, logDownSampling); + + // log node voltage + for (auto node : systemDP.mNodes) + logger->logAttribute(node->name() + ".V", node->attribute("v")); + + // log generator vars + logger->logAttribute(genDP->name() + ".Te", genDP->attribute("Te")); + logger->logAttribute(genDP->name() + ".NIterations", genDP->attribute("NIterations")); + logger->logAttribute(genDP->name() + ".Edq_t", genDP->attribute("Edq_t")); + if (SGModel=="6PCM") + logger->logAttribute(genDP->name() + ".Edq_s", genDP->attribute("Edq_s")); + logger->logAttribute(genDP->name() + ".Vdq0", genDP->attribute("Vdq0")); + logger->logAttribute(genDP->name() + ".Idq0", genDP->attribute("Idq0")); + logger->logAttribute(genDP->name() + ".omega", genDP->attribute("w_r")); + logger->logAttribute(genDP->name() + ".delta", genDP->attribute("delta")); + logger->logAttribute(genDP->name() + ".Theta", genDP->attribute("Theta")); + + // load step event + std::shared_ptr loadStepEvent = Examples::Events::createEventAddPowerConsumption("n1DP", std::round(loadStepEventTime/timeStep)*timeStep, gridParams.loadStepActivePower, systemDP, Domain::DP, logger); + + Simulation simDP(simNameDP, logLevel); + simDP.doInitFromNodesAndTerminals(true); + simDP.setSystem(systemDP); + simDP.setTimeStep(timeStep); + simDP.setFinalTime(finalTime); + simDP.setDomain(Domain::DP); + simDP.setDirectLinearSolverImplementation(DPsim::DirectLinearSolverImpl::SparseLU); + simDP.addLogger(logger); + + // Events + simDP.addEvent(loadStepEvent); + + simDP.run(); +} diff --git a/dpsim/examples/cxx/Circuits/DP_SMIB_ReducedOrderSG_LoadStep.cpp b/dpsim/examples/cxx/Circuits/DP_SMIB_ReducedOrderSG_LoadStep.cpp index bbff8cad7e..94f604c0d3 100644 --- a/dpsim/examples/cxx/Circuits/DP_SMIB_ReducedOrderSG_LoadStep.cpp +++ b/dpsim/examples/cxx/Circuits/DP_SMIB_ReducedOrderSG_LoadStep.cpp @@ -16,7 +16,7 @@ Scenario6::GridParams gridParams; // Generator parameters Examples::Components::SynchronousGeneratorKundur::MachineParameters syngenKundur; -int main(int argc, char* argv[]) { +int main(int argc, char* argv[]) { // Simulation parameters String simName = "DP_SMIB_ReducedOrderSG_VBR_LoadStep"; @@ -68,7 +68,7 @@ int main(int argc, char* argv[]) { //Synchronous generator ideal model auto genPF = SP::Ph1::SynchronGenerator::make("Generator", logLevel); - genPF->setParameters(syngenKundur.nomPower, gridParams.VnomMV, gridParams.setPointActivePower, + genPF->setParameters(syngenKundur.nomPower, gridParams.VnomMV, gridParams.setPointActivePower, gridParams.setPointVoltage, PowerflowBusType::PV); genPF->setBaseVoltage(gridParams.VnomMV); genPF->modifyPowerFlowBusType(PowerflowBusType::PV); @@ -78,10 +78,10 @@ int main(int argc, char* argv[]) { extnetPF->setParameters(gridParams.VnomMV); extnetPF->setBaseVoltage(gridParams.VnomMV); extnetPF->modifyPowerFlowBusType(PowerflowBusType::VD); - + //Line auto linePF = SP::Ph1::PiLine::make("PiLine", logLevel); - linePF->setParameters(gridParams.lineResistance, gridParams.lineInductance, + linePF->setParameters(gridParams.lineResistance, gridParams.lineInductance, gridParams.lineCapacitance, gridParams.lineConductance); linePF->setBaseVoltage(gridParams.VnomMV); @@ -114,7 +114,7 @@ int main(int argc, char* argv[]) { // ----- Dynamic simulation ------ String simNameDP = simName; Logger::setLogDir("logs/" + simNameDP); - + // Extract relevant powerflow results Real initActivePower = genPF->getApparentPower().real(); Real initReactivePower = genPF->getApparentPower().imag(); @@ -132,11 +132,11 @@ int main(int argc, char* argv[]) { genDP->setOperationalParametersPerUnit( syngenKundur.nomPower, syngenKundur.nomVoltage, syngenKundur.nomFreq, H, - syngenKundur.Ld, syngenKundur.Lq, syngenKundur.Ll, + syngenKundur.Ld, syngenKundur.Lq, syngenKundur.Ll, syngenKundur.Ld_t, syngenKundur.Lq_t, syngenKundur.Td0_t, syngenKundur.Tq0_t, - syngenKundur.Ld_s, syngenKundur.Lq_s, syngenKundur.Td0_s, syngenKundur.Tq0_s); + syngenKundur.Ld_s, syngenKundur.Lq_s, syngenKundur.Td0_s, syngenKundur.Tq0_s); genDP->setInitialValues(initElecPower, initMechPower, n1PF->voltage()(0,0)); - genDP->setModelAsCurrentSource(true); + genDP->setModelAsNortonSource(true); //Grid bus as Slack auto extnetDP = DP::Ph1::NetworkInjection::make("Slack", logLevel); @@ -144,9 +144,9 @@ int main(int argc, char* argv[]) { // Line auto lineDP = DP::Ph1::PiLine::make("PiLine", logLevel); - lineDP->setParameters(gridParams.lineResistance, gridParams.lineInductance, + lineDP->setParameters(gridParams.lineResistance, gridParams.lineInductance, gridParams.lineCapacitance, gridParams.lineConductance); - + // Topology genDP->connect({ n1DP }); lineDP->connect({ n1DP, n2DP }); @@ -166,6 +166,7 @@ int main(int argc, char* argv[]) { logger->logAttribute(genDP->name() + ".Te", genDP->attribute("Te")); logger->logAttribute(genDP->name() + ".omega", genDP->attribute("w_r")); logger->logAttribute(genDP->name() + ".delta", genDP->attribute("delta")); + logger->logAttribute(genDP->name() + ".Theta", genDP->attribute("Theta")); // load step event std::shared_ptr loadStepEvent = Examples::Events::createEventAddPowerConsumption("n1DP", std::round(loadStepEventTime/timeStep)*timeStep, gridParams.loadStepActivePower, systemDP, Domain::DP, logger); @@ -182,6 +183,6 @@ int main(int argc, char* argv[]) { // Events simDP.addEvent(loadStepEvent); - + simDP.run(); -} \ No newline at end of file +} diff --git a/dpsim/examples/cxx/Circuits/EMT_ReducedOrderSG_SMIB_Fault.cpp b/dpsim/examples/cxx/Circuits/EMT_ReducedOrderSG_SMIB_Fault.cpp index 3ef33b3e12..697fd0914d 100644 --- a/dpsim/examples/cxx/Circuits/EMT_ReducedOrderSG_SMIB_Fault.cpp +++ b/dpsim/examples/cxx/Circuits/EMT_ReducedOrderSG_SMIB_Fault.cpp @@ -12,7 +12,7 @@ const Examples::Grids::SMIB::ReducedOrderSynchronGenerator::Scenario4::GridParam // Generator parameters const Examples::Components::SynchronousGeneratorKundur::MachineParameters syngenKundur; -int main(int argc, char* argv[]) { +int main(int argc, char* argv[]) { // Simultion parameters Real switchClosed = GridParams.SwitchClosed; @@ -42,7 +42,7 @@ int main(int argc, char* argv[]) { } } - + Real logDownSampling; if (timeStep<100e-6) logDownSampling = floor(100e-6 / timeStep); @@ -63,7 +63,7 @@ int main(int argc, char* argv[]) { // Synchronous generator ideal model auto genPF = SP::Ph1::SynchronGenerator::make("Generator", Logger::Level::debug); - genPF->setParameters(syngenKundur.nomPower, GridParams.VnomMV, GridParams.setPointActivePower, + genPF->setParameters(syngenKundur.nomPower, GridParams.VnomMV, GridParams.setPointActivePower, GridParams.setPointVoltage, PowerflowBusType::PV); genPF->setBaseVoltage(GridParams.VnomMV); genPF->modifyPowerFlowBusType(PowerflowBusType::PV); @@ -73,10 +73,10 @@ int main(int argc, char* argv[]) { extnetPF->setParameters(GridParams.VnomMV); extnetPF->setBaseVoltage(GridParams.VnomMV); extnetPF->modifyPowerFlowBusType(PowerflowBusType::VD); - + // Line auto linePF = SP::Ph1::PiLine::make("PiLine", Logger::Level::debug); - linePF->setParameters(GridParams.lineResistance, GridParams.lineInductance, + linePF->setParameters(GridParams.lineResistance, GridParams.lineInductance, GridParams.lineCapacitance, GridParams.lineConductance); linePF->setBaseVoltage(GridParams.VnomMV); @@ -108,7 +108,7 @@ int main(int argc, char* argv[]) { // ----- Dynamic simulation ------ String simNameEMT = simName; Logger::setLogDir("logs/"+simNameEMT); - + // Extract relevant powerflow results Real initActivePower = genPF->getApparentPower().real(); Real initReactivePower = genPF->getApparentPower().imag(); @@ -116,13 +116,13 @@ int main(int argc, char* argv[]) { Real initMechPower = initActivePower; // Nodes - std::vector initialVoltage_n1{ n1PF->voltage()(0,0), + std::vector initialVoltage_n1{ n1PF->voltage()(0,0), n1PF->voltage()(0,0) * SHIFT_TO_PHASE_B, n1PF->voltage()(0,0) * SHIFT_TO_PHASE_C }; auto n1EMT = SimNode::make("n1EMT", PhaseType::ABC, initialVoltage_n1); - std::vector initialVoltage_n2{ n2PF->voltage()(0,0), + std::vector initialVoltage_n2{ n2PF->voltage()(0,0), n2PF->voltage()(0,0) * SHIFT_TO_PHASE_B, n2PF->voltage()(0,0) * SHIFT_TO_PHASE_C }; @@ -133,25 +133,25 @@ int main(int argc, char* argv[]) { genEMT->setOperationalParametersPerUnit( syngenKundur.nomPower, syngenKundur.nomVoltage, syngenKundur.nomFreq, H, - syngenKundur.Ld, syngenKundur.Lq, syngenKundur.Ll, + syngenKundur.Ld, syngenKundur.Lq, syngenKundur.Ll, syngenKundur.Ld_t, syngenKundur.Lq_t, syngenKundur.Td0_t, syngenKundur.Tq0_t, - syngenKundur.Ld_s, syngenKundur.Lq_s, syngenKundur.Td0_s, syngenKundur.Tq0_s); + syngenKundur.Ld_s, syngenKundur.Lq_s, syngenKundur.Td0_s, syngenKundur.Tq0_s); genEMT->setInitialValues(initElecPower, initMechPower, n1PF->voltage()(0,0)); - genEMT->setModelAsCurrentSource(true); - + genEMT->setModelAsNortonSource(true); + //Grid bus as Slack auto extnetEMT = EMT::Ph3::NetworkInjection::make("Slack", logLevel); - + // Line auto lineEMT = EMT::Ph3::PiLine::make("PiLine", logLevel); - lineEMT->setParameters(Math::singlePhaseParameterToThreePhase(GridParams.lineResistance), - Math::singlePhaseParameterToThreePhase(GridParams.lineInductance), + lineEMT->setParameters(Math::singlePhaseParameterToThreePhase(GridParams.lineResistance), + Math::singlePhaseParameterToThreePhase(GridParams.lineInductance), Math::singlePhaseParameterToThreePhase(GridParams.lineCapacitance), Math::singlePhaseParameterToThreePhase(GridParams.lineConductance)); //Breaker auto fault = CPS::EMT::Ph3::Switch::make("Br_fault", logLevel); - fault->setParameters(Math::singlePhaseParameterToThreePhase(switchOpen), + fault->setParameters(Math::singlePhaseParameterToThreePhase(switchOpen), Math::singlePhaseParameterToThreePhase(switchClosed)); fault->openSwitch(); @@ -164,7 +164,7 @@ int main(int argc, char* argv[]) { auto systemEMT = SystemTopology(GridParams.nomFreq, SystemNodeList{n1EMT, n2EMT}, SystemComponentList{genEMT, lineEMT, fault, extnetEMT}); - + // Logging auto loggerEMT = DataLogger::make(simNameEMT, true, logDownSampling); loggerEMT->logAttribute("v_gen", genEMT->attribute("v_intf")); @@ -192,6 +192,6 @@ int main(int argc, char* argv[]) { simEMT.addEvent(sw1); auto sw2 = SwitchEvent3Ph::make(endTimeFault, fault, false); simEMT.addEvent(sw2); - + simEMT.run(); -} \ No newline at end of file +} diff --git a/dpsim/examples/cxx/Circuits/EMT_ReducedOrderSG_VBR_Load_Fault.cpp b/dpsim/examples/cxx/Circuits/EMT_ReducedOrderSG_VBR_Load_Fault.cpp index 89b45c93f2..c6e90b5af6 100644 --- a/dpsim/examples/cxx/Circuits/EMT_ReducedOrderSG_VBR_Load_Fault.cpp +++ b/dpsim/examples/cxx/Circuits/EMT_ReducedOrderSG_VBR_Load_Fault.cpp @@ -18,7 +18,7 @@ const Examples::Components::ExcitationSystemEremia::Parameters excitationEremia; // Turbine Goverour const Examples::Components::TurbineGovernor::TurbineGovernorPSAT1 turbineGovernor; -int main(int argc, char* argv[]) { +int main(int argc, char* argv[]) { // Simultion parameters Real switchClosed = GridParams.SwitchClosed; @@ -37,7 +37,7 @@ int main(int argc, char* argv[]) { // Command line args processing CommandLineArgs args(argc, argv); if (argc > 1) { - if (args.options.find("SGModel") != args.options.end()) + if (args.options.find("SGModel") != args.options.end()) SGModel = args.getOptionString("SGModel"); if (args.options.find("WITHEXCITER") != args.options.end()) withExciter = args.getOptionBool("WITHEXCITER"); @@ -60,14 +60,14 @@ int main(int argc, char* argv[]) { logDownSampling = 1.0; Logger::Level logLevel = Logger::Level::off; std::string simName ="EMT_SynGen" + SGModel + "Order_VBR_Load_Fault" + stepSize_str + inertia_str; - - + + // ----- Dynamic simulation ------ String simNameEMT = simName; Logger::setLogDir("logs/"+simNameEMT); - + // Nodes - std::vector initialVoltage_n1{ GridParams.initTerminalVolt, + std::vector initialVoltage_n1{ GridParams.initTerminalVolt, GridParams.initTerminalVolt * SHIFT_TO_PHASE_B, GridParams.initTerminalVolt * SHIFT_TO_PHASE_C }; @@ -79,20 +79,20 @@ int main(int argc, char* argv[]) { genEMT->setOperationalParametersPerUnit( syngenKundur.nomPower, syngenKundur.nomVoltage, syngenKundur.nomFreq, H, - syngenKundur.Ld, syngenKundur.Lq, syngenKundur.Ll, + syngenKundur.Ld, syngenKundur.Lq, syngenKundur.Ll, syngenKundur.Ld_t, syngenKundur.Lq_t, syngenKundur.Td0_t, syngenKundur.Tq0_t, - syngenKundur.Ld_s, syngenKundur.Lq_s, syngenKundur.Td0_s, syngenKundur.Tq0_s); - genEMT->setInitialValues(GridParams.initComplexElectricalPower, GridParams.mechPower, + syngenKundur.Ld_s, syngenKundur.Lq_s, syngenKundur.Td0_s, syngenKundur.Tq0_s); + genEMT->setInitialValues(GridParams.initComplexElectricalPower, GridParams.mechPower, GridParams.initTerminalVolt); - genEMT->setModelAsCurrentSource(true); + genEMT->setModelAsNortonSource(true); // Exciter std::shared_ptr exciterEMT = nullptr; if (withExciter) { exciterEMT = Signal::Exciter::make("SynGen_Exciter", logLevel); - exciterEMT->setParameters(excitationEremia.Ta, excitationEremia.Ka, - excitationEremia.Te, excitationEremia.Ke, - excitationEremia.Tf, excitationEremia.Kf, + exciterEMT->setParameters(excitationEremia.Ta, excitationEremia.Ka, + excitationEremia.Te, excitationEremia.Ke, + excitationEremia.Tf, excitationEremia.Kf, excitationEremia.Tr); genEMT->addExciter(exciterEMT); } @@ -101,21 +101,21 @@ int main(int argc, char* argv[]) { std::shared_ptr turbineGovernorEMT = nullptr; if (withTurbineGovernor) { turbineGovernorEMT = Signal::TurbineGovernorType1::make("SynGen_TurbineGovernor", logLevel); - turbineGovernorEMT->setParameters(turbineGovernor.T3, turbineGovernor.T4, - turbineGovernor.T5, turbineGovernor.Tc, turbineGovernor.Ts, turbineGovernor.R, + turbineGovernorEMT->setParameters(turbineGovernor.T3, turbineGovernor.T4, + turbineGovernor.T5, turbineGovernor.Tc, turbineGovernor.Ts, turbineGovernor.R, turbineGovernor.Tmin, turbineGovernor.Tmax, turbineGovernor.OmegaRef); genEMT->addGovernor(turbineGovernorEMT); } // Load auto load = CPS::EMT::Ph3::RXLoad::make("Load", logLevel); - load->setParameters(Math::singlePhaseParameterToThreePhase(GridParams.initActivePower/3), + load->setParameters(Math::singlePhaseParameterToThreePhase(GridParams.initActivePower/3), Math::singlePhaseParameterToThreePhase(GridParams.initReactivePower/3), GridParams.VnomMV); //Breaker auto fault = CPS::EMT::Ph3::Switch::make("Br_fault", logLevel); - fault->setParameters(Math::singlePhaseParameterToThreePhase(switchOpen), + fault->setParameters(Math::singlePhaseParameterToThreePhase(switchOpen), Math::singlePhaseParameterToThreePhase(switchClosed)); fault->openSwitch(); @@ -137,8 +137,8 @@ int main(int argc, char* argv[]) { loggerEMT->logAttribute("w_r", genEMT->attribute("w_r")); loggerEMT->logAttribute("Vdq0", genEMT->attribute("Vdq0")); loggerEMT->logAttribute("Idq0", genEMT->attribute("Idq0")); - - // Exciter + + // Exciter if (withExciter) { loggerEMT->logAttribute("Ef", exciterEMT->attribute("Ef")); } @@ -163,7 +163,7 @@ int main(int argc, char* argv[]) { simEMT.addEvent(sw1); auto sw2 = SwitchEvent3Ph::make(endTimeFault, fault, false); simEMT.addEvent(sw2); - + simEMT.run(); simEMT.logStepTimes(simNameEMT + "_step_times"); -} \ No newline at end of file +} diff --git a/dpsim/examples/cxx/Circuits/EMT_SMIB_ReducedOrderSGIterative_LoadStep.cpp b/dpsim/examples/cxx/Circuits/EMT_SMIB_ReducedOrderSGIterative_LoadStep.cpp new file mode 100644 index 0000000000..d1f97617b9 --- /dev/null +++ b/dpsim/examples/cxx/Circuits/EMT_SMIB_ReducedOrderSGIterative_LoadStep.cpp @@ -0,0 +1,203 @@ +#include +#include "../Examples.h" + +using namespace DPsim; +using namespace CPS; +using namespace CPS::CIM; +using namespace Examples::Grids::SMIB::ReducedOrderSynchronGenerator; + +// Default configuration of scenario +Scenario6::Config defaultConfig; + +// Grid parameters +Scenario6::GridParams gridParams; + +// Generator parameters +Examples::Components::SynchronousGeneratorKundur::MachineParameters syngenKundur; + +int main(int argc, char* argv[]) { + + // Simulation parameters + String simName = "EMT_SMIB_ReducedOrderSGIterative_LoadStep"; + Real timeStep = 10e-6; + Real finalTime = 35; + + // Default configuration + Real loadStepEventTime = defaultConfig.loadStepEventTime; + Real H = syngenKundur.H; + Real tolerance = defaultConfig.tolerance; + int maxIter = defaultConfig.maxIter; + String SGModel = defaultConfig.sgType + "Iter"; + SGModel = "4TPM"; // options: "4PCM", "4TPM", "6PCM" + + // Command line args processing + CommandLineArgs args(argc, argv); + if (argc > 1) { + if (args.options.find("SimName") != args.options.end()) + simName = args.getOptionString("SimName"); + if (args.options.find("TimeStep") != args.options.end()) + timeStep = args.getOptionReal("TimeStep"); + if (args.options.find("Tolerance") != args.options.end()) + tolerance = args.getOptionReal("Tolerance"); + if (args.options.find("MaxIter") != args.options.end()) + maxIter = int(args.getOptionReal("MaxIter")); + if (args.options.find("loadStepEventTime") != args.options.end()) + loadStepEventTime = args.getOptionReal("loadStepEventTime"); + if (args.options.find("inertia") != args.options.end()) + H = args.getOptionReal("inertia"); + if (args.options.find("SGModel") != args.options.end()) + SGModel = args.getOptionString("SGModel"); + } + + std::cout << "Simulation Parameters: " << std::endl; + std::cout << "SimName: " << simName << std::endl; + std::cout << "Time Step: " << timeStep << std::endl; + std::cout << "Tolerance: " << tolerance << std::endl; + std::cout << "Max N° of Iterations: " << maxIter << std::endl; + std::cout << "SG: " << SGModel << std::endl; + + // Configure logging + Logger::Level logLevel = Logger::Level::info; + + // apply downsampling for simulation step sizes lower than 10us + Real logDownSampling; + if (timeStep < 10e-6) + logDownSampling = floor((10e-6) / timeStep); + else + logDownSampling = 1.0; + + // ----- POWERFLOW FOR INITIALIZATION ----- + String simNamePF = simName + "_PF"; + Logger::setLogDir("logs/" + simNamePF); + + // Components + auto n1PF = SimNode::make("n1", PhaseType::Single); + auto n2PF = SimNode::make("n2", PhaseType::Single); + + //Synchronous generator ideal model + auto genPF = SP::Ph1::SynchronGenerator::make("Generator", logLevel); + genPF->setParameters(syngenKundur.nomPower, gridParams.VnomMV, gridParams.setPointActivePower, + gridParams.setPointVoltage, PowerflowBusType::PV); + genPF->setBaseVoltage(gridParams.VnomMV); + genPF->modifyPowerFlowBusType(PowerflowBusType::PV); + + //Grid bus as Slack + auto extnetPF = SP::Ph1::NetworkInjection::make("Slack", logLevel); + extnetPF->setParameters(gridParams.VnomMV); + extnetPF->setBaseVoltage(gridParams.VnomMV); + extnetPF->modifyPowerFlowBusType(PowerflowBusType::VD); + + //Line + auto linePF = SP::Ph1::PiLine::make("PiLine", logLevel); + linePF->setParameters(gridParams.lineResistance, gridParams.lineInductance, + gridParams.lineCapacitance, gridParams.lineConductance); + linePF->setBaseVoltage(gridParams.VnomMV); + + // Topology + genPF->connect({ n1PF }); + linePF->connect({ n1PF, n2PF }); + extnetPF->connect({ n2PF }); + auto systemPF = SystemTopology(gridParams.nomFreq, + SystemNodeList{n1PF, n2PF}, + SystemComponentList{genPF, linePF, extnetPF}); + + // Logging + auto loggerPF = DataLogger::make(simNamePF); + loggerPF->logAttribute("v1", n1PF->attribute("v")); + loggerPF->logAttribute("v2", n2PF->attribute("v")); + + // Simulation + Simulation simPF(simNamePF, logLevel); + simPF.setSystem(systemPF); + simPF.setTimeStep(0.1); + simPF.setFinalTime(0.1); + simPF.setDomain(Domain::SP); + simPF.setSolverType(Solver::Type::NRP); + simPF.setSolverAndComponentBehaviour(Solver::Behaviour::Initialization); + simPF.doInitFromNodesAndTerminals(false); + simPF.addLogger(loggerPF); + simPF.run(); + + + // ----- Dynamic simulation ------ + String simNameEMT = simName; + Logger::setLogDir("logs/" + simNameEMT); + + // Extract relevant powerflow results + Real initActivePower = genPF->getApparentPower().real(); + Real initReactivePower = genPF->getApparentPower().imag(); + Complex initElecPower = Complex(initActivePower, initReactivePower); + Real initMechPower = initActivePower; + + // Nodes + std::vector initialVoltage_n1{ n1PF->voltage()(0,0), + n1PF->voltage()(0,0) * SHIFT_TO_PHASE_B, + n1PF->voltage()(0,0) * SHIFT_TO_PHASE_C + }; + auto n1EMT = SimNode::make("n1EMT", PhaseType::ABC, initialVoltage_n1); + std::vector initialVoltage_n2{ n2PF->voltage()(0,0), + n2PF->voltage()(0,0) * SHIFT_TO_PHASE_B, + n2PF->voltage()(0,0) * SHIFT_TO_PHASE_C + }; + auto n2EMT = SimNode::make("n2EMT", PhaseType::ABC, initialVoltage_n2); + + // Synchronous generator + auto genEMT = EMT::Ph3::SynchronGenerator4OrderPCM::make("SynGen", logLevel); + genEMT->setOperationalParametersPerUnit( + syngenKundur.nomPower, syngenKundur.nomVoltage, + syngenKundur.nomFreq, H, + syngenKundur.Ld, syngenKundur.Lq, syngenKundur.Ll, + syngenKundur.Ld_t, syngenKundur.Lq_t, syngenKundur.Td0_t, syngenKundur.Tq0_t); + genEMT->setInitialValues(initElecPower, initMechPower, n1PF->voltage()(0,0)); + genEMT->setMaxIterations(maxIter); + genEMT->setTolerance(tolerance); + + //Grid bus as Slack + auto extnetEMT = EMT::Ph3::NetworkInjection::make("Slack", logLevel); + + // Line + auto lineEMT = EMT::Ph3::PiLine::make("PiLine", logLevel); + lineEMT->setParameters(Math::singlePhaseParameterToThreePhase(gridParams.lineResistance), + Math::singlePhaseParameterToThreePhase(gridParams.lineInductance), + Math::singlePhaseParameterToThreePhase(gridParams.lineCapacitance), + Math::singlePhaseParameterToThreePhase(gridParams.lineConductance)); + + // Topology + genEMT->connect({ n1EMT }); + lineEMT->connect({ n1EMT, n2EMT }); + extnetEMT->connect({ n2EMT }); + SystemTopology systemEMT = SystemTopology(gridParams.nomFreq, + SystemNodeList{n1EMT, n2EMT}, + SystemComponentList{genEMT, lineEMT, extnetEMT}); + + // Logging + // log node voltage + auto logger = DataLogger::make(simName, true, logDownSampling); + for (auto node : systemEMT.mNodes) + logger->logAttribute(node->name() + ".V", node->attribute("v")); + + // log generator vars + //logger->logAttribute(genEMT->name() + ".Tm", genEMT->attribute("Tm")); + logger->logAttribute(genEMT->name() + ".Te", genEMT->attribute("Te")); + logger->logAttribute(genEMT->name() + ".omega", genEMT->attribute("w_r")); + logger->logAttribute(genEMT->name() + ".delta", genEMT->attribute("delta")); + logger->logAttribute(genEMT->name() + ".NIterations", genEMT->attribute("NIterations")); + //logger->logAttribute(genEMT->name() + ".theta", genEMT->attribute("Theta")); + + // load step event + std::shared_ptr loadStepEvent = Examples::Events::createEventAddPowerConsumption3Ph("n1EMT", std::round(loadStepEventTime/timeStep)*timeStep, gridParams.loadStepActivePower, systemEMT, Domain::EMT, logger); + + Simulation simEMT(simNameEMT, logLevel); + simEMT.doInitFromNodesAndTerminals(true); + simEMT.setSystem(systemEMT); + simEMT.setTimeStep(timeStep); + simEMT.setFinalTime(finalTime); + simEMT.setDomain(Domain::EMT); + simEMT.setDirectLinearSolverImplementation(DPsim::DirectLinearSolverImpl::SparseLU); + simEMT.addLogger(logger); + + // Events + simEMT.addEvent(loadStepEvent); + + simEMT.run(); +} diff --git a/dpsim/examples/cxx/Circuits/EMT_SMIB_ReducedOrderSG_LoadStep.cpp b/dpsim/examples/cxx/Circuits/EMT_SMIB_ReducedOrderSG_LoadStep.cpp index c4b673356d..57f86ab872 100644 --- a/dpsim/examples/cxx/Circuits/EMT_SMIB_ReducedOrderSG_LoadStep.cpp +++ b/dpsim/examples/cxx/Circuits/EMT_SMIB_ReducedOrderSG_LoadStep.cpp @@ -17,11 +17,11 @@ Scenario6::GridParams gridParams; // Generator parameters Examples::Components::SynchronousGeneratorKundur::MachineParameters syngenKundur; -int main(int argc, char* argv[]) { +int main(int argc, char* argv[]) { // Simulation parameters String simName = "EMT_SMIB_ReducedOrderSG_LoadStep"; - Real timeStep = 100e-6; + Real timeStep = 10e-6; Real finalTime = 35; // Default configuration @@ -64,7 +64,7 @@ int main(int argc, char* argv[]) { //Synchronous generator ideal model auto genPF = SP::Ph1::SynchronGenerator::make("Generator", logLevel); - genPF->setParameters(syngenKundur.nomPower, gridParams.VnomMV, gridParams.setPointActivePower, + genPF->setParameters(syngenKundur.nomPower, gridParams.VnomMV, gridParams.setPointActivePower, gridParams.setPointVoltage, PowerflowBusType::PV); genPF->setBaseVoltage(gridParams.VnomMV); genPF->modifyPowerFlowBusType(PowerflowBusType::PV); @@ -74,10 +74,10 @@ int main(int argc, char* argv[]) { extnetPF->setParameters(gridParams.VnomMV); extnetPF->setBaseVoltage(gridParams.VnomMV); extnetPF->modifyPowerFlowBusType(PowerflowBusType::VD); - + //Line auto linePF = SP::Ph1::PiLine::make("PiLine", logLevel); - linePF->setParameters(gridParams.lineResistance, gridParams.lineInductance, + linePF->setParameters(gridParams.lineResistance, gridParams.lineInductance, gridParams.lineCapacitance, gridParams.lineConductance); linePF->setBaseVoltage(gridParams.VnomMV); @@ -110,7 +110,7 @@ int main(int argc, char* argv[]) { // ----- Dynamic simulation ------ String simNameEMT = simName; Logger::setLogDir("logs/" + simNameEMT); - + // Extract relevant powerflow results Real initActivePower = genPF->getApparentPower().real(); Real initReactivePower = genPF->getApparentPower().imag(); @@ -118,12 +118,12 @@ int main(int argc, char* argv[]) { Real initMechPower = initActivePower; // Nodes - std::vector initialVoltage_n1{ n1PF->voltage()(0,0), + std::vector initialVoltage_n1{ n1PF->voltage()(0,0), n1PF->voltage()(0,0) * SHIFT_TO_PHASE_B, n1PF->voltage()(0,0) * SHIFT_TO_PHASE_C }; auto n1EMT = SimNode::make("n1EMT", PhaseType::ABC, initialVoltage_n1); - std::vector initialVoltage_n2{ n2PF->voltage()(0,0), + std::vector initialVoltage_n2{ n2PF->voltage()(0,0), n2PF->voltage()(0,0) * SHIFT_TO_PHASE_B, n2PF->voltage()(0,0) * SHIFT_TO_PHASE_C }; @@ -134,22 +134,22 @@ int main(int argc, char* argv[]) { genEMT->setOperationalParametersPerUnit( syngenKundur.nomPower, syngenKundur.nomVoltage, syngenKundur.nomFreq, H, - syngenKundur.Ld, syngenKundur.Lq, syngenKundur.Ll, + syngenKundur.Ld, syngenKundur.Lq, syngenKundur.Ll, syngenKundur.Ld_t, syngenKundur.Lq_t, syngenKundur.Td0_t, syngenKundur.Tq0_t, - syngenKundur.Ld_s, syngenKundur.Lq_s, syngenKundur.Td0_s, syngenKundur.Tq0_s); + syngenKundur.Ld_s, syngenKundur.Lq_s, syngenKundur.Td0_s, syngenKundur.Tq0_s); genEMT->setInitialValues(initElecPower, initMechPower, n1PF->voltage()(0,0)); - genEMT->setModelAsCurrentSource(true); + genEMT->setModelAsNortonSource(true); //Grid bus as Slack auto extnetEMT = EMT::Ph3::NetworkInjection::make("Slack", logLevel); // Line auto lineEMT = EMT::Ph3::PiLine::make("PiLine", logLevel); - lineEMT->setParameters(Math::singlePhaseParameterToThreePhase(gridParams.lineResistance), - Math::singlePhaseParameterToThreePhase(gridParams.lineInductance), + lineEMT->setParameters(Math::singlePhaseParameterToThreePhase(gridParams.lineResistance), + Math::singlePhaseParameterToThreePhase(gridParams.lineInductance), Math::singlePhaseParameterToThreePhase(gridParams.lineCapacitance), Math::singlePhaseParameterToThreePhase(gridParams.lineConductance)); - + // Topology genEMT->connect({ n1EMT }); lineEMT->connect({ n1EMT, n2EMT }); @@ -185,6 +185,6 @@ int main(int argc, char* argv[]) { // Events simEMT.addEvent(loadStepEvent); - + simEMT.run(); -} \ No newline at end of file +} diff --git a/dpsim/examples/cxx/Circuits/EMT_SynGen4OrderIter_SMIB_Fault.cpp b/dpsim/examples/cxx/Circuits/EMT_SynGen4OrderIter_SMIB_Fault.cpp new file mode 100644 index 0000000000..609741218e --- /dev/null +++ b/dpsim/examples/cxx/Circuits/EMT_SynGen4OrderIter_SMIB_Fault.cpp @@ -0,0 +1,223 @@ +#include +#include "../Examples.h" + +using namespace DPsim; +using namespace CPS; +using namespace CPS::CIM; + +// ----- PARAMETRIZATION ----- +// General grid parameters +Real nomPower = 555e6; +Real VnomMV = 24e3; +Real VnomHV = 230e3; +Real nomFreq = 60; +Real ratio = VnomMV/VnomHV; +Real nomOmega= nomFreq * 2 * PI; + +// Generator +Examples::Components::SynchronousGeneratorKundur::MachineParameters syngenKundur; +Real setPointActivePower=300e6; +Real setPointVoltage=1.05*VnomMV; + +// HV line parameters referred to MV side +Examples::Grids::CIGREHVAmerican::LineParameters lineCIGREHV; +Real lineLength = 100; +Real lineResistance = lineCIGREHV.lineResistancePerKm * lineLength*std::pow(ratio,2); +Real lineInductance = lineCIGREHV.lineReactancePerKm * lineLength*std::pow(ratio,2) / nomOmega; +Real lineCapacitance = lineCIGREHV.lineSusceptancePerKm * lineLength/std::pow(ratio,2) / nomOmega; +Real lineConductance = 8e-2; + +void EMT_3ph_4OrderSynGenIter(String simName, Real timeStep, Real finalTime, Real H, + Real startTimeFault, Real endTimeFault, Real switchClosed, Real logDownSampling, + Real maxIterations, Real tolerance, Logger::Level logLevel) { + + // // ----- POWERFLOW FOR INITIALIZATION ----- + String simNamePF = simName + "_PF"; + Logger::setLogDir("logs/" + simNamePF); + + // Components + auto n1PF = SimNode::make("n1", PhaseType::Single); + auto n2PF = SimNode::make("n2", PhaseType::Single); + + //Synchronous generator ideal model + auto genPF = SP::Ph1::SynchronGenerator::make("Generator", Logger::Level::debug); + genPF->setParameters(nomPower, VnomMV, setPointActivePower, setPointVoltage, PowerflowBusType::PV); + genPF->setBaseVoltage(VnomMV); + genPF->modifyPowerFlowBusType(PowerflowBusType::PV); + + //Grid bus as Slack + auto extnetPF = SP::Ph1::NetworkInjection::make("Slack", Logger::Level::debug); + extnetPF->setParameters(VnomMV); + extnetPF->setBaseVoltage(VnomMV); + extnetPF->modifyPowerFlowBusType(PowerflowBusType::VD); + + //Line + auto linePF = SP::Ph1::PiLine::make("PiLine", Logger::Level::debug); + linePF->setParameters(lineResistance, lineInductance, lineCapacitance, lineConductance); + linePF->setBaseVoltage(VnomMV); + + // Topology + genPF->connect({ n1PF }); + linePF->connect({ n1PF, n2PF }); + extnetPF->connect({ n2PF }); + auto systemPF = SystemTopology(60, + SystemNodeList{n1PF, n2PF}, + SystemComponentList{genPF, linePF, extnetPF}); + + // Logging + auto loggerPF = DataLogger::make(simNamePF); + loggerPF->logAttribute("v1", n1PF->attribute("v")); + loggerPF->logAttribute("v2", n2PF->attribute("v")); + + // Simulation + Simulation simPF(simNamePF, Logger::Level::debug); + simPF.setSystem(systemPF); + simPF.setTimeStep(0.1); + simPF.setFinalTime(0.1); + simPF.setDomain(Domain::SP); + simPF.setSolverType(Solver::Type::NRP); + simPF.doInitFromNodesAndTerminals(false); + simPF.addLogger(loggerPF); + simPF.run(); + + + // ----- Dynamic simulation ------ + String simNameEMT = simName; + Logger::setLogDir("logs/"+simNameEMT); + + // Extract relevant powerflow results + Real initActivePower = genPF->getApparentPower().real(); + Real initReactivePower = genPF->getApparentPower().imag(); + Complex initElecPower = Complex(initActivePower, initReactivePower); + Real initMechPower = initActivePower; + + // Nodes + std::vector initialVoltage_n1{ n1PF->voltage()(0,0), + n1PF->voltage()(0,0) * SHIFT_TO_PHASE_B, + n1PF->voltage()(0,0) * SHIFT_TO_PHASE_C + }; + auto n1EMT = SimNode::make("n1EMT", PhaseType::ABC, initialVoltage_n1); + + std::vector initialVoltage_n2{ n2PF->voltage()(0,0), + n2PF->voltage()(0,0) * SHIFT_TO_PHASE_B, + n2PF->voltage()(0,0) * SHIFT_TO_PHASE_C + }; + auto n2EMT = SimNode::make("n2EMT", PhaseType::ABC, initialVoltage_n2); + + // Components + auto genEMT = EMT::Ph3::SynchronGenerator4OrderPCM::make("SynGen", logLevel); + genEMT->setOperationalParametersPerUnit( + syngenKundur.nomPower, syngenKundur.nomVoltage, + syngenKundur.nomFreq, H, + syngenKundur.Ld, syngenKundur.Lq, syngenKundur.Ll, + syngenKundur.Ld_t, syngenKundur.Lq_t, syngenKundur.Td0_t, + syngenKundur.Tq0_t); + genEMT->setInitialValues(initElecPower, initMechPower, n1PF->voltage()(0,0)); + genEMT->setMaxIterations(maxIterations); + genEMT->setTolerance(tolerance); + + //Grid bus as Slack + auto extnetEMT = EMT::Ph3::NetworkInjection::make("Slack", logLevel); + + // Line + auto lineEMT = EMT::Ph3::PiLine::make("PiLine", logLevel); + lineEMT->setParameters(Math::singlePhaseParameterToThreePhase(lineResistance), + Math::singlePhaseParameterToThreePhase(lineInductance), + Math::singlePhaseParameterToThreePhase(lineCapacitance), + Math::singlePhaseParameterToThreePhase(lineConductance)); + + //Breaker + auto fault = CPS::EMT::Ph3::Switch::make("Br_fault", logLevel); + Real switchOpen = 1e6; + fault->setParameters(Math::singlePhaseParameterToThreePhase(switchOpen), + Math::singlePhaseParameterToThreePhase(switchClosed)); + fault->openSwitch(); + + // Topology + genEMT->connect({ n1EMT }); + lineEMT->connect({ n1EMT, n2EMT }); + extnetEMT->connect({ n2EMT }); + fault->connect({EMT::SimNode::GND, n1EMT}); + auto systemEMT = SystemTopology(60, + SystemNodeList{n1EMT, n2EMT}, + SystemComponentList{genEMT, lineEMT, fault, extnetEMT}); + + // Logging + auto loggerEMT = DataLogger::make(simNameEMT, true, logDownSampling); + //loggerEMT->logAttribute("v2", n2EMT->attribute("v")); + //loggerEMT->logAttribute("v_gen", genEMT->attribute("v_intf")); + //loggerEMT->logAttribute("i_gen", genEMT->attribute("i_intf")); + loggerEMT->logAttribute("Etorque", genEMT->attribute("Te")); + loggerEMT->logAttribute("delta", genEMT->attribute("delta")); + loggerEMT->logAttribute("w_r", genEMT->attribute("w_r")); + loggerEMT->logAttribute("Vdq0", genEMT->attribute("Vdq0")); + loggerEMT->logAttribute("Idq0", genEMT->attribute("Idq0")); + loggerEMT->logAttribute("Edq0", genEMT->attribute("Edq0_t")); + loggerEMT->logAttribute("NIterations", genEMT->attribute("NIterations")); + + Simulation simEMT(simNameEMT, logLevel); + simEMT.doInitFromNodesAndTerminals(true); + simEMT.setSystem(systemEMT); + simEMT.setTimeStep(timeStep); + simEMT.setFinalTime(finalTime); + simEMT.setDomain(Domain::EMT); + simEMT.addLogger(loggerEMT); + simEMT.setDirectLinearSolverImplementation(DPsim::DirectLinearSolverImpl::SparseLU); + + // Events + auto sw1 = SwitchEvent3Ph::make(startTimeFault, fault, true); + simEMT.addEvent(sw1); + + auto sw2 = SwitchEvent3Ph::make(endTimeFault, fault, false); + simEMT.addEvent(sw2); + + simEMT.run(); + simEMT.logStepTimes(simNameEMT + "_step_times"); +} + +int main(int argc, char* argv[]) { + + // Command line args processing + CommandLineArgs args(argc, argv); + Real switchClosed = 0.001; + Real iteration = 20; + Real tolerance = 1e-6; + Real timeStep = 10e-6; + + std::string stepSize_str = ""; + std::string iteration_str = ""; + std::string tolerance_str = ""; + if (argc > 1) { + if (args.options.find("StepSize") != args.options.end()){ + timeStep = args.getOptionReal("StepSize"); + stepSize_str = "_StepSize_" + std::to_string(timeStep); + } + if (args.options.find("MaxIter") != args.options.end()){ + iteration = args.getOptionReal("MaxIter"); + iteration_str = "_MaxIter_" + std::to_string(iteration); + } + if (args.options.find("Tol") != args.options.end()){ + tolerance = args.getOptionReal("Tol"); + tolerance_str = "_Tolerance_" + std::to_string(tolerance*1e6); + } + } + + //Simultion parameters + Real H = 3.7; + Real finalTime = 10.0; + Real startTimeFault = 1.0; + Real endTimeFault = 1.1; + std::string simName ="EMT_SynGen4OrderIter_SMIB_Fault" + stepSize_str + tolerance_str + iteration_str; + + Real logDownSampling; + if (timeStep<100e-6) + logDownSampling = floor((100e-6) / timeStep); + else + logDownSampling = 1.0; + Logger::Level logLevel = Logger::Level::off; + + + EMT_3ph_4OrderSynGenIter(simName, timeStep, finalTime, H, + startTimeFault, endTimeFault, switchClosed, logDownSampling, + iteration, tolerance, logLevel); +} diff --git a/dpsim/examples/cxx/Circuits/SP_ReducedOrderSG_SMIB_Fault.cpp b/dpsim/examples/cxx/Circuits/SP_ReducedOrderSG_SMIB_Fault.cpp index 25a1282866..966675f796 100644 --- a/dpsim/examples/cxx/Circuits/SP_ReducedOrderSG_SMIB_Fault.cpp +++ b/dpsim/examples/cxx/Circuits/SP_ReducedOrderSG_SMIB_Fault.cpp @@ -18,7 +18,7 @@ const Examples::Components::ExcitationSystemEremia::Parameters excitationEremia; // Turbine Goverour const Examples::Components::TurbineGovernor::TurbineGovernorPSAT1 turbineGovernor; -int main(int argc, char* argv[]) { +int main(int argc, char* argv[]) { // Simulation parameters Real switchClosed = GridParams.SwitchClosed; @@ -78,7 +78,7 @@ int main(int argc, char* argv[]) { //Synchronous generator ideal model auto genPF = SP::Ph1::SynchronGenerator::make("Generator", Logger::Level::debug); - genPF->setParameters(syngenKundur.nomPower, GridParams.VnomMV, + genPF->setParameters(syngenKundur.nomPower, GridParams.VnomMV, GridParams.setPointActivePower, GridParams.setPointVoltage, PowerflowBusType::PV); genPF->setBaseVoltage(GridParams.VnomMV); @@ -89,10 +89,10 @@ int main(int argc, char* argv[]) { extnetPF->setParameters(GridParams.VnomMV); extnetPF->setBaseVoltage(GridParams.VnomMV); extnetPF->modifyPowerFlowBusType(PowerflowBusType::VD); - + //Line auto linePF = SP::Ph1::PiLine::make("PiLine", Logger::Level::debug); - linePF->setParameters(GridParams.lineResistance, GridParams.lineInductance, + linePF->setParameters(GridParams.lineResistance, GridParams.lineInductance, GridParams.lineCapacitance, GridParams.lineConductance); linePF->setBaseVoltage(GridParams.VnomMV); @@ -124,7 +124,7 @@ int main(int argc, char* argv[]) { // ----- Dynamic simulation ------ String simNameSP = simName; Logger::setLogDir("logs/" + simNameSP); - + // Extract relevant powerflow results Real initActivePower = genPF->getApparentPower().real(); Real initReactivePower = genPF->getApparentPower().imag(); @@ -142,19 +142,19 @@ int main(int argc, char* argv[]) { genSP->setOperationalParametersPerUnit( syngenKundur.nomPower, syngenKundur.nomVoltage, syngenKundur.nomFreq, H, - syngenKundur.Ld, syngenKundur.Lq, syngenKundur.Ll, + syngenKundur.Ld, syngenKundur.Lq, syngenKundur.Ll, syngenKundur.Ld_t, syngenKundur.Lq_t, syngenKundur.Td0_t, syngenKundur.Tq0_t, - syngenKundur.Ld_s, syngenKundur.Lq_s, syngenKundur.Td0_s, syngenKundur.Tq0_s); + syngenKundur.Ld_s, syngenKundur.Lq_s, syngenKundur.Td0_s, syngenKundur.Tq0_s); genSP->setInitialValues(initElecPower, initMechPower, n1PF->voltage()(0,0)); - genSP->setModelAsCurrentSource(true); + genSP->setModelAsNortonSource(true); // Exciter std::shared_ptr exciterSP = nullptr; if (withExciter) { exciterSP = Signal::Exciter::make("SynGen_Exciter", logLevel); - exciterSP->setParameters(excitationEremia.Ta, excitationEremia.Ka, - excitationEremia.Te, excitationEremia.Ke, - excitationEremia.Tf, excitationEremia.Kf, + exciterSP->setParameters(excitationEremia.Ta, excitationEremia.Ka, + excitationEremia.Te, excitationEremia.Ke, + excitationEremia.Tf, excitationEremia.Kf, excitationEremia.Tr); genSP->addExciter(exciterSP); } @@ -163,8 +163,8 @@ int main(int argc, char* argv[]) { std::shared_ptr turbineGovernorSP = nullptr; if (withTurbineGovernor) { turbineGovernorSP = Signal::TurbineGovernorType1::make("SynGen_TurbineGovernor", logLevel); - turbineGovernorSP->setParameters(turbineGovernor.T3, turbineGovernor.T4, - turbineGovernor.T5, turbineGovernor.Tc, turbineGovernor.Ts, turbineGovernor.R, + turbineGovernorSP->setParameters(turbineGovernor.T3, turbineGovernor.T4, + turbineGovernor.T5, turbineGovernor.Tc, turbineGovernor.Ts, turbineGovernor.R, turbineGovernor.Tmin, turbineGovernor.Tmax, turbineGovernor.OmegaRef); genSP->addGovernor(turbineGovernorSP); } @@ -175,9 +175,9 @@ int main(int argc, char* argv[]) { // Line auto lineSP = SP::Ph1::PiLine::make("PiLine", logLevel); - lineSP->setParameters(GridParams.lineResistance, GridParams.lineInductance, + lineSP->setParameters(GridParams.lineResistance, GridParams.lineInductance, GridParams.lineCapacitance, GridParams.lineConductance); - + //Breaker auto fault = CPS::SP::Ph1::Switch::make("Br_fault", logLevel); fault->setParameters(switchOpen, switchClosed); @@ -188,7 +188,7 @@ int main(int argc, char* argv[]) { lineSP->connect({ n1SP, n2SP }); extnetSP->connect({ n2SP }); fault->connect({SP::SimNode::GND, n1SP}); - + auto systemSP = SystemTopology(GridParams.nomFreq, SystemNodeList{n1SP, n2SP}, SystemComponentList{genSP, lineSP, extnetSP, fault}); @@ -237,6 +237,6 @@ int main(int argc, char* argv[]) { auto sw2 = SwitchEvent::make(endTimeFault, fault, false); simSP.addEvent(sw2); - + simSP.run(); -} \ No newline at end of file +} diff --git a/dpsim/examples/cxx/Circuits/SP_ReducedOrderSG_VBR_Load_Fault.cpp b/dpsim/examples/cxx/Circuits/SP_ReducedOrderSG_VBR_Load_Fault.cpp index f838e37917..fbbdb3751c 100644 --- a/dpsim/examples/cxx/Circuits/SP_ReducedOrderSG_VBR_Load_Fault.cpp +++ b/dpsim/examples/cxx/Circuits/SP_ReducedOrderSG_VBR_Load_Fault.cpp @@ -18,7 +18,7 @@ const Examples::Components::ExcitationSystemEremia::Parameters excitationEremia; // Turbine Goverour const Examples::Components::TurbineGovernor::TurbineGovernorPSAT1 turbineGovernor; -int main(int argc, char* argv[]) { +int main(int argc, char* argv[]) { // Simulation parameters Real switchClosed = GridParams.SwitchClosed; @@ -65,10 +65,10 @@ int main(int argc, char* argv[]) { // ----- Dynamic simulation ------ String simNameSP = simName; Logger::setLogDir("logs/" + simNameSP); - + // Nodes auto initVoltN1 = std::vector({ - Complex(GridParams.VnomMV * cos(GridParams.initVoltAngle), + Complex(GridParams.VnomMV * cos(GridParams.initVoltAngle), GridParams.VnomMV * sin(GridParams.initVoltAngle))}); auto n1SP = SimNode::make("n1SP", PhaseType::Single, initVoltN1); @@ -77,21 +77,21 @@ int main(int argc, char* argv[]) { genSP->setOperationalParametersPerUnit( syngenKundur.nomPower, syngenKundur.nomVoltage, syngenKundur.nomFreq, H, - syngenKundur.Ld, syngenKundur.Lq, syngenKundur.Ll, + syngenKundur.Ld, syngenKundur.Lq, syngenKundur.Ll, syngenKundur.Ld_t, syngenKundur.Lq_t, syngenKundur.Td0_t, syngenKundur.Tq0_t, - syngenKundur.Ld_s, syngenKundur.Lq_s, syngenKundur.Td0_s, syngenKundur.Tq0_s); - genSP->setInitialValues(GridParams.initComplexElectricalPower, GridParams.mechPower, - Complex(GridParams.VnomMV * cos(GridParams.initVoltAngle), + syngenKundur.Ld_s, syngenKundur.Lq_s, syngenKundur.Td0_s, syngenKundur.Tq0_s); + genSP->setInitialValues(GridParams.initComplexElectricalPower, GridParams.mechPower, + Complex(GridParams.VnomMV * cos(GridParams.initVoltAngle), GridParams.VnomMV * sin(GridParams.initVoltAngle))); - genSP->setModelAsCurrentSource(true); + genSP->setModelAsNortonSource(true); // Exciter std::shared_ptr exciterSP = nullptr; if (withExciter) { exciterSP = Signal::Exciter::make("SynGen_Exciter", logLevel); - exciterSP->setParameters(excitationEremia.Ta, excitationEremia.Ka, - excitationEremia.Te, excitationEremia.Ke, - excitationEremia.Tf, excitationEremia.Kf, + exciterSP->setParameters(excitationEremia.Ta, excitationEremia.Ka, + excitationEremia.Te, excitationEremia.Ke, + excitationEremia.Tf, excitationEremia.Kf, excitationEremia.Tr); genSP->addExciter(exciterSP); } @@ -100,17 +100,17 @@ int main(int argc, char* argv[]) { std::shared_ptr turbineGovernorSP = nullptr; if (withTurbineGovernor) { turbineGovernorSP = Signal::TurbineGovernorType1::make("SynGen_TurbineGovernor", logLevel); - turbineGovernorSP->setParameters(turbineGovernor.T3, turbineGovernor.T4, - turbineGovernor.T5, turbineGovernor.Tc, turbineGovernor.Ts, turbineGovernor.R, + turbineGovernorSP->setParameters(turbineGovernor.T3, turbineGovernor.T4, + turbineGovernor.T5, turbineGovernor.Tc, turbineGovernor.Ts, turbineGovernor.R, turbineGovernor.Tmin, turbineGovernor.Tmax, turbineGovernor.OmegaRef); genSP->addGovernor(turbineGovernorSP); } // Load auto load = CPS::SP::Ph1::Load::make("Load", logLevel); - load->setParameters(GridParams.initActivePower, GridParams.initReactivePower, + load->setParameters(GridParams.initActivePower, GridParams.initReactivePower, GridParams.VnomMV); - + //Breaker auto fault = CPS::SP::Ph1::Switch::make("Br_fault", logLevel); fault->setParameters(switchOpen, switchClosed); @@ -139,7 +139,7 @@ int main(int argc, char* argv[]) { } else { loggerSP->logAttribute("Edq0", genSP->attribute("Edq_t")); } - + // Exciter if (withExciter) { loggerSP->logAttribute("Ef", exciterSP->attribute("Ef")); @@ -166,6 +166,6 @@ int main(int argc, char* argv[]) { auto sw2 = SwitchEvent::make(endTimeFault, fault, false); simSP.addEvent(sw2); - + simSP.run(); -} \ No newline at end of file +} diff --git a/dpsim/examples/cxx/Circuits/SP_SMIB_ReducedOrderSG_LoadStep.cpp b/dpsim/examples/cxx/Circuits/SP_SMIB_ReducedOrderSG_LoadStep.cpp index c6376aab77..f0647d65ab 100644 --- a/dpsim/examples/cxx/Circuits/SP_SMIB_ReducedOrderSG_LoadStep.cpp +++ b/dpsim/examples/cxx/Circuits/SP_SMIB_ReducedOrderSG_LoadStep.cpp @@ -16,7 +16,7 @@ Scenario6::GridParams gridParams; // Generator parameters Examples::Components::SynchronousGeneratorKundur::MachineParameters syngenKundur; -int main(int argc, char* argv[]) { +int main(int argc, char* argv[]) { // Simulation parameters String simName = "SP_SMIB_ReducedOrderSG_LoadStep"; @@ -63,7 +63,7 @@ int main(int argc, char* argv[]) { //Synchronous generator ideal model auto genPF = SP::Ph1::SynchronGenerator::make("Generator", logLevel); - genPF->setParameters(syngenKundur.nomPower, gridParams.VnomMV, gridParams.setPointActivePower, + genPF->setParameters(syngenKundur.nomPower, gridParams.VnomMV, gridParams.setPointActivePower, gridParams.setPointVoltage, PowerflowBusType::PV); genPF->setBaseVoltage(gridParams.VnomMV); genPF->modifyPowerFlowBusType(PowerflowBusType::PV); @@ -73,10 +73,10 @@ int main(int argc, char* argv[]) { extnetPF->setParameters(gridParams.VnomMV); extnetPF->setBaseVoltage(gridParams.VnomMV); extnetPF->modifyPowerFlowBusType(PowerflowBusType::VD); - + //Line auto linePF = SP::Ph1::PiLine::make("PiLine", logLevel); - linePF->setParameters(gridParams.lineResistance, gridParams.lineInductance, + linePF->setParameters(gridParams.lineResistance, gridParams.lineInductance, gridParams.lineCapacitance, gridParams.lineConductance); linePF->setBaseVoltage(gridParams.VnomMV); @@ -109,7 +109,7 @@ int main(int argc, char* argv[]) { // ----- Dynamic simulation ------ String simNameSP = simName; Logger::setLogDir("logs/" + simNameSP); - + // Extract relevant powerflow results Real initActivePower = genPF->getApparentPower().real(); Real initReactivePower = genPF->getApparentPower().imag(); @@ -127,11 +127,11 @@ int main(int argc, char* argv[]) { genSP->setOperationalParametersPerUnit( syngenKundur.nomPower, syngenKundur.nomVoltage, syngenKundur.nomFreq, H, - syngenKundur.Ld, syngenKundur.Lq, syngenKundur.Ll, + syngenKundur.Ld, syngenKundur.Lq, syngenKundur.Ll, syngenKundur.Ld_t, syngenKundur.Lq_t, syngenKundur.Td0_t, syngenKundur.Tq0_t, - syngenKundur.Ld_s, syngenKundur.Lq_s, syngenKundur.Td0_s, syngenKundur.Tq0_s); + syngenKundur.Ld_s, syngenKundur.Lq_s, syngenKundur.Td0_s, syngenKundur.Tq0_s); genSP->setInitialValues(initElecPower, initMechPower, n1PF->voltage()(0,0)); - genSP->setModelAsCurrentSource(true); + genSP->setModelAsNortonSource(true); //Grid bus as Slack auto extnetSP = SP::Ph1::NetworkInjection::make("Slack", logLevel); @@ -139,9 +139,9 @@ int main(int argc, char* argv[]) { // Line auto lineSP = SP::Ph1::PiLine::make("PiLine", logLevel); - lineSP->setParameters(gridParams.lineResistance, gridParams.lineInductance, + lineSP->setParameters(gridParams.lineResistance, gridParams.lineInductance, gridParams.lineCapacitance, gridParams.lineConductance); - + // Topology genSP->connect({ n1SP }); lineSP->connect({ n1SP, n2SP }); @@ -177,6 +177,6 @@ int main(int argc, char* argv[]) { // Events simSP.addEvent(loadStepEvent); - + simSP.run(); -} \ No newline at end of file +} diff --git a/dpsim/examples/cxx/Circuits/SP_SynGen4OrderDCIM_SMIB_Fault.cpp b/dpsim/examples/cxx/Circuits/SP_SynGen4OrderDCIM_SMIB_Fault.cpp deleted file mode 100644 index 9f2ae212b7..0000000000 --- a/dpsim/examples/cxx/Circuits/SP_SynGen4OrderDCIM_SMIB_Fault.cpp +++ /dev/null @@ -1,170 +0,0 @@ -#include -#include "../Examples.h" - -using namespace DPsim; -using namespace CPS; -using namespace CPS::CIM; - -// Grid parameters -Examples::Grids::SMIB::ScenarioConfig2 GridParams; - -// Generator parameters -Examples::Components::SynchronousGeneratorKundur::MachineParameters syngenKundur; - -int main(int argc, char* argv[]) { - - // Simulation parameters - Real switchClosed = GridParams.SwitchClosed; - Real switchOpen = GridParams.SwitchOpen; - Real startTimeFault = 1.0; - Real endTimeFault = 1.1; - Real timeStep = 1e-6; - Real finalTime = 20; - - // Command line args processing - CommandLineArgs args(argc, argv); - std::string stepSize_str = ""; - if (argc > 1 && (args.options.find("StepSize") != args.options.end())) { - timeStep = args.getOptionReal("StepSize"); - stepSize_str = "_StepSize_" + std::to_string(timeStep); - } - - Real logDownSampling; - if (timeStep<100e-6) - logDownSampling = floor(100e-6 / timeStep); - else - logDownSampling = 1.0; - Logger::Level logLevel = Logger::Level::off; - std::string simName = "SP_SynGen4OrderDCIM_SMIB_Fault" + stepSize_str; - - // ----- POWERFLOW FOR INITIALIZATION ----- - String simNamePF = simName + "_PF"; - Logger::setLogDir("logs/" + simNamePF); - - // Components - auto n1PF = SimNode::make("n1", PhaseType::Single); - auto n2PF = SimNode::make("n2", PhaseType::Single); - - //Synchronous generator ideal model - auto genPF = SP::Ph1::SynchronGenerator::make("Generator", Logger::Level::debug); - genPF->setParameters(syngenKundur.nomPower, GridParams.VnomMV, GridParams.setPointActivePower, - GridParams.setPointVoltage, PowerflowBusType::PV); - genPF->setBaseVoltage(GridParams.VnomMV); - genPF->modifyPowerFlowBusType(PowerflowBusType::PV); - - //Grid bus as Slack - auto extnetPF = SP::Ph1::NetworkInjection::make("Slack", Logger::Level::debug); - extnetPF->setParameters(GridParams.VnomMV); - extnetPF->setBaseVoltage(GridParams.VnomMV); - extnetPF->modifyPowerFlowBusType(PowerflowBusType::VD); - - //Line - auto linePF = SP::Ph1::PiLine::make("PiLine", Logger::Level::debug); - linePF->setParameters(GridParams.lineResistance, GridParams.lineInductance, - GridParams.lineCapacitance, GridParams.lineConductance); - linePF->setBaseVoltage(GridParams.VnomMV); - - // Topology - genPF->connect({ n1PF }); - linePF->connect({ n1PF, n2PF }); - extnetPF->connect({ n2PF }); - auto systemPF = SystemTopology(GridParams.nomFreq, - SystemNodeList{n1PF, n2PF}, - SystemComponentList{genPF, linePF, extnetPF}); - - // Logging - auto loggerPF = DataLogger::make(simNamePF); - loggerPF->logAttribute("v1", n1PF->attribute("v")); - loggerPF->logAttribute("v2", n2PF->attribute("v")); - - // Simulation - Simulation simPF(simNamePF, Logger::Level::debug); - simPF.setSystem(systemPF); - simPF.setTimeStep(0.1); - simPF.setFinalTime(0.1); - simPF.setDomain(Domain::SP); - simPF.setSolverType(Solver::Type::NRP); - simPF.doInitFromNodesAndTerminals(false); - simPF.addLogger(loggerPF); - simPF.run(); - - - // ----- Dynamic simulation ------ - String simNameSP = simName; - Logger::setLogDir("logs/" + simNameSP); - - // Extract relevant powerflow results - Real initActivePower = genPF->getApparentPower().real(); - Real initReactivePower = genPF->getApparentPower().imag(); - Complex initElecPower = Complex(initActivePower, initReactivePower); - Real initMechPower = initActivePower; - - // Nodes - std::vector initialVoltage_n1{ n1PF->voltage()(0,0), 0.0, 0.0}; - std::vector initialVoltage_n2{ n2PF->voltage()(0,0), 0.0, 0.0}; - auto n1SP = SimNode::make("n1SP", PhaseType::Single, initialVoltage_n1); - auto n2SP = SimNode::make("n2SP", PhaseType::Single, initialVoltage_n2); - - // Components - auto genSP = SP::Ph1::SynchronGenerator4OrderDCIM::make("SynGen", Logger::Level::debug); - genSP->setOperationalParametersPerUnit( - syngenKundur.nomPower, syngenKundur.nomVoltage, - syngenKundur.nomFreq, syngenKundur.H, - syngenKundur.Ld, syngenKundur.Lq, syngenKundur.Ll, - syngenKundur.Ld_t, syngenKundur.Lq_t, syngenKundur.Td0_t, syngenKundur.Tq0_t, - syngenKundur.Ld_s, syngenKundur.Lq_s, syngenKundur.Td0_s, syngenKundur.Tq0_s); - genSP->setInitialValues(initElecPower, initMechPower, n1PF->voltage()(0,0)); - - //Grid bus as Slack - auto extnetSP = SP::Ph1::NetworkInjection::make("Slack", logLevel); - extnetSP->setParameters(GridParams.VnomMV); - - // Line - auto lineSP = SP::Ph1::PiLine::make("PiLine", logLevel); - lineSP->setParameters(GridParams.lineResistance, GridParams.lineInductance, - GridParams.lineCapacitance, GridParams.lineConductance); - - //Breaker - auto fault = CPS::SP::Ph1::Switch::make("Br_fault", logLevel); - fault->setParameters(switchOpen, switchClosed); - fault->open(); - - // Topology - genSP->connect({ n1SP }); - lineSP->connect({ n1SP, n2SP }); - extnetSP->connect({ n2SP }); - fault->connect({SP::SimNode::GND, n1SP}); - auto systemSP = SystemTopology(GridParams.nomFreq, - SystemNodeList{n1SP, n2SP}, - SystemComponentList{genSP, lineSP, extnetSP, fault}); - - // Logging - auto loggerSP = DataLogger::make(simNameSP, true, logDownSampling); - loggerSP->logAttribute("v_gen", genSP->attribute("v_intf")); - loggerSP->logAttribute("i_gen", genSP->attribute("i_intf")); - loggerSP->logAttribute("Te", genSP->attribute("Te")); - loggerSP->logAttribute("delta", genSP->attribute("delta")); - loggerSP->logAttribute("w_r", genSP->attribute("w_r")); - loggerSP->logAttribute("Edq0", genSP->attribute("Edq_t")); - loggerSP->logAttribute("Vdq0", genSP->attribute("Idq0")); - loggerSP->logAttribute("Idq0", genSP->attribute("Idq0")); - - Simulation simSP(simNameSP, logLevel); - simSP.doInitFromNodesAndTerminals(true); - simSP.setSystem(systemSP); - simSP.setTimeStep(timeStep); - simSP.setFinalTime(finalTime); - simSP.setDomain(Domain::SP); - simSP.setDirectLinearSolverImplementation(DPsim::DirectLinearSolverImpl::SparseLU); - simSP.addLogger(loggerSP); - //simSP.doSystemMatrixRecomputation(true); - - // Events - auto sw1 = SwitchEvent::make(startTimeFault, fault, true); - simSP.addEvent(sw1); - - auto sw2 = SwitchEvent::make(endTimeFault, fault, false); - simSP.addEvent(sw2); - - simSP.run(); -} \ No newline at end of file diff --git a/dpsim/examples/cxx/Examples.h b/dpsim/examples/cxx/Examples.h index 9cb9ac9cb6..ed7af6ae35 100644 --- a/dpsim/examples/cxx/Examples.h +++ b/dpsim/examples/cxx/Examples.h @@ -375,6 +375,10 @@ namespace Scenario6 { // adjustable using applyCommandLineArgsOptions String sgType = "4"; Real loadStepEventTime = 10.0; + + // parameters of iterative model + Real maxIter = 25; + Real tolerance = 1e-10; }; struct GridParams { diff --git a/dpsim/include/dpsim/MNASolver.h b/dpsim/include/dpsim/MNASolver.h index 8f5e12dd19..8c748732fc 100644 --- a/dpsim/include/dpsim/MNASolver.h +++ b/dpsim/include/dpsim/MNASolver.h @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -75,6 +76,8 @@ namespace DPsim { CPS::SimSignalComp::List mSimSignalComps; /// Current status of all switches encoded as bitset std::bitset mCurrentSwitchStatus; + /// List of synchronous generators that need iterate to solve the differential equations + CPS::MNASyncGenInterface::List mSyncGen; /// Source vector of known quantities Matrix mRightSideVector; diff --git a/dpsim/include/dpsim/MNASolverDirect.h b/dpsim/include/dpsim/MNASolverDirect.h index 3801fcce61..1df04ac95a 100644 --- a/dpsim/include/dpsim/MNASolverDirect.h +++ b/dpsim/include/dpsim/MNASolverDirect.h @@ -97,6 +97,7 @@ namespace DPsim { using MnaSolver::mSystemMatrixRecomputation; using MnaSolver::hasVariableComponentChanged; using MnaSolver::mNumRecomputations; + using MnaSolver::mSyncGen; using MnaSolver::mFactorizeTimes; using MnaSolver::mSolveTimes; using MnaSolver::mRecomputationTimes; @@ -167,6 +168,9 @@ namespace DPsim { /// log LU decomposition times void logLUTimes() override; + /// ### SynGen Interface ### + int mIter = 0; + // #### MNA Solver Tasks #### /// class SolveTask : public CPS::Task { diff --git a/dpsim/include/dpsim/Simulation.h b/dpsim/include/dpsim/Simulation.h index c0d4c31f90..9e50411f64 100644 --- a/dpsim/include/dpsim/Simulation.h +++ b/dpsim/include/dpsim/Simulation.h @@ -146,6 +146,9 @@ namespace DPsim { /// Prepare schedule for simulation void prepSchedule(); + /// ### SynGen Interface ### + int mMaxIterations = 10; + public: /// Simulation logger CPS::Logger::Log mLog; @@ -177,6 +180,8 @@ namespace DPsim { /// void setDirectLinearSolverConfiguration(const DirectLinearSolverConfiguration& configuration) { mDirectLinearSolverConfiguration = configuration; } /// + void setMaxNumberOfIterations(int maxIterations) {mMaxIterations = maxIterations;} + /// void doInitFromNodesAndTerminals(Bool f = true) { mInitFromNodesAndTerminals = f; } /// void doSplitSubnets(Bool splitSubnets = true) { **mSplitSubnets = splitSubnets; } diff --git a/dpsim/include/dpsim/Solver.h b/dpsim/include/dpsim/Solver.h index 0a3f1a2f65..61af394571 100644 --- a/dpsim/include/dpsim/Solver.h +++ b/dpsim/include/dpsim/Solver.h @@ -120,5 +120,9 @@ namespace DPsim { virtual CPS::Task::List getTasks() = 0; /// Log results virtual void log(Real time, Int timeStepCount) { }; + + /// ### SynGen Interface ### + int mMaxIterations = 10; + void setMaxNumberOfIterations(int maxIterations) {mMaxIterations = maxIterations;} }; } diff --git a/dpsim/src/MNASolver.cpp b/dpsim/src/MNASolver.cpp index 0fa71f341f..d5a5df8a46 100644 --- a/dpsim/src/MNASolver.cpp +++ b/dpsim/src/MNASolver.cpp @@ -311,6 +311,11 @@ void MnaSolver::identifyTopologyObjects() { for (auto comp : mSystem.mComponents) { + auto genComp = std::dynamic_pointer_cast(comp); + if (genComp) { + mSyncGen.push_back(genComp); + } + auto swComp = std::dynamic_pointer_cast(comp); if (swComp) { mSwitches.push_back(swComp); diff --git a/dpsim/src/MNASolverDirect.cpp b/dpsim/src/MNASolverDirect.cpp index c54876c325..bb5e4adba5 100644 --- a/dpsim/src/MNASolverDirect.cpp +++ b/dpsim/src/MNASolverDirect.cpp @@ -218,15 +218,14 @@ void MnaSolverDirect::solve(Real time, Int timeStepCount) { // Reset source vector mRightSideVector.setZero(); - // Add together the right side vector (computed by the components' - // pre-step tasks) + // Add together the right side vector (computed by the components' pre-step tasks) for (auto stamp : mRightVectorStamps) mRightSideVector += *stamp; if (!mIsInInitialization) MnaSolver::updateSwitchStatus(); - if (mSwitchedMatrices.size() > 0) { + if (mSwitchedMatrices.size() > 0){ auto start = std::chrono::steady_clock::now(); **mLeftSideVector = mDirectLinearSolvers[mCurrentSwitchStatus][0]->solve(mRightSideVector); auto end = std::chrono::steady_clock::now(); @@ -234,6 +233,57 @@ void MnaSolverDirect::solve(Real time, Int timeStepCount) { mSolveTimes.push_back(diff.count()); } + // CHECK: Is this really required? Or can operations actually become part of + // correctorStep and mnaPostStep? + for (auto syncGen : mSyncGen) + syncGen->updateVoltage(**mLeftSideVector); + + // Reset number of iterations + mIter = 0; + + // Additional solve steps for iterative models + if (mSyncGen.size() > 0) { + UInt numCompsRequireIter; + do { + // count synchronous generators that require iteration + numCompsRequireIter = 0; + for (auto syncGen : mSyncGen) + if (syncGen->requiresIteration()) + numCompsRequireIter++; + + // recompute solve step if at least one component demands iteration + if (numCompsRequireIter > 0){ + mIter++; + + // Reset source vector + mRightSideVector.setZero(); + + if (!mIsInInitialization) + MnaSolver::updateSwitchStatus(); + + for (auto syncGen : mSyncGen) + syncGen->correctorStep(); + + // Add together the right side vector (computed by the components' pre-step tasks) + for (auto stamp : mRightVectorStamps) + mRightSideVector += *stamp; + + if (mSwitchedMatrices.size() > 0) { + auto start = std::chrono::steady_clock::now(); + **mLeftSideVector = mDirectLinearSolvers[mCurrentSwitchStatus][0]->solve(mRightSideVector); + auto end = std::chrono::steady_clock::now(); + std::chrono::duration diff = end-start; + mSolveTimes.push_back(diff.count()); + } + + // CHECK: Is this really required? Or can operations actually become part of + // correctorStep and mnaPostStep? + for (auto syncGen : mSyncGen) + syncGen->updateVoltage(**mLeftSideVector); + } + } + while (numCompsRequireIter > 0); + } // TODO split into separate task? (dependent on x, updating all v attributes) for (UInt nodeIdx = 0; nodeIdx < mNumNetNodes; ++nodeIdx) diff --git a/dpsim/src/Simulation.cpp b/dpsim/src/Simulation.cpp index 15e1c9034e..a1c94d00c0 100644 --- a/dpsim/src/Simulation.cpp +++ b/dpsim/src/Simulation.cpp @@ -162,6 +162,7 @@ void Simulation::createMNASolver() { solver->doSystemMatrixRecomputation(mSystemMatrixRecomputation); solver->setDirectLinearSolverConfiguration(mDirectLinearSolverConfiguration); solver->initialize(); + solver->setMaxNumberOfIterations(mMaxIterations); } mSolvers.push_back(solver); } @@ -410,9 +411,9 @@ void Simulation::logStepTimes(String logName) { Real stepTimeSum = 0; for (auto meas : mStepTimes) { stepTimeSum += meas; - stepTimeLog->info("{:f}", meas); + stepTimeLog->info("{:.9f}", meas); } - SPDLOG_LOGGER_INFO(mLog, "Average step time: {:.6f}", stepTimeSum / mStepTimes.size()); + SPDLOG_LOGGER_INFO(mLog, "Average step time: {:.9f}", stepTimeSum / mStepTimes.size()); } void Simulation::logLUTimes() { diff --git a/dpsim/src/pybind/BaseComponents.cpp b/dpsim/src/pybind/BaseComponents.cpp index 49e9842b7f..ab4c81550c 100644 --- a/dpsim/src/pybind/BaseComponents.cpp +++ b/dpsim/src/pybind/BaseComponents.cpp @@ -20,4 +20,21 @@ using namespace pybind11::literals; void addBaseComponents(py::module_ mBase) { py::class_>(mBase, "Switch"); py::class_>(mBase, "SwitchPh3"); + + py::class_>(mBase, "MNASyncGenInterface", py::multiple_inheritance()) + .def("set_max_iterations", &CPS::MNASyncGenInterface::setMaxIterations, "max_iter"_a) + .def("set_tolerance", &CPS::MNASyncGenInterface::setTolerance, "tolerance"_a); + + py::class_, std::shared_ptr>, CPS::SimPowerComp>(mBase, "ReducedOrderSynchronGeneratorComplex", py::multiple_inheritance()) + .def("set_base_parameters", &CPS::Base::ReducedOrderSynchronGenerator::setBaseParameters, "nom_power"_a, "nom_voltage"_a, "nom_frequency"_a) + .def("set_initial_values", &CPS::Base::ReducedOrderSynchronGenerator::setInitialValues, "init_complex_electrical_power"_a, "init_mechanical_power"_a, "init_complex_terminal_voltage"_a) + .def("scale_inertia_constant", &CPS::Base::ReducedOrderSynchronGenerator::scaleInertiaConstant, "scaling_factor"_a) + .def("set_model_as_norton_source", &CPS::Base::ReducedOrderSynchronGenerator::setModelAsNortonSource, "model_as_norton_source"_a); + + py::class_, std::shared_ptr>, CPS::SimPowerComp>(mBase, "ReducedOrderSynchronGeneratorReal", py::multiple_inheritance()) + .def("set_base_parameters", &CPS::Base::ReducedOrderSynchronGenerator::setBaseParameters, "nom_power"_a, "nom_voltage"_a, "nom_frequency"_a) + .def("set_initial_values", &CPS::Base::ReducedOrderSynchronGenerator::setInitialValues, "init_complex_electrical_power"_a, "init_mechanical_power"_a, "init_complex_terminal_voltage"_a) + .def("scale_inertia_constant", &CPS::Base::ReducedOrderSynchronGenerator::scaleInertiaConstant, "scaling_factor"_a) + .def("set_model_as_norton_source", &CPS::Base::ReducedOrderSynchronGenerator::setModelAsNortonSource, "model_as_norton_source"_a); + } diff --git a/dpsim/src/pybind/DPComponents.cpp b/dpsim/src/pybind/DPComponents.cpp index ed0379f5f1..7a087edb98 100644 --- a/dpsim/src/pybind/DPComponents.cpp +++ b/dpsim/src/pybind/DPComponents.cpp @@ -123,11 +123,7 @@ void addDPPh1Components(py::module_ mDPPh1) { gen.setReferenceOmega(refOmegaComp->attributeTyped(refOmegaName), refDeltaComp->attributeTyped(refDeltaName)); }, "ref_omega_name"_a="w_r", "ref_omage_comp"_a, "ref_delta_name"_a="delta_r", "ref_delta_comp"_a); - py::class_, CPS::SimPowerComp>(mDPPh1, "ReducedOrderSynchronGeneratorVBR", py::multiple_inheritance()) - .def("set_base_parameters", &CPS::DP::Ph1::ReducedOrderSynchronGeneratorVBR::setBaseParameters, "nom_power"_a, "nom_voltage"_a, "nom_frequency"_a) - .def("set_initial_values", &CPS::DP::Ph1::ReducedOrderSynchronGeneratorVBR::setInitialValues, "init_complex_electrical_power"_a, "init_mechanical_power"_a, "init_complex_terminal_voltage"_a) - .def("scale_inertia_constant", &CPS::DP::Ph1::ReducedOrderSynchronGeneratorVBR::scaleInertiaConstant, "scaling_factor"_a) - .def("set_model_as_current_source", &CPS::DP::Ph1::ReducedOrderSynchronGeneratorVBR::setModelAsCurrentSource, "model_as_current_source"_a); + py::class_, CPS::Base::ReducedOrderSynchronGenerator>(mDPPh1, "ReducedOrderSynchronGeneratorVBR", py::multiple_inheritance()); py::class_, CPS::DP::Ph1::ReducedOrderSynchronGeneratorVBR>(mDPPh1, "SynchronGenerator3OrderVBR", py::multiple_inheritance()) .def(py::init(), "name"_a, "loglevel"_a = CPS::Logger::Level::off) @@ -149,6 +145,21 @@ void addDPPh1Components(py::module_ mDPPh1) { .def("set_operational_parameters_per_unit", py::overload_cast(&CPS::DP::Ph1::SynchronGenerator6bOrderVBR::setOperationalParametersPerUnit), "nom_power"_a, "nom_voltage"_a, "nom_frequency"_a, "H"_a, "Ld"_a, "Lq"_a, "L0"_a, "Ld_t"_a, "Lq_t"_a, "Td0_t"_a, "Tq0_t"_a, "Ld_s"_a, "Lq_s"_a, "Td0_s"_a, "Tq0_s"_a, "Taa"_a=0) .def("connect", &CPS::DP::Ph1::SynchronGenerator6bOrderVBR::connect); + py::class_, CPS::Base::ReducedOrderSynchronGenerator, CPS::MNASyncGenInterface>(mDPPh1, "SynchronGenerator4OrderTPM", py::multiple_inheritance()) + .def(py::init(), "name"_a, "loglevel"_a = CPS::Logger::Level::off) + .def("set_operational_parameters_per_unit", py::overload_cast(&CPS::DP::Ph1::SynchronGenerator4OrderTPM::setOperationalParametersPerUnit), "nom_power"_a, "nom_voltage"_a, "nom_frequency"_a, "H"_a, "Ld"_a, "Lq"_a, "L0"_a, "Ld_t"_a, "Lq_t"_a, "Td0_t"_a, "Tq0_t"_a) + .def("connect", &CPS::DP::Ph1::SynchronGenerator4OrderTPM::connect); + + py::class_, CPS::Base::ReducedOrderSynchronGenerator, CPS::MNASyncGenInterface>(mDPPh1, "SynchronGenerator4OrderPCM", py::multiple_inheritance()) + .def(py::init(), "name"_a, "loglevel"_a = CPS::Logger::Level::off) + .def("set_operational_parameters_per_unit", py::overload_cast(&CPS::DP::Ph1::SynchronGenerator4OrderPCM::setOperationalParametersPerUnit), "nom_power"_a, "nom_voltage"_a, "nom_frequency"_a, "H"_a, "Ld"_a, "Lq"_a, "L0"_a, "Ld_t"_a, "Lq_t"_a, "Td0_t"_a, "Tq0_t"_a) + .def("connect", &CPS::DP::Ph1::SynchronGenerator4OrderPCM::connect); + + py::class_, CPS::Base::ReducedOrderSynchronGenerator, CPS::MNASyncGenInterface>(mDPPh1, "SynchronGenerator6OrderPCM", py::multiple_inheritance()) + .def(py::init(), "name"_a, "loglevel"_a = CPS::Logger::Level::off) + .def("set_operational_parameters_per_unit", py::overload_cast(&CPS::DP::Ph1::SynchronGenerator6OrderPCM::setOperationalParametersPerUnit), "nom_power"_a, "nom_voltage"_a, "nom_frequency"_a, "H"_a, "Ld"_a, "Lq"_a, "L0"_a, "Ld_t"_a, "Lq_t"_a, "Td0_t"_a, "Tq0_t"_a, "Ld_s"_a, "Lq_s"_a, "Td0_s"_a, "Tq0_s"_a, "Taa"_a=0) + .def("connect", &CPS::DP::Ph1::SynchronGenerator6OrderPCM::connect); + py::class_, CPS::SimPowerComp>(mDPPh1, "AvVoltageSourceInverterDQ", py::multiple_inheritance()) .def(py::init(), "name"_a, "loglevel"_a = CPS::Logger::Level::off) .def(py::init(), "uid"_a, "name"_a, "loglevel"_a = CPS::Logger::Level::off, "with_trafo"_a = false) // cppcheck-suppress assignBoolToPointer diff --git a/dpsim/src/pybind/EMTComponents.cpp b/dpsim/src/pybind/EMTComponents.cpp index da36b5e1e3..6c524f0f4e 100644 --- a/dpsim/src/pybind/EMTComponents.cpp +++ b/dpsim/src/pybind/EMTComponents.cpp @@ -138,11 +138,7 @@ void addEMTPh3Components(py::module_ mEMTPh3) { "init_active_power"_a, "init_reactive_power"_a, "init_terminal_volt"_a, "init_volt_angle"_a, "init_mech_power"_a); - py::class_, CPS::SimPowerComp>(mEMTPh3, "ReducedOrderSynchronGeneratorVBR", py::multiple_inheritance()) - .def("set_base_parameters", &CPS::EMT::Ph3::ReducedOrderSynchronGeneratorVBR::setBaseParameters, "nom_power"_a, "nom_voltage"_a, "nom_frequency"_a) - .def("set_initial_values", &CPS::EMT::Ph3::ReducedOrderSynchronGeneratorVBR::setInitialValues, "init_complex_electrical_power"_a, "init_mechanical_power"_a, "init_complex_terminal_voltage"_a) - .def("scale_inertia_constant", &CPS::EMT::Ph3::ReducedOrderSynchronGeneratorVBR::scaleInertiaConstant, "scaling_factor"_a) - .def("set_model_as_current_source", &CPS::EMT::Ph3::ReducedOrderSynchronGeneratorVBR::setModelAsCurrentSource, "model_as_current_source"_a); + py::class_, CPS::Base::ReducedOrderSynchronGenerator>(mEMTPh3, "ReducedOrderSynchronGeneratorVBR", py::multiple_inheritance()); py::class_, CPS::EMT::Ph3::ReducedOrderSynchronGeneratorVBR>(mEMTPh3, "SynchronGenerator3OrderVBR", py::multiple_inheritance()) .def(py::init(), "name"_a, "loglevel"_a = CPS::Logger::Level::off) diff --git a/dpsim/src/pybind/SPComponents.cpp b/dpsim/src/pybind/SPComponents.cpp index d174ba2cd8..c0084eea31 100644 --- a/dpsim/src/pybind/SPComponents.cpp +++ b/dpsim/src/pybind/SPComponents.cpp @@ -117,11 +117,7 @@ void addSPPh1Components(py::module_ mSPPh1) { gen.setReferenceOmega(refOmegaComp->attributeTyped(refOmegaName), refDeltaComp->attributeTyped(refDeltaName)); }, "ref_omega_name"_a="w_r", "ref_omage_comp"_a, "ref_delta_name"_a="delta_r", "ref_delta_comp"_a); - py::class_, CPS::SimPowerComp>(mSPPh1, "ReducedOrderSynchronGeneratorVBR", py::multiple_inheritance()) - .def("set_base_parameters", &CPS::SP::Ph1::ReducedOrderSynchronGeneratorVBR::setBaseParameters, "nom_power"_a, "nom_voltage"_a, "nom_frequency"_a) - .def("set_initial_values", &CPS::SP::Ph1::ReducedOrderSynchronGeneratorVBR::setInitialValues, "init_complex_electrical_power"_a, "init_mechanical_power"_a, "init_complex_terminal_voltage"_a) - .def("scale_inertia_constant", &CPS::SP::Ph1::ReducedOrderSynchronGeneratorVBR::scaleInertiaConstant, "scaling_factor"_a) - .def("set_model_as_current_source", &CPS::SP::Ph1::ReducedOrderSynchronGeneratorVBR::setModelAsCurrentSource, "model_as_current_source"_a); + py::class_, CPS::Base::ReducedOrderSynchronGenerator>(mSPPh1, "ReducedOrderSynchronGeneratorVBR", py::multiple_inheritance()); py::class_, CPS::SP::Ph1::ReducedOrderSynchronGeneratorVBR>(mSPPh1, "SynchronGenerator3OrderVBR", py::multiple_inheritance()) .def(py::init(), "name"_a, "loglevel"_a = CPS::Logger::Level::off) diff --git a/examples/Notebooks/Circuits/Compare_DP_SMIB_ReducedOrderSG_VBR_PCM_LoadStep.ipynb b/examples/Notebooks/Circuits/Compare_DP_SMIB_ReducedOrderSG_VBR_PCM_LoadStep.ipynb new file mode 100644 index 0000000000..6688624907 --- /dev/null +++ b/examples/Notebooks/Circuits/Compare_DP_SMIB_ReducedOrderSG_VBR_PCM_LoadStep.ipynb @@ -0,0 +1,556 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Validation of PCM and VBR models against each other with DP SMIB Load Step" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Load Libraries" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import re\n", + "import numpy as np\n", + "import math\n", + "import os\n", + "import subprocess\n", + "import pickle\n", + "from villas.dataprocessing.readtools import *\n", + "from villas.dataprocessing.timeseries import *\n", + "import matplotlib.pyplot as plt\n", + "import dpsimpy\n", + "\n", + "#%matplotlib widget" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Simulation parameters" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "root_path = subprocess.Popen(['git', 'rev-parse', '--show-toplevel'], stdout=subprocess.PIPE).communicate()[0].rstrip().decode('utf-8')\n", + "path_exec = root_path + '/build/dpsim/examples/cxx/'\n", + "\n", + "name_executable_vbr = 'DP_SMIB_ReducedOrderSG_LoadStep'\n", + "name_vbr = \"DP_SMIB_ReducedOrderSG_VBR_LoadStep\"\n", + "\n", + "name_executable_pcm = 'DP_SMIB_ReducedOrderSGIterative_LoadStep'\n", + "name_pcm = \"DP_SMIB_ReducedOrderSGIterativePCM_LoadStep\"\n", + "\n", + "# times in s\n", + "end_time = 1\n", + "load_step_time = 0.1\n", + "roi_begin = 0.1\n", + "roi_end = 1\n", + "\n", + "# config params\n", + "timestep_vbr = 0.1e-3\n", + "timestep_pcm = 10e-6\n", + "max_iter = 10\n", + "tolerance = 1e-6\n", + "\n", + "roi_begin_idx = int(roi_begin/timestep_pcm)\n", + "roi_end_idx = int(roi_end/timestep_pcm)\n", + "\n", + "logs_path = 'logs'\n", + "if not os.path.exists(logs_path):\n", + " os.mkdir(logs_path)\n", + " \n", + "var_name = 'SynGen.Te'\n", + "\n", + "te_ref = 0.5454986888690558" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Parametrization" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from math import sqrt, pi\n", + "\n", + "### Power System\n", + "nominal_voltage_hv = 230e3\n", + "nominal_voltage_mv = 24e3\n", + "ratio = nominal_voltage_mv/nominal_voltage_hv\n", + "frequency = 60\n", + "omega = 2 * pi * frequency\n", + "\n", + "### Generator\n", + "nom_power = 555e6\n", + "set_point_active_power = 300e6\n", + "set_point_voltage = 1.05*nominal_voltage_mv\n", + "H = 3.7\n", + "Td0_t = 8.0669\n", + "Td0_s = 0.0300\n", + "Td_t = 1.3368\n", + "Td_s = 0.0230\n", + "Ld_t = 0.2999\n", + "Ld_s = 0.2299\n", + "Tq0_t = 0.9991\n", + "Tq0_s = 0.0700\n", + "Lq_t = 0.6500\n", + "Lq_s = 0.2500\n", + "Ld = 1.8099\n", + "Lq = 1.7600\n", + "L0 = 0.15\n", + "Taa = 0\n", + " \n", + "### PiLine parameters calculated from CIGRE Benchmark system\n", + "line_length = 100\n", + "line_resistance = 1.27e-4 * 529 * line_length * pow(ratio,2)\n", + "line_inductance = 9.05e-4 * 529 / omega * line_length * pow(ratio,2)\n", + "line_capacitance = (1.81e-3 / 529) / omega * line_length / pow(ratio,2)\n", + "line_conductance = 0.0048\n", + "\n", + "### Load step\n", + "load_step_activePower = 100e6; " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Run powerflow for Initialization" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "sim_name_pf = \"DP_SMIB_ReducedOrderSGIterative_LoadStep_PF\"\n", + "dpsimpy.Logger.set_log_dir(\"logs/\" + sim_name_pf)\n", + "\n", + "### Nodes\n", + "gnd_pf = dpsimpy.sp.SimNode.gnd\n", + "n1_pf = dpsimpy.sp.SimNode('n1_pf', dpsimpy.PhaseType.Single)\n", + "n2_pf = dpsimpy.sp.SimNode('n2_pf', dpsimpy.PhaseType.Single)\n", + "\n", + "### Components\n", + "\n", + "# syncrhon generator\n", + "gen_pf = dpsimpy.sp.ph1.SynchronGenerator('gen_pf', dpsimpy.LogLevel.debug)\n", + "gen_pf.set_parameters(rated_apparent_power=nom_power, rated_voltage=nominal_voltage_mv, \n", + " set_point_active_power=set_point_active_power, set_point_voltage=set_point_voltage, \n", + " powerflow_bus_type=dpsimpy.PowerflowBusType.PV)\n", + "gen_pf.set_base_voltage(nominal_voltage_mv)\n", + "gen_pf.modify_power_flow_bus_type(dpsimpy.PowerflowBusType.PV)\n", + "\n", + "# Grid bus as Slack\n", + "extnet_pf = dpsimpy.sp.ph1.NetworkInjection(\"Slack\", dpsimpy.LogLevel.debug)\n", + "extnet_pf.set_parameters(nominal_voltage_mv)\n", + "extnet_pf.set_base_voltage(nominal_voltage_mv)\n", + "extnet_pf.modify_power_flow_bus_type(dpsimpy.PowerflowBusType.VD)\n", + "\n", + "# PiLine\n", + "pi_line_pf = dpsimpy.sp.ph1.PiLine('Pi_Line_pf', dpsimpy.LogLevel.debug)\n", + "pi_line_pf.set_parameters(R=line_resistance, L=line_inductance, C=line_capacitance, G=line_conductance)\n", + "pi_line_pf.set_base_voltage(nominal_voltage_mv)\n", + "\n", + "### Connections\n", + "gen_pf.connect([n1_pf])\n", + "pi_line_pf.connect([n2_pf, n1_pf])\n", + "extnet_pf.connect([n2_pf])\n", + "\n", + "### Define system topology\n", + "system_pf = dpsimpy.SystemTopology(frequency, [n1_pf, n2_pf], [gen_pf, pi_line_pf, extnet_pf])\n", + "\n", + "# Logging\n", + "logger_pf = dpsimpy.Logger(sim_name_pf)\n", + "logger_pf.log_attribute('n1.v', 'v', n1_pf)\n", + "logger_pf.log_attribute('n2.v', 'v', n2_pf)\n", + "logger_pf.log_attribute('p_inj', 'p_inj', extnet_pf)\n", + "logger_pf.log_attribute('q_inj', 'q_inj', extnet_pf)\n", + "\n", + "sim_pf = dpsimpy.Simulation(sim_name_pf, dpsimpy.LogLevel.debug)\n", + "sim_pf.set_system(system_pf)\n", + "sim_pf.set_domain(dpsimpy.Domain.SP)\n", + "sim_pf.set_solver(dpsimpy.Solver.NRP)\n", + "sim_pf.set_solver_component_behaviour(dpsimpy.SolverBehaviour.Initialization)\n", + "sim_pf.set_time_step(0.1)\n", + "sim_pf.set_final_time(0.5)\n", + "sim_pf.add_logger(logger_pf)\n", + "sim_pf.run()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## DPSim Topology for DP simulations" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def dp_reducedOrderSG_loadStep(sim_pf, gen_pf, gen_model=\"4TPM\", event_time=0.1, final_time=0.5, time_step=10e-6, max_iter=10, tolerance=1e-6):\n", + " \n", + " # ## Extract relevant powerflow results\n", + " init_electrical_power = gen_pf.get_apparent_power()\n", + " init_mechanical_power = gen_pf.get_apparent_power().real\n", + "\n", + " ### DPsim DP simulation\n", + " name = \"DP_SMIB_ReducedOrderSGIterative_LoadStep_\" + gen_model\n", + " dpsimpy.Logger.set_log_dir(\"logs/\" + name)\n", + "\n", + " ### Nodes\n", + " gnd = dpsimpy.dp.SimNode.gnd\n", + " n1 = dpsimpy.dp.SimNode('n1', dpsimpy.PhaseType.Single) \n", + " n1.set_initial_voltage(sim_pf.get_idobj_attr(n1_pf.name(), 'v').get()[0])\n", + "\n", + " n2 = dpsimpy.dp.SimNode('n2', dpsimpy.PhaseType.Single)\n", + " n2.set_initial_voltage(sim_pf.get_idobj_attr(n2_pf.name(), 'v').get()[0])\n", + "\n", + "\n", + " ### Components\n", + "\n", + " # syncrhon generator\n", + " gen = None\n", + " if (gen_model==\"4VBR\"):\n", + " gen = dpsimpy.dp.ph1.SynchronGenerator4OrderVBR('gen', dpsimpy.LogLevel.debug)\n", + " gen.set_operational_parameters_per_unit(nom_power=nom_power, nom_voltage=nominal_voltage_mv, nom_frequency=frequency, \n", + " H=H, Ld=Ld, Lq=Lq, L0=L0, Ld_t=Ld_t, Lq_t=Lq_t, Td0_t=Td0_t, Tq0_t=Tq0_t)\t\t\n", + " elif (gen_model==\"6VBR\"):\n", + " gen = dpsimpy.dp.ph1.SynchronGenerator6bOrderVBR('gen', dpsimpy.LogLevel.debug)\n", + " gen.set_operational_parameters_per_unit(nom_power=nom_power, nom_voltage=nominal_voltage_mv, nom_frequency=frequency, \n", + " H=H, Ld=Ld, Lq=Lq, L0=L0, Ld_t=Ld_t, Lq_t=Lq_t, Td0_t=Td0_t, Tq0_t=Tq0_t,\n", + " Ld_s=Ld_s, Lq_s=Lq_s, Td0_s=Td0_s, Tq0_s=Tq0_s)\n", + " elif (gen_model==\"4TPM\"):\n", + " gen = dpsimpy.dp.ph1.SynchronGenerator4OrderTPM('gen', dpsimpy.LogLevel.debug)\n", + " gen.set_operational_parameters_per_unit(nom_power=nom_power, nom_voltage=nominal_voltage_mv, nom_frequency=frequency, \n", + " H=H, Ld=Ld, Lq=Lq, L0=L0, Ld_t=Ld_t, Lq_t=Lq_t, Td0_t=Td0_t, Tq0_t=Tq0_t)\n", + " gen.set_max_iterations(max_iter=max_iter)\n", + " gen.set_tolerance(tolerance=tolerance)\n", + " elif (gen_model==\"4PCM\"):\n", + " gen = dpsimpy.dp.ph1.SynchronGenerator4OrderPCM('gen', dpsimpy.LogLevel.debug)\n", + " gen.set_operational_parameters_per_unit(nom_power=nom_power, nom_voltage=nominal_voltage_mv, nom_frequency=frequency, \n", + " H=H, Ld=Ld, Lq=Lq, L0=L0, Ld_t=Ld_t, Lq_t=Lq_t, Td0_t=Td0_t, Tq0_t=Tq0_t)\n", + " gen.set_max_iterations(max_iter=max_iter)\n", + " gen.set_tolerance(tolerance=tolerance)\t\t\n", + " elif (gen_model==\"6PCM\"):\n", + " gen = dpsimpy.dp.ph1.SynchronGenerator6OrderPCM('gen', dpsimpy.LogLevel.debug)\n", + " gen.set_operational_parameters_per_unit(nom_power=nom_power, nom_voltage=nominal_voltage_mv, nom_frequency=frequency, \n", + " H=H, Ld=Ld, Lq=Lq, L0=L0, Ld_t=Ld_t, Lq_t=Lq_t, Td0_t=Td0_t, Tq0_t=Tq0_t,\n", + " Ld_s=Ld_s, Lq_s=Lq_s, Td0_s=Td0_s, Tq0_s=Tq0_s)\n", + " gen.set_max_iterations(max_iter=max_iter)\n", + " gen.set_tolerance(tolerance=tolerance)\n", + " gen.set_initial_values(init_complex_electrical_power=init_electrical_power, init_mechanical_power=init_mechanical_power, \n", + " init_complex_terminal_voltage=sim_pf.get_idobj_attr(n1_pf.name(), 'v').get()[0][0])\n", + "\n", + " # Switch\n", + " switch = dpsimpy.dp.ph1.Switch('Load_Add_Switch_', dpsimpy.LogLevel.debug)\n", + " resistance = abs(sim_pf.get_idobj_attr(n1_pf.name(), 'v').get()[0][0])**2 / load_step_activePower\n", + " switch.set_parameters(1e9, resistance)\n", + " switch.open()\n", + "\n", + " # pi line\n", + " pi_line = dpsimpy.dp.ph1.PiLine('PiLine', dpsimpy.LogLevel.debug)\n", + " pi_line.set_parameters(series_resistance=line_resistance,\n", + " series_inductance=line_inductance,\n", + " parallel_capacitance=line_capacitance,\n", + " parallel_conductance=line_conductance)\n", + "\n", + " # Slack\n", + " slack = dpsimpy.dp.ph1.NetworkInjection('slack', dpsimpy.LogLevel.debug)\n", + " slack.set_parameters(V_ref=nominal_voltage_mv)\n", + " \n", + " ### Connections\n", + " gen.connect([n1])\n", + " switch.connect([gnd, n1])\n", + " pi_line.connect([n1, n2])\n", + " slack.connect([n2])\n", + " \n", + " ### Define system topology\n", + " system = dpsimpy.SystemTopology(frequency, [n1, n2], [gen, pi_line, slack, switch])\n", + "\n", + " ### Logging\n", + " logger = dpsimpy.Logger(name)\n", + " logger.log_attribute('Te', 'Te', gen)\n", + "\n", + " \n", + " ### Simulation\n", + " sim = dpsimpy.Simulation(name, dpsimpy.LogLevel.debug)\n", + " sim.set_system(system)\n", + " sim.do_init_from_nodes_and_terminals(True)\n", + " sim.set_domain(dpsimpy.Domain.DP)\n", + " sim.set_direct_solver_implementation(dpsimpy.DirectLinearSolverImpl.SparseLU)\n", + " sim.set_time_step(time_step)\n", + " sim.set_final_time(final_time)\n", + " if (gen_model==\"4VBR\" or gen_model==\"6VBR\"):\n", + " sim.do_system_matrix_recomputation(True)\n", + " \n", + " sw_event_1 = dpsimpy.event.SwitchEvent(event_time, switch, True)\n", + " sim.add_event(sw_event_1)\n", + " \n", + " sim.add_logger(logger)\n", + " sim.run()\n", + " \n", + " return name" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Run DPSim simulations (DP Domain)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 4th Order VBR" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "log_name = dp_reducedOrderSG_loadStep(sim_pf, gen_pf, gen_model=\"4VBR\", event_time=load_step_time, final_time=end_time, time_step=timestep_vbr)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# read Simulink log file\n", + "\n", + "file_path = os.getcwd() + \"/logs/\" + log_name + \"/\" + log_name + \".csv\"\n", + "ts_dpsim_4VBR = read_timeseries_dpsim(file_path)\n", + "ts_dpsim_4VBR['Te'] = TimeSeries('Te', ts_dpsim_4VBR['Te'].interpolate(timestep_pcm).time, ts_dpsim_4VBR['Te'].interpolate(timestep_pcm).values)\n", + "ts_dpsim_4VBR_roi={}\n", + "ts_dpsim_4VBR_roi['Te'] = TimeSeries('Te', ts_dpsim_4VBR['Te'].time[roi_begin_idx:roi_end_idx], ts_dpsim_4VBR['Te'].values[roi_begin_idx:roi_end_idx])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 6th Order VBR" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "log_name = dp_reducedOrderSG_loadStep(sim_pf, gen_pf, gen_model=\"6VBR\", event_time=load_step_time, final_time=end_time, time_step=timestep_vbr)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# read Simulink log file\n", + "\n", + "file_path = os.getcwd() + \"/logs/\" + log_name + \"/\" + log_name + \".csv\"\n", + "ts_dpsim_6VBR = read_timeseries_dpsim(file_path)\n", + "ts_dpsim_6VBR['Te'] = TimeSeries('Te', ts_dpsim_6VBR['Te'].interpolate(timestep_pcm).time, ts_dpsim_6VBR['Te'].interpolate(timestep_pcm).values)\n", + "ts_dpsim_6VBR_roi={}\n", + "ts_dpsim_6VBR_roi['Te'] = TimeSeries('Te', ts_dpsim_6VBR['Te'].time[roi_begin_idx:roi_end_idx], ts_dpsim_6VBR['Te'].values[roi_begin_idx:roi_end_idx])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 4th Order PCM" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "log_name = dp_reducedOrderSG_loadStep(sim_pf, gen_pf, gen_model=\"4PCM\", event_time=load_step_time, final_time=end_time, time_step=timestep_pcm, max_iter=max_iter, tolerance=tolerance)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# read Simulink log file\n", + "\n", + "file_path = os.getcwd() + \"/logs/\" + log_name + \"/\" + log_name + \".csv\"\n", + "ts_dpsim_4PCM = read_timeseries_dpsim(file_path)\n", + "ts_dpsim_4PCM_roi={}\n", + "ts_dpsim_4PCM_roi['Te'] = TimeSeries('Te', ts_dpsim_4PCM['Te'].time[roi_begin_idx:roi_end_idx], ts_dpsim_4PCM['Te'].values[roi_begin_idx:roi_end_idx])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 6th Order PCM" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "log_name = dp_reducedOrderSG_loadStep(sim_pf, gen_pf, gen_model=\"6PCM\", event_time=load_step_time, final_time=end_time, time_step=timestep_pcm, max_iter=max_iter, tolerance=tolerance)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# read Simulink log file\n", + "file_path = os.getcwd() + \"/logs/\" + log_name + \"/\" + log_name + \".csv\"\n", + "ts_dpsim_6PCM = read_timeseries_dpsim(file_path)\n", + "ts_dpsim_6PCM_roi={}\n", + "ts_dpsim_6PCM_roi['Te'] = TimeSeries('Te', ts_dpsim_6PCM['Te'].time[roi_begin_idx:roi_end_idx], ts_dpsim_6PCM['Te'].values[roi_begin_idx:roi_end_idx])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Compare Results" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 4 Order" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "plt.figure()\n", + "plt.plot(ts_dpsim_4VBR['Te'].time, ts_dpsim_4VBR['Te'].values, label='VBR')\n", + "plt.plot(ts_dpsim_4PCM['Te'].time, ts_dpsim_4PCM['Te'].values, linestyle='--', label='4 Order PCM - MaxIter 10')\n", + "plt.legend()\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "rmse = ts_dpsim_4VBR_roi['Te'].rmse(ts_dpsim_4PCM_roi['Te'], ts_dpsim_4VBR_roi['Te'])/te_ref*100\n", + "print('RMSE of PCM 4 Order = {}%'.format(rmse))\n", + "assert(rmse<0.449)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 6 Order" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "plt.figure()\n", + "plt.plot(ts_dpsim_6VBR['Te'].time, ts_dpsim_6VBR['Te'].values, label='6 Order VBR')\n", + "plt.plot(ts_dpsim_6PCM['Te'].time, ts_dpsim_6PCM['Te'].values, linestyle='--', label='6 Order PCM - MaxIter 10')\n", + "plt.legend()\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "rmse = ts_dpsim_6VBR_roi['Te'].rmse(ts_dpsim_6PCM_roi['Te'], ts_dpsim_6VBR_roi['Te'])/te_ref*100\n", + "print('RMSE of PCM 6 Order = {}%'.format(rmse))\n", + "assert(rmse<0.381)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.13" + }, + "tests": { + "skip": true + }, + "vscode": { + "interpreter": { + "hash": "e7370f93d1d0cde622a1f8e1c04877d8463912d04d973331ad4851f04de6915a" + } + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/examples/Notebooks/Circuits/Compare_DP_SMIB_ReducedOrderSG_VBR_TPM_LoadStep.ipynb b/examples/Notebooks/Circuits/Compare_DP_SMIB_ReducedOrderSG_VBR_TPM_LoadStep.ipynb new file mode 100644 index 0000000000..c1205244e5 --- /dev/null +++ b/examples/Notebooks/Circuits/Compare_DP_SMIB_ReducedOrderSG_VBR_TPM_LoadStep.ipynb @@ -0,0 +1,297 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Validation of TPM and VBR models against each other with DP SMIB Load Step" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Load Libraries" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import re\n", + "import numpy as np\n", + "import math\n", + "import os\n", + "import subprocess\n", + "import pickle\n", + "from villas.dataprocessing.readtools import *\n", + "from villas.dataprocessing.timeseries import *\n", + "import matplotlib.pyplot as plt\n", + "\n", + "#%matplotlib widget" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Simulation parameters" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "root_path = subprocess.Popen(['git', 'rev-parse', '--show-toplevel'], stdout=subprocess.PIPE).communicate()[0].rstrip().decode('utf-8')\n", + "path_exec = root_path + '/build/dpsim/examples/cxx/'\n", + "\n", + "name_executable_vbr = 'DP_SMIB_ReducedOrderSG_LoadStep'\n", + "name_vbr = \"DP_SMIB_ReducedOrderSG_VBR_LoadStep\"\n", + "\n", + "name_executable_tpm = 'DP_SMIB_ReducedOrderSGIterative_LoadStep'\n", + "name_tpm = \"DP_SMIB_ReducedOrderSGIterative_LoadStep\"\n", + "\n", + "# times in s\n", + "timestep = 1e-3\n", + "load_step_time = 10.0\n", + "roi_begin = 10.0\n", + "roi_end = 12.0\n", + "\n", + "# tpm config params\n", + "max_iter_array = [0, 1, 2, 5, 10, 20]\n", + "tolerance = 1e-10\n", + "sg_model = '4TPM'\n", + "\n", + "\n", + "roi_begin_idx = int(roi_begin/timestep)\n", + "roi_end_idx = int(roi_end/timestep)\n", + "\n", + "timestep_str = '{:1.6f}'.format(timestep)\n", + "\n", + "logs_path = 'logs'\n", + "if not os.path.exists(logs_path):\n", + " os.mkdir(logs_path)\n", + " \n", + "var_name = 'SynGen.Te'\n", + "\n", + "te_ref = 0.5454986888690558" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## VBR Simulations" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "options_list_vbr = []\n", + "options_list_vbr.append('loadStepEventTime=' + str(load_step_time))\n", + "options_list_vbr.append('TimeStep=' + timestep_str)\n", + "options_list_vbr.append('SimName=' + name_vbr)\n", + "\n", + "args_options_list_vbr = []\n", + "for option in options_list_vbr:\n", + " args_options_list_vbr.extend(['--option', option])\n", + "\n", + "simVBR = subprocess.Popen([path_exec + name_executable_vbr] + args_options_list_vbr, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)\n", + "print(simVBR.communicate()[0].decode())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "results_path_vbr = logs_path + \"/\" + name_vbr + \"/\" + name_vbr + \".csv\"\n", + "ts_vbr = read_timeseries_dpsim(results_path_vbr)[var_name]\n", + "ts_vbr_roi = TimeSeries(ts_vbr.name+'roi',ts_vbr.time[roi_begin_idx:roi_end_idx],ts_vbr.values[roi_begin_idx:roi_end_idx])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## TPM Simulations (with different maximum iterations)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "options_list_tpm = []\n", + "options_list_tpm.append('loadStepEventTime=' + str(load_step_time))\n", + "options_list_tpm.append('TimeStep=' + timestep_str)\n", + "options_list_tpm.append('Tolerance=' + str(tolerance))\n", + "options_list_tpm.append('SGModel=' + str(sg_model)) \n", + "\n", + "args_options_list_tpm = []\n", + "for option in options_list_tpm:\n", + " args_options_list_tpm.extend(['--option', option])\n", + "\n", + "for max_iter in max_iter_array:\n", + " name_iter = name_tpm + '_MaxIter' + str(max_iter)\n", + " simTPM = subprocess.Popen([path_exec + name_executable_tpm, '--option', 'SimName=' + name_iter, '--option', 'MaxIter=' + str(max_iter)] + args_options_list_tpm, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)\n", + " print(simTPM.communicate()[0].decode())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "ts_tpm = []\n", + "ts_tpm_roi = []\n", + "for max_iter in max_iter_array:\n", + " name_iter = name_tpm + '_MaxIter' + str(max_iter)\n", + " results_path_iter = logs_path + \"/\" + name_iter + \"/\" + name_iter + \".csv\"\n", + " ts_tpm.append(read_timeseries_dpsim(results_path_iter)[var_name])\n", + " ts_tpm_roi.append(TimeSeries(ts_tpm[-1].name+'roi',ts_tpm[-1].time[roi_begin_idx:roi_end_idx],ts_tpm[-1].values[roi_begin_idx:roi_end_idx]))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Comparative Plots" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Complete" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "plt.figure()\n", + "plt.plot(ts_vbr.time, ts_vbr.values, label='VBR')\n", + "\n", + "for max_iter in max_iter_array:\n", + " max_iter_idx = max_iter_array.index(max_iter)\n", + " plt.plot(ts_tpm[max_iter_idx].time, ts_tpm[max_iter_idx].values, linestyle='--', label='TPM - MaxIter' + str(max_iter))\n", + "\n", + "plt.legend()\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### ROI" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "plt.figure()\n", + "plt.plot(ts_vbr_roi.time, ts_vbr_roi.values, label='VBR')\n", + "\n", + "for max_iter in max_iter_array:\n", + " max_iter_idx = max_iter_array.index(max_iter)\n", + " plt.plot(ts_tpm_roi[max_iter_idx].time, ts_tpm_roi[max_iter_idx].values, linestyle='--', label='TPM - MaxIter' + str(max_iter))\n", + "\n", + "plt.legend()\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## RMSE Calculation" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "rmse_list = []\n", + "for max_iter in max_iter_array:\n", + " max_iter_idx = max_iter_array.index(max_iter)\n", + " rmse_list.append(ts_vbr_roi.rmse(ts_tpm_roi[max_iter_idx], ts_vbr_roi)/te_ref*100)\n", + " print('RMSE of TPM with MaxIter={}: {}%'.format(max_iter,rmse_list[-1]))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Assertions" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert(rmse_list[0]<0.32)\n", + "assert(rmse_list[1]<0.54)\n", + "assert(rmse_list[2]<0.31)\n", + "assert(rmse_list[3]<0.07)\n", + "assert(rmse_list[4]<0.003)\n", + "assert(rmse_list[5]<2.4e-5)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.13" + }, + "vscode": { + "interpreter": { + "hash": "e7370f93d1d0cde622a1f8e1c04877d8463912d04d973331ad4851f04de6915a" + } + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/examples/Notebooks/Circuits/SP_Validation_ReducedOrderSG_VBR_SMIB_Fault.ipynb b/examples/Notebooks/Circuits/SP_Validation_ReducedOrderSG_VBR_SMIB_Fault_withPSAT.ipynb similarity index 100% rename from examples/Notebooks/Circuits/SP_Validation_ReducedOrderSG_VBR_SMIB_Fault.ipynb rename to examples/Notebooks/Circuits/SP_Validation_ReducedOrderSG_VBR_SMIB_Fault_withPSAT.ipynb diff --git a/examples/Notebooks/Grids/WSCC_9-bus_dyn.ipynb b/examples/Notebooks/Grids/DP_WSCC9bus_SGTrStab.ipynb similarity index 98% rename from examples/Notebooks/Grids/WSCC_9-bus_dyn.ipynb rename to examples/Notebooks/Grids/DP_WSCC9bus_SGTrStab.ipynb index 1a48645090..22df4fdce6 100644 --- a/examples/Notebooks/Grids/WSCC_9-bus_dyn.ipynb +++ b/examples/Notebooks/Grids/DP_WSCC9bus_SGTrStab.ipynb @@ -4,7 +4,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Dynamic WSCC 9-bus System " + "# DP Simulation of WSCC 9-bus System with Transient Stability Synchronous Generator" ] }, { @@ -306,7 +306,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.12" + "version": "3.9.13" } }, "nbformat": 4, diff --git a/examples/Notebooks/Grids/WSCC_9-bus_dyn_switch.ipynb b/examples/Notebooks/Grids/DP_WSCC9bus_SGTrStab_Switch.ipynb similarity index 97% rename from examples/Notebooks/Grids/WSCC_9-bus_dyn_switch.ipynb rename to examples/Notebooks/Grids/DP_WSCC9bus_SGTrStab_Switch.ipynb index 1197f064b6..9fc74088e8 100644 --- a/examples/Notebooks/Grids/WSCC_9-bus_dyn_switch.ipynb +++ b/examples/Notebooks/Grids/DP_WSCC9bus_SGTrStab_Switch.ipynb @@ -4,7 +4,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Dynamic WSCC 9-bus System with Switch" + "# DP Simulation of WSCC 9-bus System with Transient Stability Synchronous Generator and Switch" ] }, { @@ -21,7 +21,7 @@ " content = requests.get(url, stream=True).content\n", " out_file.write(content)\n", "\n", - "url = 'https://git.rwth-aachen.de/acs/public/grid-data/cim-grid-data/-/raw/master/WSCC-09/WSCC-09_RX_Dyn/WSCC-09_RX'\n", + "url = 'https://raw.githubusercontent.com/dpsim-simulator/cim-grid-data/master/WSCC-09/WSCC-09_RX_Dyn/WSCC-09_RX'\n", "filename = 'WSCC-09'\n", "download_grid_data(filename+'_EQ.xml', url+'_EQ.xml')\n", "download_grid_data(filename+'_TP.xml', url+'_TP.xml')\n", @@ -284,7 +284,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3.9.2 64-bit", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, @@ -298,7 +298,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.2" + "version": "3.9.13" }, "vscode": { "interpreter": { diff --git a/examples/Notebooks/Grids/WSCC_9-bus.ipynb b/examples/Notebooks/Grids/DP_WSCC9bus_SGVoltageSource.ipynb similarity index 96% rename from examples/Notebooks/Grids/WSCC_9-bus.ipynb rename to examples/Notebooks/Grids/DP_WSCC9bus_SGVoltageSource.ipynb index 9d9408fee0..6747701970 100644 --- a/examples/Notebooks/Grids/WSCC_9-bus.ipynb +++ b/examples/Notebooks/Grids/DP_WSCC9bus_SGVoltageSource.ipynb @@ -4,7 +4,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# WSCC 9-bus System Static Test" + "# DP Simulation of WSCC 9-bus System with Synchronous Generator as Voltage Source" ] }, { @@ -21,8 +21,8 @@ " content = requests.get(url, stream=True).content\n", " out_file.write(content)\n", "\n", - "url = 'https://git.rwth-aachen.de/acs/public/grid-data/cim-grid-data/-/raw/master/WSCC-09/WSCC-09_RX/WSCC-09_RX'\n", - "filename = 'WSCC-09'\n", + "url = 'https://raw.githubusercontent.com/dpsim-simulator/cim-grid-data/master/WSCC-09/WSCC-09_RX/WSCC-09_RX'\n", + "filename = 'WSCC-09_RX_WithoutSyngenParams'\n", "download_grid_data(filename+'_EQ.xml', url+'_EQ.xml')\n", "download_grid_data(filename+'_TP.xml', url+'_TP.xml')\n", "download_grid_data(filename+'_SV.xml', url+'_SV.xml')\n", @@ -313,7 +313,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.12" + "version": "3.9.13" } }, "nbformat": 4, diff --git a/examples/Notebooks/Grids/PF_CIGRE_MV_withDG.ipynb b/examples/Notebooks/Grids/PF_CIGRE_MV_withDG.ipynb index 9dbb958296..895c85570c 100644 --- a/examples/Notebooks/Grids/PF_CIGRE_MV_withDG.ipynb +++ b/examples/Notebooks/Grids/PF_CIGRE_MV_withDG.ipynb @@ -35,7 +35,7 @@ " content = requests.get(url, stream=True).content\n", " out_file.write(content)\n", "\n", - "url = 'https://git.rwth-aachen.de/acs/public/grid-data/cim-grid-data/-/raw/master/CIGRE_MV/NEPLAN/CIGRE_MV_no_tapchanger_noLoad1_LeftFeeder_With_LoadFlow_Results/Rootnet_FULL_NE_28J17h'\n", + "url = 'https://raw.githubusercontent.com/dpsim-simulator/cim-grid-data/master/CIGRE_MV/NEPLAN/CIGRE_MV_no_tapchanger_noLoad1_LeftFeeder_With_LoadFlow_Results/Rootnet_FULL_NE_28J17h'\n", "filename = 'CIGRE-MV'\n", "download_grid_data(filename+'_EQ.xml', url+'_EQ.xml')\n", "download_grid_data(filename+'_TP.xml', url+'_TP.xml')\n", diff --git a/examples/Notebooks/Grids/SP_WSCC9bus_SG4thOrder_Fault_PSAT_Validation.ipynb b/examples/Notebooks/Grids/SP_WSCC9bus_SG4thOrder_Fault_PSAT_Validation.ipynb new file mode 100644 index 0000000000..e0e02be227 --- /dev/null +++ b/examples/Notebooks/Grids/SP_WSCC9bus_SG4thOrder_Fault_PSAT_Validation.ipynb @@ -0,0 +1,369 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# SP Simulation of WSCC 9-bus System with 4th Order Synchronous Generator and PSAT Validation" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import villas.dataprocessing.readtools as rt\n", + "import villas.dataprocessing.plottools as pt\n", + "from villas.dataprocessing.timeseries import TimeSeries as ts\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "\n", + "import re\n", + "import math\n", + "import os\n", + "import subprocess\n", + "import requests\n", + "import urllib.request\n", + "\n", + "def download_grid_data(name, url):\n", + " with open(name, 'wb') as out_file:\n", + " content = requests.get(url, stream=True).content\n", + " out_file.write(content)\n", + "\n", + "#%matplotlib widget\n", + "\n", + "PEAK1PH_TO_RMS3PH=np.sqrt(3./2.)\n", + "\n", + "name_exec = 'SP_WSCC9bus_SGReducedOrderVBR'\n", + "\n", + "order_names_list = ['4th'] # ['3rd', '4th', '6th']\n", + "order_options_list = ['sgType=4'] # ['sgType=3', 'sgType=4', 'sgType=6b']\n", + "sim_names_list = [name_exec + '_' + order_name for order_name in order_names_list]\n", + "num_orders = len(order_names_list)\n", + "\n", + "timestep = 1e-3\n", + "duration = 30\n", + "\n", + "view_time_interval = [0.1,1.0]\n", + "\n", + "root_path = subprocess.Popen(['git', 'rev-parse', '--show-toplevel'], stdout=subprocess.PIPE).communicate()[0].rstrip().decode('utf-8')\n", + "\n", + "path_exec = root_path + '/build/dpsim/examples/cxx/'\n", + "\n", + "cim_file = 'WSCC-09_Dyn_Fourth'\n", + "\n", + "cim_url = 'https://raw.githubusercontent.com/dpsim-simulator/cim-grid-data/master/WSCC-09/WSCC-09_Dyn_Fourth/WSCC-09_Dyn_Fourth'\n", + "download_grid_data(cim_file+'_EQ.xml', cim_url+'_EQ.xml')\n", + "download_grid_data(cim_file+'_TP.xml', cim_url+'_TP.xml')\n", + "download_grid_data(cim_file+'_SV.xml', cim_url+'_SV.xml')\n", + "download_grid_data(cim_file+'_DI.xml', cim_url+'_DI.xml')\n", + "\n", + "psat_results_url = 'https://raw.githubusercontent.com/dpsim-simulator/reference-results/master/PSAT/WSCC-9bus/d_009_fault_dpsim_4th_order.out'\n", + "psat_results_file = 'd_009_fault_dpsim_4th_order.out'\n", + "urllib.request.urlretrieve(psat_results_url, psat_results_file) " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Run Simulation" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "for order_idx in range(num_orders):\n", + " sim = subprocess.Popen([path_exec+name_exec, '--name', sim_names_list[order_idx], '--timestep', str(timestep), '--duration', str(duration), cim_file +'_DI.xml', cim_file +'_EQ.xml', cim_file +'_SV.xml', cim_file +'_TP.xml', '--option', order_options_list[order_idx]], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)\n", + " print(sim.communicate()[0].decode())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Simulation" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "ts_dpsim = []\n", + "for order_idx in range(num_orders):\n", + " path = 'logs/' + sim_names_list[order_idx] + '/'\n", + " logName = sim_names_list[order_idx]\n", + " logFilename = path + logName + '.csv'\n", + " print(logFilename)\n", + " ts_dpsim.append(rt.read_timeseries_dpsim(logFilename))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Validation with PSAT" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "syngen_power_name_dpsim_list = ['GEN1.Te', 'GEN2.Te', 'GEN3.Te']\n", + "syngen_power_name_psat_list = ['p_Syn_1', 'p_Syn_2', 'p_Syn_3']\n", + "\n", + "syngen_omega_name_dpsim_list = ['GEN1.omega', 'GEN2.omega', 'GEN3.omega']\n", + "syngen_omega_name_psat_list = ['omega_Syn_1', 'omega_Syn_2', 'omega_Syn_3']\n", + "\n", + "syngen_delta_name_dpsim_list = ['GEN1.delta', 'GEN2.delta', 'GEN3.delta']\n", + "syngen_delta_name_psat_list = ['delta_Syn_1', 'delta_Syn_2', 'delta_Syn_3']\n", + "\n", + "bus_volt_name_dpsim_list = ['BUS1.V', 'BUS2.V', 'BUS3.V', 'BUS4.V', 'BUS5.V', 'BUS6.V', 'BUS7.V', 'BUS8.V', 'BUS9.V']\n", + "bus_volt_name_psat_list = ['V_Bus 1', 'V_Bus 2', 'V_Bus 3', 'V_Bus 4', 'V_Bus 5', 'V_Bus 6', 'V_Bus 7', 'V_Bus 8', 'V_Bus 9']\n", + "bus_angle_name_psat_list = ['theta_Bus 1', 'theta_Bus 2', 'theta_Bus 3', 'theta_Bus 4', 'theta_Bus 5', 'theta_Bus 6', 'theta_Bus 7', 'theta_Bus 8', 'theta_Bus 9']\n", + "\n", + "timeseries_names_psat = syngen_power_name_psat_list+syngen_omega_name_psat_list+syngen_delta_name_psat_list+bus_volt_name_psat_list+bus_angle_name_psat_list\n", + "\n", + "ts_psat = []\n", + "for order_idx in range(num_orders):\n", + " ts_psat.append(rt.read_timeseries_PSAT(psat_results_file, timeseries_names_psat))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Rotor speeds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "for order_idx in range(num_orders):\n", + " plt.figure(figsize=(12,9))\n", + " #plt.subplot(num_orders,1,order_idx+1)\n", + " for syngen_omega_name_dpsim in syngen_omega_name_dpsim_list:\n", + " plt.plot(ts_dpsim[order_idx][syngen_omega_name_dpsim].time, ts_dpsim[order_idx][syngen_omega_name_dpsim].values, label=syngen_omega_name_dpsim+', '+order_names_list[order_idx]+' (dpsim)')\n", + " for syngen_omega_name_psat in syngen_omega_name_psat_list:\n", + " plt.plot(ts_psat[order_idx][syngen_omega_name_psat].time, ts_psat[order_idx][syngen_omega_name_psat].values, label=syngen_omega_name_psat+', '+order_names_list[order_idx]+' (psat)', linestyle='--')\n", + " \n", + " #plt.ylim([0.99,1.02])\n", + " plt.xlim(view_time_interval)\n", + " \n", + " plt.xlabel('time (s)')\n", + " plt.ylabel('mechanical speed (p.u)')\n", + " plt.legend()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Rotor angle" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "plt.figure(figsize=(12,9))\n", + "for order_idx in range(num_orders):\n", + " plt.subplot(num_orders,1,order_idx+1)\n", + " for syngen_delta_name_dpsim in syngen_delta_name_dpsim_list:\n", + " plt.plot(ts_dpsim[order_idx][syngen_delta_name_dpsim].time, ts_dpsim[order_idx][syngen_delta_name_dpsim].values/np.pi*180, label=syngen_delta_name_dpsim+', '+order_names_list[order_idx]+' (dpsim)')\n", + " for syngen_delta_name_psat in syngen_delta_name_psat_list:\n", + " plt.plot(ts_psat[order_idx][syngen_delta_name_psat].time, ts_psat[order_idx][syngen_delta_name_psat].values/np.pi*180, label=syngen_delta_name_psat+', '+order_names_list[order_idx]+' (psat)', linestyle='--')\n", + " \n", + " plt.xlim(view_time_interval)\n", + " plt.xlabel('time (s)')\n", + " plt.ylabel('rotor angle (rad)')\n", + " plt.legend()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Rotor angles with reference angle of GEN1" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "for order_idx in range(num_orders):\n", + " plt.figure(figsize=(12,9))\n", + " #plt.subplot(num_orders,1,order_idx+1)\n", + " for syngen_delta_name_dpsim in syngen_delta_name_dpsim_list:\n", + " plt.plot(ts_dpsim[order_idx][syngen_delta_name_dpsim].time, (ts_dpsim[order_idx][syngen_delta_name_dpsim].values-ts_dpsim[order_idx]['GEN1.delta'].values+ts_dpsim[order_idx]['GEN1.delta'].values[0])/np.pi*180, label=syngen_delta_name_dpsim+', '+order_names_list[order_idx]+' (dpsim)')\n", + " for syngen_delta_name_psat in syngen_delta_name_psat_list:\n", + " plt.plot(ts_psat[order_idx][syngen_delta_name_psat].time, (ts_psat[order_idx][syngen_delta_name_psat].values-ts_psat[order_idx]['delta_Syn_3'].values+ts_psat[order_idx]['delta_Syn_3'].values[0])/np.pi*180, label=syngen_delta_name_psat+', '+order_names_list[order_idx]+' (psat)', linestyle='--')\n", + " \n", + " plt.xlim(view_time_interval)\n", + " plt.xlabel('time (s)')\n", + " plt.ylabel('rotor angle (rad)')\n", + " plt.legend()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Bus voltages" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "plt.figure(figsize=(12,12))\n", + "for order_idx in range(num_orders):\n", + " plt.subplot(num_orders,1,order_idx+1)\n", + " for bus_volt_name_dpsim in bus_volt_name_dpsim_list:\n", + " if bus_volt_name_dpsim == 'BUS1.V':\n", + " plt.plot(ts_dpsim[order_idx][bus_volt_name_dpsim].time, ts_dpsim[order_idx][bus_volt_name_dpsim].abs().values/16.5e3, label=bus_volt_name_dpsim +', '+order_names_list[order_idx]+' (dpsim)', color='C'+str(bus_volt_name_dpsim_list.index(bus_volt_name_dpsim)))\n", + " elif bus_volt_name_dpsim == 'BUS2.V':\n", + " plt.plot(ts_dpsim[order_idx][bus_volt_name_dpsim].time, ts_dpsim[order_idx][bus_volt_name_dpsim].abs().values/18e3, label=bus_volt_name_dpsim +', '+order_names_list[order_idx]+' (dpsim)', color='C'+str(bus_volt_name_dpsim_list.index(bus_volt_name_dpsim)))\n", + " elif bus_volt_name_dpsim == 'BUS3.V':\n", + " plt.plot(ts_dpsim[order_idx][bus_volt_name_dpsim].time, ts_dpsim[order_idx][bus_volt_name_dpsim].abs().values/13.8e3, label=bus_volt_name_dpsim +', '+order_names_list[order_idx]+' (dpsim)', color='C'+str(bus_volt_name_dpsim_list.index(bus_volt_name_dpsim)))\n", + " else:\n", + " plt.plot(ts_dpsim[order_idx][bus_volt_name_dpsim].time, ts_dpsim[order_idx][bus_volt_name_dpsim].abs().values/230e3, label=bus_volt_name_dpsim +', '+order_names_list[order_idx]+' (dpsim)', color='C'+str(bus_volt_name_dpsim_list.index(bus_volt_name_dpsim)))\n", + " for bus_volt_name_psat in bus_volt_name_psat_list:\n", + " plt.plot(ts_psat[order_idx][bus_volt_name_psat].time, ts_psat[order_idx][bus_volt_name_psat].values, label=bus_volt_name_psat +', '+order_names_list[order_idx]+' (psat)', linestyle='--', color='C'+str(bus_volt_name_psat_list.index(bus_volt_name_psat)))\n", + " \n", + " plt.xlim(view_time_interval)\n", + " plt.xlabel('time (s)')\n", + " plt.ylabel('voltage (p.u.)')\n", + " plt.legend()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Bus angles" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "plt.figure(figsize=(12,12))\n", + "for order_idx in range(num_orders):\n", + " plt.subplot(num_orders,1,order_idx+1)\n", + " for bus_volt_name_dpsim in bus_volt_name_dpsim_list:\n", + " plt.plot(ts_dpsim[order_idx][bus_volt_name_dpsim].time, ts_dpsim[order_idx][bus_volt_name_dpsim].phase().values/180*np.pi-ts_dpsim[order_idx]['BUS1.V'].phase().values/180*np.pi, label=bus_volt_name_dpsim +', '+order_names_list[order_idx]+' (dpsim)', color='C'+str(bus_volt_name_dpsim_list.index(bus_volt_name_dpsim)))\n", + " for bus_angle_name_psat in bus_angle_name_psat_list:\n", + " plt.plot(ts_psat[order_idx][bus_angle_name_psat].time, ts_psat[order_idx][bus_angle_name_psat].values, label=bus_angle_name_psat +', '+order_names_list[order_idx]+' (psat)', linestyle='--', color='C'+str(bus_angle_name_psat_list.index(bus_angle_name_psat)))\n", + " \n", + " plt.xlim(view_time_interval)\n", + " plt.xlabel('time (s)')\n", + " plt.ylabel('angle (rad)')\n", + " plt.legend()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## SG active power" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "for order_idx in range(num_orders):\n", + " plt.figure(figsize=(12,9))\n", + " #plt.subplot(num_orders,1,order_idx+1)\n", + " for syngen_power_name_dpsim in syngen_power_name_dpsim_list:\n", + " plt.plot(ts_dpsim[order_idx][syngen_power_name_dpsim].time, ts_dpsim[order_idx][syngen_power_name_dpsim].values, label=syngen_power_name_dpsim+', '+order_names_list[order_idx]+' (dpsim)')\n", + " for syngen_power_name_psat in syngen_power_name_psat_list:\n", + " plt.plot(ts_psat[order_idx][syngen_power_name_psat].time, ts_psat[order_idx][syngen_power_name_psat].values, label=syngen_power_name_psat+', '+order_names_list[order_idx]+' (psat)', linestyle='--')\n", + " \n", + " plt.xlim(view_time_interval)\n", + " plt.xlabel('time (s)')\n", + " plt.ylabel('torque (p.u)')\n", + " plt.legend()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Errors and Assertions" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "for order_idx in range(num_orders):\n", + " print('{} order:'.format(order_names_list[order_idx]))\n", + " \n", + " rmse_gen_1 = ts_psat[order_idx]['p_Syn_3'].rmse(ts_psat[order_idx]['p_Syn_3'],ts_dpsim[order_idx]['GEN1.Te'])\n", + " print('{}: {}'.format('GEN1',rmse_gen_1))\n", + " assert(rmse_gen_1 < 1e-2)\n", + " \n", + " rmse_gen_2 = ts_psat[order_idx]['p_Syn_1'].rmse(ts_psat[order_idx]['p_Syn_1'],ts_dpsim[order_idx]['GEN2.Te'])\n", + " print('{}: {}'.format('GEN2',rmse_gen_2))\n", + " assert(rmse_gen_2 < 1e-2)\n", + " \n", + " rmse_gen_3 = ts_psat[order_idx]['p_Syn_2'].rmse(ts_psat[order_idx]['p_Syn_2'],ts_dpsim[order_idx]['GEN3.Te'])\n", + " print('{}: {}'.format('GEN3',rmse_gen_3))\n", + " assert(rmse_gen_3 < 1e-2)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.13" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/examples/Notebooks/Grids/SP_WSCC_9-bus_dyn_switch.ipynb b/examples/Notebooks/Grids/SP_WSCC9bus_SGTrStab_Switch_PSAT_Validation.ipynb similarity index 96% rename from examples/Notebooks/Grids/SP_WSCC_9-bus_dyn_switch.ipynb rename to examples/Notebooks/Grids/SP_WSCC9bus_SGTrStab_Switch_PSAT_Validation.ipynb index 52d3ed9e2b..16099921b3 100644 --- a/examples/Notebooks/Grids/SP_WSCC_9-bus_dyn_switch.ipynb +++ b/examples/Notebooks/Grids/SP_WSCC9bus_SGTrStab_Switch_PSAT_Validation.ipynb @@ -4,7 +4,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Dynamic WSCC 9-bus System with Switch" + "# SP Simulation of WSCC 9-bus System with Transient Stability Synchronous Generator and PSAT Validation" ] }, { @@ -21,8 +21,8 @@ " content = requests.get(url, stream=True).content\n", " out_file.write(content)\n", "\n", - "url = 'https://git.rwth-aachen.de/acs/public/grid-data/cim-grid-data/-/raw/master/WSCC-09/WSCC-09_RX_Dyn/WSCC-09_RX'\n", - "filename = 'WSCC-09'\n", + "url = 'https://raw.githubusercontent.com/dpsim-simulator/cim-grid-data/master/WSCC-09/WSCC-09_RX_Dyn/WSCC-09_RX'\n", + "filename = 'WSCC-09_RX_Dyn_Second'\n", "download_grid_data(filename+'_EQ.xml', url+'_EQ.xml')\n", "download_grid_data(filename+'_TP.xml', url+'_TP.xml')\n", "download_grid_data(filename+'_SV.xml', url+'_SV.xml')\n", @@ -226,8 +226,8 @@ "if not os.path.exists('reference-results'):\n", " os.mkdir('reference-results')\n", "\n", - "url = 'https://raw.githubusercontent.com/dpsim-simulator/reference-results/master/PSAT/WSCC-9bus/d_009_fault_dpsim_01.out'\n", - "local_file = 'reference-results/d_009_fault_dpsim_01.out'\n", + "url = 'https://raw.githubusercontent.com/dpsim-simulator/reference-results/master/PSAT/WSCC-9bus/d_009_fault_dpsim_2nd_order.out'\n", + "local_file = 'reference-results/d_009_fault_dpsim_2nd_order.out'\n", "urllib.request.urlretrieve(url, local_file) \n", "\n", "syngen_power_name_dpsim_list = ['P_elec_1', 'P_elec_2', 'P_elec_3']\n", @@ -397,7 +397,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3.9.2 64-bit", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, @@ -411,7 +411,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.2" + "version": "3.9.13" }, "vscode": { "interpreter": { diff --git a/examples/Notebooks/Performance/DP_WSCC9bus_SGReducedOrderVBR_validate_KLU_using_different_settings.ipynb b/examples/Notebooks/Performance/DP_WSCC9bus_SGReducedOrderVBR_validate_KLU_using_different_settings.ipynb index 9e503a7ac9..fc2a84fe8c 100644 --- a/examples/Notebooks/Performance/DP_WSCC9bus_SGReducedOrderVBR_validate_KLU_using_different_settings.ipynb +++ b/examples/Notebooks/Performance/DP_WSCC9bus_SGReducedOrderVBR_validate_KLU_using_different_settings.ipynb @@ -49,8 +49,9 @@ " content = requests.get(url, stream=True).content\n", " out_file.write(content)\n", "\n", - "filename = 'WSCC-09_RX'\n", - "url = 'https://raw.githubusercontent.com/dpsim-simulator/cim-grid-data/master/WSCC-09/WSCC-09_RX_Dyn_Full/WSCC-09_RX'\n", + "filename = 'WSCC-09_Dyn_Fourth'\n", + "\n", + "url = 'https://raw.githubusercontent.com/dpsim-simulator/cim-grid-data/master/WSCC-09/WSCC-09_Dyn_Fourth/WSCC-09_Dyn_Fourth'\n", "download_grid_data(filename+'_EQ.xml', url+'_EQ.xml')\n", "download_grid_data(filename+'_TP.xml', url+'_TP.xml')\n", "download_grid_data(filename+'_SV.xml', url+'_SV.xml')\n", @@ -115,7 +116,7 @@ " gen_pf = system_pf.component(comp.name())\n", " comp.get_terminal(index=0).set_power(- gen_pf.get_apparent_power())\n", " comp.scale_inertia_constant(intertia_scaling_factor)\n", - " comp.set_model_as_current_source(False)\n", + " comp.set_model_as_norton_source(False)\n", "\n", " logger = dpsimpy.Logger(sim_name)#, True, log_down_sampling)\n", " for node in sys.nodes:\n", @@ -213,7 +214,7 @@ "metadata": {}, "outputs": [], "source": [ - "tolerance = 1e-4\n", + "tolerance = 1.1e-3\n", "\n", "for entry in ts_dpsim_rv_amd:\n", " amd_values = ts_dpsim_rv_amd[entry].values\n", @@ -222,6 +223,8 @@ " # 2-norm of errors\n", " error_1 = np.linalg.norm(amd_values-amd_nv_values, ord=2)\n", " error_2 = np.linalg.norm(amd_ra_values-amd_values, ord=2)\n", + " print('RV Error for {} for AMD vs NV: {}'.format(entry,error_1))\n", + " print('RV Error for {} for AMD vs RA: {}'.format(entry,error_2))\n", " assert(error_1 < tolerance)\n", " assert(error_2 < tolerance)\n", "\n", @@ -232,9 +235,19 @@ " # 2-norm of errors\n", " error_1 = np.linalg.norm(amd_values-amd_nv_values, ord=2)\n", " error_2 = np.linalg.norm(amd_ra_values-amd_values, ord=2)\n", + " print('LV Error for {} for AMD vs NV: {}'.format(entry,error_1))\n", + " print('LV Error for {} for AMD vs RA: {}'.format(entry,error_2))\n", " assert(error_1 < tolerance)\n", " assert(error_2 < tolerance)" ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "dbec8b31-82e3-44fb-a3d0-9f4df6796dbe", + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { @@ -253,7 +266,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.10" + "version": "3.9.13" }, "vscode": { "interpreter": { diff --git a/examples/Notebooks/Performance/DP_WSCC9bus_SGReducedOrderVBR_validate_SparseLU_DenseLU_KLU_using_partial_refactorization.ipynb b/examples/Notebooks/Performance/DP_WSCC9bus_SGReducedOrderVBR_validate_SparseLU_DenseLU_KLU_using_partial_refactorization.ipynb index 0b05dcae70..8dd5943c29 100644 --- a/examples/Notebooks/Performance/DP_WSCC9bus_SGReducedOrderVBR_validate_SparseLU_DenseLU_KLU_using_partial_refactorization.ipynb +++ b/examples/Notebooks/Performance/DP_WSCC9bus_SGReducedOrderVBR_validate_SparseLU_DenseLU_KLU_using_partial_refactorization.ipynb @@ -50,8 +50,9 @@ " content = requests.get(url, stream=True).content\n", " out_file.write(content)\n", "\n", - "filename = 'WSCC-09_RX'\n", - "url = 'https://raw.githubusercontent.com/dpsim-simulator/cim-grid-data/master/WSCC-09/WSCC-09_RX_Dyn_Full/WSCC-09_RX'\n", + "filename = 'WSCC-09_Dyn_Fourth'\n", + "\n", + "url = 'https://raw.githubusercontent.com/dpsim-simulator/cim-grid-data/master/WSCC-09/WSCC-09_Dyn_Fourth/WSCC-09_Dyn_Fourth'\n", "download_grid_data(filename+'_EQ.xml', url+'_EQ.xml')\n", "download_grid_data(filename+'_TP.xml', url+'_TP.xml')\n", "download_grid_data(filename+'_SV.xml', url+'_SV.xml')\n", @@ -116,7 +117,7 @@ " gen_pf = system_pf.component(comp.name())\n", " comp.get_terminal(index=0).set_power(- gen_pf.get_apparent_power())\n", " comp.scale_inertia_constant(intertia_scaling_factor)\n", - " comp.set_model_as_current_source(False)\n", + " comp.set_model_as_norton_source(False)\n", "\n", " logger = dpsimpy.Logger(sim_name)#, True, log_down_sampling)\n", " for node in sys.nodes:\n", @@ -194,28 +195,90 @@ "metadata": {}, "outputs": [], "source": [ - "tolerance = 1e-4\n", + "relative_solver_error_tolerance = 4.31e-9\n", + "close_to_zero_tolerance = 1e-12\n", + "\n", + "maxerror = 0.0\n", + "maxerrorn = 0.0\n", "\n", "for entry in ts_dpsim_rv_sparse:\n", " sparse_values = ts_dpsim_rv_sparse[entry].values\n", " klu_values = ts_dpsim_rv_klu[entry].values\n", " denselu_values = ts_dpsim_rv_denselu[entry].values\n", - " # 2-norm of errors\n", - " error_1 = np.linalg.norm(sparse_values-klu_values, ord=2)\n", - " error_2 = np.linalg.norm(denselu_values-sparse_values, ord=2)\n", - " assert(error_1 < tolerance)\n", - " assert(error_2 < tolerance)\n", + " \n", + " N = len(klu_values)\n", + " \n", + " relativ_diff_sparse_klu = np.zeros(N, dtype=complex)\n", + " relativ_diff_sparse_denselu = np.zeros(N, dtype=complex)\n", + " \n", + " for i in range(0, N-1):\n", + " if abs(klu_values[i]) > close_to_zero_tolerance:\n", + " relativ_diff_sparse_klu[i] = (sparse_values[i]-klu_values[i])/klu_values[i]\n", + " relativ_diff_sparse_denselu[i] = (sparse_values[i]-denselu_values[i])/klu_values[i]\n", + " if relativ_diff_sparse_klu[i] > maxerror:\n", + " maxerror = relativ_diff_sparse_klu[i]\n", + " if relativ_diff_sparse_denselu[i] > maxerror:\n", + " maxerror = relativ_diff_sparse_denselu[i]\n", + " \n", + " error_1 = np.linalg.norm(relativ_diff_sparse_klu, ord=2)\n", + " error_2 = np.linalg.norm(relativ_diff_sparse_denselu, ord=2)\n", + " print('RV Entry {} Maximum Difference of SparseLU vs. KLU: {}'.format(entry,error_1))\n", + " print('RV Entry {} Maximum Difference of DenseLU vs. SparseLU: {}'.format(entry,error_2))\n", + " if error_1 > maxerrorn:\n", + " maxerrorn = error_1\n", + " if error_2 > maxerrorn:\n", + " maxerrorn = error_2\n", "\n", "for entry in ts_dpsim_lv_sparse:\n", " sparse_values = ts_dpsim_lv_sparse[entry].values\n", " klu_values = ts_dpsim_lv_klu[entry].values\n", " denselu_values = ts_dpsim_lv_denselu[entry].values\n", - " # 2-norm of errors\n", - " error_1 = np.linalg.norm(sparse_values-klu_values, ord=2)\n", - " error_2 = np.linalg.norm(denselu_values-sparse_values, ord=2)\n", - " assert(error_1 < tolerance)\n", - " assert(error_2 < tolerance)" + " \n", + " N = len(klu_values)\n", + " \n", + " relativ_diff_sparse_klu = np.zeros(N, dtype=complex)\n", + " relativ_diff_sparse_denselu = np.zeros(N, dtype=complex)\n", + " \n", + " for i in range(0, N-1):\n", + " if abs(klu_values[i]) > close_to_zero_tolerance:\n", + " relativ_diff_sparse_klu[i] = (sparse_values[i]-klu_values[i])/klu_values[i]\n", + " relativ_diff_sparse_denselu[i] = (sparse_values[i]-denselu_values[i])/klu_values[i]\n", + " if relativ_diff_sparse_klu[i] > maxerror:\n", + " maxerror = relativ_diff_sparse_klu[i]\n", + " if relativ_diff_sparse_denselu[i] > maxerror:\n", + " maxerror = relativ_diff_sparse_denselu[i]\n", + " \n", + " error_1 = np.linalg.norm(relativ_diff_sparse_klu, ord=2)\n", + " error_2 = np.linalg.norm(relativ_diff_sparse_denselu, ord=2)\n", + " print('LV Entry {} Maximum Difference of SparseLU vs. KLU: {}'.format(entry,error_1))\n", + " print('LV Entry {} Maximum Difference of DenseLU vs. SparseLU: {}'.format(entry,error_2))\n", + " if error_1 > maxerrorn:\n", + " maxerrorn = error_1\n", + " if error_2 > maxerrorn:\n", + " maxerrorn = error_2\n", + " \n", + "print('Maximum error in all values: {}'.format(maxerror))\n", + "print('Maximum norm of errors: {}'.format(maxerrorn))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f3371595-8a85-4923-a6c3-3a828720c62c", + "metadata": {}, + "outputs": [], + "source": [ + "assert(maxerror < relative_solver_error_tolerance)\n", + "assert(maxerrorn < relative_solver_error_tolerance)" ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4734f42f-8050-401f-bd4e-621b6bcb2ffc", + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { @@ -234,7 +297,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.10" + "version": "3.9.13" }, "vscode": { "interpreter": { diff --git a/examples/Notebooks/Performance/WSCC_9bus_mult_coupled_validate_SparseLU_DenseLU_KLU.ipynb b/examples/Notebooks/Performance/WSCC_9bus_mult_coupled_validate_SparseLU_DenseLU_KLU.ipynb index 12e4062bcb..f256fb87e2 100644 --- a/examples/Notebooks/Performance/WSCC_9bus_mult_coupled_validate_SparseLU_DenseLU_KLU.ipynb +++ b/examples/Notebooks/Performance/WSCC_9bus_mult_coupled_validate_SparseLU_DenseLU_KLU.ipynb @@ -33,7 +33,7 @@ " out_file.write(content)\n", "\n", "url = 'https://raw.githubusercontent.com/dpsim-simulator/cim-grid-data/master/WSCC-09/WSCC-09_RX/WSCC-09_RX'\n", - "filename = 'WSCC-09_RX'\n", + "filename = 'WSCC-09_RX_WithoutSyngenParams'\n", "download_grid_data(filename+'_EQ.xml', url+'_EQ.xml')\n", "download_grid_data(filename+'_TP.xml', url+'_TP.xml')\n", "download_grid_data(filename+'_SV.xml', url+'_SV.xml')\n", @@ -165,7 +165,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.10" + "version": "3.9.13" } }, "nbformat": 4,